qiskit 1.4.1__cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl → 1.4.3__cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qiskit/VERSION.txt +1 -1
- qiskit/_accelerate.abi3.so +0 -0
- qiskit/circuit/duration.py +16 -16
- qiskit/circuit/library/standard_gates/r.py +4 -3
- qiskit/circuit/library/standard_gates/x.py +1 -2
- qiskit/circuit/parameterexpression.py +6 -1
- qiskit/circuit/quantumcircuit.py +9 -9
- 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/binary_io/circuits.py +21 -4
- qiskit/qpy/binary_io/parse_sympy_repr.py +121 -0
- qiskit/qpy/binary_io/schedules.py +61 -18
- qiskit/qpy/binary_io/value.py +8 -5
- qiskit/qpy/interface.py +19 -5
- 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 +3 -1
- qiskit/synthesis/discrete_basis/solovay_kitaev.py +36 -13
- qiskit/transpiler/passes/layout/sabre_layout.py +4 -1
- qiskit/transpiler/passes/layout/vf2_utils.py +2 -5
- qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +2 -2
- qiskit/transpiler/passes/scheduling/alap.py +2 -2
- qiskit/transpiler/passes/scheduling/alignments/align_measures.py +3 -3
- qiskit/transpiler/passes/scheduling/asap.py +2 -2
- qiskit/transpiler/passes/scheduling/dynamical_decoupling.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/synthesis/solovay_kitaev_synthesis.py +29 -19
- qiskit/visualization/timeline/core.py +1 -1
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/METADATA +4 -3
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/RECORD +709 -708
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/WHEEL +1 -1
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/entry_points.txt +0 -0
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info/licenses}/LICENSE.txt +0 -0
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/top_level.txt +0 -0
qiskit/VERSION.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.
|
1
|
+
1.4.3
|
qiskit/_accelerate.abi3.so
CHANGED
Binary file
|
qiskit/circuit/duration.py
CHANGED
@@ -65,29 +65,29 @@ def convert_durations_to_dt(qc: QuantumCircuit, dt_in_sec: float, inplace=True):
|
|
65
65
|
|
66
66
|
for instruction in circ.data:
|
67
67
|
operation = instruction.operation
|
68
|
-
if operation.
|
68
|
+
if operation._unit == "dt" or operation._duration is None:
|
69
69
|
continue
|
70
70
|
|
71
|
-
if not operation.
|
72
|
-
raise CircuitError(f"Invalid time unit: '{operation.
|
71
|
+
if not operation._unit.endswith("s"):
|
72
|
+
raise CircuitError(f"Invalid time unit: '{operation._unit}'")
|
73
73
|
|
74
|
-
duration = operation.
|
75
|
-
if operation.
|
76
|
-
duration = apply_prefix(duration, operation.
|
74
|
+
duration = operation._duration
|
75
|
+
if operation._unit != "s":
|
76
|
+
duration = apply_prefix(duration, operation._unit)
|
77
77
|
|
78
|
-
operation.
|
79
|
-
operation.
|
78
|
+
operation._duration = duration_in_dt(duration, dt_in_sec)
|
79
|
+
operation._unit = "dt"
|
80
80
|
|
81
|
-
if circ.
|
82
|
-
if not circ.
|
83
|
-
raise CircuitError(f"Invalid time unit: '{circ.
|
81
|
+
if circ._duration is not None and circ._unit != "dt":
|
82
|
+
if not circ._unit.endswith("s"):
|
83
|
+
raise CircuitError(f"Invalid time unit: '{circ._unit}'")
|
84
84
|
|
85
|
-
duration = circ.
|
86
|
-
if circ.
|
87
|
-
duration = apply_prefix(duration, circ.
|
85
|
+
duration = circ._duration
|
86
|
+
if circ._unit != "s":
|
87
|
+
duration = apply_prefix(duration, circ._unit)
|
88
88
|
|
89
|
-
circ.
|
90
|
-
circ.
|
89
|
+
circ._duration = duration_in_dt(duration, dt_in_sec)
|
90
|
+
circ._unit = "dt"
|
91
91
|
|
92
92
|
if not inplace:
|
93
93
|
return circ
|
@@ -306,8 +306,7 @@ class CCXGate(SingletonControlledGate):
|
|
306
306
|
r"""CCX gate, also known as Toffoli gate.
|
307
307
|
|
308
308
|
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
|
309
|
-
with the :meth:`~qiskit.circuit.QuantumCircuit.ccx`
|
310
|
-
:meth:`~qiskit.circuit.QuantumCircuit.toffoli` methods.
|
309
|
+
with the :meth:`~qiskit.circuit.QuantumCircuit.ccx` method.
|
311
310
|
|
312
311
|
**Circuit symbol:**
|
313
312
|
|
@@ -25,7 +25,7 @@ import operator
|
|
25
25
|
import numpy
|
26
26
|
import symengine
|
27
27
|
|
28
|
-
from qiskit.circuit.exceptions import CircuitError
|
28
|
+
from qiskit.circuit.exceptions import CircuitError, QiskitError
|
29
29
|
|
30
30
|
# This type is redefined at the bottom to insert the full reference to "ParameterExpression", so it
|
31
31
|
# can safely be used by runtime type-checkers like Sphinx. Mypy does not need this because it
|
@@ -528,6 +528,9 @@ class ParameterExpression:
|
|
528
528
|
def __str__(self):
|
529
529
|
from sympy import sympify, sstr
|
530
530
|
|
531
|
+
if not isinstance(self._symbol_expr, symengine.Basic):
|
532
|
+
raise QiskitError("Invalid ParameterExpression")
|
533
|
+
|
531
534
|
return sstr(sympify(self._symbol_expr), full_prec=False)
|
532
535
|
|
533
536
|
def __complex__(self):
|
@@ -608,6 +611,8 @@ class ParameterExpression:
|
|
608
611
|
return False
|
609
612
|
from sympy import sympify
|
610
613
|
|
614
|
+
if not isinstance(self._symbol_expr, symengine.Basic):
|
615
|
+
raise QiskitError("Invalid ParameterExpression")
|
611
616
|
return sympify(self._symbol_expr).equals(sympify(other._symbol_expr))
|
612
617
|
elif isinstance(other, numbers.Number):
|
613
618
|
return len(self.parameters) == 0 and complex(self._symbol_expr) == other
|
qiskit/circuit/quantumcircuit.py
CHANGED
@@ -1546,8 +1546,8 @@ class QuantumCircuit:
|
|
1546
1546
|
for instruction in reversed(self.data):
|
1547
1547
|
reverse_circ._append(instruction.replace(operation=instruction.operation.reverse_ops()))
|
1548
1548
|
|
1549
|
-
reverse_circ.
|
1550
|
-
reverse_circ.
|
1549
|
+
reverse_circ._duration = self._duration
|
1550
|
+
reverse_circ._unit = self._unit
|
1551
1551
|
return reverse_circ
|
1552
1552
|
|
1553
1553
|
def reverse_bits(self) -> "QuantumCircuit":
|
@@ -2620,8 +2620,8 @@ class QuantumCircuit:
|
|
2620
2620
|
"""
|
2621
2621
|
if _standard_gate:
|
2622
2622
|
self._data.append(instruction)
|
2623
|
-
self.
|
2624
|
-
self.
|
2623
|
+
self._duration = None
|
2624
|
+
self._unit = "dt"
|
2625
2625
|
return instruction
|
2626
2626
|
|
2627
2627
|
old_style = not isinstance(instruction, CircuitInstruction)
|
@@ -2643,8 +2643,8 @@ class QuantumCircuit:
|
|
2643
2643
|
self._data.append_manual_params(instruction, params)
|
2644
2644
|
|
2645
2645
|
# Invalidate whole circuit duration if an instruction is added
|
2646
|
-
self.
|
2647
|
-
self.
|
2646
|
+
self._duration = None
|
2647
|
+
self._unit = "dt"
|
2648
2648
|
return instruction.operation if old_style else instruction
|
2649
2649
|
|
2650
2650
|
@typing.overload
|
@@ -6584,7 +6584,7 @@ class QuantumCircuit:
|
|
6584
6584
|
Raises:
|
6585
6585
|
CircuitError: if ``self`` is a not-yet scheduled circuit.
|
6586
6586
|
"""
|
6587
|
-
if self.
|
6587
|
+
if self._duration is None:
|
6588
6588
|
# circuit has only delays, this is kind of scheduled
|
6589
6589
|
for instruction in self._data:
|
6590
6590
|
if not isinstance(instruction.operation, Delay):
|
@@ -6626,7 +6626,7 @@ class QuantumCircuit:
|
|
6626
6626
|
Raises:
|
6627
6627
|
CircuitError: if ``self`` is a not-yet scheduled circuit.
|
6628
6628
|
"""
|
6629
|
-
if self.
|
6629
|
+
if self._duration is None:
|
6630
6630
|
# circuit has only delays, this is kind of scheduled
|
6631
6631
|
for instruction in self._data:
|
6632
6632
|
if not isinstance(instruction.operation, Delay):
|
@@ -6637,7 +6637,7 @@ class QuantumCircuit:
|
|
6637
6637
|
|
6638
6638
|
qubits = [self.qubits[q] if isinstance(q, int) else q for q in qubits]
|
6639
6639
|
|
6640
|
-
stops = {q: self.
|
6640
|
+
stops = {q: self._duration for q in qubits}
|
6641
6641
|
dones = {q: False for q in qubits}
|
6642
6642
|
for instruction in reversed(self._data):
|
6643
6643
|
for q in qubits:
|
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:
|
@@ -73,6 +73,6 @@ def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_ord
|
|
73
73
|
|
74
74
|
dagcircuit = core_circuit_to_dag(circuit, copy_operations, qubit_order, clbit_order)
|
75
75
|
|
76
|
-
dagcircuit.
|
77
|
-
dagcircuit.
|
76
|
+
dagcircuit._duration = circuit._duration
|
77
|
+
dagcircuit._unit = circuit._unit
|
78
78
|
return dagcircuit
|
@@ -74,7 +74,6 @@ def dag_to_circuit(dag, copy_operations=True):
|
|
74
74
|
circuit._calibrations_prop = dag._calibrations_prop
|
75
75
|
|
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
|
@@ -525,8 +525,6 @@ class _DAGDependencyV2:
|
|
525
525
|
target_dag = _DAGDependencyV2()
|
526
526
|
target_dag.name = self.name
|
527
527
|
target_dag._global_phase = self._global_phase
|
528
|
-
target_dag.duration = self.duration
|
529
|
-
target_dag.unit = self.unit
|
530
528
|
target_dag.metadata = self.metadata
|
531
529
|
target_dag._key_cache = self._key_cache
|
532
530
|
target_dag.comm_checker = self.comm_checker
|
@@ -534,6 +532,9 @@ class _DAGDependencyV2:
|
|
534
532
|
target_dag.add_qubits(self.qubits)
|
535
533
|
target_dag.add_clbits(self.clbits)
|
536
534
|
|
535
|
+
target_dag.duration = self.duration
|
536
|
+
target_dag.unit = self.unit
|
537
|
+
|
537
538
|
for qreg in self.qregs.values():
|
538
539
|
target_dag.add_qreg(qreg)
|
539
540
|
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/binary_io/circuits.py
CHANGED
@@ -638,7 +638,7 @@ def _read_custom_operations(file_obj, version, vectors):
|
|
638
638
|
return custom_operations
|
639
639
|
|
640
640
|
|
641
|
-
def _read_calibrations(file_obj, version, vectors, metadata_deserializer):
|
641
|
+
def _read_calibrations(file_obj, version, vectors, metadata_deserializer, trust_input=False):
|
642
642
|
calibrations = {}
|
643
643
|
|
644
644
|
header = formats.CALIBRATION._make(
|
@@ -656,7 +656,12 @@ def _read_calibrations(file_obj, version, vectors, metadata_deserializer):
|
|
656
656
|
params = tuple(
|
657
657
|
value.read_value(file_obj, version, vectors) for _ in range(defheader.num_params)
|
658
658
|
)
|
659
|
-
schedule = schedules.read_schedule_block(
|
659
|
+
schedule = schedules.read_schedule_block(
|
660
|
+
file_obj,
|
661
|
+
version,
|
662
|
+
metadata_deserializer,
|
663
|
+
trust_input=trust_input,
|
664
|
+
)
|
660
665
|
|
661
666
|
if name not in calibrations:
|
662
667
|
calibrations[name] = {(qubits, params): schedule}
|
@@ -771,6 +776,11 @@ def _write_instruction(
|
|
771
776
|
custom_operations[gate_class_name] = instruction.operation
|
772
777
|
custom_operations_list.append(gate_class_name)
|
773
778
|
|
779
|
+
elif isinstance(instruction.operation, library.MCMTGate):
|
780
|
+
gate_class_name = instruction.operation.name + "_" + str(uuid.uuid4())
|
781
|
+
custom_operations[gate_class_name] = instruction.operation
|
782
|
+
custom_operations_list.append(gate_class_name)
|
783
|
+
|
774
784
|
condition_type = type_keys.Condition.NONE
|
775
785
|
condition_register = b""
|
776
786
|
condition_value = 0
|
@@ -1327,7 +1337,9 @@ def write_circuit(
|
|
1327
1337
|
_write_layout(file_obj, circuit)
|
1328
1338
|
|
1329
1339
|
|
1330
|
-
def read_circuit(
|
1340
|
+
def read_circuit(
|
1341
|
+
file_obj, version, metadata_deserializer=None, use_symengine=False, trust_input=False
|
1342
|
+
):
|
1331
1343
|
"""Read a single QuantumCircuit object from the file like object.
|
1332
1344
|
|
1333
1345
|
Args:
|
@@ -1345,6 +1357,7 @@ def read_circuit(file_obj, version, metadata_deserializer=None, use_symengine=Fa
|
|
1345
1357
|
supported in all platforms. Please check that your target platform is supported by
|
1346
1358
|
the symengine library before setting this option, as it will be required by qpy to
|
1347
1359
|
deserialize the payload.
|
1360
|
+
trust_input (bool): If true, deserialize vulnerable schedule block payloads.
|
1348
1361
|
Returns:
|
1349
1362
|
QuantumCircuit: The circuit object from the file.
|
1350
1363
|
|
@@ -1454,7 +1467,11 @@ def read_circuit(file_obj, version, metadata_deserializer=None, use_symengine=Fa
|
|
1454
1467
|
# Read calibrations
|
1455
1468
|
if version >= 5:
|
1456
1469
|
circ._calibrations_prop = _read_calibrations(
|
1457
|
-
file_obj,
|
1470
|
+
file_obj,
|
1471
|
+
version,
|
1472
|
+
vectors,
|
1473
|
+
metadata_deserializer,
|
1474
|
+
trust_input=trust_input,
|
1458
1475
|
)
|
1459
1476
|
|
1460
1477
|
for vec_name, (vector, initialized_params) in vectors.items():
|
@@ -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()
|
@@ -99,7 +99,7 @@ def _read_discriminator(file_obj, version):
|
|
99
99
|
return Discriminator(name=name, **params)
|
100
100
|
|
101
101
|
|
102
|
-
def _loads_symbolic_expr(expr_bytes, use_symengine=False):
|
102
|
+
def _loads_symbolic_expr(expr_bytes, use_symengine=False, trust_input=False):
|
103
103
|
if expr_bytes == b"":
|
104
104
|
return None
|
105
105
|
expr_bytes = zlib.decompress(expr_bytes)
|
@@ -108,12 +108,18 @@ def _loads_symbolic_expr(expr_bytes, use_symengine=False):
|
|
108
108
|
else:
|
109
109
|
from sympy import parse_expr
|
110
110
|
|
111
|
+
if not trust_input:
|
112
|
+
raise QpyError(
|
113
|
+
"This payload can not be loaded unless you set ``trust_payload`` to "
|
114
|
+
"True, as it's using sympy for serialization of symbolic expressions which "
|
115
|
+
"is insecure."
|
116
|
+
)
|
111
117
|
expr_txt = expr_bytes.decode(common.ENCODE)
|
112
118
|
expr = parse_expr(expr_txt)
|
113
119
|
return sym.sympify(expr)
|
114
120
|
|
115
121
|
|
116
|
-
def _read_symbolic_pulse(file_obj, version):
|
122
|
+
def _read_symbolic_pulse(file_obj, version, trust_input=False):
|
117
123
|
make = formats.SYMBOLIC_PULSE._make
|
118
124
|
pack = formats.SYMBOLIC_PULSE_PACK
|
119
125
|
size = formats.SYMBOLIC_PULSE_SIZE
|
@@ -125,9 +131,13 @@ def _read_symbolic_pulse(file_obj, version):
|
|
125
131
|
)
|
126
132
|
)
|
127
133
|
pulse_type = file_obj.read(header.type_size).decode(common.ENCODE)
|
128
|
-
envelope = _loads_symbolic_expr(file_obj.read(header.envelope_size))
|
129
|
-
constraints = _loads_symbolic_expr(
|
130
|
-
|
134
|
+
envelope = _loads_symbolic_expr(file_obj.read(header.envelope_size), trust_input=trust_input)
|
135
|
+
constraints = _loads_symbolic_expr(
|
136
|
+
file_obj.read(header.constraints_size), trust_input=trust_input
|
137
|
+
)
|
138
|
+
valid_amp_conditions = _loads_symbolic_expr(
|
139
|
+
file_obj.read(header.valid_amp_conditions_size), trust_input=trust_input
|
140
|
+
)
|
131
141
|
parameters = common.read_mapping(
|
132
142
|
file_obj,
|
133
143
|
deserializer=value.loads_value,
|
@@ -189,7 +199,7 @@ def _read_symbolic_pulse(file_obj, version):
|
|
189
199
|
raise NotImplementedError(f"Unknown class '{class_name}'")
|
190
200
|
|
191
201
|
|
192
|
-
def _read_symbolic_pulse_v6(file_obj, version, use_symengine):
|
202
|
+
def _read_symbolic_pulse_v6(file_obj, version, use_symengine, trust_input=False):
|
193
203
|
make = formats.SYMBOLIC_PULSE_V2._make
|
194
204
|
pack = formats.SYMBOLIC_PULSE_PACK_V2
|
195
205
|
size = formats.SYMBOLIC_PULSE_SIZE_V2
|
@@ -202,10 +212,14 @@ def _read_symbolic_pulse_v6(file_obj, version, use_symengine):
|
|
202
212
|
)
|
203
213
|
class_name = file_obj.read(header.class_name_size).decode(common.ENCODE)
|
204
214
|
pulse_type = file_obj.read(header.type_size).decode(common.ENCODE)
|
205
|
-
envelope = _loads_symbolic_expr(
|
206
|
-
|
215
|
+
envelope = _loads_symbolic_expr(
|
216
|
+
file_obj.read(header.envelope_size), use_symengine, trust_input=trust_input
|
217
|
+
)
|
218
|
+
constraints = _loads_symbolic_expr(
|
219
|
+
file_obj.read(header.constraints_size), use_symengine, trust_input=trust_input
|
220
|
+
)
|
207
221
|
valid_amp_conditions = _loads_symbolic_expr(
|
208
|
-
file_obj.read(header.valid_amp_conditions_size), use_symengine
|
222
|
+
file_obj.read(header.valid_amp_conditions_size), use_symengine, trust_input=trust_input
|
209
223
|
)
|
210
224
|
parameters = common.read_mapping(
|
211
225
|
file_obj,
|
@@ -277,15 +291,21 @@ def _read_alignment_context(file_obj, version):
|
|
277
291
|
|
278
292
|
|
279
293
|
# pylint: disable=too-many-return-statements
|
280
|
-
def _loads_operand(type_key, data_bytes, version, use_symengine):
|
294
|
+
def _loads_operand(type_key, data_bytes, version, use_symengine, trust_input=False):
|
281
295
|
if type_key == type_keys.ScheduleOperand.WAVEFORM:
|
282
296
|
return common.data_from_binary(data_bytes, _read_waveform, version=version)
|
283
297
|
if type_key == type_keys.ScheduleOperand.SYMBOLIC_PULSE:
|
284
298
|
if version < 6:
|
285
|
-
return common.data_from_binary(
|
299
|
+
return common.data_from_binary(
|
300
|
+
data_bytes, _read_symbolic_pulse, version=version, trust_input=trust_input
|
301
|
+
)
|
286
302
|
else:
|
287
303
|
return common.data_from_binary(
|
288
|
-
data_bytes,
|
304
|
+
data_bytes,
|
305
|
+
_read_symbolic_pulse_v6,
|
306
|
+
version=version,
|
307
|
+
use_symengine=use_symengine,
|
308
|
+
trust_input=trust_input,
|
289
309
|
)
|
290
310
|
if type_key == type_keys.ScheduleOperand.CHANNEL:
|
291
311
|
return common.data_from_binary(data_bytes, _read_channel, version=version)
|
@@ -307,14 +327,20 @@ def _loads_operand(type_key, data_bytes, version, use_symengine):
|
|
307
327
|
return value.loads_value(type_key, data_bytes, version, {})
|
308
328
|
|
309
329
|
|
310
|
-
def _read_element(file_obj, version, metadata_deserializer, use_symengine):
|
330
|
+
def _read_element(file_obj, version, metadata_deserializer, use_symengine, trust_input=False):
|
311
331
|
type_key = common.read_type_key(file_obj)
|
312
332
|
|
313
333
|
if type_key == type_keys.Program.SCHEDULE_BLOCK:
|
314
|
-
return read_schedule_block(
|
334
|
+
return read_schedule_block(
|
335
|
+
file_obj, version, metadata_deserializer, use_symengine, trust_input=trust_input
|
336
|
+
)
|
315
337
|
|
316
338
|
operands = common.read_sequence(
|
317
|
-
file_obj,
|
339
|
+
file_obj,
|
340
|
+
deserializer=_loads_operand,
|
341
|
+
version=version,
|
342
|
+
use_symengine=use_symengine,
|
343
|
+
trust_input=trust_input,
|
318
344
|
)
|
319
345
|
name = value.read_value(file_obj, version, {})
|
320
346
|
|
@@ -326,7 +352,7 @@ def _read_element(file_obj, version, metadata_deserializer, use_symengine):
|
|
326
352
|
return instance
|
327
353
|
|
328
354
|
|
329
|
-
def _loads_reference_item(type_key, data_bytes, metadata_deserializer, version):
|
355
|
+
def _loads_reference_item(type_key, data_bytes, metadata_deserializer, version, trust_input=False):
|
330
356
|
if type_key == type_keys.Value.NULL:
|
331
357
|
return None
|
332
358
|
if type_key == type_keys.Program.SCHEDULE_BLOCK:
|
@@ -335,6 +361,7 @@ def _loads_reference_item(type_key, data_bytes, metadata_deserializer, version):
|
|
335
361
|
deserializer=read_schedule_block,
|
336
362
|
version=version,
|
337
363
|
metadata_deserializer=metadata_deserializer,
|
364
|
+
trust_input=trust_input,
|
338
365
|
)
|
339
366
|
|
340
367
|
raise QpyError(
|
@@ -408,6 +435,9 @@ def _dumps_symbolic_expr(expr, use_symengine):
|
|
408
435
|
else:
|
409
436
|
from sympy import srepr, sympify
|
410
437
|
|
438
|
+
if not isinstance(expr, sym.Basic):
|
439
|
+
raise QiskitError("Invalid ParameterExpression")
|
440
|
+
|
411
441
|
expr_bytes = srepr(sympify(expr)).encode(common.ENCODE)
|
412
442
|
return zlib.compress(expr_bytes)
|
413
443
|
|
@@ -512,7 +542,9 @@ def _dumps_reference_item(schedule, metadata_serializer, version):
|
|
512
542
|
|
513
543
|
|
514
544
|
@ignore_pulse_deprecation_warnings
|
515
|
-
def read_schedule_block(
|
545
|
+
def read_schedule_block(
|
546
|
+
file_obj, version, metadata_deserializer=None, use_symengine=False, trust_input=False
|
547
|
+
):
|
516
548
|
"""Read a single ScheduleBlock from the file like object.
|
517
549
|
|
518
550
|
Args:
|
@@ -529,6 +561,14 @@ def read_schedule_block(file_obj, version, metadata_deserializer=None, use_symen
|
|
529
561
|
native mechanism. This is a faster serialization alternative, but not supported in all
|
530
562
|
platforms. Please check that your target platform is supported by the symengine library
|
531
563
|
before setting this option, as it will be required by qpy to deserialize the payload.
|
564
|
+
trust_input (bool): if set to ``False`` (the default),
|
565
|
+
:class:`.ScheduleBlock` objects in the payload that were
|
566
|
+
serialized using ``sympy`` are not allowed and will error. This
|
567
|
+
is because the ``sympy`` parsing uses :func:`eval`, which
|
568
|
+
can allow for arbitrary code execution.
|
569
|
+
The flag should only be set
|
570
|
+
to ``True`` if you trust the QPY payload you are loading.
|
571
|
+
|
532
572
|
Returns:
|
533
573
|
ScheduleBlock: The schedule block object from the file.
|
534
574
|
|
@@ -556,7 +596,9 @@ def read_schedule_block(file_obj, version, metadata_deserializer=None, use_symen
|
|
556
596
|
alignment_context=context,
|
557
597
|
)
|
558
598
|
for _ in range(data.num_elements):
|
559
|
-
block_elm = _read_element(
|
599
|
+
block_elm = _read_element(
|
600
|
+
file_obj, version, metadata_deserializer, use_symengine, trust_input=trust_input
|
601
|
+
)
|
560
602
|
block.append(block_elm, inplace=True)
|
561
603
|
|
562
604
|
# Load references
|
@@ -566,6 +608,7 @@ def read_schedule_block(file_obj, version, metadata_deserializer=None, use_symen
|
|
566
608
|
deserializer=_loads_reference_item,
|
567
609
|
version=version,
|
568
610
|
metadata_deserializer=metadata_deserializer,
|
611
|
+
trust_input=trust_input,
|
569
612
|
)
|
570
613
|
ref_dict = {}
|
571
614
|
for key_str, schedule in flat_key_refdict.items():
|
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):
|
@@ -171,6 +172,9 @@ def _write_parameter_expression(file_obj, obj, use_symengine, *, version):
|
|
171
172
|
else:
|
172
173
|
from sympy import srepr, sympify
|
173
174
|
|
175
|
+
if not isinstance(obj._symbol_expr, symengine.Basic):
|
176
|
+
raise exceptions.QpyError("Invalid ParameterExpression")
|
177
|
+
|
174
178
|
expr_bytes = srepr(sympify(obj._symbol_expr)).encode(common.ENCODE)
|
175
179
|
else:
|
176
180
|
with io.BytesIO() as buf:
|
@@ -419,9 +423,9 @@ def _read_parameter_expression(file_obj):
|
|
419
423
|
data = formats.PARAMETER_EXPR(
|
420
424
|
*struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE))
|
421
425
|
)
|
422
|
-
from sympy.parsing.sympy_parser import parse_expr
|
423
426
|
|
424
|
-
|
427
|
+
sympy_str = file_obj.read(data.expr_size).decode(common.ENCODE)
|
428
|
+
expr_ = symengine.sympify(parse_sympy_repr(sympy_str))
|
425
429
|
symbol_map = {}
|
426
430
|
for _ in range(data.map_elements):
|
427
431
|
elem_data = formats.PARAM_EXPR_MAP_ELEM(
|
@@ -460,9 +464,8 @@ def _read_parameter_expression_v3(file_obj, vectors, use_symengine):
|
|
460
464
|
if use_symengine:
|
461
465
|
expr_ = common.load_symengine_payload(payload)
|
462
466
|
else:
|
463
|
-
|
464
|
-
|
465
|
-
expr_ = symengine.sympify(parse_expr(payload.decode(common.ENCODE)))
|
467
|
+
sympy_str = payload.decode(common.ENCODE)
|
468
|
+
expr_ = symengine.sympify(parse_sympy_repr(sympy_str))
|
466
469
|
|
467
470
|
symbol_map = {}
|
468
471
|
for _ in range(data.map_elements):
|