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
@@ -15,7 +15,7 @@
|
|
15
15
|
from __future__ import annotations
|
16
16
|
from abc import ABC, abstractmethod
|
17
17
|
|
18
|
-
from qiskit._accelerate.
|
18
|
+
from qiskit._accelerate.circuit import CircuitData
|
19
19
|
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
|
20
20
|
from qiskit.circuit.parametertable import ParameterTable, ParameterView
|
21
21
|
|
@@ -127,10 +127,32 @@ class BlueprintCircuit(QuantumCircuit, ABC):
|
|
127
127
|
self._build()
|
128
128
|
return super()._append(instruction, _qargs, _cargs)
|
129
129
|
|
130
|
-
def compose(
|
131
|
-
|
132
|
-
|
133
|
-
|
130
|
+
def compose(
|
131
|
+
self,
|
132
|
+
other,
|
133
|
+
qubits=None,
|
134
|
+
clbits=None,
|
135
|
+
front=False,
|
136
|
+
inplace=False,
|
137
|
+
wrap=False,
|
138
|
+
*,
|
139
|
+
copy=True,
|
140
|
+
var_remap=None,
|
141
|
+
inline_captures=False,
|
142
|
+
):
|
143
|
+
if not self._is_built:
|
144
|
+
self._build()
|
145
|
+
return super().compose(
|
146
|
+
other,
|
147
|
+
qubits,
|
148
|
+
clbits,
|
149
|
+
front,
|
150
|
+
inplace,
|
151
|
+
wrap,
|
152
|
+
copy=copy,
|
153
|
+
var_remap=var_remap,
|
154
|
+
inline_captures=False,
|
155
|
+
)
|
134
156
|
|
135
157
|
def inverse(self, annotated: bool = False):
|
136
158
|
if not self._is_built:
|
@@ -178,10 +200,10 @@ class BlueprintCircuit(QuantumCircuit, ABC):
|
|
178
200
|
self._build()
|
179
201
|
return super().num_connected_components(unitary_only=unitary_only)
|
180
202
|
|
181
|
-
def copy_empty_like(self, name=None):
|
203
|
+
def copy_empty_like(self, name=None, *, vars_mode="alike"):
|
182
204
|
if not self._is_built:
|
183
205
|
self._build()
|
184
|
-
cpy = super().copy_empty_like(name=name)
|
206
|
+
cpy = super().copy_empty_like(name=name, vars_mode=vars_mode)
|
185
207
|
# The base `copy_empty_like` will typically trigger code that `BlueprintCircuit` treats as
|
186
208
|
# an "invalidation", so we have to manually restore properties deleted by that that
|
187
209
|
# `copy_empty_like` is supposed to propagate.
|
@@ -11,6 +11,7 @@
|
|
11
11
|
# that they have been altered from the originals.
|
12
12
|
"""Prepare a quantum state from the state where all qubits are 0."""
|
13
13
|
|
14
|
+
import cmath
|
14
15
|
from typing import Union, Optional
|
15
16
|
|
16
17
|
import math
|
@@ -339,20 +340,20 @@ class StatePreparation(Gate):
|
|
339
340
|
a_complex = complex(a_complex)
|
340
341
|
b_complex = complex(b_complex)
|
341
342
|
mag_a = abs(a_complex)
|
342
|
-
final_r =
|
343
|
+
final_r = math.sqrt(mag_a**2 + abs(b_complex) ** 2)
|
343
344
|
if final_r < _EPS:
|
344
345
|
theta = 0
|
345
346
|
phi = 0
|
346
347
|
final_r = 0
|
347
348
|
final_t = 0
|
348
349
|
else:
|
349
|
-
theta = 2 *
|
350
|
-
a_arg =
|
351
|
-
b_arg =
|
350
|
+
theta = 2 * math.acos(mag_a / final_r)
|
351
|
+
a_arg = cmath.phase(a_complex)
|
352
|
+
b_arg = cmath.phase(b_complex)
|
352
353
|
final_t = a_arg + b_arg
|
353
354
|
phi = b_arg - a_arg
|
354
355
|
|
355
|
-
return final_r *
|
356
|
+
return final_r * cmath.exp(1.0j * final_t / 2), theta, phi
|
356
357
|
|
357
358
|
def _multiplex(self, target_gate, list_of_angles, last_cnot=True):
|
358
359
|
"""
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
17
17
|
from collections.abc import Sequence
|
18
18
|
|
19
19
|
import cmath
|
20
|
+
import math
|
20
21
|
import numpy as np
|
21
22
|
|
22
23
|
from qiskit.circuit.gate import Gate
|
@@ -87,7 +88,7 @@ class Diagonal(QuantumCircuit):
|
|
87
88
|
number of qubits.
|
88
89
|
"""
|
89
90
|
self._check_input(diag)
|
90
|
-
num_qubits = int(
|
91
|
+
num_qubits = int(math.log2(len(diag)))
|
91
92
|
|
92
93
|
circuit = QuantumCircuit(num_qubits, name="Diagonal")
|
93
94
|
|
@@ -100,7 +101,7 @@ class Diagonal(QuantumCircuit):
|
|
100
101
|
for i in range(0, n, 2):
|
101
102
|
diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1])
|
102
103
|
angles_rz.append(rz_angle)
|
103
|
-
num_act_qubits = int(
|
104
|
+
num_act_qubits = int(math.log2(n))
|
104
105
|
ctrl_qubits = list(range(num_qubits - num_act_qubits + 1, num_qubits))
|
105
106
|
target_qubit = num_qubits - num_act_qubits
|
106
107
|
|
@@ -118,7 +119,7 @@ class Diagonal(QuantumCircuit):
|
|
118
119
|
"""Check if ``diag`` is in valid format."""
|
119
120
|
if not isinstance(diag, (list, np.ndarray)):
|
120
121
|
raise CircuitError("Diagonal entries must be in a list or numpy array.")
|
121
|
-
num_qubits =
|
122
|
+
num_qubits = math.log2(len(diag))
|
122
123
|
if num_qubits < 1 or not num_qubits.is_integer():
|
123
124
|
raise CircuitError("The number of diagonal entries is not a positive power of 2.")
|
124
125
|
if not np.allclose(np.abs(diag), 1, atol=_EPS):
|
@@ -134,7 +135,7 @@ class DiagonalGate(Gate):
|
|
134
135
|
diag: list of the :math:`2^k` diagonal entries (for a diagonal gate on :math:`k` qubits).
|
135
136
|
"""
|
136
137
|
Diagonal._check_input(diag)
|
137
|
-
num_qubits = int(
|
138
|
+
num_qubits = int(math.log2(len(diag)))
|
138
139
|
|
139
140
|
super().__init__("diagonal", num_qubits, diag)
|
140
141
|
|
@@ -21,7 +21,7 @@ Generic isometries from m to n qubits.
|
|
21
21
|
|
22
22
|
from __future__ import annotations
|
23
23
|
|
24
|
-
import
|
24
|
+
import math
|
25
25
|
import numpy as np
|
26
26
|
from qiskit.circuit.exceptions import CircuitError
|
27
27
|
from qiskit.circuit.instruction import Instruction
|
@@ -29,6 +29,7 @@ from qiskit.circuit.quantumcircuit import QuantumCircuit
|
|
29
29
|
from qiskit.circuit.quantumregister import QuantumRegister
|
30
30
|
from qiskit.exceptions import QiskitError
|
31
31
|
from qiskit.quantum_info.operators.predicates import is_isometry
|
32
|
+
from qiskit._accelerate import isometry as isometry_rs
|
32
33
|
|
33
34
|
from .diagonal import Diagonal
|
34
35
|
from .uc import UCGate
|
@@ -93,8 +94,8 @@ class Isometry(Instruction):
|
|
93
94
|
self._epsilon = epsilon
|
94
95
|
|
95
96
|
# Check if the isometry has the right dimension and if the columns are orthonormal
|
96
|
-
n =
|
97
|
-
m =
|
97
|
+
n = math.log2(isometry.shape[0])
|
98
|
+
m = math.log2(isometry.shape[1])
|
98
99
|
if not n.is_integer() or n < 0:
|
99
100
|
raise QiskitError(
|
100
101
|
"The number of rows of the isometry is not a non negative power of 2."
|
@@ -150,18 +151,22 @@ class Isometry(Instruction):
|
|
150
151
|
# to keep a copyof the input isometry)
|
151
152
|
remaining_isometry = self.iso_data.astype(complex) # note: "astype" does copy the isometry
|
152
153
|
diag = []
|
153
|
-
m = int(
|
154
|
+
m = int(math.log2(self.iso_data.shape[1]))
|
154
155
|
# Decompose the column with index column_index and attache the gate to the circuit object.
|
155
156
|
# Return the isometry that is left to decompose, where the columns up to index column_index
|
156
157
|
# correspond to the firstfew columns of the identity matrix up to diag, and hence we only
|
157
158
|
# have to save a list containing them.
|
158
159
|
for column_index in range(2**m):
|
159
|
-
|
160
|
+
remaining_isometry, diag = self._decompose_column(
|
161
|
+
circuit, q, diag, remaining_isometry, column_index
|
162
|
+
)
|
160
163
|
# extract phase of the state that was sent to the basis state ket(column_index)
|
161
164
|
diag.append(remaining_isometry[column_index, 0])
|
162
165
|
# remove first column (which is now stored in diag)
|
163
166
|
remaining_isometry = remaining_isometry[:, 1:]
|
164
|
-
if len(diag) > 1 and not
|
167
|
+
if len(diag) > 1 and not isometry_rs.diag_is_identity_up_to_global_phase(
|
168
|
+
diag, self._epsilon
|
169
|
+
):
|
165
170
|
diagonal = Diagonal(np.conj(diag))
|
166
171
|
circuit.append(diagonal, q_input)
|
167
172
|
return circuit
|
@@ -170,9 +175,12 @@ class Isometry(Instruction):
|
|
170
175
|
"""
|
171
176
|
Decomposes the column with index column_index.
|
172
177
|
"""
|
173
|
-
n = int(
|
178
|
+
n = int(math.log2(self.iso_data.shape[0]))
|
174
179
|
for s in range(n):
|
175
|
-
|
180
|
+
remaining_isometry, diag = self._disentangle(
|
181
|
+
circuit, q, diag, remaining_isometry, column_index, s
|
182
|
+
)
|
183
|
+
return remaining_isometry, diag
|
176
184
|
|
177
185
|
def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s):
|
178
186
|
"""
|
@@ -185,16 +193,22 @@ class Isometry(Instruction):
|
|
185
193
|
# (note that we remove columns of the isometry during the procedure for efficiency)
|
186
194
|
k_prime = 0
|
187
195
|
v = remaining_isometry
|
188
|
-
n = int(
|
196
|
+
n = int(math.log2(self.iso_data.shape[0]))
|
189
197
|
|
190
198
|
# MCG to set one entry to zero (preparation for disentangling with UCGate):
|
191
|
-
index1 = 2 *
|
192
|
-
index2 = (2 *
|
199
|
+
index1 = 2 * isometry_rs.a(k, s + 1) * 2**s + isometry_rs.b(k, s + 1)
|
200
|
+
index2 = (2 * isometry_rs.a(k, s + 1) + 1) * 2**s + isometry_rs.b(k, s + 1)
|
193
201
|
target_label = n - s - 1
|
194
202
|
# Check if a MCG is required
|
195
|
-
if
|
203
|
+
if (
|
204
|
+
isometry_rs.k_s(k, s) == 0
|
205
|
+
and isometry_rs.b(k, s + 1) != 0
|
206
|
+
and np.abs(v[index2, k_prime]) > self._epsilon
|
207
|
+
):
|
196
208
|
# Find the MCG, decompose it and apply it to the remaining isometry
|
197
|
-
gate =
|
209
|
+
gate = isometry_rs.reverse_qubit_state(
|
210
|
+
[v[index1, k_prime], v[index2, k_prime]], 0, self._epsilon
|
211
|
+
)
|
198
212
|
control_labels = [
|
199
213
|
i
|
200
214
|
for i, x in enumerate(_get_binary_rep_as_list(k, n))
|
@@ -204,57 +218,49 @@ class Isometry(Instruction):
|
|
204
218
|
circuit, q, gate, control_labels, target_label
|
205
219
|
)
|
206
220
|
# apply the MCG to the remaining isometry
|
207
|
-
|
221
|
+
v = isometry_rs.apply_multi_controlled_gate(v, control_labels, target_label, gate)
|
208
222
|
# correct for the implementation "up to diagonal"
|
209
|
-
diag_mcg_inverse = np.conj(diagonal_mcg).
|
210
|
-
|
223
|
+
diag_mcg_inverse = np.conj(diagonal_mcg).astype(complex, copy=False)
|
224
|
+
v = isometry_rs.apply_diagonal_gate(
|
225
|
+
v, control_labels + [target_label], diag_mcg_inverse
|
226
|
+
)
|
211
227
|
# update the diag according to the applied diagonal gate
|
212
|
-
|
228
|
+
diag = isometry_rs.apply_diagonal_gate_to_diag(
|
229
|
+
diag, control_labels + [target_label], diag_mcg_inverse, n
|
230
|
+
)
|
213
231
|
|
214
232
|
# UCGate to disentangle a qubit:
|
215
233
|
# Find the UCGate, decompose it and apply it to the remaining isometry
|
216
234
|
single_qubit_gates = self._find_squs_for_disentangling(v, k, s)
|
217
|
-
if not
|
235
|
+
if not isometry_rs.ucg_is_identity_up_to_global_phase(single_qubit_gates, self._epsilon):
|
218
236
|
control_labels = list(range(target_label))
|
219
237
|
diagonal_ucg = self._append_ucg_up_to_diagonal(
|
220
238
|
circuit, q, single_qubit_gates, control_labels, target_label
|
221
239
|
)
|
222
240
|
# merge the diagonal into the UCGate for efficient application of both together
|
223
|
-
diagonal_ucg_inverse = np.conj(diagonal_ucg).
|
224
|
-
single_qubit_gates =
|
241
|
+
diagonal_ucg_inverse = np.conj(diagonal_ucg).astype(complex, copy=False)
|
242
|
+
single_qubit_gates = isometry_rs.merge_ucgate_and_diag(
|
243
|
+
single_qubit_gates, diagonal_ucg_inverse
|
244
|
+
)
|
225
245
|
# apply the UCGate (with the merged diagonal gate) to the remaining isometry
|
226
|
-
|
246
|
+
v = isometry_rs.apply_ucg(v, len(control_labels), single_qubit_gates)
|
227
247
|
# update the diag according to the applied diagonal gate
|
228
|
-
|
248
|
+
diag = isometry_rs.apply_diagonal_gate_to_diag(
|
229
249
|
diag, control_labels + [target_label], diagonal_ucg_inverse, n
|
230
250
|
)
|
231
251
|
# # correct for the implementation "up to diagonal"
|
232
252
|
# diag_inv = np.conj(diag).tolist()
|
233
253
|
# _apply_diagonal_gate(v, control_labels + [target_label], diag_inv)
|
254
|
+
return v, diag
|
234
255
|
|
235
256
|
# This method finds the single-qubit gates for a UCGate to disentangle a qubit:
|
236
257
|
# we consider the n-qubit state v[:,0] starting with k zeros (in the computational basis).
|
237
258
|
# The qubit with label n-s-1 is disentangled into the basis state k_s(k,s).
|
238
259
|
def _find_squs_for_disentangling(self, v, k, s):
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
else:
|
244
|
-
i_start = _a(k, s + 1) + 1
|
245
|
-
id_list = [np.eye(2, 2) for _ in range(i_start)]
|
246
|
-
squs = [
|
247
|
-
_reverse_qubit_state(
|
248
|
-
[
|
249
|
-
v[2 * i * 2**s + _b(k, s), k_prime],
|
250
|
-
v[(2 * i + 1) * 2**s + _b(k, s), k_prime],
|
251
|
-
],
|
252
|
-
_k_s(k, s),
|
253
|
-
self._epsilon,
|
254
|
-
)
|
255
|
-
for i in range(i_start, 2 ** (n - s - 1))
|
256
|
-
]
|
257
|
-
return id_list + squs
|
260
|
+
res = isometry_rs.find_squs_for_disentangling(
|
261
|
+
v, k, s, self._epsilon, n=int(math.log2(self.iso_data.shape[0]))
|
262
|
+
)
|
263
|
+
return res
|
258
264
|
|
259
265
|
# Append a UCGate up to diagonal to the circuit circ.
|
260
266
|
def _append_ucg_up_to_diagonal(self, circ, q, single_qubit_gates, control_labels, target_label):
|
@@ -264,7 +270,7 @@ class Isometry(Instruction):
|
|
264
270
|
q_ancillas_zero,
|
265
271
|
q_ancillas_dirty,
|
266
272
|
) = self._define_qubit_role(q)
|
267
|
-
n = int(
|
273
|
+
n = int(math.log2(self.iso_data.shape[0]))
|
268
274
|
qubits = q_input + q_ancillas_for_output
|
269
275
|
# Note that we have to reverse the control labels, since controls are provided by
|
270
276
|
# increasing qubit number toa UCGate by convention
|
@@ -286,7 +292,7 @@ class Isometry(Instruction):
|
|
286
292
|
q_ancillas_zero,
|
287
293
|
q_ancillas_dirty,
|
288
294
|
) = self._define_qubit_role(q)
|
289
|
-
n = int(
|
295
|
+
n = int(math.log2(self.iso_data.shape[0]))
|
290
296
|
qubits = q_input + q_ancillas_for_output
|
291
297
|
control_qubits = _reverse_qubit_oder(_get_qubits_by_label(control_labels, qubits, n))
|
292
298
|
target_qubit = _get_qubits_by_label([target_label], qubits, n)[0]
|
@@ -307,8 +313,8 @@ class Isometry(Instruction):
|
|
307
313
|
|
308
314
|
def _define_qubit_role(self, q):
|
309
315
|
|
310
|
-
n = int(
|
311
|
-
m = int(
|
316
|
+
n = int(math.log2(self.iso_data.shape[0]))
|
317
|
+
m = int(math.log2(self.iso_data.shape[1]))
|
312
318
|
|
313
319
|
# Define the role of the qubits
|
314
320
|
q_input = q[:m]
|
@@ -337,146 +343,6 @@ class Isometry(Instruction):
|
|
337
343
|
return self._inverse
|
338
344
|
|
339
345
|
|
340
|
-
# Find special unitary matrix that maps [c0,c1] to [r,0] or [0,r] if basis_state=0 or
|
341
|
-
# basis_state=1 respectively
|
342
|
-
def _reverse_qubit_state(state, basis_state, epsilon):
|
343
|
-
state = np.array(state)
|
344
|
-
r = np.linalg.norm(state)
|
345
|
-
if r < epsilon:
|
346
|
-
return np.eye(2, 2)
|
347
|
-
if basis_state == 0:
|
348
|
-
m = np.array([[np.conj(state[0]), np.conj(state[1])], [-state[1], state[0]]]) / r
|
349
|
-
else:
|
350
|
-
m = np.array([[-state[1], state[0]], [np.conj(state[0]), np.conj(state[1])]]) / r
|
351
|
-
return m
|
352
|
-
|
353
|
-
|
354
|
-
# Methods for applying gates to matrices (should be moved to Qiskit AER)
|
355
|
-
|
356
|
-
# Input: matrix m with 2^n rows (and arbitrary many columns). Think of the columns as states
|
357
|
-
# on n qubits. The method applies a uniformly controlled gate (UCGate) to all the columns, where
|
358
|
-
# the UCGate is specified by the inputs k and single_qubit_gates:
|
359
|
-
|
360
|
-
# k = number of controls. We assume that the controls are on the k most significant qubits
|
361
|
-
# (and the target is on the (k+1)th significant qubit)
|
362
|
-
# single_qubit_gates = [u_0,...,u_{2^k-1}], where the u_i's are 2*2 unitaries
|
363
|
-
# (provided as numpy arrays)
|
364
|
-
|
365
|
-
# The order of the single-qubit unitaries is such that the first unitary u_0 is applied to the
|
366
|
-
# (k+1)th significant qubit if the control qubits are in the state ket(0...00), the gate u_1 is
|
367
|
-
# applied if the control qubits are in the state ket(0...01), and so on.
|
368
|
-
|
369
|
-
# The input matrix m and the single-qubit gates have to be of dtype=complex.
|
370
|
-
|
371
|
-
|
372
|
-
def _apply_ucg(m, k, single_qubit_gates):
|
373
|
-
# ToDo: Improve efficiency by parallelizing the gate application. A generalized version of
|
374
|
-
# ToDo: this method should be implemented by the state vector simulator in Qiskit AER.
|
375
|
-
num_qubits = int(np.log2(m.shape[0]))
|
376
|
-
num_col = m.shape[1]
|
377
|
-
spacing = 2 ** (num_qubits - k - 1)
|
378
|
-
for j in range(2 ** (num_qubits - 1)):
|
379
|
-
i = (j // spacing) * spacing + j
|
380
|
-
gate_index = i // (2 ** (num_qubits - k))
|
381
|
-
for col in range(num_col):
|
382
|
-
m[np.array([i, i + spacing]), np.array([col, col])] = np.ndarray.flatten(
|
383
|
-
single_qubit_gates[gate_index].dot(np.array([[m[i, col]], [m[i + spacing, col]]]))
|
384
|
-
).tolist()
|
385
|
-
return m
|
386
|
-
|
387
|
-
|
388
|
-
# Apply a diagonal gate with diagonal entries liste in diag and acting on qubits with labels
|
389
|
-
# action_qubit_labels to a matrix m.
|
390
|
-
# The input matrix m has to be of dtype=complex
|
391
|
-
# The qubit labels are such that label 0 corresponds to the most significant qubit, label 1 to
|
392
|
-
# the second most significant qubit, and so on ...
|
393
|
-
|
394
|
-
|
395
|
-
def _apply_diagonal_gate(m, action_qubit_labels, diag):
|
396
|
-
# ToDo: Improve efficiency by parallelizing the gate application. A generalized version of
|
397
|
-
# ToDo: this method should be implemented by the state vector simulator in Qiskit AER.
|
398
|
-
num_qubits = int(np.log2(m.shape[0]))
|
399
|
-
num_cols = m.shape[1]
|
400
|
-
basis_states = list(itertools.product([0, 1], repeat=num_qubits))
|
401
|
-
for state in basis_states:
|
402
|
-
state_on_action_qubits = [state[i] for i in action_qubit_labels]
|
403
|
-
diag_index = _bin_to_int(state_on_action_qubits)
|
404
|
-
i = _bin_to_int(state)
|
405
|
-
for j in range(num_cols):
|
406
|
-
m[i, j] = diag[diag_index] * m[i, j]
|
407
|
-
return m
|
408
|
-
|
409
|
-
|
410
|
-
# Special case of the method _apply_diagonal_gate, where the input m is a diagonal matrix on the
|
411
|
-
# log2(len(m_diagonal)) least significant qubits (this method is more efficient in this case
|
412
|
-
# than _apply_diagonal_gate). The input m_diagonal is provided as a list of diagonal entries.
|
413
|
-
# The diagonal diag is applied on the qubits with labels listed in action_qubit_labels. The input
|
414
|
-
# num_qubits gives the total number of considered qubits (this input is required to interpret the
|
415
|
-
# action_qubit_labels in relation to the least significant qubits).
|
416
|
-
|
417
|
-
|
418
|
-
def _apply_diagonal_gate_to_diag(m_diagonal, action_qubit_labels, diag, num_qubits):
|
419
|
-
if not m_diagonal:
|
420
|
-
return m_diagonal
|
421
|
-
basis_states = list(itertools.product([0, 1], repeat=num_qubits))
|
422
|
-
for state in basis_states[: len(m_diagonal)]:
|
423
|
-
state_on_action_qubits = [state[i] for i in action_qubit_labels]
|
424
|
-
diag_index = _bin_to_int(state_on_action_qubits)
|
425
|
-
i = _bin_to_int(state)
|
426
|
-
m_diagonal[i] *= diag[diag_index]
|
427
|
-
return m_diagonal
|
428
|
-
|
429
|
-
|
430
|
-
# Apply a MC single-qubit gate (given by the 2*2 unitary input: gate) with controlling on
|
431
|
-
# the qubits with label control_labels and acting on the qubit with label target_label
|
432
|
-
# to a matrix m. The input matrix m and the gate have to be of dtype=complex. The qubit labels are
|
433
|
-
# such that label 0 corresponds to the most significant qubit, label 1 to the second most
|
434
|
-
# significant qubit, and so on ...
|
435
|
-
|
436
|
-
|
437
|
-
def _apply_multi_controlled_gate(m, control_labels, target_label, gate):
|
438
|
-
# ToDo: This method should be integrated into the state vector simulator in Qiskit AER.
|
439
|
-
num_qubits = int(np.log2(m.shape[0]))
|
440
|
-
num_cols = m.shape[1]
|
441
|
-
control_labels.sort()
|
442
|
-
free_qubits = num_qubits - len(control_labels) - 1
|
443
|
-
basis_states_free = list(itertools.product([0, 1], repeat=free_qubits))
|
444
|
-
for state_free in basis_states_free:
|
445
|
-
(e1, e2) = _construct_basis_states(state_free, control_labels, target_label)
|
446
|
-
for i in range(num_cols):
|
447
|
-
m[np.array([e1, e2]), np.array([i, i])] = np.ndarray.flatten(
|
448
|
-
gate.dot(np.array([[m[e1, i]], [m[e2, i]]]))
|
449
|
-
).tolist()
|
450
|
-
return m
|
451
|
-
|
452
|
-
|
453
|
-
# Helper method for _apply_multi_controlled_gate. This constructs the basis states the MG gate
|
454
|
-
# is acting on for a specific state state_free of the qubits we neither control nor act on.
|
455
|
-
|
456
|
-
|
457
|
-
def _construct_basis_states(state_free, control_labels, target_label):
|
458
|
-
e1 = []
|
459
|
-
e2 = []
|
460
|
-
j = 0
|
461
|
-
for i in range(len(state_free) + len(control_labels) + 1):
|
462
|
-
if i in control_labels:
|
463
|
-
e1.append(1)
|
464
|
-
e2.append(1)
|
465
|
-
elif i == target_label:
|
466
|
-
e1.append(0)
|
467
|
-
e2.append(1)
|
468
|
-
else:
|
469
|
-
e1.append(state_free[j])
|
470
|
-
e2.append(state_free[j])
|
471
|
-
j += 1
|
472
|
-
out1 = _bin_to_int(e1)
|
473
|
-
out2 = _bin_to_int(e2)
|
474
|
-
return out1, out2
|
475
|
-
|
476
|
-
|
477
|
-
# Some helper methods:
|
478
|
-
|
479
|
-
|
480
346
|
# Get the qubits in the list qubits corresponding to the labels listed in labels. The total number
|
481
347
|
# of qubits is given by num_qubits (and determines the convention for the qubit labeling)
|
482
348
|
|
@@ -495,14 +361,6 @@ def _reverse_qubit_oder(qubits):
|
|
495
361
|
# Convert list of binary digits to integer
|
496
362
|
|
497
363
|
|
498
|
-
def _bin_to_int(binary_digits_as_list):
|
499
|
-
return int("".join(str(x) for x in binary_digits_as_list), 2)
|
500
|
-
|
501
|
-
|
502
|
-
def _ct(m):
|
503
|
-
return np.transpose(np.conjugate(m))
|
504
|
-
|
505
|
-
|
506
364
|
def _get_binary_rep_as_list(n, num_digits):
|
507
365
|
binary_string = np.binary_repr(n).zfill(num_digits)
|
508
366
|
binary = []
|
@@ -510,64 +368,3 @@ def _get_binary_rep_as_list(n, num_digits):
|
|
510
368
|
for c in line:
|
511
369
|
binary.append(int(c))
|
512
370
|
return binary[-num_digits:]
|
513
|
-
|
514
|
-
|
515
|
-
# absorb a diagonal gate into a UCGate
|
516
|
-
|
517
|
-
|
518
|
-
def _merge_UCGate_and_diag(single_qubit_gates, diag):
|
519
|
-
for i, gate in enumerate(single_qubit_gates):
|
520
|
-
single_qubit_gates[i] = np.array([[diag[2 * i], 0.0], [0.0, diag[2 * i + 1]]]).dot(gate)
|
521
|
-
return single_qubit_gates
|
522
|
-
|
523
|
-
|
524
|
-
# Helper variables/functions for the column-by-column decomposition
|
525
|
-
|
526
|
-
|
527
|
-
# a(k,s) and b(k,s) are positive integers such that k = a(k,s)2^s + b(k,s)
|
528
|
-
# (with the maximal choice of a(k,s))
|
529
|
-
|
530
|
-
|
531
|
-
def _a(k, s):
|
532
|
-
return k // 2**s
|
533
|
-
|
534
|
-
|
535
|
-
def _b(k, s):
|
536
|
-
return k - (_a(k, s) * 2**s)
|
537
|
-
|
538
|
-
|
539
|
-
# given a binary representation of k with binary digits [k_{n-1},..,k_1,k_0],
|
540
|
-
# the method k_s(k, s) returns k_s
|
541
|
-
|
542
|
-
|
543
|
-
def _k_s(k, s):
|
544
|
-
if k == 0:
|
545
|
-
return 0
|
546
|
-
else:
|
547
|
-
num_digits = s + 1
|
548
|
-
return _get_binary_rep_as_list(k, num_digits)[0]
|
549
|
-
|
550
|
-
|
551
|
-
# Check if a gate of a special form is equal to the identity gate up to global phase
|
552
|
-
|
553
|
-
|
554
|
-
def _ucg_is_identity_up_to_global_phase(single_qubit_gates, epsilon):
|
555
|
-
if not np.abs(single_qubit_gates[0][0, 0]) < epsilon:
|
556
|
-
global_phase = 1.0 / (single_qubit_gates[0][0, 0])
|
557
|
-
else:
|
558
|
-
return False
|
559
|
-
for gate in single_qubit_gates:
|
560
|
-
if not np.allclose(global_phase * gate, np.eye(2, 2)):
|
561
|
-
return False
|
562
|
-
return True
|
563
|
-
|
564
|
-
|
565
|
-
def _diag_is_identity_up_to_global_phase(diag, epsilon):
|
566
|
-
if not np.abs(diag[0]) < epsilon:
|
567
|
-
global_phase = 1.0 / (diag[0])
|
568
|
-
else:
|
569
|
-
return False
|
570
|
-
for d in diag:
|
571
|
-
if not np.abs(global_phase * d - 1) < epsilon:
|
572
|
-
return False
|
573
|
-
return True
|
@@ -63,13 +63,13 @@ class PauliGate(Gate):
|
|
63
63
|
r"""Return inverted pauli gate (itself)."""
|
64
64
|
return PauliGate(self.params[0]) # self-inverse
|
65
65
|
|
66
|
-
def __array__(self, dtype=None):
|
66
|
+
def __array__(self, dtype=None, copy=None):
|
67
67
|
"""Return a Numpy.array for the pauli gate.
|
68
68
|
i.e. tensor product of the paulis"""
|
69
69
|
# pylint: disable=cyclic-import
|
70
70
|
from qiskit.quantum_info.operators import Pauli
|
71
71
|
|
72
|
-
return Pauli(self.params[0]).__array__(dtype=dtype)
|
72
|
+
return Pauli(self.params[0]).__array__(dtype=dtype, copy=copy)
|
73
73
|
|
74
74
|
def validate_parameter(self, parameter):
|
75
75
|
if isinstance(parameter, str):
|
@@ -147,8 +147,11 @@ class PermutationGate(Gate):
|
|
147
147
|
|
148
148
|
super().__init__(name="permutation", num_qubits=num_qubits, params=[pattern])
|
149
149
|
|
150
|
-
def __array__(self, dtype=None):
|
150
|
+
def __array__(self, dtype=None, copy=None):
|
151
151
|
"""Return a numpy.array for the Permutation gate."""
|
152
|
+
if copy is False:
|
153
|
+
raise ValueError("unable to avoid copy while creating an array as requested")
|
154
|
+
|
152
155
|
nq = len(self.pattern)
|
153
156
|
mat = np.zeros((2**nq, 2**nq), dtype=dtype)
|
154
157
|
|
@@ -12,13 +12,14 @@
|
|
12
12
|
|
13
13
|
"""Rotation around an arbitrary axis on the Bloch sphere."""
|
14
14
|
|
15
|
+
import math
|
15
16
|
import numpy
|
16
17
|
from qiskit.circuit.gate import Gate
|
17
18
|
from qiskit.circuit.exceptions import CircuitError
|
18
19
|
|
19
20
|
|
20
21
|
class RVGate(Gate):
|
21
|
-
r"""Rotation around arbitrary rotation axis :math
|
22
|
+
r"""Rotation around arbitrary rotation axis :math:`\vec{v}` where :math:`\|\vec{v}\|_2` is
|
22
23
|
angle of rotation in radians.
|
23
24
|
|
24
25
|
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
|
@@ -36,14 +37,17 @@ class RVGate(Gate):
|
|
36
37
|
|
37
38
|
.. math::
|
38
39
|
|
39
|
-
\newcommand{\rotationangle}{
|
40
|
-
|
41
|
-
R(\vec{v}) = e^{-i \vec{v}\cdot\vec{\sigma}} =
|
40
|
+
\newcommand{\rotationangle}{\frac{\|\vec{v}\|_2}{2}}
|
41
|
+
R(\vec{v}) = e^{-i \vec{v}\cdot\vec{\sigma} / 2} =
|
42
42
|
\begin{pmatrix}
|
43
|
-
\cos\left(\rotationangle\right)
|
44
|
-
|
45
|
-
-(i v_x
|
46
|
-
|
43
|
+
\cos\left(\rotationangle\right)
|
44
|
+
-i \frac{v_z}{\|\vec{v}\|_2} \sin\left(\rotationangle\right)
|
45
|
+
& -(i \frac{v_x}{\|\vec{v}\|_2}
|
46
|
+
+ \frac{v_y}{\|\vec{v}\|_2}) \sin\left(\rotationangle\right) \\
|
47
|
+
-(i \frac{v_x}{\|\vec{v}\|_2}
|
48
|
+
- \frac{v_y}{\|\vec{v}\|_2}) \sin\left(\rotationangle\right)
|
49
|
+
& \cos\left(\rotationangle\right)
|
50
|
+
+ i \frac{v_z}{\|\vec{v}\|_2} \sin\left(\rotationangle\right)
|
47
51
|
\end{pmatrix}
|
48
52
|
"""
|
49
53
|
|
@@ -79,12 +83,12 @@ class RVGate(Gate):
|
|
79
83
|
def to_matrix(self):
|
80
84
|
"""Return a numpy.array for the R(v) gate."""
|
81
85
|
v = numpy.asarray(self.params, dtype=float)
|
82
|
-
angle =
|
86
|
+
angle = math.sqrt(v.dot(v))
|
83
87
|
if angle == 0:
|
84
88
|
return numpy.array([[1, 0], [0, 1]])
|
85
89
|
nx, ny, nz = v / angle
|
86
|
-
sin =
|
87
|
-
cos =
|
90
|
+
sin = math.sin(angle / 2)
|
91
|
+
cos = math.cos(angle / 2)
|
88
92
|
return numpy.array(
|
89
93
|
[
|
90
94
|
[cos - 1j * nz * sin, (-ny - 1j * nx) * sin],
|