qiskit 2.0.0rc1__cp39-abi3-macosx_11_0_arm64.whl → 2.0.1__cp39-abi3-macosx_11_0_arm64.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 +1 -4
- qiskit/_accelerate.abi3.so +0 -0
- qiskit/circuit/__init__.py +11 -5
- qiskit/circuit/classical/expr/constructors.py +0 -12
- qiskit/circuit/library/__init__.py +449 -163
- qiskit/circuit/library/graph_state.py +1 -0
- qiskit/circuit/library/n_local/efficient_su2.py +1 -1
- qiskit/circuit/library/n_local/excitation_preserving.py +1 -1
- qiskit/circuit/library/quantum_volume.py +9 -0
- qiskit/circuit/library/standard_gates/r.py +5 -4
- qiskit/circuit/library/standard_gates/x.py +1 -2
- qiskit/circuit/parameterexpression.py +7 -0
- qiskit/circuit/quantumcircuit.py +405 -80
- qiskit/circuit/tools/pi_check.py +3 -0
- qiskit/converters/circuit_to_dag.py +2 -2
- qiskit/converters/dag_to_circuit.py +2 -3
- qiskit/dagcircuit/dagdependency_v2.py +3 -2
- qiskit/primitives/statevector_estimator.py +1 -1
- qiskit/qpy/__init__.py +21 -0
- qiskit/qpy/binary_io/circuits.py +5 -0
- qiskit/qpy/binary_io/parse_sympy_repr.py +121 -0
- qiskit/qpy/binary_io/value.py +5 -5
- qiskit/result/models.py +1 -2
- qiskit/result/result.py +10 -8
- qiskit/synthesis/discrete_basis/commutator_decompose.py +30 -6
- qiskit/synthesis/discrete_basis/gate_sequence.py +10 -4
- qiskit/synthesis/discrete_basis/generate_basis_approximations.py +1 -1
- qiskit/synthesis/discrete_basis/solovay_kitaev.py +36 -13
- qiskit/transpiler/passes/__init__.py +2 -0
- qiskit/transpiler/passes/basis/basis_translator.py +1 -1
- qiskit/transpiler/passes/layout/full_ancilla_allocation.py +2 -3
- qiskit/transpiler/passes/layout/sabre_layout.py +3 -1
- qiskit/transpiler/passes/layout/vf2_utils.py +2 -5
- qiskit/transpiler/passes/optimization/__init__.py +1 -0
- qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +2 -2
- qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
- qiskit/transpiler/passes/scheduling/padding/base_padding.py +2 -2
- qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +5 -5
- qiskit/transpiler/passes/scheduling/padding/pad_delay.py +1 -1
- qiskit/transpiler/passes/scheduling/time_unit_conversion.py +10 -6
- qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +29 -19
- qiskit/transpiler/passes/synthesis/unitary_synthesis.py +2 -1
- qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +13 -7
- qiskit/transpiler/target.py +11 -0
- qiskit/visualization/circuit/text.py +1 -1
- qiskit/visualization/counts_visualization.py +4 -0
- qiskit/visualization/library.py +4 -1
- qiskit/visualization/state_visualization.py +13 -2
- qiskit/visualization/timeline/core.py +1 -1
- qiskit/visualization/timeline/plotters/matplotlib.py +4 -1
- {qiskit-2.0.0rc1.dist-info → qiskit-2.0.1.dist-info}/METADATA +4 -3
- {qiskit-2.0.0rc1.dist-info → qiskit-2.0.1.dist-info}/RECORD +57 -56
- {qiskit-2.0.0rc1.dist-info → qiskit-2.0.1.dist-info}/WHEEL +1 -1
- {qiskit-2.0.0rc1.dist-info → qiskit-2.0.1.dist-info}/entry_points.txt +0 -0
- {qiskit-2.0.0rc1.dist-info → qiskit-2.0.1.dist-info/licenses}/LICENSE.txt +0 -0
- {qiskit-2.0.0rc1.dist-info → qiskit-2.0.1.dist-info}/top_level.txt +0 -0
qiskit/circuit/tools/pi_check.py
CHANGED
@@ -49,7 +49,10 @@ def pi_check(inpt, eps=1e-9, output="text", ndigits=None):
|
|
49
49
|
if isinstance(inpt, ParameterExpression):
|
50
50
|
param_str = str(inpt)
|
51
51
|
from sympy import sympify
|
52
|
+
import symengine
|
52
53
|
|
54
|
+
if not isinstance(inpt._symbol_expr, symengine.Basic):
|
55
|
+
raise QiskitError("Invalid ParameterExpression provided")
|
53
56
|
expr = sympify(inpt._symbol_expr)
|
54
57
|
syms = expr.atoms()
|
55
58
|
for sym in syms:
|
@@ -75,6 +75,6 @@ def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_ord
|
|
75
75
|
|
76
76
|
dagcircuit = core_circuit_to_dag(circuit, copy_operations, qubit_order, clbit_order)
|
77
77
|
|
78
|
-
dagcircuit.
|
79
|
-
dagcircuit.
|
78
|
+
dagcircuit._duration = circuit._duration
|
79
|
+
dagcircuit._unit = circuit._unit
|
80
80
|
return dagcircuit
|
@@ -74,7 +74,6 @@ def dag_to_circuit(dag, copy_operations=True):
|
|
74
74
|
circuit.add_stretch(stretch)
|
75
75
|
circuit.metadata = dag.metadata or {}
|
76
76
|
circuit._data = circuit_data
|
77
|
-
|
78
|
-
circuit.
|
79
|
-
circuit._unit = dag.unit
|
77
|
+
circuit._duration = dag._duration
|
78
|
+
circuit._unit = dag._unit
|
80
79
|
return circuit
|
@@ -447,8 +447,6 @@ class _DAGDependencyV2:
|
|
447
447
|
target_dag = _DAGDependencyV2()
|
448
448
|
target_dag.name = self.name
|
449
449
|
target_dag._global_phase = self._global_phase
|
450
|
-
target_dag.duration = self.duration
|
451
|
-
target_dag.unit = self.unit
|
452
450
|
target_dag.metadata = self.metadata
|
453
451
|
target_dag._key_cache = self._key_cache
|
454
452
|
target_dag.comm_checker = self.comm_checker
|
@@ -456,6 +454,9 @@ class _DAGDependencyV2:
|
|
456
454
|
target_dag.add_qubits(self.qubits)
|
457
455
|
target_dag.add_clbits(self.clbits)
|
458
456
|
|
457
|
+
target_dag.duration = self.duration
|
458
|
+
target_dag.unit = self.unit
|
459
|
+
|
459
460
|
for qreg in self.qregs.values():
|
460
461
|
target_dag.add_qreg(qreg)
|
461
462
|
for creg in self.cregs.values():
|
@@ -33,7 +33,7 @@ class StatevectorEstimator(BaseEstimatorV2):
|
|
33
33
|
Simple implementation of :class:`BaseEstimatorV2` with full state vector simulation.
|
34
34
|
|
35
35
|
This class is implemented via :class:`~.Statevector` which turns provided circuits into
|
36
|
-
pure state vectors. These states are subsequently acted on by :class
|
36
|
+
pure state vectors. These states are subsequently acted on by :class:`~.SparsePauliOp`,
|
37
37
|
which implies that, at present, this implementation is only compatible with Pauli-based
|
38
38
|
observables.
|
39
39
|
|
qiskit/qpy/__init__.py
CHANGED
@@ -194,6 +194,27 @@ of QPY in qiskit-terra 0.18.0.
|
|
194
194
|
* - Qiskit (qiskit-terra for < 1.0.0) version
|
195
195
|
- :func:`.dump` format(s) output versions
|
196
196
|
- :func:`.load` maximum supported version (older format versions can always be read)
|
197
|
+
* - 2.0.0
|
198
|
+
- 13, 14
|
199
|
+
- 14
|
200
|
+
* - 1.4.2
|
201
|
+
- 10, 11, 12, 13
|
202
|
+
- 13
|
203
|
+
* - 1.4.1
|
204
|
+
- 10, 11, 12, 13
|
205
|
+
- 13
|
206
|
+
* - 1.4.0
|
207
|
+
- 10, 11, 12, 13
|
208
|
+
- 13
|
209
|
+
* - 1.3.3
|
210
|
+
- 10, 11, 12, 13
|
211
|
+
- 13
|
212
|
+
* - 1.3.2
|
213
|
+
- 10, 11, 12, 13
|
214
|
+
- 13
|
215
|
+
* - 1.3.1
|
216
|
+
- 10, 11, 12, 13
|
217
|
+
- 13
|
197
218
|
* - 1.3.0
|
198
219
|
- 10, 11, 12, 13
|
199
220
|
- 13
|
qiskit/qpy/binary_io/circuits.py
CHANGED
@@ -783,6 +783,11 @@ def _write_instruction(
|
|
783
783
|
custom_operations[gate_class_name] = instruction.operation
|
784
784
|
custom_operations_list.append(gate_class_name)
|
785
785
|
|
786
|
+
elif isinstance(instruction.operation, library.MCMTGate):
|
787
|
+
gate_class_name = instruction.operation.name + "_" + str(uuid.uuid4())
|
788
|
+
custom_operations[gate_class_name] = instruction.operation
|
789
|
+
custom_operations_list.append(gate_class_name)
|
790
|
+
|
786
791
|
condition_type = type_keys.Condition.NONE
|
787
792
|
condition_register = b""
|
788
793
|
condition_value = 0
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# This code is part of Qiskit.
|
2
|
+
#
|
3
|
+
# (C) Copyright IBM 2025.
|
4
|
+
#
|
5
|
+
# This code is licensed under the Apache License, Version 2.0. You may
|
6
|
+
# obtain a copy of this license in the LICENSE.txt file in the root directory
|
7
|
+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
|
8
|
+
#
|
9
|
+
# Any modifications or derivative works of this code must retain this
|
10
|
+
# copyright notice, and modified files need to carry a notice indicating
|
11
|
+
# that they have been altered from the originals.
|
12
|
+
|
13
|
+
"""Parser for sympy expressions srepr from ParameterExpression internals."""
|
14
|
+
|
15
|
+
import ast
|
16
|
+
|
17
|
+
from qiskit.qpy.exceptions import QpyError
|
18
|
+
|
19
|
+
|
20
|
+
ALLOWED_CALLERS = {
|
21
|
+
"Abs",
|
22
|
+
"Add",
|
23
|
+
"Sub",
|
24
|
+
"Mul",
|
25
|
+
"Div",
|
26
|
+
"Pow",
|
27
|
+
"Symbol",
|
28
|
+
"Integer",
|
29
|
+
"Rational",
|
30
|
+
"Complex",
|
31
|
+
"Float",
|
32
|
+
"log",
|
33
|
+
"sin",
|
34
|
+
"cos",
|
35
|
+
"tan",
|
36
|
+
"atan",
|
37
|
+
"acos",
|
38
|
+
"asin",
|
39
|
+
"exp",
|
40
|
+
"conjugate",
|
41
|
+
}
|
42
|
+
|
43
|
+
UNARY = {
|
44
|
+
"sin",
|
45
|
+
"cos",
|
46
|
+
"tan",
|
47
|
+
"atan",
|
48
|
+
"acos",
|
49
|
+
"asin",
|
50
|
+
"conjugate",
|
51
|
+
"Symbol",
|
52
|
+
"Integer",
|
53
|
+
"Complex",
|
54
|
+
"Abs",
|
55
|
+
"Float",
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
class ParseSympyWalker(ast.NodeVisitor):
|
60
|
+
"""A custom ast walker that is passed the sympy srepr from QPY < 13 and creates a custom
|
61
|
+
expression."""
|
62
|
+
|
63
|
+
def __init__(self):
|
64
|
+
self.stack = []
|
65
|
+
|
66
|
+
def visit_UnaryOp(self, node: ast.UnaryOp): # pylint: disable=invalid-name
|
67
|
+
"""Visit a python unary op node"""
|
68
|
+
self.visit(node.operand)
|
69
|
+
arg = self.stack.pop()
|
70
|
+
if isinstance(node.op, ast.UAdd):
|
71
|
+
self.stack.append(+arg)
|
72
|
+
elif isinstance(node.op, ast.USub):
|
73
|
+
self.stack.append(-arg)
|
74
|
+
elif isinstance(node.op, ast.Not):
|
75
|
+
self.stack.append(not arg)
|
76
|
+
elif isinstance(node.op, ast.Invert):
|
77
|
+
self.stack.append(~arg)
|
78
|
+
else:
|
79
|
+
raise QpyError(f"Invalid unary op as part of sympy srepr: {node.op}")
|
80
|
+
|
81
|
+
def visit_Constant(self, node: ast.Constant): # pylint: disable=invalid-name
|
82
|
+
"""Visit a constant node."""
|
83
|
+
self.stack.append(node.value)
|
84
|
+
|
85
|
+
def visit_Call(self, node: ast.Call): # pylint: disable=invalid-name
|
86
|
+
"""Visit a call node
|
87
|
+
|
88
|
+
This can only be parameter expression allowed sympy call types.
|
89
|
+
"""
|
90
|
+
import sympy
|
91
|
+
|
92
|
+
if isinstance(node.func, ast.Name):
|
93
|
+
name = node.func.id
|
94
|
+
else:
|
95
|
+
raise QpyError("Unknown node type")
|
96
|
+
|
97
|
+
if name not in ALLOWED_CALLERS:
|
98
|
+
raise QpyError(f"{name} is not part of a valid sympy expression srepr")
|
99
|
+
|
100
|
+
args = node.args
|
101
|
+
if name in UNARY:
|
102
|
+
if len(args) != 1:
|
103
|
+
raise QpyError(f"{name} has an invalid number of args in sympy srepr")
|
104
|
+
self.visit(args[0])
|
105
|
+
obj = getattr(sympy, name)(self.stack.pop())
|
106
|
+
self.stack.append(obj)
|
107
|
+
else:
|
108
|
+
for arg in args:
|
109
|
+
self.visit(arg)
|
110
|
+
out_args = [self.stack.pop() for _ in range(len(args))]
|
111
|
+
out_args.reverse()
|
112
|
+
obj = getattr(sympy, name)(*out_args)
|
113
|
+
self.stack.append(obj)
|
114
|
+
|
115
|
+
|
116
|
+
def parse_sympy_repr(sympy_repr: str):
|
117
|
+
"""Parse a given sympy srepr into a symbolic expression object."""
|
118
|
+
tree = ast.parse(sympy_repr, mode="eval")
|
119
|
+
visitor = ParseSympyWalker()
|
120
|
+
visitor.visit(tree)
|
121
|
+
return visitor.stack.pop()
|
qiskit/qpy/binary_io/value.py
CHANGED
@@ -34,6 +34,7 @@ from qiskit.circuit.parameterexpression import (
|
|
34
34
|
)
|
35
35
|
from qiskit.circuit.parametervector import ParameterVector, ParameterVectorElement
|
36
36
|
from qiskit.qpy import common, formats, exceptions, type_keys
|
37
|
+
from qiskit.qpy.binary_io.parse_sympy_repr import parse_sympy_repr
|
37
38
|
|
38
39
|
|
39
40
|
def _write_parameter(file_obj, obj):
|
@@ -473,9 +474,9 @@ def _read_parameter_expression(file_obj):
|
|
473
474
|
data = formats.PARAMETER_EXPR(
|
474
475
|
*struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE))
|
475
476
|
)
|
476
|
-
from sympy.parsing.sympy_parser import parse_expr
|
477
477
|
|
478
|
-
|
478
|
+
sympy_str = file_obj.read(data.expr_size).decode(common.ENCODE)
|
479
|
+
expr_ = symengine.sympify(parse_sympy_repr(sympy_str))
|
479
480
|
symbol_map = {}
|
480
481
|
for _ in range(data.map_elements):
|
481
482
|
elem_data = formats.PARAM_EXPR_MAP_ELEM(
|
@@ -514,9 +515,8 @@ def _read_parameter_expression_v3(file_obj, vectors, use_symengine):
|
|
514
515
|
if use_symengine:
|
515
516
|
expr_ = common.load_symengine_payload(payload)
|
516
517
|
else:
|
517
|
-
|
518
|
-
|
519
|
-
expr_ = symengine.sympify(parse_expr(payload.decode(common.ENCODE)))
|
518
|
+
sympy_str = payload.decode(common.ENCODE)
|
519
|
+
expr_ = symengine.sympify(parse_sympy_repr(sympy_str))
|
520
520
|
|
521
521
|
symbol_map = {}
|
522
522
|
for _ in range(data.map_elements):
|
qiskit/result/models.py
CHANGED
@@ -145,8 +145,7 @@ class ExperimentResult:
|
|
145
145
|
status (str): The status of the experiment
|
146
146
|
seed (int): The seed used for simulation (if run on a simulator)
|
147
147
|
meas_return (str): The type of measurement returned
|
148
|
-
header (dict): A free form dictionary
|
149
|
-
header for the experiment
|
148
|
+
header (dict): A free form dictionary header for the experiment
|
150
149
|
kwargs: Arbitrary extra fields
|
151
150
|
|
152
151
|
Raises:
|
qiskit/result/result.py
CHANGED
@@ -34,6 +34,9 @@ class Result:
|
|
34
34
|
each experiment success)
|
35
35
|
results (list[ExperimentResult]): corresponding results for array of
|
36
36
|
experiments of the input
|
37
|
+
date (str): optional date field
|
38
|
+
status (str): optional status field
|
39
|
+
header (dict): an optional free form dictionary header
|
37
40
|
"""
|
38
41
|
|
39
42
|
_metadata = {}
|
@@ -88,7 +91,7 @@ class Result:
|
|
88
91
|
"backend_name": self.backend_name,
|
89
92
|
"backend_version": self.backend_version,
|
90
93
|
"date": self.date,
|
91
|
-
"header":
|
94
|
+
"header": self.header,
|
92
95
|
"job_id": self.job_id,
|
93
96
|
"status": self.status,
|
94
97
|
"success": self.success,
|
@@ -128,11 +131,10 @@ class Result:
|
|
128
131
|
the get_xxx method, and the data will be post-processed for the data type.
|
129
132
|
|
130
133
|
Args:
|
131
|
-
experiment (str or QuantumCircuit or
|
134
|
+
experiment (str or QuantumCircuit or int or None): the index of the
|
132
135
|
experiment. Several types are accepted for convenience::
|
133
136
|
* str: the name of the experiment.
|
134
137
|
* QuantumCircuit: the name of the circuit instance will be used.
|
135
|
-
* Schedule: the name of the schedule instance will be used.
|
136
138
|
* int: the position of the experiment.
|
137
139
|
* None: if there is only one experiment, returns it.
|
138
140
|
|
@@ -179,7 +181,7 @@ class Result:
|
|
179
181
|
['00000', '01000', '10100', '10100', '11101', '11100', '00101', ..., '01010']
|
180
182
|
|
181
183
|
Args:
|
182
|
-
experiment (str or QuantumCircuit or
|
184
|
+
experiment (str or QuantumCircuit or int or None): the index of the
|
183
185
|
experiment, as specified by ``data()``.
|
184
186
|
|
185
187
|
Returns:
|
@@ -231,7 +233,7 @@ class Result:
|
|
231
233
|
"""Get the histogram data of an experiment.
|
232
234
|
|
233
235
|
Args:
|
234
|
-
experiment (str or QuantumCircuit or
|
236
|
+
experiment (str or QuantumCircuit or int or None): the index of the
|
235
237
|
experiment, as specified by ``data([experiment])``.
|
236
238
|
|
237
239
|
Returns:
|
@@ -283,7 +285,7 @@ class Result:
|
|
283
285
|
"""Get the final statevector of an experiment.
|
284
286
|
|
285
287
|
Args:
|
286
|
-
experiment (str or QuantumCircuit or
|
288
|
+
experiment (str or QuantumCircuit or int or None): the index of the
|
287
289
|
experiment, as specified by ``data()``.
|
288
290
|
decimals (int): the number of decimals in the statevector.
|
289
291
|
If None, does not round.
|
@@ -305,7 +307,7 @@ class Result:
|
|
305
307
|
"""Get the final unitary of an experiment.
|
306
308
|
|
307
309
|
Args:
|
308
|
-
experiment (str or QuantumCircuit or
|
310
|
+
experiment (str or QuantumCircuit or int or None): the index of the
|
309
311
|
experiment, as specified by ``data()``.
|
310
312
|
decimals (int): the number of decimals in the unitary.
|
311
313
|
If None, does not round.
|
@@ -326,7 +328,7 @@ class Result:
|
|
326
328
|
"""Return a single experiment result from a given key.
|
327
329
|
|
328
330
|
Args:
|
329
|
-
key (str or QuantumCircuit or
|
331
|
+
key (str or QuantumCircuit or int or None): the index of the
|
330
332
|
experiment, as specified by ``data()``.
|
331
333
|
|
332
334
|
Returns:
|
@@ -53,16 +53,40 @@ def _compute_rotation_axis(matrix: np.ndarray) -> np.ndarray:
|
|
53
53
|
"""
|
54
54
|
_check_is_so3(matrix)
|
55
55
|
|
56
|
+
# If theta represents the rotation angle, then trace = 1 + 2cos(theta).
|
56
57
|
trace = _compute_trace_so3(matrix)
|
57
|
-
|
58
|
-
if
|
59
|
-
|
60
|
-
y = 1 / (2 * math.sin(theta)) * (matrix[0][2] - matrix[2][0])
|
61
|
-
z = 1 / (2 * math.sin(theta)) * (matrix[1][0] - matrix[0][1])
|
62
|
-
else:
|
58
|
+
|
59
|
+
if trace >= 3 - 1e-10:
|
60
|
+
# The matrix is the identity (rotation by 0)
|
63
61
|
x = 1.0
|
64
62
|
y = 0.0
|
65
63
|
z = 0.0
|
64
|
+
|
65
|
+
elif trace <= -1 + 1e-10:
|
66
|
+
# The matrix is the 180-degree rotation
|
67
|
+
squares = (1 + np.diagonal(matrix)) / 2
|
68
|
+
index_of_max = np.argmax(squares)
|
69
|
+
|
70
|
+
if index_of_max == 0:
|
71
|
+
x = math.sqrt(squares[0])
|
72
|
+
y = matrix[0][1] / (2 * x)
|
73
|
+
z = matrix[0][2] / (2 * x)
|
74
|
+
elif index_of_max == 1:
|
75
|
+
y = math.sqrt(squares[1])
|
76
|
+
x = matrix[0][1] / (2 * y)
|
77
|
+
z = matrix[1][2] / (2 * y)
|
78
|
+
else:
|
79
|
+
z = math.sqrt(squares[2])
|
80
|
+
x = matrix[0][2] / (2 * z)
|
81
|
+
y = matrix[1][2] / (2 * z)
|
82
|
+
|
83
|
+
else:
|
84
|
+
# The matrix is the rotation by theta with sin(theta)!=0
|
85
|
+
theta = math.acos(0.5 * (trace - 1))
|
86
|
+
x = 1 / (2 * math.sin(theta)) * (matrix[2][1] - matrix[1][2])
|
87
|
+
y = 1 / (2 * math.sin(theta)) * (matrix[0][2] - matrix[2][0])
|
88
|
+
z = 1 / (2 * math.sin(theta)) * (matrix[1][0] - matrix[0][1])
|
89
|
+
|
66
90
|
return np.array([x, y, z])
|
67
91
|
|
68
92
|
|
@@ -55,7 +55,6 @@ class GateSequence:
|
|
55
55
|
self.name = " ".join(self.labels)
|
56
56
|
self.global_phase = global_phase
|
57
57
|
self.product = so3_matrix
|
58
|
-
self.product_su2 = su2_matrix
|
59
58
|
|
60
59
|
def remove_cancelling_pair(self, indices: Sequence[int]) -> None:
|
61
60
|
"""Remove a pair of indices that cancel each other and *do not* change the matrices."""
|
@@ -106,6 +105,16 @@ class GateSequence:
|
|
106
105
|
|
107
106
|
return circuit
|
108
107
|
|
108
|
+
def _to_u2(self):
|
109
|
+
"""Creates the U2 matrix corresponding to the stored sequence of gates
|
110
|
+
and the global phase.
|
111
|
+
"""
|
112
|
+
u2 = np.eye(2, dtype=complex)
|
113
|
+
for mat in self.gates:
|
114
|
+
u2 = mat.to_matrix().dot(u2)
|
115
|
+
u2 = np.exp(1j * self.global_phase) * u2
|
116
|
+
return u2
|
117
|
+
|
109
118
|
def to_dag(self):
|
110
119
|
"""Convert to a :class:`.DAGCircuit`.
|
111
120
|
|
@@ -149,7 +158,6 @@ class GateSequence:
|
|
149
158
|
so3 = _convert_su2_to_so3(su2)
|
150
159
|
|
151
160
|
self.product = so3.dot(self.product)
|
152
|
-
self.product_su2 = su2.dot(self.product_su2)
|
153
161
|
self.global_phase = self.global_phase + phase
|
154
162
|
|
155
163
|
self.gates.append(gate)
|
@@ -172,7 +180,6 @@ class GateSequence:
|
|
172
180
|
adjoint.labels = [inv.name for inv in adjoint.gates]
|
173
181
|
adjoint.name = " ".join(adjoint.labels)
|
174
182
|
adjoint.product = np.conj(self.product).T
|
175
|
-
adjoint.product_su2 = np.conj(self.product_su2).T
|
176
183
|
adjoint.global_phase = -self.global_phase
|
177
184
|
|
178
185
|
return adjoint
|
@@ -190,7 +197,6 @@ class GateSequence:
|
|
190
197
|
out.matrices = self.matrices.copy()
|
191
198
|
out.global_phase = self.global_phase
|
192
199
|
out.product = self.product.copy()
|
193
|
-
out.product_su2 = self.product_su2.copy()
|
194
200
|
out.name = self.name
|
195
201
|
out._eulers = self._eulers
|
196
202
|
return out
|
@@ -76,7 +76,7 @@ def _check_candidate_greedy(candidate, existing_sequences, tol=1e-10):
|
|
76
76
|
return False
|
77
77
|
|
78
78
|
for existing in existing_sequences:
|
79
|
-
if matrix_equal(existing.
|
79
|
+
if matrix_equal(existing.product, candidate.product, ignore_phase=True, atol=tol):
|
80
80
|
# is the new sequence less or more efficient?
|
81
81
|
return len(candidate.gates) < len(existing.gates)
|
82
82
|
return True
|
@@ -24,7 +24,7 @@ from .generate_basis_approximations import generate_basic_approximations, _1q_ga
|
|
24
24
|
class SolovayKitaevDecomposition:
|
25
25
|
"""The Solovay Kitaev discrete decomposition algorithm.
|
26
26
|
|
27
|
-
This class is called recursively by the transpiler pass, which is why it is
|
27
|
+
This class is called recursively by the transpiler pass, which is why it is separated.
|
28
28
|
See :class:`qiskit.transpiler.passes.SolovayKitaev` for more information.
|
29
29
|
"""
|
30
30
|
|
@@ -33,7 +33,7 @@ class SolovayKitaevDecomposition:
|
|
33
33
|
) -> None:
|
34
34
|
"""
|
35
35
|
Args:
|
36
|
-
basic_approximations: A specification of the basic
|
36
|
+
basic_approximations: A specification of the basic SO(3) approximations in terms
|
37
37
|
of discrete gates. At each iteration this algorithm, the remaining error is
|
38
38
|
approximated with the closest sequence of gates in this set.
|
39
39
|
If a ``str``, this specifies a ``.npy`` filename from which to load the
|
@@ -116,23 +116,33 @@ class SolovayKitaevDecomposition:
|
|
116
116
|
"""
|
117
117
|
# make input matrix SU(2) and get the according global phase
|
118
118
|
z = 1 / np.sqrt(np.linalg.det(gate_matrix))
|
119
|
-
|
119
|
+
|
120
|
+
gate_matrix_su2 = z * gate_matrix
|
121
|
+
gate_matrix_as_sequence = GateSequence.from_matrix(gate_matrix_su2)
|
120
122
|
global_phase = np.arctan2(np.imag(z), np.real(z))
|
121
123
|
|
122
124
|
# get the decomposition as GateSequence type
|
123
|
-
decomposition = self._recurse(
|
125
|
+
decomposition = self._recurse(
|
126
|
+
gate_matrix_as_sequence, recursion_degree, check_input=check_input
|
127
|
+
)
|
124
128
|
|
125
129
|
# simplify
|
126
130
|
_remove_identities(decomposition)
|
127
131
|
_remove_inverse_follows_gate(decomposition)
|
128
132
|
|
133
|
+
# adjust to the correct SU(2) phase
|
134
|
+
adjust_phase = (
|
135
|
+
np.pi if _should_adjust_phase(decomposition._to_u2(), gate_matrix_su2) else 0.0
|
136
|
+
)
|
137
|
+
|
129
138
|
# convert to a circuit and attach the right phases
|
130
139
|
if return_dag:
|
131
140
|
out = decomposition.to_dag()
|
132
141
|
else:
|
133
142
|
out = decomposition.to_circuit()
|
134
143
|
|
135
|
-
out.global_phase
|
144
|
+
out.global_phase += adjust_phase
|
145
|
+
out.global_phase -= global_phase
|
136
146
|
|
137
147
|
return out
|
138
148
|
|
@@ -155,17 +165,20 @@ class SolovayKitaevDecomposition:
|
|
155
165
|
raise ValueError("Shape of U must be (3, 3) but is", sequence.shape)
|
156
166
|
|
157
167
|
if n == 0:
|
158
|
-
|
168
|
+
res = self.find_basic_approximation(sequence)
|
159
169
|
|
160
|
-
|
170
|
+
else:
|
171
|
+
u_n1 = self._recurse(sequence, n - 1, check_input=check_input)
|
161
172
|
|
162
|
-
|
163
|
-
|
164
|
-
|
173
|
+
v_n, w_n = commutator_decompose(
|
174
|
+
sequence.dot(u_n1.adjoint()).product, check_input=check_input
|
175
|
+
)
|
176
|
+
|
177
|
+
v_n1 = self._recurse(v_n, n - 1, check_input=check_input)
|
178
|
+
w_n1 = self._recurse(w_n, n - 1, check_input=check_input)
|
179
|
+
res = v_n1.dot(w_n1).dot(v_n1.adjoint()).dot(w_n1.adjoint()).dot(u_n1)
|
165
180
|
|
166
|
-
|
167
|
-
w_n1 = self._recurse(w_n, n - 1, check_input=check_input)
|
168
|
-
return v_n1.dot(w_n1).dot(v_n1.adjoint()).dot(w_n1.adjoint()).dot(u_n1)
|
181
|
+
return res
|
169
182
|
|
170
183
|
def find_basic_approximation(self, sequence: GateSequence) -> GateSequence:
|
171
184
|
"""Find ``GateSequence`` in ``self._basic_approximations`` that approximates ``sequence``.
|
@@ -215,3 +228,13 @@ def _remove_identities(sequence):
|
|
215
228
|
sequence.gates.pop(index)
|
216
229
|
else:
|
217
230
|
index += 1
|
231
|
+
|
232
|
+
|
233
|
+
def _should_adjust_phase(computed: np.ndarray, target: np.ndarray) -> bool:
|
234
|
+
"""
|
235
|
+
The implemented SolovayKitaevDecomposition has a global phase uncertainty of +-1,
|
236
|
+
due to approximating not the original SU(2) matrix but its projection onto SO(3).
|
237
|
+
This function returns ``True`` if the global phase of the computed approximation
|
238
|
+
should be adjusted (by adding pi) to better much the target.
|
239
|
+
"""
|
240
|
+
return np.linalg.norm(-computed - target) < np.linalg.norm(computed - target)
|
@@ -91,6 +91,7 @@ Optimizations
|
|
91
91
|
Split2QUnitaries
|
92
92
|
RemoveIdentityEquivalent
|
93
93
|
ContractIdleWiresInControlFlow
|
94
|
+
LightCone
|
94
95
|
|
95
96
|
Scheduling
|
96
97
|
=============
|
@@ -225,6 +226,7 @@ from .optimization import OptimizeAnnotated
|
|
225
226
|
from .optimization import RemoveIdentityEquivalent
|
226
227
|
from .optimization import Split2QUnitaries
|
227
228
|
from .optimization import ContractIdleWiresInControlFlow
|
229
|
+
from .optimization import LightCone
|
228
230
|
|
229
231
|
# circuit analysis
|
230
232
|
from .analysis import ResourceEstimation
|
@@ -18,7 +18,7 @@ import logging
|
|
18
18
|
from collections import defaultdict
|
19
19
|
|
20
20
|
from qiskit.transpiler.basepasses import TransformationPass
|
21
|
-
from qiskit._accelerate.
|
21
|
+
from qiskit._accelerate.basis_translator import base_run
|
22
22
|
|
23
23
|
logger = logging.getLogger(__name__)
|
24
24
|
|
@@ -92,9 +92,8 @@ class FullAncillaAllocation(AnalysisPass):
|
|
92
92
|
|
93
93
|
if idle_physical_qubits:
|
94
94
|
if self.ancilla_name in dag.qregs:
|
95
|
-
qreg = QuantumRegister(
|
96
|
-
len(idle_physical_qubits),
|
97
|
-
name=f"{self.ancilla_name}{QuantumRegister.instances_count + 1}",
|
95
|
+
qreg = QuantumRegister._new_with_prefix(
|
96
|
+
len(idle_physical_qubits), self.ancilla_name
|
98
97
|
)
|
99
98
|
else:
|
100
99
|
qreg = QuantumRegister(len(idle_physical_qubits), name=self.ancilla_name)
|
@@ -305,6 +305,9 @@ class SabreLayout(TransformationPass):
|
|
305
305
|
# the layout and routing together as part of resolving the Sabre result.
|
306
306
|
physical_qubits = QuantumRegister(self.coupling_map.size(), "q")
|
307
307
|
mapped_dag = DAGCircuit()
|
308
|
+
mapped_dag.name = dag.name
|
309
|
+
mapped_dag.metadata = dag.metadata
|
310
|
+
mapped_dag.global_phase = dag.global_phase
|
308
311
|
mapped_dag.add_qreg(physical_qubits)
|
309
312
|
mapped_dag.add_clbits(dag.clbits)
|
310
313
|
for creg in dag.cregs.values():
|
@@ -319,7 +322,6 @@ class SabreLayout(TransformationPass):
|
|
319
322
|
mapped_dag.add_captured_stretch(stretch)
|
320
323
|
for stretch in dag.iter_declared_stretches():
|
321
324
|
mapped_dag.add_declared_stretch(stretch)
|
322
|
-
mapped_dag.global_phase = dag.global_phase
|
323
325
|
self.property_set["original_qubit_indices"] = {
|
324
326
|
bit: index for index, bit in enumerate(dag.qubits)
|
325
327
|
}
|
@@ -179,11 +179,8 @@ def build_average_error_map(target, coupling_map):
|
|
179
179
|
coupling_map = target.build_coupling_map()
|
180
180
|
if not built and coupling_map is not None and num_qubits is not None:
|
181
181
|
for qubit in range(num_qubits):
|
182
|
-
|
183
|
-
|
184
|
-
(coupling_map.graph.out_degree(qubit) + coupling_map.graph.in_degree(qubit))
|
185
|
-
/ num_qubits,
|
186
|
-
)
|
182
|
+
degree = len(set(coupling_map.graph.neighbors_undirected(qubit)))
|
183
|
+
avg_map.add_error((qubit, qubit), degree / num_qubits)
|
187
184
|
for edge in coupling_map.graph.edge_list():
|
188
185
|
avg_map.add_error(edge, (avg_map[edge[0], edge[0]] + avg_map[edge[1], edge[1]]) / 2)
|
189
186
|
built = True
|
@@ -39,3 +39,4 @@ from .remove_identity_equiv import RemoveIdentityEquivalent
|
|
39
39
|
from .split_2q_unitaries import Split2QUnitaries
|
40
40
|
from .collect_and_collapse import CollectAndCollapse
|
41
41
|
from .contract_idle_wires_in_control_flow import ContractIdleWiresInControlFlow
|
42
|
+
from .light_cone import LightCone
|
@@ -496,7 +496,6 @@ class TemplateSubstitution:
|
|
496
496
|
parameter constraints, returns None.
|
497
497
|
"""
|
498
498
|
import sympy as sym
|
499
|
-
from sympy.parsing.sympy_parser import parse_expr
|
500
499
|
|
501
500
|
if _optionals.HAS_SYMENGINE:
|
502
501
|
import symengine
|
@@ -572,7 +571,8 @@ class TemplateSubstitution:
|
|
572
571
|
if isinstance(circuit_param, ParameterExpression):
|
573
572
|
circ_param_sym = circuit_param.sympify()
|
574
573
|
else:
|
575
|
-
|
574
|
+
# if it's not a ParameterExpression we're a float
|
575
|
+
circ_param_sym = sym.Float(circuit_param)
|
576
576
|
equations.append(sym.Eq(template_param.sympify(), circ_param_sym))
|
577
577
|
|
578
578
|
for param in template_param.parameters:
|
@@ -59,7 +59,7 @@ class InstructionDurationCheck(AnalysisPass):
|
|
59
59
|
self.property_set["reschedule_required"] = False
|
60
60
|
|
61
61
|
# Rescheduling is not necessary
|
62
|
-
if self.acquire_align == 1 and self.pulse_align == 1:
|
62
|
+
if (self.acquire_align == 1 and self.pulse_align == 1) or dag._num_stretches != 0:
|
63
63
|
return
|
64
64
|
|
65
65
|
# Check delay durations
|