Skip to content

Commit

Permalink
Merge branch 'Qiskit:main' into move-target
Browse files Browse the repository at this point in the history
  • Loading branch information
raynelfss authored Apr 10, 2024
2 parents c3d79ff + 01e9a7c commit 5e2d100
Show file tree
Hide file tree
Showing 18 changed files with 307 additions and 147 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Qiskit

[![License](https://img.shields.io/github/license/Qiskit/qiskit.svg?)](https://opensource.org/licenses/Apache-2.0) <!--- long-description-skip-begin -->
[![Release](https://img.shields.io/github/release/Qiskit/qiskit.svg)](https://github.com/Qiskit/qiskit/releases)
[![Current Release](https://img.shields.io/github/release/Qiskit/qiskit.svg?logo=Qiskit)](https://github.com/Qiskit/qiskit/releases)
[![Extended Support Release](https://img.shields.io/github/v/release/Qiskit/qiskit?sort=semver&filter=0.*&logo=Qiskit&label=extended%20support)](https://github.com/Qiskit/qiskit/releases?q=tag%3A0)
[![Downloads](https://img.shields.io/pypi/dm/qiskit.svg)](https://pypi.org/project/qiskit/)
[![Coverage Status](https://coveralls.io/repos/github/Qiskit/qiskit/badge.svg?branch=main)](https://coveralls.io/github/Qiskit/qiskit?branch=main)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/qiskit)
Expand Down
2 changes: 1 addition & 1 deletion crates/accelerate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ numpy = "0.21.0"
rand = "0.8"
rand_pcg = "0.3"
rand_distr = "0.4.3"
ahash = "0.8.6"
ahash = "0.8.11"
num-traits = "0.2"
num-complex = "0.4"
num-bigint = "0.4"
Expand Down
6 changes: 4 additions & 2 deletions qiskit/circuit/library/blueprintcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ def _append(self, instruction, _qargs=None, _cargs=None):
self._build()
return super()._append(instruction, _qargs, _cargs)

def compose(self, other, qubits=None, clbits=None, front=False, inplace=False, wrap=False):
def compose(
self, other, qubits=None, clbits=None, front=False, inplace=False, wrap=False, *, copy=True
):
if not self._is_built:
self._build()
return super().compose(other, qubits, clbits, front, inplace, wrap)
return super().compose(other, qubits, clbits, front, inplace, wrap, copy=copy)

def inverse(self, annotated: bool = False):
if not self._is_built:
Expand Down
5 changes: 4 additions & 1 deletion qiskit/circuit/library/generalized_gates/unitary.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def __init__(
data: numpy.ndarray | Gate | BaseOperator,
label: str | None = None,
check_input: bool = True,
*,
num_qubits: int | None = None,
) -> None:
"""Create a gate from a numeric unitary matrix.
Expand All @@ -81,6 +83,7 @@ def __init__(
be skipped. This should only ever be used if you know the
input is unitary, setting this to ``False`` and passing in
a non-unitary matrix will result unexpected behavior and errors.
num_qubits: If given, the number of qubits in the matrix. If not given, it is inferred.
Raises:
ValueError: If input data is not an N-qubit unitary operator.
Expand All @@ -97,7 +100,7 @@ def __init__(
# Convert to numpy array in case not already an array
data = numpy.asarray(data, dtype=complex)
input_dim, output_dim = data.shape
num_qubits = int(math.log2(input_dim))
num_qubits = num_qubits if num_qubits is not None else int(math.log2(input_dim))
if check_input:
# Check input is unitary
if not is_unitary_matrix(data):
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/phase_oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def synthesizer(boolean_expression):

super().__init__(oracle.num_qubits, name="Phase Oracle")

self.compose(oracle, inplace=True)
self.compose(oracle, inplace=True, copy=False)

def evaluate_bitstring(self, bitstring: str) -> bool:
"""Evaluate the oracle on a bitstring.
Expand Down
77 changes: 39 additions & 38 deletions qiskit/circuit/library/quantum_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
from typing import Optional, Union

import numpy as np
from qiskit.quantum_info.random import random_unitary
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library.generalized_gates.permutation import Permutation
from qiskit.circuit import QuantumCircuit, CircuitInstruction
from qiskit.circuit.library.generalized_gates import PermutationGate, UnitaryGate


class QuantumVolume(QuantumCircuit):
Expand Down Expand Up @@ -60,6 +59,8 @@ def __init__(
depth: Optional[int] = None,
seed: Optional[Union[int, np.random.Generator]] = None,
classical_permutation: bool = True,
*,
flatten: bool = False,
) -> None:
"""Create quantum volume model circuit of size num_qubits x depth.
Expand All @@ -69,46 +70,46 @@ def __init__(
seed: Random number generator or generator seed.
classical_permutation: use classical permutations at every layer,
rather than quantum.
flatten: If ``False`` (the default), construct a circuit that contains a single
instruction, which in turn has the actual volume structure. If ``True``, construct
the volume structure directly.
"""
# Initialize RNG
if seed is None:
rng_set = np.random.default_rng()
seed = rng_set.integers(low=1, high=1000)
if isinstance(seed, np.random.Generator):
rng = seed
else:
rng = np.random.default_rng(seed)
import scipy.stats

# Parameters
depth = depth or num_qubits # how many layers of SU(4)
width = int(np.floor(num_qubits / 2)) # how many SU(4)s fit in each layer
name = "quantum_volume_" + str([num_qubits, depth, seed]).replace(" ", "")
width = num_qubits // 2 # how many SU(4)s fit in each layer
rng = seed if isinstance(seed, np.random.Generator) else np.random.default_rng(seed)
if seed is None:
# Get the internal entropy used to seed the default RNG, if no seed was given. This
# stays in the output name, so effectively stores a way of regenerating the circuit.
# This is just best-effort only, for backwards compatibility, and isn't critical (if
# someone needs full reproducibility, they should be manually controlling the seeding).
seed = getattr(getattr(rng.bit_generator, "seed_seq", None), "entropy", None)

# Generator random unitary seeds in advance.
# Note that this means we are constructing multiple new generator
# objects from low-entropy integer seeds rather than pass the shared
# generator object to the random_unitary function. This is done so
# that we can use the integer seed as a label for the generated gates.
unitary_seeds = rng.integers(low=1, high=1000, size=[depth, width])
super().__init__(
num_qubits, name="quantum_volume_" + str([num_qubits, depth, seed]).replace(" ", "")
)
base = self if flatten else QuantumCircuit(num_qubits, name=self.name)

# For each layer, generate a permutation of qubits
# Then generate and apply a Haar-random SU(4) to each pair
circuit = QuantumCircuit(num_qubits, name=name)
perm_0 = list(range(num_qubits))
for d in range(depth):
perm = rng.permutation(perm_0)
if not classical_permutation:
layer_perm = Permutation(num_qubits, perm)
circuit.compose(layer_perm, inplace=True)
for w in range(width):
seed_u = unitary_seeds[d][w]
su4 = random_unitary(4, seed=seed_u).to_instruction()
su4.label = "su4_" + str(seed_u)
if classical_permutation:
physical_qubits = int(perm[2 * w]), int(perm[2 * w + 1])
circuit.compose(su4, [physical_qubits[0], physical_qubits[1]], inplace=True)
else:
circuit.compose(su4, [2 * w, 2 * w + 1], inplace=True)

super().__init__(*circuit.qregs, name=circuit.name)
self.compose(circuit.to_instruction(), qubits=self.qubits, inplace=True)
unitaries = scipy.stats.unitary_group.rvs(4, depth * width, rng).reshape(depth, width, 4, 4)
qubits = tuple(base.qubits)
for row in unitaries:
perm = rng.permutation(num_qubits)
if classical_permutation:
for w, unitary in enumerate(row):
gate = UnitaryGate(unitary, check_input=False, num_qubits=2)
qubit = 2 * w
base._append(
CircuitInstruction(gate, (qubits[perm[qubit]], qubits[perm[qubit + 1]]))
)
else:
base._append(CircuitInstruction(PermutationGate(perm), qubits))
for w, unitary in enumerate(row):
gate = UnitaryGate(unitary, check_input=False, num_qubits=2)
qubit = 2 * w
base._append(CircuitInstruction(gate, qubits[qubit : qubit + 2]))
if not flatten:
self._append(CircuitInstruction(base.to_instruction(), tuple(self.qubits)))
Loading

0 comments on commit 5e2d100

Please sign in to comment.