qiskit 1.0.2__cp38-abi3-win32.whl → 1.1.0__cp38-abi3-win32.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/__init__.py +27 -16
- qiskit/_accelerate.pyd +0 -0
- qiskit/_numpy_compat.py +73 -0
- qiskit/assembler/__init__.py +5 -10
- qiskit/assembler/disassemble.py +5 -6
- qiskit/circuit/__init__.py +1061 -232
- qiskit/circuit/_classical_resource_map.py +10 -6
- qiskit/circuit/_utils.py +18 -8
- qiskit/circuit/annotated_operation.py +21 -0
- qiskit/circuit/barrier.py +10 -13
- qiskit/circuit/bit.py +0 -1
- qiskit/circuit/classical/__init__.py +2 -2
- qiskit/circuit/classical/expr/__init__.py +39 -5
- qiskit/circuit/classical/expr/constructors.py +84 -1
- qiskit/circuit/classical/expr/expr.py +83 -13
- qiskit/circuit/classical/expr/visitors.py +83 -0
- qiskit/circuit/classical/types/__init__.py +5 -4
- qiskit/circuit/classicalfunction/__init__.py +1 -0
- qiskit/circuit/commutation_checker.py +86 -51
- qiskit/circuit/controlflow/_builder_utils.py +9 -1
- qiskit/circuit/controlflow/break_loop.py +8 -22
- qiskit/circuit/controlflow/builder.py +116 -1
- qiskit/circuit/controlflow/continue_loop.py +8 -22
- qiskit/circuit/controlflow/control_flow.py +47 -8
- qiskit/circuit/controlflow/for_loop.py +8 -23
- qiskit/circuit/controlflow/if_else.py +13 -27
- qiskit/circuit/controlflow/switch_case.py +14 -21
- qiskit/circuit/controlflow/while_loop.py +9 -23
- qiskit/circuit/controlledgate.py +2 -2
- qiskit/circuit/delay.py +7 -5
- qiskit/circuit/gate.py +20 -7
- qiskit/circuit/instruction.py +31 -30
- qiskit/circuit/instructionset.py +9 -22
- qiskit/circuit/library/__init__.py +3 -13
- qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
- qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
- qiskit/circuit/library/blueprintcircuit.py +29 -7
- qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
- qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
- qiskit/circuit/library/generalized_gates/isometry.py +51 -254
- qiskit/circuit/library/generalized_gates/pauli.py +2 -2
- qiskit/circuit/library/generalized_gates/permutation.py +4 -1
- qiskit/circuit/library/generalized_gates/rv.py +15 -11
- qiskit/circuit/library/generalized_gates/uc.py +2 -98
- qiskit/circuit/library/generalized_gates/unitary.py +9 -4
- qiskit/circuit/library/hamiltonian_gate.py +11 -5
- qiskit/circuit/library/n_local/efficient_su2.py +5 -5
- qiskit/circuit/library/n_local/n_local.py +100 -49
- qiskit/circuit/library/n_local/two_local.py +3 -59
- qiskit/circuit/library/overlap.py +3 -3
- qiskit/circuit/library/phase_oracle.py +1 -1
- qiskit/circuit/library/quantum_volume.py +39 -38
- qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
- qiskit/circuit/library/standard_gates/global_phase.py +4 -2
- qiskit/circuit/library/standard_gates/i.py +1 -2
- qiskit/circuit/library/standard_gates/iswap.py +1 -2
- qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
- qiskit/circuit/library/standard_gates/p.py +31 -15
- qiskit/circuit/library/standard_gates/r.py +4 -3
- qiskit/circuit/library/standard_gates/rx.py +7 -4
- qiskit/circuit/library/standard_gates/rxx.py +4 -3
- qiskit/circuit/library/standard_gates/ry.py +7 -4
- qiskit/circuit/library/standard_gates/ryy.py +4 -3
- qiskit/circuit/library/standard_gates/rz.py +7 -4
- qiskit/circuit/library/standard_gates/rzx.py +4 -3
- qiskit/circuit/library/standard_gates/rzz.py +4 -3
- qiskit/circuit/library/standard_gates/s.py +4 -8
- qiskit/circuit/library/standard_gates/t.py +2 -4
- qiskit/circuit/library/standard_gates/u.py +16 -11
- qiskit/circuit/library/standard_gates/u1.py +6 -2
- qiskit/circuit/library/standard_gates/u2.py +4 -2
- qiskit/circuit/library/standard_gates/u3.py +9 -5
- qiskit/circuit/library/standard_gates/x.py +22 -11
- qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
- qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
- qiskit/circuit/library/standard_gates/z.py +1 -2
- qiskit/circuit/measure.py +4 -1
- qiskit/circuit/operation.py +13 -8
- qiskit/circuit/parameter.py +11 -6
- qiskit/circuit/quantumcircuit.py +1910 -260
- qiskit/circuit/quantumcircuitdata.py +2 -2
- qiskit/circuit/reset.py +5 -2
- qiskit/circuit/store.py +95 -0
- qiskit/compiler/assembler.py +22 -22
- qiskit/compiler/transpiler.py +63 -112
- qiskit/converters/__init__.py +17 -2
- qiskit/converters/circuit_to_dag.py +7 -0
- qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
- qiskit/converters/circuit_to_gate.py +2 -0
- qiskit/converters/circuit_to_instruction.py +22 -0
- qiskit/converters/dag_to_circuit.py +4 -0
- qiskit/converters/dag_to_dagdependency_v2.py +44 -0
- qiskit/dagcircuit/collect_blocks.py +15 -10
- qiskit/dagcircuit/dagcircuit.py +434 -124
- qiskit/dagcircuit/dagdependency.py +19 -12
- qiskit/dagcircuit/dagdependency_v2.py +641 -0
- qiskit/dagcircuit/dagdepnode.py +19 -16
- qiskit/dagcircuit/dagnode.py +14 -4
- qiskit/passmanager/passmanager.py +11 -11
- qiskit/primitives/__init__.py +22 -12
- qiskit/primitives/backend_estimator.py +3 -5
- qiskit/primitives/backend_estimator_v2.py +410 -0
- qiskit/primitives/backend_sampler_v2.py +287 -0
- qiskit/primitives/base/base_estimator.py +4 -9
- qiskit/primitives/base/base_sampler.py +2 -2
- qiskit/primitives/containers/__init__.py +6 -4
- qiskit/primitives/containers/bit_array.py +293 -2
- qiskit/primitives/containers/data_bin.py +123 -50
- qiskit/primitives/containers/estimator_pub.py +10 -3
- qiskit/primitives/containers/observables_array.py +2 -2
- qiskit/primitives/containers/pub_result.py +1 -1
- qiskit/primitives/containers/sampler_pub.py +19 -3
- qiskit/primitives/containers/sampler_pub_result.py +74 -0
- qiskit/primitives/containers/shape.py +4 -4
- qiskit/primitives/statevector_estimator.py +4 -4
- qiskit/primitives/statevector_sampler.py +7 -12
- qiskit/providers/__init__.py +65 -34
- qiskit/providers/backend.py +2 -2
- qiskit/providers/backend_compat.py +8 -10
- qiskit/providers/basic_provider/__init__.py +2 -23
- qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
- qiskit/providers/basic_provider/basic_simulator.py +81 -21
- qiskit/providers/fake_provider/__init__.py +1 -1
- qiskit/providers/fake_provider/fake_1q.py +1 -1
- qiskit/providers/fake_provider/fake_backend.py +3 -408
- qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
- qiskit/providers/models/__init__.py +2 -2
- qiskit/providers/provider.py +16 -0
- qiskit/pulse/builder.py +4 -1
- qiskit/pulse/parameter_manager.py +60 -4
- qiskit/pulse/schedule.py +29 -13
- qiskit/pulse/utils.py +61 -20
- qiskit/qasm2/__init__.py +1 -5
- qiskit/qasm2/parse.py +1 -4
- qiskit/qasm3/__init__.py +42 -5
- qiskit/qasm3/ast.py +19 -0
- qiskit/qasm3/exporter.py +178 -106
- qiskit/qasm3/printer.py +27 -5
- qiskit/qobj/converters/pulse_instruction.py +6 -6
- qiskit/qpy/__init__.py +299 -67
- qiskit/qpy/binary_io/circuits.py +216 -47
- qiskit/qpy/binary_io/schedules.py +42 -36
- qiskit/qpy/binary_io/value.py +201 -22
- qiskit/qpy/common.py +1 -1
- qiskit/qpy/exceptions.py +20 -0
- qiskit/qpy/formats.py +29 -0
- qiskit/qpy/type_keys.py +21 -0
- qiskit/quantum_info/analysis/distance.py +3 -3
- qiskit/quantum_info/analysis/make_observable.py +2 -1
- qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
- qiskit/quantum_info/operators/channel/chi.py +9 -8
- qiskit/quantum_info/operators/channel/choi.py +10 -9
- qiskit/quantum_info/operators/channel/kraus.py +2 -1
- qiskit/quantum_info/operators/channel/ptm.py +10 -9
- qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
- qiskit/quantum_info/operators/channel/stinespring.py +2 -1
- qiskit/quantum_info/operators/channel/superop.py +12 -11
- qiskit/quantum_info/operators/channel/transformations.py +12 -11
- qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
- qiskit/quantum_info/operators/operator.py +43 -30
- qiskit/quantum_info/operators/scalar_op.py +10 -9
- qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
- qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
- qiskit/quantum_info/operators/symplectic/pauli.py +53 -6
- qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
- qiskit/quantum_info/operators/symplectic/random.py +3 -2
- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +61 -36
- qiskit/quantum_info/states/densitymatrix.py +13 -13
- qiskit/quantum_info/states/stabilizerstate.py +3 -3
- qiskit/quantum_info/states/statevector.py +14 -13
- qiskit/quantum_info/states/utils.py +5 -3
- qiskit/result/__init__.py +6 -0
- qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
- qiskit/result/mitigation/local_readout_mitigator.py +2 -1
- qiskit/result/mitigation/utils.py +3 -2
- qiskit/scheduler/__init__.py +10 -1
- qiskit/scheduler/methods/__init__.py +1 -8
- qiskit/synthesis/__init__.py +3 -6
- qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
- qiskit/synthesis/evolution/lie_trotter.py +7 -14
- qiskit/synthesis/evolution/qdrift.py +3 -4
- qiskit/synthesis/linear/cnot_synth.py +1 -3
- qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
- qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
- qiskit/synthesis/permutation/__init__.py +1 -0
- qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
- qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
- qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
- qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
- qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
- qiskit/synthesis/unitary/aqc/__init__.py +1 -1
- qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
- qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
- qiskit/synthesis/unitary/qsd.py +3 -2
- qiskit/transpiler/__init__.py +7 -3
- qiskit/transpiler/layout.py +140 -61
- qiskit/transpiler/passes/__init__.py +10 -2
- qiskit/transpiler/passes/basis/basis_translator.py +9 -4
- qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
- qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
- qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
- qiskit/transpiler/passes/layout/apply_layout.py +8 -3
- qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
- qiskit/transpiler/passes/layout/set_layout.py +1 -1
- qiskit/transpiler/passes/optimization/__init__.py +2 -0
- qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
- qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
- qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
- qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
- qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
- qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
- qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
- qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
- qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
- qiskit/transpiler/passes/routing/__init__.py +1 -0
- qiskit/transpiler/passes/routing/basic_swap.py +13 -2
- qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +8 -1
- qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
- qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
- qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
- qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
- qiskit/transpiler/passes/scheduling/__init__.py +1 -1
- qiskit/transpiler/passes/scheduling/alap.py +1 -2
- qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
- qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
- qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
- qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
- qiskit/transpiler/passes/scheduling/asap.py +1 -2
- qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
- qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
- qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
- qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
- qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
- qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
- qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
- qiskit/transpiler/passes/utils/gates_basis.py +3 -3
- qiskit/transpiler/passmanager.py +44 -1
- qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
- qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
- qiskit/transpiler/preset_passmanagers/common.py +4 -6
- qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
- qiskit/utils/__init__.py +3 -2
- qiskit/utils/optionals.py +6 -2
- qiskit/utils/parallel.py +24 -15
- qiskit/visualization/array.py +1 -1
- qiskit/visualization/bloch.py +2 -3
- qiskit/visualization/circuit/matplotlib.py +44 -14
- qiskit/visualization/circuit/text.py +38 -18
- qiskit/visualization/counts_visualization.py +3 -6
- qiskit/visualization/dag_visualization.py +6 -7
- qiskit/visualization/gate_map.py +9 -1
- qiskit/visualization/pulse_v2/interface.py +8 -3
- qiskit/visualization/state_visualization.py +3 -2
- qiskit/visualization/timeline/interface.py +18 -8
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/METADATA +12 -8
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/RECORD +261 -251
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/WHEEL +1 -1
- qiskit/_qasm2.pyd +0 -0
- qiskit/_qasm3.pyd +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/LICENSE.txt +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/entry_points.txt +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/top_level.txt +0 -0
@@ -29,26 +29,58 @@ import math
|
|
29
29
|
import io
|
30
30
|
import base64
|
31
31
|
import warnings
|
32
|
-
from typing import
|
32
|
+
from typing import Optional, Type, TYPE_CHECKING
|
33
33
|
|
34
34
|
import logging
|
35
35
|
|
36
36
|
import numpy as np
|
37
37
|
|
38
38
|
from qiskit.circuit import QuantumRegister, QuantumCircuit, Gate
|
39
|
-
from qiskit.circuit.library.standard_gates import
|
39
|
+
from qiskit.circuit.library.standard_gates import (
|
40
|
+
CXGate,
|
41
|
+
U3Gate,
|
42
|
+
U2Gate,
|
43
|
+
U1Gate,
|
44
|
+
UGate,
|
45
|
+
PhaseGate,
|
46
|
+
RXGate,
|
47
|
+
RYGate,
|
48
|
+
RZGate,
|
49
|
+
SXGate,
|
50
|
+
XGate,
|
51
|
+
RGate,
|
52
|
+
)
|
40
53
|
from qiskit.exceptions import QiskitError
|
41
54
|
from qiskit.quantum_info.operators import Operator
|
42
|
-
from qiskit.synthesis.two_qubit.weyl import transform_to_magic_basis
|
43
55
|
from qiskit.synthesis.one_qubit.one_qubit_decompose import (
|
44
56
|
OneQubitEulerDecomposer,
|
45
57
|
DEFAULT_ATOL,
|
46
58
|
)
|
59
|
+
from qiskit.utils.deprecation import deprecate_func
|
47
60
|
from qiskit._accelerate import two_qubit_decompose
|
48
61
|
|
62
|
+
if TYPE_CHECKING:
|
63
|
+
from qiskit.dagcircuit.dagcircuit import DAGCircuit
|
64
|
+
|
49
65
|
logger = logging.getLogger(__name__)
|
50
66
|
|
51
67
|
|
68
|
+
GATE_NAME_MAP = {
|
69
|
+
"cx": CXGate,
|
70
|
+
"rx": RXGate,
|
71
|
+
"sx": SXGate,
|
72
|
+
"x": XGate,
|
73
|
+
"rz": RZGate,
|
74
|
+
"u": UGate,
|
75
|
+
"p": PhaseGate,
|
76
|
+
"u1": U1Gate,
|
77
|
+
"u2": U2Gate,
|
78
|
+
"u3": U3Gate,
|
79
|
+
"ry": RYGate,
|
80
|
+
"r": RGate,
|
81
|
+
}
|
82
|
+
|
83
|
+
|
52
84
|
def decompose_two_qubit_product_gate(special_unitary_matrix: np.ndarray):
|
53
85
|
r"""Decompose :math:`U = U_l \otimes U_r` where :math:`U \in SU(4)`,
|
54
86
|
and :math:`U_l,~U_r \in SU(2)`.
|
@@ -118,13 +150,9 @@ class TwoQubitWeylDecomposition:
|
|
118
150
|
|
119
151
|
\pi /4 \geq a \geq b \geq |c|
|
120
152
|
|
121
|
-
This
|
122
|
-
|
123
|
-
|
124
|
-
thus avoiding problems of numerical stability.
|
125
|
-
|
126
|
-
Passing non-None fidelity to specializations is treated as an assertion, raising QiskitError if
|
127
|
-
forcing the specialization is more approximate than asserted.
|
153
|
+
This class avoids some problems of numerical instability near high-symmetry loci within the Weyl
|
154
|
+
chamber. If there is a high-symmetry gate "nearby" (in terms of the requested average gate fidelity),
|
155
|
+
then it return a canonicalized decomposition of that high-symmetry gate.
|
128
156
|
|
129
157
|
References:
|
130
158
|
1. Cross, A. W., Bishop, L. S., Sheldon, S., Nation, P. D. & Gambetta, J. M.,
|
@@ -151,237 +179,30 @@ class TwoQubitWeylDecomposition:
|
|
151
179
|
requested_fidelity: Optional[float] # None means no automatic specialization
|
152
180
|
calculated_fidelity: float # Fidelity after specialization
|
153
181
|
|
154
|
-
|
155
|
-
_is_flipped_from_original: bool # The approx is closest to a Weyl reflection of the original?
|
156
|
-
|
157
|
-
_default_1q_basis: ClassVar[str] = "ZYZ" # Default one qubit basis (explicit parameterization)
|
158
|
-
|
159
|
-
def __init_subclass__(cls, **kwargs):
|
160
|
-
"""Subclasses should be concrete, not factories.
|
161
|
-
|
162
|
-
Make explicitly-instantiated subclass __new__ call base __new__ with fidelity=None"""
|
163
|
-
super().__init_subclass__(**kwargs)
|
164
|
-
cls.__new__ = lambda cls, *a, fidelity=None, **k: TwoQubitWeylDecomposition.__new__(
|
165
|
-
cls, *a, fidelity=None, **k
|
166
|
-
)
|
167
|
-
|
168
|
-
@staticmethod
|
169
|
-
def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9), _unpickling=False):
|
170
|
-
"""Perform the Weyl chamber decomposition, and optionally choose a specialized subclass."""
|
171
|
-
|
172
|
-
# The flip into the Weyl Chamber is described in B. Kraus and J. I. Cirac, Phys. Rev. A 63,
|
173
|
-
# 062309 (2001).
|
174
|
-
#
|
175
|
-
# FIXME: There's a cleaner-seeming method based on choosing branch cuts carefully, in Andrew
|
176
|
-
# M. Childs, Henry L. Haselgrove, and Michael A. Nielsen, Phys. Rev. A 68, 052311, but I
|
177
|
-
# wasn't able to get that to work.
|
178
|
-
#
|
179
|
-
# The overall decomposition scheme is taken from Drury and Love, arXiv:0806.4015 [quant-ph].
|
180
|
-
|
181
|
-
if _unpickling:
|
182
|
-
return super().__new__(cls)
|
183
|
-
|
184
|
-
pi = np.pi
|
185
|
-
pi2 = np.pi / 2
|
186
|
-
pi4 = np.pi / 4
|
187
|
-
|
188
|
-
# Make U be in SU(4)
|
189
|
-
U = np.array(unitary_matrix, dtype=complex, copy=True)
|
190
|
-
detU = np.linalg.det(U)
|
191
|
-
U *= detU ** (-0.25)
|
192
|
-
global_phase = cmath.phase(detU) / 4
|
193
|
-
|
194
|
-
Up = transform_to_magic_basis(U, reverse=True)
|
195
|
-
M2 = Up.T.dot(Up)
|
196
|
-
|
197
|
-
# M2 is a symmetric complex matrix. We need to decompose it as M2 = P D P^T where
|
198
|
-
# P ∈ SO(4), D is diagonal with unit-magnitude elements.
|
199
|
-
#
|
200
|
-
# We can't use raw `eig` directly because it isn't guaranteed to give us real or othogonal
|
201
|
-
# eigenvectors. Instead, since `M2` is complex-symmetric,
|
202
|
-
# M2 = A + iB
|
203
|
-
# for real-symmetric `A` and `B`, and as
|
204
|
-
# M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1
|
205
|
-
# we must have `A` and `B` commute, and consequently they are simultaneously diagonalizable.
|
206
|
-
# Mixing them together _should_ account for any degeneracy problems, but it's not
|
207
|
-
# guaranteed, so we repeat it a little bit. The fixed seed is to make failures
|
208
|
-
# deterministic; the value is not important.
|
209
|
-
state = np.random.default_rng(2020)
|
210
|
-
for _ in range(100): # FIXME: this randomized algorithm is horrendous
|
211
|
-
M2real = state.normal() * M2.real + state.normal() * M2.imag
|
212
|
-
_, P = np.linalg.eigh(M2real)
|
213
|
-
D = P.T.dot(M2).dot(P).diagonal()
|
214
|
-
if np.allclose(P.dot(np.diag(D)).dot(P.T), M2, rtol=0, atol=1.0e-13):
|
215
|
-
break
|
216
|
-
else:
|
217
|
-
raise QiskitError(
|
218
|
-
"TwoQubitWeylDecomposition: failed to diagonalize M2."
|
219
|
-
" Please report this at https://github.com/Qiskit/qiskit-terra/issues/4159."
|
220
|
-
f" Input: {U.tolist()}"
|
221
|
-
)
|
222
|
-
|
223
|
-
d = -np.angle(D) / 2
|
224
|
-
d[3] = -d[0] - d[1] - d[2]
|
225
|
-
cs = np.mod((d[:3] + d[3]) / 2, 2 * np.pi)
|
226
|
-
|
227
|
-
# Reorder the eigenvalues to get in the Weyl chamber
|
228
|
-
cstemp = np.mod(cs, pi2)
|
229
|
-
np.minimum(cstemp, pi2 - cstemp, cstemp)
|
230
|
-
order = np.argsort(cstemp)[[1, 2, 0]]
|
231
|
-
cs = cs[order]
|
232
|
-
d[:3] = d[order]
|
233
|
-
P[:, :3] = P[:, order]
|
234
|
-
|
235
|
-
# Fix the sign of P to be in SO(4)
|
236
|
-
if np.real(np.linalg.det(P)) < 0:
|
237
|
-
P[:, -1] = -P[:, -1]
|
238
|
-
|
239
|
-
# Find K1, K2 so that U = K1.A.K2, with K being product of single-qubit unitaries
|
240
|
-
K1 = transform_to_magic_basis(Up @ P @ np.diag(np.exp(1j * d)))
|
241
|
-
K2 = transform_to_magic_basis(P.T)
|
242
|
-
|
243
|
-
K1l, K1r, phase_l = decompose_two_qubit_product_gate(K1)
|
244
|
-
K2l, K2r, phase_r = decompose_two_qubit_product_gate(K2)
|
245
|
-
global_phase += phase_l + phase_r
|
246
|
-
|
247
|
-
K1l = K1l.copy()
|
248
|
-
|
249
|
-
# Flip into Weyl chamber
|
250
|
-
if cs[0] > pi2:
|
251
|
-
cs[0] -= 3 * pi2
|
252
|
-
K1l = K1l.dot(_ipy)
|
253
|
-
K1r = K1r.dot(_ipy)
|
254
|
-
global_phase += pi2
|
255
|
-
if cs[1] > pi2:
|
256
|
-
cs[1] -= 3 * pi2
|
257
|
-
K1l = K1l.dot(_ipx)
|
258
|
-
K1r = K1r.dot(_ipx)
|
259
|
-
global_phase += pi2
|
260
|
-
conjs = 0
|
261
|
-
if cs[0] > pi4:
|
262
|
-
cs[0] = pi2 - cs[0]
|
263
|
-
K1l = K1l.dot(_ipy)
|
264
|
-
K2r = _ipy.dot(K2r)
|
265
|
-
conjs += 1
|
266
|
-
global_phase -= pi2
|
267
|
-
if cs[1] > pi4:
|
268
|
-
cs[1] = pi2 - cs[1]
|
269
|
-
K1l = K1l.dot(_ipx)
|
270
|
-
K2r = _ipx.dot(K2r)
|
271
|
-
conjs += 1
|
272
|
-
global_phase += pi2
|
273
|
-
if conjs == 1:
|
274
|
-
global_phase -= pi
|
275
|
-
if cs[2] > pi2:
|
276
|
-
cs[2] -= 3 * pi2
|
277
|
-
K1l = K1l.dot(_ipz)
|
278
|
-
K1r = K1r.dot(_ipz)
|
279
|
-
global_phase += pi2
|
280
|
-
if conjs == 1:
|
281
|
-
global_phase -= pi
|
282
|
-
if conjs == 1:
|
283
|
-
cs[2] = pi2 - cs[2]
|
284
|
-
K1l = K1l.dot(_ipz)
|
285
|
-
K2r = _ipz.dot(K2r)
|
286
|
-
global_phase += pi2
|
287
|
-
if cs[2] > pi4:
|
288
|
-
cs[2] -= pi2
|
289
|
-
K1l = K1l.dot(_ipz)
|
290
|
-
K1r = K1r.dot(_ipz)
|
291
|
-
global_phase -= pi2
|
292
|
-
|
293
|
-
a, b, c = cs[1], cs[0], cs[2]
|
294
|
-
|
295
|
-
# Save the non-specialized decomposition for later comparison
|
296
|
-
od = super().__new__(TwoQubitWeylDecomposition)
|
297
|
-
od.a = a
|
298
|
-
od.b = b
|
299
|
-
od.c = c
|
300
|
-
od.K1l = K1l
|
301
|
-
od.K1r = K1r
|
302
|
-
od.K2l = K2l
|
303
|
-
od.K2r = K2r
|
304
|
-
od.global_phase = global_phase
|
305
|
-
od.requested_fidelity = fidelity
|
306
|
-
od.calculated_fidelity = 1.0
|
307
|
-
od.unitary_matrix = np.array(unitary_matrix, dtype=complex, copy=True)
|
308
|
-
od.unitary_matrix.setflags(write=False)
|
309
|
-
od._original_decomposition = None
|
310
|
-
od._is_flipped_from_original = False
|
311
|
-
|
312
|
-
def is_close(ap, bp, cp):
|
313
|
-
da, db, dc = a - ap, b - bp, c - cp
|
314
|
-
tr = 4 * complex(
|
315
|
-
math.cos(da) * math.cos(db) * math.cos(dc),
|
316
|
-
math.sin(da) * math.sin(db) * math.sin(dc),
|
317
|
-
)
|
318
|
-
fid = trace_to_fid(tr)
|
319
|
-
return fid >= fidelity
|
320
|
-
|
321
|
-
if fidelity is None: # Don't specialize if None
|
322
|
-
instance = super().__new__(
|
323
|
-
TwoQubitWeylGeneral if cls is TwoQubitWeylDecomposition else cls
|
324
|
-
)
|
325
|
-
elif is_close(0, 0, 0):
|
326
|
-
instance = super().__new__(TwoQubitWeylIdEquiv)
|
327
|
-
elif is_close(pi4, pi4, pi4) or is_close(pi4, pi4, -pi4):
|
328
|
-
instance = super().__new__(TwoQubitWeylSWAPEquiv)
|
329
|
-
elif (lambda x: is_close(x, x, x))(_closest_partial_swap(a, b, c)):
|
330
|
-
instance = super().__new__(TwoQubitWeylPartialSWAPEquiv)
|
331
|
-
elif (lambda x: is_close(x, x, -x))(_closest_partial_swap(a, b, -c)):
|
332
|
-
instance = super().__new__(TwoQubitWeylPartialSWAPFlipEquiv)
|
333
|
-
elif is_close(a, 0, 0):
|
334
|
-
instance = super().__new__(TwoQubitWeylControlledEquiv)
|
335
|
-
elif is_close(pi4, pi4, c):
|
336
|
-
instance = super().__new__(TwoQubitWeylMirrorControlledEquiv)
|
337
|
-
elif is_close((a + b) / 2, (a + b) / 2, c):
|
338
|
-
instance = super().__new__(TwoQubitWeylfSimaabEquiv)
|
339
|
-
elif is_close(a, (b + c) / 2, (b + c) / 2):
|
340
|
-
instance = super().__new__(TwoQubitWeylfSimabbEquiv)
|
341
|
-
elif is_close(a, (b - c) / 2, (c - b) / 2):
|
342
|
-
instance = super().__new__(TwoQubitWeylfSimabmbEquiv)
|
343
|
-
else:
|
344
|
-
instance = super().__new__(TwoQubitWeylGeneral)
|
345
|
-
|
346
|
-
instance._original_decomposition = od
|
347
|
-
return instance
|
182
|
+
_specializations = two_qubit_decompose.Specialization
|
348
183
|
|
349
184
|
def __init__(
|
350
185
|
self,
|
351
|
-
unitary_matrix:
|
352
|
-
fidelity: float | None =
|
186
|
+
unitary_matrix: np.ndarray,
|
187
|
+
fidelity: float | None = 1.0 - 1.0e-9,
|
188
|
+
*,
|
189
|
+
_specialization: two_qubit_decompose.Specialization | None = None,
|
353
190
|
):
|
354
|
-
|
355
|
-
|
356
|
-
unitary_matrix
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
self.
|
362
|
-
self.K1l
|
363
|
-
self.
|
364
|
-
self.
|
365
|
-
self.
|
191
|
+
unitary_matrix = np.asarray(unitary_matrix, dtype=complex)
|
192
|
+
self._inner_decomposition = two_qubit_decompose.TwoQubitWeylDecomposition(
|
193
|
+
unitary_matrix, fidelity=fidelity, _specialization=_specialization
|
194
|
+
)
|
195
|
+
self.a = self._inner_decomposition.a
|
196
|
+
self.b = self._inner_decomposition.b
|
197
|
+
self.c = self._inner_decomposition.c
|
198
|
+
self.global_phase = self._inner_decomposition.global_phase
|
199
|
+
self.K1l = self._inner_decomposition.K1l
|
200
|
+
self.K1r = self._inner_decomposition.K1r
|
201
|
+
self.K2l = self._inner_decomposition.K2l
|
202
|
+
self.K2r = self._inner_decomposition.K2r
|
203
|
+
self.unitary_matrix = unitary_matrix
|
366
204
|
self.requested_fidelity = fidelity
|
367
|
-
self.
|
368
|
-
self.specialize()
|
369
|
-
|
370
|
-
# Update the phase after specialization:
|
371
|
-
if self._is_flipped_from_original:
|
372
|
-
da, db, dc = (np.pi / 2 - od.a) - self.a, od.b - self.b, -od.c - self.c
|
373
|
-
tr = 4 * complex(
|
374
|
-
math.cos(da) * math.cos(db) * math.cos(dc),
|
375
|
-
math.sin(da) * math.sin(db) * math.sin(dc),
|
376
|
-
)
|
377
|
-
else:
|
378
|
-
da, db, dc = od.a - self.a, od.b - self.b, od.c - self.c
|
379
|
-
tr = 4 * complex(
|
380
|
-
math.cos(da) * math.cos(db) * math.cos(dc),
|
381
|
-
math.sin(da) * math.sin(db) * math.sin(dc),
|
382
|
-
)
|
383
|
-
self.global_phase += cmath.phase(tr)
|
384
|
-
self.calculated_fidelity = trace_to_fid(tr)
|
205
|
+
self.calculated_fidelity = self._inner_decomposition.calculated_fidelity
|
385
206
|
if logger.isEnabledFor(logging.DEBUG):
|
386
207
|
actual_fidelity = self.actual_fidelity()
|
387
208
|
logger.debug(
|
@@ -395,62 +216,34 @@ class TwoQubitWeylDecomposition:
|
|
395
216
|
"Requested fidelity different from actual by %s",
|
396
217
|
self.calculated_fidelity - actual_fidelity,
|
397
218
|
)
|
398
|
-
if self.requested_fidelity and self.calculated_fidelity + 1.0e-13 < self.requested_fidelity:
|
399
|
-
raise QiskitError(
|
400
|
-
f"{self.__class__.__name__}: "
|
401
|
-
f"calculated fidelity: {self.calculated_fidelity} "
|
402
|
-
f"is worse than requested fidelity: {self.requested_fidelity}."
|
403
|
-
)
|
404
219
|
|
220
|
+
@deprecate_func(since="1.1.0", removal_timeline="in the 2.0.0 release")
|
405
221
|
def specialize(self):
|
406
|
-
"""Make changes to the decomposition to comply with any
|
222
|
+
"""Make changes to the decomposition to comply with any specializations.
|
407
223
|
|
408
|
-
|
409
|
-
|
410
|
-
|
224
|
+
This method will always raise a ``NotImplementedError`` because
|
225
|
+
there are no specializations to comply with in the current implementation.
|
226
|
+
"""
|
411
227
|
raise NotImplementedError
|
412
228
|
|
413
229
|
def circuit(
|
414
230
|
self, *, euler_basis: str | None = None, simplify: bool = False, atol: float = DEFAULT_ATOL
|
415
231
|
) -> QuantumCircuit:
|
416
232
|
"""Returns Weyl decomposition in circuit form."""
|
417
|
-
|
418
|
-
|
419
|
-
if euler_basis is None:
|
420
|
-
euler_basis = self._default_1q_basis
|
421
|
-
oneq_decompose = OneQubitEulerDecomposer(euler_basis)
|
422
|
-
c1l, c1r, c2l, c2r = (
|
423
|
-
oneq_decompose(k, simplify=simplify, atol=atol)
|
424
|
-
for k in (self.K1l, self.K1r, self.K2l, self.K2r)
|
233
|
+
circuit_sequence = self._inner_decomposition.circuit(
|
234
|
+
euler_basis=euler_basis, simplify=simplify, atol=atol
|
425
235
|
)
|
426
|
-
circ = QuantumCircuit(2, global_phase=
|
427
|
-
|
428
|
-
|
429
|
-
self._weyl_gate(simplify, circ, atol)
|
430
|
-
circ.compose(c1r, [0], inplace=True)
|
431
|
-
circ.compose(c1l, [1], inplace=True)
|
236
|
+
circ = QuantumCircuit(2, global_phase=circuit_sequence.global_phase)
|
237
|
+
for name, params, qubits in circuit_sequence:
|
238
|
+
getattr(circ, name)(*params, *qubits)
|
432
239
|
return circ
|
433
240
|
|
434
|
-
def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
|
435
|
-
"""Appends U_d(a, b, c) to the circuit.
|
436
|
-
|
437
|
-
Can be overridden in subclasses for special cases"""
|
438
|
-
if not simplify or abs(self.a) > atol:
|
439
|
-
circ.rxx(-self.a * 2, 0, 1)
|
440
|
-
if not simplify or abs(self.b) > atol:
|
441
|
-
circ.ryy(-self.b * 2, 0, 1)
|
442
|
-
if not simplify or abs(self.c) > atol:
|
443
|
-
circ.rzz(-self.c * 2, 0, 1)
|
444
|
-
|
445
241
|
def actual_fidelity(self, **kwargs) -> float:
|
446
242
|
"""Calculates the actual fidelity of the decomposed circuit to the input unitary."""
|
447
243
|
circ = self.circuit(**kwargs)
|
448
244
|
trace = np.trace(Operator(circ).data.T.conj() @ self.unitary_matrix)
|
449
245
|
return trace_to_fid(trace)
|
450
246
|
|
451
|
-
def __getnewargs_ex__(self):
|
452
|
-
return (self.unitary_matrix,), {"_unpickling": True}
|
453
|
-
|
454
247
|
def __repr__(self):
|
455
248
|
"""Represent with enough precision to allow copy-paste debugging of all corner cases"""
|
456
249
|
prefix = f"{type(self).__qualname__}.from_bytes("
|
@@ -461,12 +254,15 @@ class TwoQubitWeylDecomposition:
|
|
461
254
|
b64ascii[-1] += ","
|
462
255
|
pretty = [f"# {x.rstrip()}" for x in str(self).splitlines()]
|
463
256
|
indent = "\n" + " " * 4
|
257
|
+
specialization_variant = str(self._inner_decomposition.specialization).split(".")[1]
|
258
|
+
specialization_repr = f"{type(self).__qualname__}._specializations.{specialization_variant}"
|
464
259
|
lines = (
|
465
260
|
[prefix]
|
466
261
|
+ pretty
|
467
262
|
+ b64ascii
|
468
263
|
+ [
|
469
264
|
f"requested_fidelity={self.requested_fidelity},",
|
265
|
+
f"_specialization={specialization_repr},",
|
470
266
|
f"calculated_fidelity={self.calculated_fidelity},",
|
471
267
|
f"actual_fidelity={self.actual_fidelity()},",
|
472
268
|
f"abc={(self.a, self.b, self.c)})",
|
@@ -476,7 +272,12 @@ class TwoQubitWeylDecomposition:
|
|
476
272
|
|
477
273
|
@classmethod
|
478
274
|
def from_bytes(
|
479
|
-
cls,
|
275
|
+
cls,
|
276
|
+
bytes_in: bytes,
|
277
|
+
*,
|
278
|
+
requested_fidelity: float,
|
279
|
+
_specialization: two_qubit_decompose.Specialization | None = None,
|
280
|
+
**kwargs,
|
480
281
|
) -> "TwoQubitWeylDecomposition":
|
481
282
|
"""Decode bytes into :class:`.TwoQubitWeylDecomposition`."""
|
482
283
|
# Used by __repr__
|
@@ -484,121 +285,15 @@ class TwoQubitWeylDecomposition:
|
|
484
285
|
b64 = base64.decodebytes(bytes_in)
|
485
286
|
with io.BytesIO(b64) as f:
|
486
287
|
arr = np.load(f, allow_pickle=False)
|
487
|
-
return cls(arr, fidelity=requested_fidelity)
|
288
|
+
return cls(arr, fidelity=requested_fidelity, _specialization=_specialization)
|
488
289
|
|
489
290
|
def __str__(self):
|
490
|
-
|
291
|
+
specialization = str(self._inner_decomposition.specialization).split(".")[1]
|
292
|
+
pre = f"{self.__class__.__name__} [specialization={specialization}] (\n\t"
|
491
293
|
circ_indent = "\n\t".join(self.circuit(simplify=True).draw("text").lines(-1))
|
492
294
|
return f"{pre}{circ_indent}\n)"
|
493
295
|
|
494
296
|
|
495
|
-
class TwoQubitWeylIdEquiv(TwoQubitWeylDecomposition):
|
496
|
-
r""":math:`U \sim U_d(0,0,0) \sim Id`
|
497
|
-
|
498
|
-
This gate binds 0 parameters, we make it canonical by setting
|
499
|
-
:math:`K2_l = Id` , :math:`K2_r = Id`.
|
500
|
-
"""
|
501
|
-
|
502
|
-
def specialize(self):
|
503
|
-
self.a = self.b = self.c = 0.0
|
504
|
-
self.K1l = self.K1l @ self.K2l
|
505
|
-
self.K1r = self.K1r @ self.K2r
|
506
|
-
self.K2l = _id.copy()
|
507
|
-
self.K2r = _id.copy()
|
508
|
-
|
509
|
-
|
510
|
-
class TwoQubitWeylSWAPEquiv(TwoQubitWeylDecomposition):
|
511
|
-
r""":math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4) \sim \text{SWAP}`
|
512
|
-
|
513
|
-
This gate binds 0 parameters, we make it canonical by setting
|
514
|
-
:math:`K2_l = Id` , :math:`K2_r = Id`.
|
515
|
-
"""
|
516
|
-
|
517
|
-
def specialize(self):
|
518
|
-
if self.c > 0:
|
519
|
-
self.K1l = self.K1l @ self.K2r
|
520
|
-
self.K1r = self.K1r @ self.K2l
|
521
|
-
else:
|
522
|
-
self._is_flipped_from_original = True
|
523
|
-
self.K1l = self.K1l @ _ipz @ self.K2r
|
524
|
-
self.K1r = self.K1r @ _ipz @ self.K2l
|
525
|
-
self.global_phase = self.global_phase + np.pi / 2
|
526
|
-
self.a = self.b = self.c = np.pi / 4
|
527
|
-
self.K2l = _id.copy()
|
528
|
-
self.K2r = _id.copy()
|
529
|
-
|
530
|
-
def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
|
531
|
-
del self, simplify, atol # unused
|
532
|
-
circ.swap(0, 1)
|
533
|
-
circ.global_phase -= 3 * np.pi / 4
|
534
|
-
|
535
|
-
|
536
|
-
def _closest_partial_swap(a, b, c) -> float:
|
537
|
-
r"""A good approximation to the best value x to get the minimum
|
538
|
-
trace distance for :math:`U_d(x, x, x)` from :math:`U_d(a, b, c)`.
|
539
|
-
"""
|
540
|
-
m = (a + b + c) / 3
|
541
|
-
am, bm, cm = a - m, b - m, c - m
|
542
|
-
ab, bc, ca = a - b, b - c, c - a
|
543
|
-
|
544
|
-
return m + am * bm * cm * (6 + ab * ab + bc * bc + ca * ca) / 18
|
545
|
-
|
546
|
-
|
547
|
-
class TwoQubitWeylPartialSWAPEquiv(TwoQubitWeylDecomposition):
|
548
|
-
r""":math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim \text{SWAP}^\alpha`
|
549
|
-
This gate binds 3 parameters, we make it canonical by setting:
|
550
|
-
:math:`K2_l = Id`.
|
551
|
-
"""
|
552
|
-
|
553
|
-
def specialize(self):
|
554
|
-
self.a = self.b = self.c = _closest_partial_swap(self.a, self.b, self.c)
|
555
|
-
self.K1l = self.K1l @ self.K2l
|
556
|
-
self.K1r = self.K1r @ self.K2l
|
557
|
-
self.K2r = self.K2l.T.conj() @ self.K2r
|
558
|
-
self.K2l = _id.copy()
|
559
|
-
|
560
|
-
|
561
|
-
class TwoQubitWeylPartialSWAPFlipEquiv(TwoQubitWeylDecomposition):
|
562
|
-
r""":math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim \text{SWAP}^\alpha`
|
563
|
-
(a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv
|
564
|
-
similar to how :math:`x = (\pm \sqrt(x))^2`)
|
565
|
-
This gate binds 3 parameters, we make it canonical by setting:
|
566
|
-
:math:`K2_l = Id`.
|
567
|
-
"""
|
568
|
-
|
569
|
-
def specialize(self):
|
570
|
-
self.a = self.b = _closest_partial_swap(self.a, self.b, -self.c)
|
571
|
-
self.c = -self.a
|
572
|
-
self.K1l = self.K1l @ self.K2l
|
573
|
-
self.K1r = self.K1r @ _ipz @ self.K2l @ _ipz
|
574
|
-
self.K2r = _ipz @ self.K2l.T.conj() @ _ipz @ self.K2r
|
575
|
-
self.K2l = _id.copy()
|
576
|
-
|
577
|
-
|
578
|
-
_oneq_xyx = OneQubitEulerDecomposer("XYX")
|
579
|
-
_oneq_zyz = OneQubitEulerDecomposer("ZYZ")
|
580
|
-
|
581
|
-
|
582
|
-
class TwoQubitWeylControlledEquiv(TwoQubitWeylDecomposition):
|
583
|
-
r""":math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}`
|
584
|
-
This gate binds 4 parameters, we make it canonical by setting:
|
585
|
-
:math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` ,
|
586
|
-
:math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` .
|
587
|
-
"""
|
588
|
-
|
589
|
-
_default_1q_basis = "XYX"
|
590
|
-
|
591
|
-
def specialize(self):
|
592
|
-
self.b = self.c = 0
|
593
|
-
k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
|
594
|
-
k2rtheta, k2rphi, k2rlambda, k2rphase = _oneq_xyx.angles_and_phase(self.K2r)
|
595
|
-
self.global_phase += k2lphase + k2rphase
|
596
|
-
self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
|
597
|
-
self.K1r = self.K1r @ np.asarray(RXGate(k2rphi))
|
598
|
-
self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
|
599
|
-
self.K2r = np.asarray(RYGate(k2rtheta)) @ np.asarray(RXGate(k2rlambda))
|
600
|
-
|
601
|
-
|
602
297
|
class TwoQubitControlledUDecomposer:
|
603
298
|
r"""Decompose two-qubit unitary in terms of a desired
|
604
299
|
:math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}`
|
@@ -627,18 +322,23 @@ class TwoQubitControlledUDecomposer:
|
|
627
322
|
|
628
323
|
circ = QuantumCircuit(2)
|
629
324
|
circ.rxx(test_angle, 0, 1)
|
630
|
-
decomposer_rxx =
|
325
|
+
decomposer_rxx = TwoQubitWeylDecomposition(
|
326
|
+
Operator(circ).data,
|
327
|
+
fidelity=None,
|
328
|
+
_specialization=two_qubit_decompose.Specialization.ControlledEquiv,
|
329
|
+
)
|
631
330
|
|
632
331
|
circ = QuantumCircuit(2)
|
633
332
|
circ.append(rxx_equivalent_gate(test_angle), qargs=[0, 1])
|
634
|
-
decomposer_equiv =
|
333
|
+
decomposer_equiv = TwoQubitWeylDecomposition(
|
334
|
+
Operator(circ).data,
|
335
|
+
fidelity=None,
|
336
|
+
_specialization=two_qubit_decompose.Specialization.ControlledEquiv,
|
337
|
+
)
|
635
338
|
|
636
339
|
scale = decomposer_rxx.a / decomposer_equiv.a
|
637
340
|
|
638
|
-
if (
|
639
|
-
not isinstance(decomp, TwoQubitWeylControlledEquiv)
|
640
|
-
or abs(decomp.a * 2 - test_angle / scale) > atol
|
641
|
-
):
|
341
|
+
if abs(decomp.a * 2 - test_angle / scale) > atol:
|
642
342
|
raise QiskitError(
|
643
343
|
f"{rxx_equivalent_gate.__name__} is not equivalent to an RXXGate."
|
644
344
|
)
|
@@ -706,7 +406,7 @@ class TwoQubitControlledUDecomposer:
|
|
706
406
|
|
707
407
|
circ = QuantumCircuit(2)
|
708
408
|
circ.append(self.rxx_equivalent_gate(self.scale * angle), qargs=[0, 1])
|
709
|
-
decomposer_inv =
|
409
|
+
decomposer_inv = TwoQubitWeylDecomposition(Operator(circ).data)
|
710
410
|
|
711
411
|
oneq_decompose = OneQubitEulerDecomposer("ZYZ")
|
712
412
|
|
@@ -763,97 +463,6 @@ class TwoQubitControlledUDecomposer:
|
|
763
463
|
return circ
|
764
464
|
|
765
465
|
|
766
|
-
class TwoQubitWeylMirrorControlledEquiv(TwoQubitWeylDecomposition):
|
767
|
-
r""":math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot \text{Ctrl-U}`
|
768
|
-
|
769
|
-
This gate binds 4 parameters, we make it canonical by setting:
|
770
|
-
:math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)`.
|
771
|
-
"""
|
772
|
-
|
773
|
-
def specialize(self):
|
774
|
-
self.a = self.b = np.pi / 4
|
775
|
-
k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_zyz.angles_and_phase(self.K2l)
|
776
|
-
k2rtheta, k2rphi, k2rlambda, k2rphase = _oneq_zyz.angles_and_phase(self.K2r)
|
777
|
-
self.global_phase += k2lphase + k2rphase
|
778
|
-
self.K1r = self.K1r @ np.asarray(RZGate(k2lphi))
|
779
|
-
self.K1l = self.K1l @ np.asarray(RZGate(k2rphi))
|
780
|
-
self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RZGate(k2llambda))
|
781
|
-
self.K2r = np.asarray(RYGate(k2rtheta)) @ np.asarray(RZGate(k2rlambda))
|
782
|
-
|
783
|
-
def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
|
784
|
-
circ.swap(0, 1)
|
785
|
-
circ.rzz((np.pi / 4 - self.c) * 2, 0, 1)
|
786
|
-
circ.global_phase += np.pi / 4
|
787
|
-
|
788
|
-
|
789
|
-
# These next 3 gates use the definition of fSim from https://arxiv.org/pdf/2001.08343.pdf eq (1)
|
790
|
-
class TwoQubitWeylfSimaabEquiv(TwoQubitWeylDecomposition):
|
791
|
-
r""":math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|`
|
792
|
-
|
793
|
-
This gate binds 5 parameters, we make it canonical by setting:
|
794
|
-
:math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`.
|
795
|
-
"""
|
796
|
-
|
797
|
-
def specialize(self):
|
798
|
-
self.a = self.b = (self.a + self.b) / 2
|
799
|
-
k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_zyz.angles_and_phase(self.K2l)
|
800
|
-
self.global_phase += k2lphase
|
801
|
-
self.K1r = self.K1r @ np.asarray(RZGate(k2lphi))
|
802
|
-
self.K1l = self.K1l @ np.asarray(RZGate(k2lphi))
|
803
|
-
self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RZGate(k2llambda))
|
804
|
-
self.K2r = np.asarray(RZGate(-k2lphi)) @ self.K2r
|
805
|
-
|
806
|
-
|
807
|
-
class TwoQubitWeylfSimabbEquiv(TwoQubitWeylDecomposition):
|
808
|
-
r""":math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0`
|
809
|
-
|
810
|
-
This gate binds 5 parameters, we make it canonical by setting:
|
811
|
-
:math:`K2_l = Ry(\theta_l)Rx(\lambda_l)`.
|
812
|
-
"""
|
813
|
-
|
814
|
-
_default_1q_basis = "XYX"
|
815
|
-
|
816
|
-
def specialize(self):
|
817
|
-
self.b = self.c = (self.b + self.c) / 2
|
818
|
-
k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
|
819
|
-
self.global_phase += k2lphase
|
820
|
-
self.K1r = self.K1r @ np.asarray(RXGate(k2lphi))
|
821
|
-
self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
|
822
|
-
self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
|
823
|
-
self.K2r = np.asarray(RXGate(-k2lphi)) @ self.K2r
|
824
|
-
|
825
|
-
|
826
|
-
class TwoQubitWeylfSimabmbEquiv(TwoQubitWeylDecomposition):
|
827
|
-
r""":math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0`
|
828
|
-
|
829
|
-
This gate binds 5 parameters, we make it canonical by setting:
|
830
|
-
:math:`K2_l = Ry(\theta_l)Rx(\lambda_l)`.
|
831
|
-
"""
|
832
|
-
|
833
|
-
_default_1q_basis = "XYX"
|
834
|
-
|
835
|
-
def specialize(self):
|
836
|
-
self.b = (self.b - self.c) / 2
|
837
|
-
self.c = -self.b
|
838
|
-
k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
|
839
|
-
self.global_phase += k2lphase
|
840
|
-
self.K1r = self.K1r @ _ipz @ np.asarray(RXGate(k2lphi)) @ _ipz
|
841
|
-
self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
|
842
|
-
self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
|
843
|
-
self.K2r = _ipz @ np.asarray(RXGate(-k2lphi)) @ _ipz @ self.K2r
|
844
|
-
|
845
|
-
|
846
|
-
class TwoQubitWeylGeneral(TwoQubitWeylDecomposition):
|
847
|
-
"""U has no special symmetry.
|
848
|
-
|
849
|
-
This gate binds all 6 possible parameters, so there is no need to make the single-qubit
|
850
|
-
pre-/post-gates canonical.
|
851
|
-
"""
|
852
|
-
|
853
|
-
def specialize(self):
|
854
|
-
pass # Nothing to do
|
855
|
-
|
856
|
-
|
857
466
|
def Ud(a, b, c):
|
858
467
|
r"""Generates the array :math:`e^{(i a XX + i b YY + i c ZZ)}`"""
|
859
468
|
return np.array(
|
@@ -904,6 +513,7 @@ class TwoQubitBasisDecomposer:
|
|
904
513
|
If ``False``, don't attempt optimization. If ``None``, attempt optimization but don't raise
|
905
514
|
if unknown.
|
906
515
|
|
516
|
+
|
907
517
|
.. automethod:: __call__
|
908
518
|
"""
|
909
519
|
|
@@ -917,152 +527,34 @@ class TwoQubitBasisDecomposer:
|
|
917
527
|
self.gate = gate
|
918
528
|
self.basis_fidelity = basis_fidelity
|
919
529
|
self.pulse_optimize = pulse_optimize
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
[
|
935
|
-
[-1j * cmath.exp(-1j * b), cmath.exp(-1j * b)],
|
936
|
-
[-1j * cmath.exp(1j * b), -cmath.exp(1j * b)],
|
937
|
-
],
|
938
|
-
dtype=complex,
|
939
|
-
)
|
940
|
-
)
|
941
|
-
K11r = (
|
942
|
-
1
|
943
|
-
/ math.sqrt(2)
|
944
|
-
* np.array(
|
945
|
-
[
|
946
|
-
[1j * cmath.exp(-1j * b), -cmath.exp(-1j * b)],
|
947
|
-
[cmath.exp(1j * b), -1j * cmath.exp(1j * b)],
|
948
|
-
],
|
949
|
-
dtype=complex,
|
950
|
-
)
|
951
|
-
)
|
952
|
-
K12l = 1 / (1 + 1j) * np.array([[1j, 1j], [-1, 1]], dtype=complex)
|
953
|
-
K12r = 1 / math.sqrt(2) * np.array([[1j, 1], [-1, -1j]], dtype=complex)
|
954
|
-
K32lK21l = (
|
955
|
-
1
|
956
|
-
/ math.sqrt(2)
|
957
|
-
* np.array(
|
958
|
-
[
|
959
|
-
[1 + 1j * np.cos(2 * b), 1j * np.sin(2 * b)],
|
960
|
-
[1j * np.sin(2 * b), 1 - 1j * np.cos(2 * b)],
|
961
|
-
],
|
962
|
-
dtype=complex,
|
963
|
-
)
|
964
|
-
)
|
965
|
-
K21r = (
|
966
|
-
1
|
967
|
-
/ (1 - 1j)
|
968
|
-
* np.array(
|
969
|
-
[
|
970
|
-
[-1j * cmath.exp(-2j * b), cmath.exp(-2j * b)],
|
971
|
-
[1j * cmath.exp(2j * b), cmath.exp(2j * b)],
|
972
|
-
],
|
973
|
-
dtype=complex,
|
974
|
-
)
|
975
|
-
)
|
976
|
-
K22l = 1 / math.sqrt(2) * np.array([[1, -1], [1, 1]], dtype=complex)
|
977
|
-
K22r = np.array([[0, 1], [-1, 0]], dtype=complex)
|
978
|
-
K31l = (
|
979
|
-
1
|
980
|
-
/ math.sqrt(2)
|
981
|
-
* np.array(
|
982
|
-
[[cmath.exp(-1j * b), cmath.exp(-1j * b)], [-cmath.exp(1j * b), cmath.exp(1j * b)]],
|
983
|
-
dtype=complex,
|
984
|
-
)
|
985
|
-
)
|
986
|
-
K31r = 1j * np.array([[cmath.exp(1j * b), 0], [0, -cmath.exp(-1j * b)]], dtype=complex)
|
987
|
-
K32r = (
|
988
|
-
1
|
989
|
-
/ (1 - 1j)
|
990
|
-
* np.array(
|
991
|
-
[
|
992
|
-
[cmath.exp(1j * b), -cmath.exp(-1j * b)],
|
993
|
-
[-1j * cmath.exp(1j * b), -1j * cmath.exp(-1j * b)],
|
994
|
-
],
|
995
|
-
dtype=complex,
|
996
|
-
)
|
530
|
+
# Use cx as gate name for pulse optimal decomposition detection
|
531
|
+
# otherwise use USER_GATE as a unique key to support custom gates
|
532
|
+
# including parameterized gates like UnitaryGate.
|
533
|
+
if isinstance(gate, CXGate):
|
534
|
+
gate_name = "cx"
|
535
|
+
else:
|
536
|
+
gate_name = "USER_GATE"
|
537
|
+
|
538
|
+
self._inner_decomposer = two_qubit_decompose.TwoQubitBasisDecomposer(
|
539
|
+
gate_name,
|
540
|
+
Operator(gate).data,
|
541
|
+
basis_fidelity=basis_fidelity,
|
542
|
+
euler_basis=euler_basis,
|
543
|
+
pulse_optimize=pulse_optimize,
|
997
544
|
)
|
998
|
-
|
999
|
-
k1rd = basis.K1r.T.conj()
|
1000
|
-
k2ld = basis.K2l.T.conj()
|
1001
|
-
k2rd = basis.K2r.T.conj()
|
1002
|
-
|
1003
|
-
# Pre-build the fixed parts of the matrices used in 3-part decomposition
|
1004
|
-
self.u0l = K31l.dot(k1ld)
|
1005
|
-
self.u0r = K31r.dot(k1rd)
|
1006
|
-
self.u1l = k2ld.dot(K32lK21l).dot(k1ld)
|
1007
|
-
self.u1ra = k2rd.dot(K32r)
|
1008
|
-
self.u1rb = K21r.dot(k1rd)
|
1009
|
-
self.u2la = k2ld.dot(K22l)
|
1010
|
-
self.u2lb = K11l.dot(k1ld)
|
1011
|
-
self.u2ra = k2rd.dot(K22r)
|
1012
|
-
self.u2rb = K11r.dot(k1rd)
|
1013
|
-
self.u3l = k2ld.dot(K12l)
|
1014
|
-
self.u3r = k2rd.dot(K12r)
|
1015
|
-
|
1016
|
-
# Pre-build the fixed parts of the matrices used in the 2-part decomposition
|
1017
|
-
self.q0l = K12l.T.conj().dot(k1ld)
|
1018
|
-
self.q0r = K12r.T.conj().dot(_ipz).dot(k1rd)
|
1019
|
-
self.q1la = k2ld.dot(K11l.T.conj())
|
1020
|
-
self.q1lb = K11l.dot(k1ld)
|
1021
|
-
self.q1ra = k2rd.dot(_ipz).dot(K11r.T.conj())
|
1022
|
-
self.q1rb = K11r.dot(k1rd)
|
1023
|
-
self.q2l = k2ld.dot(K12l)
|
1024
|
-
self.q2r = k2rd.dot(K12r)
|
1025
|
-
|
1026
|
-
# Decomposition into different number of gates
|
1027
|
-
# In the future could use different decomposition functions for different basis classes, etc
|
545
|
+
self.is_supercontrolled = self._inner_decomposer.super_controlled
|
1028
546
|
if not self.is_supercontrolled:
|
1029
547
|
warnings.warn(
|
1030
|
-
"Only know how to decompose properly for supercontrolled basis gate.
|
1031
|
-
"This gate is ~Ud({}, {}, {})".format(basis.a, basis.b, basis.c),
|
548
|
+
"Only know how to decompose properly for a supercontrolled basis gate.",
|
1032
549
|
stacklevel=2,
|
1033
550
|
)
|
1034
|
-
self.decomposition_fns = [
|
1035
|
-
self.decomp0,
|
1036
|
-
self.decomp1,
|
1037
|
-
self.decomp2_supercontrolled,
|
1038
|
-
self.decomp3_supercontrolled,
|
1039
|
-
]
|
1040
|
-
self._rqc = None
|
1041
551
|
|
1042
|
-
def
|
1043
|
-
|
1044
|
-
|
1045
|
-
for a different number of basis gates.
|
552
|
+
def num_basis_gates(self, unitary):
|
553
|
+
"""Computes the number of basis gates needed in
|
554
|
+
a decomposition of input unitary
|
1046
555
|
"""
|
1047
|
-
|
1048
|
-
|
1049
|
-
# This doesn't come up if either c1==0 or c2==0 but otherwise be careful.
|
1050
|
-
ta, tb, tc = target.a, target.b, target.c
|
1051
|
-
bb = self.basis.b
|
1052
|
-
return [
|
1053
|
-
4
|
1054
|
-
* complex(
|
1055
|
-
math.cos(ta) * math.cos(tb) * math.cos(tc),
|
1056
|
-
math.sin(ta) * math.sin(tb) * math.sin(tc),
|
1057
|
-
),
|
1058
|
-
4
|
1059
|
-
* complex(
|
1060
|
-
math.cos(math.pi / 4 - ta) * math.cos(bb - tb) * math.cos(tc),
|
1061
|
-
math.sin(math.pi / 4 - ta) * math.sin(bb - tb) * math.sin(tc),
|
1062
|
-
),
|
1063
|
-
4 * math.cos(tc),
|
1064
|
-
4,
|
1065
|
-
]
|
556
|
+
unitary = np.asarray(unitary, dtype=complex)
|
557
|
+
return self._inner_decomposer.num_basis_gates(unitary)
|
1066
558
|
|
1067
559
|
@staticmethod
|
1068
560
|
def decomp0(target):
|
@@ -1078,9 +570,7 @@ class TwoQubitBasisDecomposer:
|
|
1078
570
|
which is optimal for all targets and bases
|
1079
571
|
"""
|
1080
572
|
|
1081
|
-
|
1082
|
-
U0r = target.K1r.dot(target.K2r)
|
1083
|
-
return U0r, U0l
|
573
|
+
return two_qubit_decompose.TwoQubitBasisDecomposer.decomp0(target)
|
1084
574
|
|
1085
575
|
def decomp1(self, target):
|
1086
576
|
r"""Decompose target :math:`\sim U_d(x, y, z)` with :math:`1` use of the basis gate
|
@@ -1094,13 +584,7 @@ class TwoQubitBasisDecomposer:
|
|
1094
584
|
|
1095
585
|
which is optimal for all targets and bases with ``z==0`` or ``c==0``.
|
1096
586
|
"""
|
1097
|
-
|
1098
|
-
U0l = target.K1l.dot(self.basis.K1l.T.conj())
|
1099
|
-
U0r = target.K1r.dot(self.basis.K1r.T.conj())
|
1100
|
-
U1l = self.basis.K2l.T.conj().dot(target.K2l)
|
1101
|
-
U1r = self.basis.K2r.T.conj().dot(target.K2r)
|
1102
|
-
|
1103
|
-
return U1r, U1l, U0r, U0l
|
587
|
+
return self._inner_decomposer.decomp1(target)
|
1104
588
|
|
1105
589
|
def decomp2_supercontrolled(self, target):
|
1106
590
|
r"""
|
@@ -1119,15 +603,7 @@ class TwoQubitBasisDecomposer:
|
|
1119
603
|
This is an exact decomposition for supercontrolled basis and target :math:`\sim U_d(x, y, 0)`.
|
1120
604
|
No guarantees for non-supercontrolled basis.
|
1121
605
|
"""
|
1122
|
-
|
1123
|
-
U0l = target.K1l.dot(self.q0l)
|
1124
|
-
U0r = target.K1r.dot(self.q0r)
|
1125
|
-
U1l = self.q1la.dot(rz_array(-2 * target.a)).dot(self.q1lb)
|
1126
|
-
U1r = self.q1ra.dot(rz_array(2 * target.b)).dot(self.q1rb)
|
1127
|
-
U2l = self.q2l.dot(target.K2l)
|
1128
|
-
U2r = self.q2r.dot(target.K2r)
|
1129
|
-
|
1130
|
-
return U2r, U2l, U1r, U1l, U0r, U0l
|
606
|
+
return self._inner_decomposer.decomp2_supercontrolled(target)
|
1131
607
|
|
1132
608
|
def decomp3_supercontrolled(self, target):
|
1133
609
|
r"""
|
@@ -1135,26 +611,17 @@ class TwoQubitBasisDecomposer:
|
|
1135
611
|
This is an exact decomposition for supercontrolled basis :math:`\sim U_d(\pi/4, b, 0)`, all b,
|
1136
612
|
and any target. No guarantees for non-supercontrolled basis.
|
1137
613
|
"""
|
1138
|
-
|
1139
|
-
U0l = target.K1l.dot(self.u0l)
|
1140
|
-
U0r = target.K1r.dot(self.u0r)
|
1141
|
-
U1l = self.u1l
|
1142
|
-
U1r = self.u1ra.dot(rz_array(-2 * target.c)).dot(self.u1rb)
|
1143
|
-
U2l = self.u2la.dot(rz_array(-2 * target.a)).dot(self.u2lb)
|
1144
|
-
U2r = self.u2ra.dot(rz_array(2 * target.b)).dot(self.u2rb)
|
1145
|
-
U3l = self.u3l.dot(target.K2l)
|
1146
|
-
U3r = self.u3r.dot(target.K2r)
|
1147
|
-
|
1148
|
-
return U3r, U3l, U2r, U2l, U1r, U1l, U0r, U0l
|
614
|
+
return self._inner_decomposer.decomp3_supercontrolled(target)
|
1149
615
|
|
1150
616
|
def __call__(
|
1151
617
|
self,
|
1152
618
|
unitary: Operator | np.ndarray,
|
1153
619
|
basis_fidelity: float | None = None,
|
1154
620
|
approximate: bool = True,
|
621
|
+
use_dag: bool = False,
|
1155
622
|
*,
|
1156
623
|
_num_basis_uses: int | None = None,
|
1157
|
-
) -> QuantumCircuit:
|
624
|
+
) -> QuantumCircuit | DAGCircuit:
|
1158
625
|
r"""Decompose a two-qubit ``unitary`` over fixed basis and :math:`SU(2)` using the best
|
1159
626
|
approximation given that each basis application has a finite ``basis_fidelity``.
|
1160
627
|
|
@@ -1163,6 +630,8 @@ class TwoQubitBasisDecomposer:
|
|
1163
630
|
basis_fidelity (float or None): Fidelity to be assumed for applications of KAK Gate.
|
1164
631
|
If given, overrides ``basis_fidelity`` given at init.
|
1165
632
|
approximate (bool): Approximates if basis fidelities are less than 1.0.
|
633
|
+
use_dag (bool): If true a :class:`.DAGCircuit` is returned instead of a
|
634
|
+
:class:`QuantumCircuit` when this class is called.
|
1166
635
|
_num_basis_uses (int): force a particular approximation by passing a number in [0, 3].
|
1167
636
|
|
1168
637
|
Returns:
|
@@ -1171,313 +640,55 @@ class TwoQubitBasisDecomposer:
|
|
1171
640
|
Raises:
|
1172
641
|
QiskitError: if ``pulse_optimize`` is True but we don't know how to do it.
|
1173
642
|
"""
|
1174
|
-
basis_fidelity = basis_fidelity or self.basis_fidelity
|
1175
|
-
if approximate is False:
|
1176
|
-
basis_fidelity = 1.0
|
1177
|
-
unitary = np.asarray(unitary, dtype=complex)
|
1178
643
|
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
best_nbasis = _num_basis_uses
|
1186
|
-
decomposition = self.decomposition_fns[best_nbasis](target_decomposed)
|
1187
|
-
|
1188
|
-
# attempt pulse optimal decomposition
|
1189
|
-
try:
|
1190
|
-
if self.pulse_optimize in {None, True}:
|
1191
|
-
return_circuit = self._pulse_optimal_chooser(
|
1192
|
-
best_nbasis, decomposition, target_decomposed
|
1193
|
-
)
|
1194
|
-
if return_circuit:
|
1195
|
-
return return_circuit
|
1196
|
-
except QiskitError:
|
1197
|
-
if self.pulse_optimize:
|
1198
|
-
raise
|
1199
|
-
|
1200
|
-
# do default decomposition
|
644
|
+
sequence = self._inner_decomposer(
|
645
|
+
np.asarray(unitary, dtype=complex),
|
646
|
+
basis_fidelity,
|
647
|
+
approximate,
|
648
|
+
_num_basis_uses=_num_basis_uses,
|
649
|
+
)
|
1201
650
|
q = QuantumRegister(2)
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
def _pulse_optimal_chooser(
|
1217
|
-
self, best_nbasis, decomposition, target_decomposed
|
1218
|
-
) -> QuantumCircuit:
|
1219
|
-
"""Determine method to find pulse optimal circuit. This method may be
|
1220
|
-
removed once a more general approach is used.
|
1221
|
-
|
1222
|
-
Returns:
|
1223
|
-
QuantumCircuit: pulse optimal quantum circuit.
|
1224
|
-
None: Probably ``nbasis==1`` and original circuit is fine.
|
1225
|
-
|
1226
|
-
Raises:
|
1227
|
-
QiskitError: Decomposition for selected basis not implemented.
|
1228
|
-
"""
|
1229
|
-
circuit = None
|
1230
|
-
if self.pulse_optimize and best_nbasis in {0, 1}:
|
1231
|
-
# already pulse optimal
|
1232
|
-
return None
|
1233
|
-
elif self.pulse_optimize and best_nbasis > 3:
|
1234
|
-
raise QiskitError(
|
1235
|
-
f"Unexpected number of entangling gates ({best_nbasis}) in decomposition."
|
1236
|
-
)
|
1237
|
-
if self._decomposer1q.basis in {"ZSX", "ZSXX"}:
|
1238
|
-
if isinstance(self.gate, CXGate):
|
1239
|
-
if best_nbasis == 3:
|
1240
|
-
circuit = self._get_sx_vz_3cx_efficient_euler(decomposition, target_decomposed)
|
1241
|
-
elif best_nbasis == 2:
|
1242
|
-
circuit = self._get_sx_vz_2cx_efficient_euler(decomposition, target_decomposed)
|
1243
|
-
else:
|
1244
|
-
raise QiskitError("pulse_optimizer currently only works with CNOT entangling gate")
|
651
|
+
if use_dag:
|
652
|
+
from qiskit.dagcircuit.dagcircuit import DAGCircuit
|
653
|
+
|
654
|
+
dag = DAGCircuit()
|
655
|
+
dag.global_phase = sequence.global_phase
|
656
|
+
dag.add_qreg(q)
|
657
|
+
for name, params, qubits in sequence:
|
658
|
+
if name == "USER_GATE":
|
659
|
+
dag.apply_operation_back(self.gate, tuple(q[x] for x in qubits), check=False)
|
660
|
+
else:
|
661
|
+
gate = GATE_NAME_MAP[name](*params)
|
662
|
+
dag.apply_operation_back(gate, tuple(q[x] for x in qubits), check=False)
|
663
|
+
return dag
|
1245
664
|
else:
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
665
|
+
circ = QuantumCircuit(q, global_phase=sequence.global_phase)
|
666
|
+
for name, params, qubits in sequence:
|
667
|
+
try:
|
668
|
+
getattr(circ, name)(*params, *qubits)
|
669
|
+
except AttributeError as exc:
|
670
|
+
if name == "USER_GATE":
|
671
|
+
circ.append(self.gate, qubits)
|
672
|
+
elif name == "u3":
|
673
|
+
gate = U3Gate(*params)
|
674
|
+
circ.append(gate, qubits)
|
675
|
+
elif name == "u2":
|
676
|
+
gate = U2Gate(*params)
|
677
|
+
circ.append(gate, qubits)
|
678
|
+
elif name == "u1":
|
679
|
+
gate = U1Gate(*params)
|
680
|
+
circ.append(gate, qubits)
|
681
|
+
else:
|
682
|
+
raise QiskitError(f"Unknown gate {name}") from exc
|
683
|
+
|
684
|
+
return circ
|
1251
685
|
|
1252
|
-
def
|
1253
|
-
"""
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
This first decomposes each unitary from the KAK decomposition into ZXZ on the source
|
1258
|
-
qubit of the CNOTs and XZX on the targets in order to commute operators to beginning and
|
1259
|
-
end of decomposition. The beginning and ending single qubit gates are then
|
1260
|
-
collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
|
1261
|
-
if performance is a concern.
|
1262
|
-
"""
|
1263
|
-
best_nbasis = 2 # by assumption
|
1264
|
-
num_1q_uni = len(decomposition)
|
1265
|
-
# list of euler angle decompositions on qubits 0 and 1
|
1266
|
-
euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
|
1267
|
-
euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
|
1268
|
-
global_phase = 0.0
|
1269
|
-
|
1270
|
-
# decompose source unitaries to zxz
|
1271
|
-
zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
|
1272
|
-
for iqubit, decomp in enumerate(decomposition[0::2]):
|
1273
|
-
euler_angles = zxz_decomposer.angles_and_phase(decomp)
|
1274
|
-
euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
|
1275
|
-
global_phase += euler_angles[3]
|
1276
|
-
# decompose target unitaries to xzx
|
1277
|
-
xzx_decomposer = OneQubitEulerDecomposer("XZX")
|
1278
|
-
for iqubit, decomp in enumerate(decomposition[1::2]):
|
1279
|
-
euler_angles = xzx_decomposer.angles_and_phase(decomp)
|
1280
|
-
euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
|
1281
|
-
global_phase += euler_angles[3]
|
1282
|
-
qc = QuantumCircuit(2)
|
1283
|
-
qc.global_phase = target_decomposed.global_phase
|
1284
|
-
qc.global_phase -= best_nbasis * self.basis.global_phase
|
1285
|
-
qc.global_phase += global_phase
|
1286
|
-
|
1287
|
-
# TODO: make this more effecient to avoid double decomposition
|
1288
|
-
# prepare beginning 0th qubit local unitary
|
1289
|
-
circ = QuantumCircuit(1)
|
1290
|
-
circ.rz(euler_q0[0][0], 0)
|
1291
|
-
circ.rx(euler_q0[0][1], 0)
|
1292
|
-
circ.rz(euler_q0[0][2] + euler_q0[1][0] + math.pi / 2, 0)
|
1293
|
-
# re-decompose to basis of 1q decomposer
|
1294
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1295
|
-
qc.compose(qceuler, [0], inplace=True)
|
1296
|
-
|
1297
|
-
# prepare beginning 1st qubit local unitary
|
1298
|
-
circ = QuantumCircuit(1)
|
1299
|
-
circ.rx(euler_q1[0][0], 0)
|
1300
|
-
circ.rz(euler_q1[0][1], 0)
|
1301
|
-
circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
|
1302
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1303
|
-
qc.compose(qceuler, [1], inplace=True)
|
1304
|
-
|
1305
|
-
qc.cx(0, 1)
|
1306
|
-
# the central decompositions are dependent on the specific form of the
|
1307
|
-
# unitaries coming out of the two qubit decomposer which have some flexibility
|
1308
|
-
# of choice.
|
1309
|
-
qc.sx(0)
|
1310
|
-
qc.rz(euler_q0[1][1] - math.pi, 0)
|
1311
|
-
qc.sx(0)
|
1312
|
-
qc.rz(euler_q1[1][1], 1)
|
1313
|
-
qc.global_phase += math.pi / 2
|
1314
|
-
|
1315
|
-
qc.cx(0, 1)
|
1316
|
-
|
1317
|
-
circ = QuantumCircuit(1)
|
1318
|
-
circ.rz(euler_q0[1][2] + euler_q0[2][0] + math.pi / 2, 0)
|
1319
|
-
circ.rx(euler_q0[2][1], 0)
|
1320
|
-
circ.rz(euler_q0[2][2], 0)
|
1321
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1322
|
-
qc.compose(qceuler, [0], inplace=True)
|
1323
|
-
circ = QuantumCircuit(1)
|
1324
|
-
circ.rx(euler_q1[1][2] + euler_q1[2][0], 0)
|
1325
|
-
circ.rz(euler_q1[2][1], 0)
|
1326
|
-
circ.rx(euler_q1[2][2], 0)
|
1327
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1328
|
-
qc.compose(qceuler, [1], inplace=True)
|
1329
|
-
|
1330
|
-
return qc
|
1331
|
-
|
1332
|
-
def _get_sx_vz_3cx_efficient_euler(self, decomposition, target_decomposed):
|
1333
|
-
"""
|
1334
|
-
Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming
|
1335
|
-
three CNOT gates are needed.
|
1336
|
-
|
1337
|
-
This first decomposes each unitary from the KAK decomposition into ZXZ on the source
|
1338
|
-
qubit of the CNOTs and XZX on the targets in order commute operators to beginning and
|
1339
|
-
end of decomposition. Inserting Hadamards reverses the direction of the CNOTs and transforms
|
1340
|
-
a variable Rx -> variable virtual Rz. The beginning and ending single qubit gates are then
|
1341
|
-
collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
|
1342
|
-
if performance is a concern.
|
1343
|
-
"""
|
1344
|
-
best_nbasis = 3 # by assumption
|
1345
|
-
num_1q_uni = len(decomposition)
|
1346
|
-
# create structure to hold euler angles: 1st index represents unitary "group" wrt cx
|
1347
|
-
# 2nd index represents index of euler triple.
|
1348
|
-
euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
|
1349
|
-
euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
|
1350
|
-
global_phase = 0.0
|
1351
|
-
atol = 1e-10 # absolute tolerance for floats
|
1352
|
-
|
1353
|
-
# decompose source unitaries to zxz
|
1354
|
-
zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
|
1355
|
-
for iqubit, decomp in enumerate(decomposition[0::2]):
|
1356
|
-
euler_angles = zxz_decomposer.angles_and_phase(decomp)
|
1357
|
-
euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
|
1358
|
-
global_phase += euler_angles[3]
|
1359
|
-
# decompose target unitaries to xzx
|
1360
|
-
xzx_decomposer = OneQubitEulerDecomposer("XZX")
|
1361
|
-
for iqubit, decomp in enumerate(decomposition[1::2]):
|
1362
|
-
euler_angles = xzx_decomposer.angles_and_phase(decomp)
|
1363
|
-
euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
|
1364
|
-
global_phase += euler_angles[3]
|
1365
|
-
|
1366
|
-
qc = QuantumCircuit(2)
|
1367
|
-
qc.global_phase = target_decomposed.global_phase
|
1368
|
-
qc.global_phase -= best_nbasis * self.basis.global_phase
|
1369
|
-
qc.global_phase += global_phase
|
1370
|
-
|
1371
|
-
x12 = euler_q0[1][2] + euler_q0[2][0]
|
1372
|
-
x12_isNonZero = not math.isclose(x12, 0, abs_tol=atol)
|
1373
|
-
x12_isOddMult = None
|
1374
|
-
x12_isPiMult = math.isclose(math.sin(x12), 0, abs_tol=atol)
|
1375
|
-
if x12_isPiMult:
|
1376
|
-
x12_isOddMult = math.isclose(math.cos(x12), -1, abs_tol=atol)
|
1377
|
-
x12_phase = math.pi * math.cos(x12)
|
1378
|
-
x02_add = x12 - euler_q0[1][0]
|
1379
|
-
x12_isHalfPi = math.isclose(x12, math.pi / 2, abs_tol=atol)
|
1380
|
-
|
1381
|
-
# TODO: make this more effecient to avoid double decomposition
|
1382
|
-
circ = QuantumCircuit(1)
|
1383
|
-
circ.rz(euler_q0[0][0], 0)
|
1384
|
-
circ.rx(euler_q0[0][1], 0)
|
1385
|
-
if x12_isNonZero and x12_isPiMult:
|
1386
|
-
circ.rz(euler_q0[0][2] - x02_add, 0)
|
1387
|
-
else:
|
1388
|
-
circ.rz(euler_q0[0][2] + euler_q0[1][0], 0)
|
1389
|
-
circ.h(0)
|
1390
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1391
|
-
qc.compose(qceuler, [0], inplace=True)
|
1392
|
-
|
1393
|
-
circ = QuantumCircuit(1)
|
1394
|
-
circ.rx(euler_q1[0][0], 0)
|
1395
|
-
circ.rz(euler_q1[0][1], 0)
|
1396
|
-
circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
|
1397
|
-
circ.h(0)
|
1398
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1399
|
-
qc.compose(qceuler, [1], inplace=True)
|
1400
|
-
|
1401
|
-
qc.cx(1, 0)
|
1402
|
-
|
1403
|
-
if x12_isPiMult:
|
1404
|
-
# even or odd multiple
|
1405
|
-
if x12_isNonZero:
|
1406
|
-
qc.global_phase += x12_phase
|
1407
|
-
if x12_isNonZero and x12_isOddMult:
|
1408
|
-
qc.rz(-euler_q0[1][1], 0)
|
1409
|
-
else:
|
1410
|
-
qc.rz(euler_q0[1][1], 0)
|
1411
|
-
qc.global_phase += math.pi
|
1412
|
-
if x12_isHalfPi:
|
1413
|
-
qc.sx(0)
|
1414
|
-
qc.global_phase -= math.pi / 4
|
1415
|
-
elif x12_isNonZero and not x12_isPiMult:
|
1416
|
-
# this is non-optimal but doesn't seem to occur currently
|
1417
|
-
if self.pulse_optimize is None:
|
1418
|
-
qc.compose(self._decomposer1q(Operator(RXGate(x12)).data), [0], inplace=True)
|
1419
|
-
else:
|
1420
|
-
raise QiskitError("possible non-pulse-optimal decomposition encountered")
|
1421
|
-
if math.isclose(euler_q1[1][1], math.pi / 2, abs_tol=atol):
|
1422
|
-
qc.sx(1)
|
1423
|
-
qc.global_phase -= math.pi / 4
|
1424
|
-
else:
|
1425
|
-
# this is non-optimal but doesn't seem to occur currently
|
1426
|
-
if self.pulse_optimize is None:
|
1427
|
-
qc.compose(
|
1428
|
-
self._decomposer1q(Operator(RXGate(euler_q1[1][1])).data), [1], inplace=True
|
1429
|
-
)
|
1430
|
-
else:
|
1431
|
-
raise QiskitError("possible non-pulse-optimal decomposition encountered")
|
1432
|
-
qc.rz(euler_q1[1][2] + euler_q1[2][0], 1)
|
1433
|
-
|
1434
|
-
qc.cx(1, 0)
|
1435
|
-
|
1436
|
-
qc.rz(euler_q0[2][1], 0)
|
1437
|
-
if math.isclose(euler_q1[2][1], math.pi / 2, abs_tol=atol):
|
1438
|
-
qc.sx(1)
|
1439
|
-
qc.global_phase -= math.pi / 4
|
1440
|
-
else:
|
1441
|
-
# this is non-optimal but doesn't seem to occur currently
|
1442
|
-
if self.pulse_optimize is None:
|
1443
|
-
qc.compose(
|
1444
|
-
self._decomposer1q(Operator(RXGate(euler_q1[2][1])).data), [1], inplace=True
|
1445
|
-
)
|
1446
|
-
else:
|
1447
|
-
raise QiskitError("possible non-pulse-optimal decomposition encountered")
|
1448
|
-
|
1449
|
-
qc.cx(1, 0)
|
1450
|
-
|
1451
|
-
circ = QuantumCircuit(1)
|
1452
|
-
circ.h(0)
|
1453
|
-
circ.rz(euler_q0[2][2] + euler_q0[3][0], 0)
|
1454
|
-
circ.rx(euler_q0[3][1], 0)
|
1455
|
-
circ.rz(euler_q0[3][2], 0)
|
1456
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1457
|
-
qc.compose(qceuler, [0], inplace=True)
|
1458
|
-
|
1459
|
-
circ = QuantumCircuit(1)
|
1460
|
-
circ.h(0)
|
1461
|
-
circ.rx(euler_q1[2][2] + euler_q1[3][0], 0)
|
1462
|
-
circ.rz(euler_q1[3][1], 0)
|
1463
|
-
circ.rx(euler_q1[3][2], 0)
|
1464
|
-
qceuler = self._decomposer1q(Operator(circ).data)
|
1465
|
-
qc.compose(qceuler, [1], inplace=True)
|
1466
|
-
|
1467
|
-
# TODO: fix the sign problem to avoid correction here
|
1468
|
-
if cmath.isclose(
|
1469
|
-
target_decomposed.unitary_matrix[0, 0], -(Operator(qc).data[0, 0]), abs_tol=atol
|
1470
|
-
):
|
1471
|
-
qc.global_phase += math.pi
|
1472
|
-
return qc
|
1473
|
-
|
1474
|
-
def num_basis_gates(self, unitary):
|
1475
|
-
"""Computes the number of basis gates needed in
|
1476
|
-
a decomposition of input unitary
|
686
|
+
def traces(self, target):
|
687
|
+
r"""
|
688
|
+
Give the expected traces :math:`\Big\vert\text{Tr}(U \cdot U_\text{target}^{\dag})\Big\vert`
|
689
|
+
for a different number of basis gates.
|
1477
690
|
"""
|
1478
|
-
return
|
1479
|
-
self.basis.b, self.basis_fidelity, np.asarray(unitary, dtype=complex)
|
1480
|
-
)
|
691
|
+
return self._inner_decomposer.traces(target._inner_decomposition)
|
1481
692
|
|
1482
693
|
|
1483
694
|
class TwoQubitDecomposeUpToDiagonal:
|