qiskit 1.2.0__cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl → 1.2.1__cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qiskit/VERSION.txt +1 -1
- qiskit/_accelerate.abi3.so +0 -0
- qiskit/circuit/__init__.py +10 -0
- qiskit/circuit/library/basis_change/qft.py +3 -1
- qiskit/circuit/library/data_preparation/initializer.py +5 -2
- qiskit/circuit/library/data_preparation/state_preparation.py +2 -2
- qiskit/circuit/library/standard_gates/__init__.py +32 -25
- qiskit/circuit/quantumcircuit.py +39 -14
- qiskit/pulse/library/symbolic_pulses.py +4 -3
- qiskit/quantum_info/operators/operator.py +24 -0
- qiskit/synthesis/clifford/clifford_decompose_bm.py +1 -1
- qiskit/synthesis/clifford/clifford_decompose_greedy.py +1 -1
- qiskit/synthesis/linear/cnot_synth.py +1 -1
- qiskit/synthesis/one_qubit/one_qubit_decompose.py +2 -1
- qiskit/synthesis/permutation/permutation_full.py +2 -2
- qiskit/synthesis/permutation/permutation_lnn.py +3 -1
- qiskit/synthesis/two_qubit/two_qubit_decompose.py +2 -2
- qiskit/transpiler/instruction_durations.py +4 -0
- qiskit/transpiler/passes/__init__.py +2 -0
- qiskit/transpiler/passes/optimization/__init__.py +1 -0
- qiskit/transpiler/passes/optimization/hoare_opt.py +12 -8
- qiskit/transpiler/passes/optimization/split_2q_unitaries.py +16 -20
- qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +9 -0
- qiskit/utils/optionals.py +173 -150
- qiskit/visualization/gate_map.py +28 -6
- {qiskit-1.2.0.dist-info → qiskit-1.2.1.dist-info}/METADATA +1 -1
- {qiskit-1.2.0.dist-info → qiskit-1.2.1.dist-info}/RECORD +688 -688
- {qiskit-1.2.0.dist-info → qiskit-1.2.1.dist-info}/LICENSE.txt +0 -0
- {qiskit-1.2.0.dist-info → qiskit-1.2.1.dist-info}/WHEEL +0 -0
- {qiskit-1.2.0.dist-info → qiskit-1.2.1.dist-info}/entry_points.txt +0 -0
- {qiskit-1.2.0.dist-info → qiskit-1.2.1.dist-info}/top_level.txt +0 -0
qiskit/VERSION.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.1
|
qiskit/_accelerate.abi3.so
CHANGED
Binary file
|
qiskit/circuit/__init__.py
CHANGED
@@ -580,6 +580,16 @@ The :class:`Store` instruction is particularly special, in that it allows writin
|
|
580
580
|
:class:`Qubit` nor :class:`Clbit` operands, but has an explicit :attr:`~Store.lvalue` and
|
581
581
|
:attr:`~Store.rvalue`.
|
582
582
|
|
583
|
+
For example, to determine the parity of a bitstring ``cr`` and store it in another register ``creg``,
|
584
|
+
the :class:`Store` instruction can be used in the following way::
|
585
|
+
|
586
|
+
parity = expr.lift(cr[0])
|
587
|
+
for i in range(1,n):
|
588
|
+
parity = expr.bit_xor(cr[i], parity)
|
589
|
+
qc.store(creg[0], parity)
|
590
|
+
|
591
|
+
|
592
|
+
|
583
593
|
.. autoclass:: Store
|
584
594
|
:show-inheritance:
|
585
595
|
:members:
|
@@ -315,8 +315,10 @@ class QFTGate(Gate):
|
|
315
315
|
"""
|
316
316
|
super().__init__(name="qft", num_qubits=num_qubits, params=[])
|
317
317
|
|
318
|
-
def __array__(self, dtype=complex):
|
318
|
+
def __array__(self, dtype=complex, copy=None):
|
319
319
|
"""Return a numpy array for the QFTGate."""
|
320
|
+
if copy is False:
|
321
|
+
raise ValueError("unable to avoid copy while creating an array as requested")
|
320
322
|
n = self.num_qubits
|
321
323
|
nums = np.arange(2**n)
|
322
324
|
outer = np.outer(nums, nums)
|
@@ -21,6 +21,7 @@ import typing
|
|
21
21
|
from qiskit.circuit.quantumcircuit import QuantumCircuit
|
22
22
|
from qiskit.circuit.quantumregister import QuantumRegister
|
23
23
|
from qiskit.circuit.instruction import Instruction
|
24
|
+
from qiskit.circuit.library.generalized_gates import Isometry
|
24
25
|
from .state_preparation import StatePreparation
|
25
26
|
|
26
27
|
if typing.TYPE_CHECKING:
|
@@ -86,9 +87,11 @@ class Initialize(Instruction):
|
|
86
87
|
"""Call to create a circuit with gates that take the desired vector to zero.
|
87
88
|
|
88
89
|
Returns:
|
89
|
-
|
90
|
+
QuantumCircuit: circuit to take ``self.params`` vector to :math:`|{00\\ldots0}\\rangle`
|
90
91
|
"""
|
91
|
-
|
92
|
+
|
93
|
+
isom = Isometry(self.params, 0, 0)
|
94
|
+
return isom._gates_to_uncompute()
|
92
95
|
|
93
96
|
@property
|
94
97
|
def params(self):
|
@@ -173,8 +173,8 @@ class StatePreparation(Gate):
|
|
173
173
|
q = QuantumRegister(self.num_qubits, "q")
|
174
174
|
initialize_circuit = QuantumCircuit(q, name="init_def")
|
175
175
|
|
176
|
-
isom = Isometry(self.
|
177
|
-
initialize_circuit.
|
176
|
+
isom = Isometry(self.params, 0, 0)
|
177
|
+
initialize_circuit.compose(isom.definition, copy=False, inplace=True)
|
178
178
|
|
179
179
|
# invert the circuit to create the desired vector from zero (assuming
|
180
180
|
# the qubits are in the zero state)
|
@@ -54,6 +54,13 @@ def get_standard_gate_name_mapping():
|
|
54
54
|
from qiskit.circuit.delay import Delay
|
55
55
|
from qiskit.circuit.reset import Reset
|
56
56
|
|
57
|
+
lambda_ = Parameter("λ")
|
58
|
+
theta = Parameter("ϴ")
|
59
|
+
phi = Parameter("φ")
|
60
|
+
gamma = Parameter("γ")
|
61
|
+
beta = Parameter("β")
|
62
|
+
time = Parameter("t")
|
63
|
+
|
57
64
|
# Standard gates library mapping, multicontrolled gates not included since they're
|
58
65
|
# variable width
|
59
66
|
gates = [
|
@@ -61,38 +68,37 @@ def get_standard_gate_name_mapping():
|
|
61
68
|
SXGate(),
|
62
69
|
XGate(),
|
63
70
|
CXGate(),
|
64
|
-
RZGate(
|
65
|
-
RGate(
|
66
|
-
Reset(),
|
71
|
+
RZGate(lambda_),
|
72
|
+
RGate(theta, phi),
|
67
73
|
C3SXGate(),
|
68
74
|
CCXGate(),
|
69
75
|
DCXGate(),
|
70
76
|
CHGate(),
|
71
|
-
CPhaseGate(
|
72
|
-
CRXGate(
|
73
|
-
CRYGate(
|
74
|
-
CRZGate(
|
77
|
+
CPhaseGate(theta),
|
78
|
+
CRXGate(theta),
|
79
|
+
CRYGate(theta),
|
80
|
+
CRZGate(theta),
|
75
81
|
CSwapGate(),
|
76
82
|
CSXGate(),
|
77
|
-
CUGate(
|
78
|
-
CU1Gate(
|
79
|
-
CU3Gate(
|
83
|
+
CUGate(theta, phi, lambda_, gamma),
|
84
|
+
CU1Gate(lambda_),
|
85
|
+
CU3Gate(theta, phi, lambda_),
|
80
86
|
CYGate(),
|
81
87
|
CZGate(),
|
82
88
|
CCZGate(),
|
83
|
-
GlobalPhaseGate(
|
89
|
+
GlobalPhaseGate(theta),
|
84
90
|
HGate(),
|
85
|
-
PhaseGate(
|
91
|
+
PhaseGate(theta),
|
86
92
|
RCCXGate(),
|
87
93
|
RC3XGate(),
|
88
|
-
RXGate(
|
89
|
-
RXXGate(
|
90
|
-
RYGate(
|
91
|
-
RYYGate(
|
92
|
-
RZZGate(
|
93
|
-
RZXGate(
|
94
|
-
XXMinusYYGate(
|
95
|
-
XXPlusYYGate(
|
94
|
+
RXGate(theta),
|
95
|
+
RXXGate(theta),
|
96
|
+
RYGate(theta),
|
97
|
+
RYYGate(theta),
|
98
|
+
RZZGate(theta),
|
99
|
+
RZXGate(theta),
|
100
|
+
XXMinusYYGate(theta, beta),
|
101
|
+
XXPlusYYGate(theta, beta),
|
96
102
|
ECRGate(),
|
97
103
|
SGate(),
|
98
104
|
SdgGate(),
|
@@ -103,13 +109,14 @@ def get_standard_gate_name_mapping():
|
|
103
109
|
SXdgGate(),
|
104
110
|
TGate(),
|
105
111
|
TdgGate(),
|
106
|
-
UGate(
|
107
|
-
U1Gate(
|
108
|
-
U2Gate(
|
109
|
-
U3Gate(
|
112
|
+
UGate(theta, phi, lambda_),
|
113
|
+
U1Gate(lambda_),
|
114
|
+
U2Gate(phi, lambda_),
|
115
|
+
U3Gate(theta, phi, lambda_),
|
110
116
|
YGate(),
|
111
117
|
ZGate(),
|
112
|
-
Delay(
|
118
|
+
Delay(time),
|
119
|
+
Reset(),
|
113
120
|
Measure(),
|
114
121
|
]
|
115
122
|
name_mapping = {gate.name: gate for gate in gates}
|
qiskit/circuit/quantumcircuit.py
CHANGED
@@ -1152,17 +1152,41 @@ class QuantumCircuit:
|
|
1152
1152
|
"""The unit that :attr:`duration` is specified in."""
|
1153
1153
|
self.metadata = {} if metadata is None else metadata
|
1154
1154
|
"""Arbitrary user-defined metadata for the circuit.
|
1155
|
-
|
1155
|
+
|
1156
1156
|
Qiskit will not examine the content of this mapping, but it will pass it through the
|
1157
1157
|
transpiler and reattach it to the output, so you can track your own metadata."""
|
1158
1158
|
|
1159
1159
|
@classmethod
|
1160
|
-
def _from_circuit_data(cls, data: CircuitData) -> typing.Self:
|
1160
|
+
def _from_circuit_data(cls, data: CircuitData, add_regs: bool = False) -> typing.Self:
|
1161
1161
|
"""A private constructor from rust space circuit data."""
|
1162
1162
|
out = QuantumCircuit()
|
1163
|
+
|
1164
|
+
if data.num_qubits > 0:
|
1165
|
+
if add_regs:
|
1166
|
+
qr = QuantumRegister(name="q", bits=data.qubits)
|
1167
|
+
out.qregs = [qr]
|
1168
|
+
out._qubit_indices = {
|
1169
|
+
bit: BitLocations(index, [(qr, index)]) for index, bit in enumerate(data.qubits)
|
1170
|
+
}
|
1171
|
+
else:
|
1172
|
+
out._qubit_indices = {
|
1173
|
+
bit: BitLocations(index, []) for index, bit in enumerate(data.qubits)
|
1174
|
+
}
|
1175
|
+
|
1176
|
+
if data.num_clbits > 0:
|
1177
|
+
if add_regs:
|
1178
|
+
cr = ClassicalRegister(name="c", bits=data.clbits)
|
1179
|
+
out.cregs = [cr]
|
1180
|
+
out._clbit_indices = {
|
1181
|
+
bit: BitLocations(index, [(cr, index)]) for index, bit in enumerate(data.clbits)
|
1182
|
+
}
|
1183
|
+
else:
|
1184
|
+
out._clbit_indices = {
|
1185
|
+
bit: BitLocations(index, []) for index, bit in enumerate(data.clbits)
|
1186
|
+
}
|
1187
|
+
|
1163
1188
|
out._data = data
|
1164
|
-
|
1165
|
-
out._clbit_indices = {bit: BitLocations(index, []) for index, bit in enumerate(data.clbits)}
|
1189
|
+
|
1166
1190
|
return out
|
1167
1191
|
|
1168
1192
|
@staticmethod
|
@@ -3013,16 +3037,7 @@ class QuantumCircuit:
|
|
3013
3037
|
self._ancillas.append(bit)
|
3014
3038
|
|
3015
3039
|
if isinstance(register, QuantumRegister):
|
3016
|
-
self.
|
3017
|
-
|
3018
|
-
for idx, bit in enumerate(register):
|
3019
|
-
if bit in self._qubit_indices:
|
3020
|
-
self._qubit_indices[bit].registers.append((register, idx))
|
3021
|
-
else:
|
3022
|
-
self._data.add_qubit(bit)
|
3023
|
-
self._qubit_indices[bit] = BitLocations(
|
3024
|
-
self._data.num_qubits - 1, [(register, idx)]
|
3025
|
-
)
|
3040
|
+
self._add_qreg(register)
|
3026
3041
|
|
3027
3042
|
elif isinstance(register, ClassicalRegister):
|
3028
3043
|
self.cregs.append(register)
|
@@ -3041,6 +3056,16 @@ class QuantumCircuit:
|
|
3041
3056
|
else:
|
3042
3057
|
raise CircuitError("expected a register")
|
3043
3058
|
|
3059
|
+
def _add_qreg(self, qreg: QuantumRegister) -> None:
|
3060
|
+
self.qregs.append(qreg)
|
3061
|
+
|
3062
|
+
for idx, bit in enumerate(qreg):
|
3063
|
+
if bit in self._qubit_indices:
|
3064
|
+
self._qubit_indices[bit].registers.append((qreg, idx))
|
3065
|
+
else:
|
3066
|
+
self._data.add_qubit(bit)
|
3067
|
+
self._qubit_indices[bit] = BitLocations(self._data.num_qubits - 1, [(qreg, idx)])
|
3068
|
+
|
3044
3069
|
def add_bits(self, bits: Iterable[Bit]) -> None:
|
3045
3070
|
"""Add Bits to the circuit."""
|
3046
3071
|
duplicate_bits = {
|
@@ -1773,12 +1773,13 @@ def Square(
|
|
1773
1773
|
is the sign function with the convention :math:`\\text{sign}\\left(0\\right)=1`.
|
1774
1774
|
|
1775
1775
|
Args:
|
1776
|
-
duration: Pulse length in terms of the sampling period
|
1777
|
-
amp: The magnitude of the amplitude of the square wave. Wave range is
|
1776
|
+
duration: Pulse length in terms of the sampling period ``dt``.
|
1777
|
+
amp: The magnitude of the amplitude of the square wave. Wave range is
|
1778
|
+
:math:`\\left[-\\texttt{amp},\\texttt{amp}\\right]`.
|
1778
1779
|
phase: The phase of the square wave (note that this is not equivalent to the angle of
|
1779
1780
|
the complex amplitude).
|
1780
1781
|
freq: The frequency of the square wave, in terms of 1 over sampling period.
|
1781
|
-
If not provided defaults to a single cycle (i.e :math
|
1782
|
+
If not provided defaults to a single cycle (i.e :math:`\\frac{1}{\\text{duration}}`).
|
1782
1783
|
The frequency is limited to the range :math:`\\left(0,0.5\\right]` (the Nyquist frequency).
|
1783
1784
|
angle: The angle in radians of the complex phase factor uniformly
|
1784
1785
|
scaling the pulse. Default value 0.
|
@@ -55,6 +55,30 @@ class Operator(LinearOp):
|
|
55
55
|
.. math::
|
56
56
|
|
57
57
|
\rho \mapsto M \rho M^\dagger.
|
58
|
+
|
59
|
+
For example, the following operator :math:`M = X` applied to the zero state
|
60
|
+
:math:`|\psi\rangle=|0\rangle (\rho = |0\rangle\langle 0|)` changes it to the
|
61
|
+
one state :math:`|\psi\rangle=|1\rangle (\rho = |1\rangle\langle 1|)`:
|
62
|
+
|
63
|
+
.. code-block:: python
|
64
|
+
|
65
|
+
>>> import numpy as np
|
66
|
+
>>> from qiskit.quantum_info import Operator
|
67
|
+
>>> op = Operator(np.array([[0.0, 1.0], [1.0, 0.0]])) # Represents Pauli X operator
|
68
|
+
|
69
|
+
>>> from qiskit.quantum_info import Statevector
|
70
|
+
>>> sv = Statevector(np.array([1.0, 0.0]))
|
71
|
+
>>> sv.evolve(op)
|
72
|
+
Statevector([0.+0.j, 1.+0.j],
|
73
|
+
dims=(2,))
|
74
|
+
|
75
|
+
>>> from qiskit.quantum_info import DensityMatrix
|
76
|
+
>>> dm = DensityMatrix(np.array([[1.0, 0.0], [0.0, 0.0]]))
|
77
|
+
>>> dm.evolve(op)
|
78
|
+
DensityMatrix([[0.+0.j, 0.+0.j],
|
79
|
+
[0.+0.j, 1.+0.j]],
|
80
|
+
dims=(2,))
|
81
|
+
|
58
82
|
"""
|
59
83
|
|
60
84
|
def __init__(
|
@@ -41,7 +41,7 @@ def synth_clifford_bm(clifford: Clifford) -> QuantumCircuit:
|
|
41
41
|
`arXiv:2003.09412 [quant-ph] <https://arxiv.org/abs/2003.09412>`_
|
42
42
|
"""
|
43
43
|
circuit = QuantumCircuit._from_circuit_data(
|
44
|
-
synth_clifford_bm_inner(clifford.tableau.astype(bool))
|
44
|
+
synth_clifford_bm_inner(clifford.tableau.astype(bool)), add_regs=True
|
45
45
|
)
|
46
46
|
circuit.name = str(clifford)
|
47
47
|
return circuit
|
@@ -51,7 +51,7 @@ def synth_clifford_greedy(clifford: Clifford) -> QuantumCircuit:
|
|
51
51
|
`arXiv:2105.02291 [quant-ph] <https://arxiv.org/abs/2105.02291>`_
|
52
52
|
"""
|
53
53
|
circuit = QuantumCircuit._from_circuit_data(
|
54
|
-
synth_clifford_greedy_inner(clifford.tableau.astype(bool))
|
54
|
+
synth_clifford_greedy_inner(clifford.tableau.astype(bool)), add_regs=True
|
55
55
|
)
|
56
56
|
circuit.name = str(clifford)
|
57
57
|
return circuit
|
@@ -42,7 +42,7 @@ def synth_permutation_basic(pattern: list[int] | np.ndarray[int]) -> QuantumCirc
|
|
42
42
|
Returns:
|
43
43
|
The synthesized quantum circuit.
|
44
44
|
"""
|
45
|
-
return QuantumCircuit._from_circuit_data(_synth_permutation_basic(pattern))
|
45
|
+
return QuantumCircuit._from_circuit_data(_synth_permutation_basic(pattern), add_regs=True)
|
46
46
|
|
47
47
|
|
48
48
|
def synth_permutation_acg(pattern: list[int] | np.ndarray[int]) -> QuantumCircuit:
|
@@ -75,4 +75,4 @@ def synth_permutation_acg(pattern: list[int] | np.ndarray[int]) -> QuantumCircui
|
|
75
75
|
*Routing Permutations on Graphs Via Matchings.*,
|
76
76
|
`(Full paper) <https://www.cs.tau.ac.il/~nogaa/PDFS/r.pdf>`_
|
77
77
|
"""
|
78
|
-
return QuantumCircuit._from_circuit_data(_synth_permutation_acg(pattern))
|
78
|
+
return QuantumCircuit._from_circuit_data(_synth_permutation_acg(pattern), add_regs=True)
|
@@ -49,4 +49,6 @@ def synth_permutation_depth_lnn_kms(pattern: list[int] | np.ndarray[int]) -> Qua
|
|
49
49
|
# In the permutation synthesis code below the notation is opposite:
|
50
50
|
# [2, 4, 3, 0, 1] means that 0 maps to 2, 1 to 3, 2 to 3, 3 to 0, and 4 to 1.
|
51
51
|
# This is why we invert the pattern.
|
52
|
-
return QuantumCircuit._from_circuit_data(
|
52
|
+
return QuantumCircuit._from_circuit_data(
|
53
|
+
_synth_permutation_depth_lnn_kms(pattern), add_regs=True
|
54
|
+
)
|
@@ -233,7 +233,7 @@ class TwoQubitWeylDecomposition:
|
|
233
233
|
circuit_data = self._inner_decomposition.circuit(
|
234
234
|
euler_basis=euler_basis, simplify=simplify, atol=atol
|
235
235
|
)
|
236
|
-
return QuantumCircuit._from_circuit_data(circuit_data)
|
236
|
+
return QuantumCircuit._from_circuit_data(circuit_data, add_regs=True)
|
237
237
|
|
238
238
|
def actual_fidelity(self, **kwargs) -> float:
|
239
239
|
"""Calculates the actual fidelity of the decomposed circuit to the input unitary."""
|
@@ -671,7 +671,7 @@ class TwoQubitBasisDecomposer:
|
|
671
671
|
approximate,
|
672
672
|
_num_basis_uses=_num_basis_uses,
|
673
673
|
)
|
674
|
-
return QuantumCircuit._from_circuit_data(circ_data)
|
674
|
+
return QuantumCircuit._from_circuit_data(circ_data, add_regs=True)
|
675
675
|
else:
|
676
676
|
sequence = self._inner_decomposer(
|
677
677
|
np.asarray(unitary, dtype=complex),
|
@@ -18,6 +18,7 @@ import qiskit.circuit
|
|
18
18
|
from qiskit.circuit import Barrier, Delay, Instruction, ParameterExpression
|
19
19
|
from qiskit.circuit.duration import duration_in_dt
|
20
20
|
from qiskit.providers import Backend
|
21
|
+
from qiskit.providers.backend import BackendV2
|
21
22
|
from qiskit.transpiler.exceptions import TranspilerError
|
22
23
|
from qiskit.utils.units import apply_prefix
|
23
24
|
|
@@ -75,6 +76,9 @@ class InstructionDurations:
|
|
75
76
|
TranspilerError: If dt and dtm is different in the backend.
|
76
77
|
"""
|
77
78
|
# All durations in seconds in gate_length
|
79
|
+
if isinstance(backend, BackendV2):
|
80
|
+
return backend.target.durations()
|
81
|
+
|
78
82
|
instruction_durations = []
|
79
83
|
backend_properties = backend.properties()
|
80
84
|
if hasattr(backend_properties, "_gates"):
|
@@ -71,6 +71,7 @@ Optimizations
|
|
71
71
|
Collect1qRuns
|
72
72
|
Collect2qBlocks
|
73
73
|
CollectMultiQBlocks
|
74
|
+
CollectAndCollapse
|
74
75
|
CollectLinearFunctions
|
75
76
|
CollectCliffords
|
76
77
|
ConsolidateBlocks
|
@@ -238,6 +239,7 @@ from .optimization import HoareOptimizer
|
|
238
239
|
from .optimization import TemplateOptimization
|
239
240
|
from .optimization import InverseCancellation
|
240
241
|
from .optimization import EchoRZXWeylDecomposition
|
242
|
+
from .optimization import CollectAndCollapse
|
241
243
|
from .optimization import CollectLinearFunctions
|
242
244
|
from .optimization import CollectCliffords
|
243
245
|
from .optimization import ResetAfterMeasureSimplification
|
@@ -203,20 +203,24 @@ class HoareOptimizer(TransformationPass):
|
|
203
203
|
"""
|
204
204
|
import z3
|
205
205
|
|
206
|
-
|
206
|
+
# Pre-generate all DAG nodes, since we later iterate over them, while
|
207
|
+
# potentially modifying and removing some of them.
|
208
|
+
nodes = list(dag.topological_op_nodes())
|
209
|
+
for node in nodes:
|
207
210
|
gate = node.op
|
208
|
-
|
211
|
+
_, ctrlvar, trgtqb, trgtvar = self._seperate_ctrl_trgt(node)
|
209
212
|
|
210
213
|
ctrl_ones = z3.And(*ctrlvar)
|
211
214
|
|
212
|
-
remove_ctrl, new_dag,
|
215
|
+
remove_ctrl, new_dag, _ = self._remove_control(gate, ctrlvar, trgtvar)
|
213
216
|
|
214
217
|
if remove_ctrl:
|
215
|
-
|
216
|
-
|
217
|
-
node
|
218
|
-
|
219
|
-
node
|
218
|
+
# We are replacing a node by a new node over a smaller number of qubits.
|
219
|
+
# This can be done using substitute_node_with_dag, which furthermore returns
|
220
|
+
# a mapping from old node ids to new nodes.
|
221
|
+
mapped_nodes = dag.substitute_node_with_dag(node, new_dag)
|
222
|
+
node = next(iter(mapped_nodes.values()))
|
223
|
+
gate = node.op
|
220
224
|
_, ctrlvar, trgtqb, trgtvar = self._seperate_ctrl_trgt(node)
|
221
225
|
|
222
226
|
ctrl_ones = z3.And(*ctrlvar)
|
@@ -9,8 +9,8 @@
|
|
9
9
|
# Any modifications or derivative works of this code must retain this
|
10
10
|
# copyright notice, and modified files need to carry a notice indicating
|
11
11
|
# that they have been altered from the originals.
|
12
|
+
|
12
13
|
"""Splits each two-qubit gate in the `dag` into two single-qubit gates, if possible without error."""
|
13
|
-
from typing import Optional
|
14
14
|
|
15
15
|
from qiskit.transpiler.basepasses import TransformationPass
|
16
16
|
from qiskit.circuit.quantumcircuitdata import CircuitInstruction
|
@@ -20,37 +20,33 @@ from qiskit.synthesis.two_qubit.two_qubit_decompose import TwoQubitWeylDecomposi
|
|
20
20
|
|
21
21
|
|
22
22
|
class Split2QUnitaries(TransformationPass):
|
23
|
-
"""Attempt to splits two-qubit
|
23
|
+
"""Attempt to splits two-qubit unitaries in a :class:`.DAGCircuit` into two single-qubit gates.
|
24
24
|
|
25
|
-
This pass will analyze all
|
26
|
-
matrix
|
27
|
-
|
28
|
-
|
29
|
-
:class:`.UnitaryGate`.
|
25
|
+
This pass will analyze all :class:`.UnitaryGate` instances and determine whether the
|
26
|
+
matrix is actually a product of 2 single qubit gates. In these cases the 2q gate can be
|
27
|
+
simplified into two single qubit gates and this pass will perform this optimization and will
|
28
|
+
replace the two qubit gate with two single qubit :class:`.UnitaryGate`.
|
30
29
|
"""
|
31
30
|
|
32
|
-
def __init__(self, fidelity:
|
33
|
-
"""
|
34
|
-
|
31
|
+
def __init__(self, fidelity: float = 1.0 - 1e-16):
|
32
|
+
"""
|
35
33
|
Args:
|
36
|
-
fidelity
|
34
|
+
fidelity: Allowed tolerance for splitting two-qubit unitaries and gate decompositions.
|
37
35
|
"""
|
38
36
|
super().__init__()
|
39
37
|
self.requested_fidelity = fidelity
|
40
38
|
|
41
|
-
def run(self, dag: DAGCircuit):
|
39
|
+
def run(self, dag: DAGCircuit) -> DAGCircuit:
|
42
40
|
"""Run the Split2QUnitaries pass on `dag`."""
|
41
|
+
|
43
42
|
for node in dag.topological_op_nodes():
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
or node.matrix is None
|
49
|
-
or node.is_parameterized()
|
50
|
-
):
|
43
|
+
# We only attempt to split UnitaryGate objects, but this could be extended in future
|
44
|
+
# -- however we need to ensure that we can compile the resulting single-qubit unitaries
|
45
|
+
# to the supported basis gate set.
|
46
|
+
if not (len(node.qargs) == 2 and node.op.name == "unitary"):
|
51
47
|
continue
|
52
48
|
|
53
|
-
decomp = TwoQubitWeylDecomposition(node.
|
49
|
+
decomp = TwoQubitWeylDecomposition(node.matrix, fidelity=self.requested_fidelity)
|
54
50
|
if (
|
55
51
|
decomp._inner_decomposition.specialization
|
56
52
|
== TwoQubitWeylDecomposition._specializations.IdEquiv
|
@@ -343,6 +343,7 @@ def generate_preset_pass_manager(
|
|
343
343
|
# Parse non-target dependent pm options
|
344
344
|
initial_layout = _parse_initial_layout(initial_layout)
|
345
345
|
approximation_degree = _parse_approximation_degree(approximation_degree)
|
346
|
+
seed_transpiler = _parse_seed_transpiler(seed_transpiler)
|
346
347
|
|
347
348
|
pm_options = {
|
348
349
|
"target": target,
|
@@ -516,3 +517,11 @@ def _parse_approximation_degree(approximation_degree):
|
|
516
517
|
if approximation_degree < 0.0 or approximation_degree > 1.0:
|
517
518
|
raise TranspilerError("Approximation degree must be in [0.0, 1.0]")
|
518
519
|
return approximation_degree
|
520
|
+
|
521
|
+
|
522
|
+
def _parse_seed_transpiler(seed_transpiler):
|
523
|
+
if seed_transpiler is None:
|
524
|
+
return None
|
525
|
+
if not isinstance(seed_transpiler, int) or seed_transpiler < 0:
|
526
|
+
raise ValueError("Expected non-negative integer as seed for transpiler.")
|
527
|
+
return seed_transpiler
|