qiskit 1.4.1__cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 1.4.2__cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.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/parameterexpression.py +6 -1
- qiskit/circuit/tools/pi_check.py +3 -0
- qiskit/qpy/binary_io/circuits.py +16 -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/generate_basis_approximations.py +2 -0
- qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +2 -2
- {qiskit-1.4.1.dist-info → qiskit-1.4.2.dist-info}/METADATA +1 -1
- {qiskit-1.4.1.dist-info → qiskit-1.4.2.dist-info}/RECORD +748 -747
- {qiskit-1.4.1.dist-info → qiskit-1.4.2.dist-info}/WHEEL +1 -1
- {qiskit-1.4.1.dist-info → qiskit-1.4.2.dist-info}/LICENSE.txt +0 -0
- {qiskit-1.4.1.dist-info → qiskit-1.4.2.dist-info}/entry_points.txt +0 -0
- {qiskit-1.4.1.dist-info → qiskit-1.4.2.dist-info}/top_level.txt +0 -0
qiskit/VERSION.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.
|
1
|
+
1.4.2
|
qiskit/_accelerate.abi3.so
CHANGED
Binary file
|
@@ -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/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:
|
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}
|
@@ -1327,7 +1332,9 @@ def write_circuit(
|
|
1327
1332
|
_write_layout(file_obj, circuit)
|
1328
1333
|
|
1329
1334
|
|
1330
|
-
def read_circuit(
|
1335
|
+
def read_circuit(
|
1336
|
+
file_obj, version, metadata_deserializer=None, use_symengine=False, trust_input=False
|
1337
|
+
):
|
1331
1338
|
"""Read a single QuantumCircuit object from the file like object.
|
1332
1339
|
|
1333
1340
|
Args:
|
@@ -1345,6 +1352,7 @@ def read_circuit(file_obj, version, metadata_deserializer=None, use_symengine=Fa
|
|
1345
1352
|
supported in all platforms. Please check that your target platform is supported by
|
1346
1353
|
the symengine library before setting this option, as it will be required by qpy to
|
1347
1354
|
deserialize the payload.
|
1355
|
+
trust_input (bool): If true, deserialize vulnerable schedule block payloads.
|
1348
1356
|
Returns:
|
1349
1357
|
QuantumCircuit: The circuit object from the file.
|
1350
1358
|
|
@@ -1454,7 +1462,11 @@ def read_circuit(file_obj, version, metadata_deserializer=None, use_symengine=Fa
|
|
1454
1462
|
# Read calibrations
|
1455
1463
|
if version >= 5:
|
1456
1464
|
circ._calibrations_prop = _read_calibrations(
|
1457
|
-
file_obj,
|
1465
|
+
file_obj,
|
1466
|
+
version,
|
1467
|
+
vectors,
|
1468
|
+
metadata_deserializer,
|
1469
|
+
trust_input=trust_input,
|
1458
1470
|
)
|
1459
1471
|
|
1460
1472
|
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):
|
qiskit/qpy/interface.py
CHANGED
@@ -238,6 +238,7 @@ def dump(
|
|
238
238
|
def load(
|
239
239
|
file_obj: BinaryIO,
|
240
240
|
metadata_deserializer: Optional[Type[JSONDecoder]] = None,
|
241
|
+
trust_payload: bool = False,
|
241
242
|
) -> List[QPY_SUPPORTED_TYPES]:
|
242
243
|
"""Load a QPY binary file
|
243
244
|
|
@@ -277,6 +278,18 @@ def load(
|
|
277
278
|
If this is not specified the circuit metadata will
|
278
279
|
be parsed as JSON with the stdlib ``json.load()`` function using
|
279
280
|
the default ``JSONDecoder`` class.
|
281
|
+
trust_payload: if set to ``False`` (the default),
|
282
|
+
:class:`.ScheduleBlock` objects in the payload that were
|
283
|
+
serialized using ``sympy`` are not allowed and will error. This
|
284
|
+
is because the ``sympy`` parsing uses :func:`eval`, which
|
285
|
+
can allow for arbitrary code execution.
|
286
|
+
The flag should only be set
|
287
|
+
to ``True`` if you trust the QPY payload you are loading.
|
288
|
+
|
289
|
+
.. warning::
|
290
|
+
|
291
|
+
If ``trust_payload`` is set to ``True`` this can enable arbitrary
|
292
|
+
code execution because internally ``sympy`` relies on :func:`eval`.
|
280
293
|
|
281
294
|
Returns:
|
282
295
|
The list of Qiskit programs contained in the QPY data.
|
@@ -344,6 +357,11 @@ def load(
|
|
344
357
|
else:
|
345
358
|
type_key = common.read_type_key(file_obj)
|
346
359
|
|
360
|
+
if data.qpy_version < 10:
|
361
|
+
use_symengine = False
|
362
|
+
else:
|
363
|
+
use_symengine = data.symbolic_encoding == type_keys.SymExprEncoding.SYMENGINE
|
364
|
+
|
347
365
|
if type_key == type_keys.Program.CIRCUIT:
|
348
366
|
loader = binary_io.read_circuit
|
349
367
|
elif type_key == type_keys.Program.SCHEDULE_BLOCK:
|
@@ -359,11 +377,6 @@ def load(
|
|
359
377
|
else:
|
360
378
|
raise TypeError(f"Invalid payload format data kind '{type_key}'.")
|
361
379
|
|
362
|
-
if data.qpy_version < 10:
|
363
|
-
use_symengine = False
|
364
|
-
else:
|
365
|
-
use_symengine = data.symbolic_encoding == type_keys.SymExprEncoding.SYMENGINE
|
366
|
-
|
367
380
|
programs = []
|
368
381
|
for _ in range(data.num_programs):
|
369
382
|
programs.append(
|
@@ -372,6 +385,7 @@ def load(
|
|
372
385
|
data.qpy_version,
|
373
386
|
metadata_deserializer=metadata_deserializer,
|
374
387
|
use_symengine=use_symengine,
|
388
|
+
trust_input=trust_payload,
|
375
389
|
)
|
376
390
|
)
|
377
391
|
return programs
|
@@ -495,7 +495,6 @@ class TemplateSubstitution:
|
|
495
495
|
parameter constraints, returns None.
|
496
496
|
"""
|
497
497
|
import sympy as sym
|
498
|
-
from sympy.parsing.sympy_parser import parse_expr
|
499
498
|
|
500
499
|
if _optionals.HAS_SYMENGINE:
|
501
500
|
import symengine
|
@@ -571,7 +570,8 @@ class TemplateSubstitution:
|
|
571
570
|
if isinstance(circuit_param, ParameterExpression):
|
572
571
|
circ_param_sym = circuit_param.sympify()
|
573
572
|
else:
|
574
|
-
|
573
|
+
# if it's not a ParameterExpression we're a float
|
574
|
+
circ_param_sym = sym.Float(circuit_param)
|
575
575
|
equations.append(sym.Eq(template_param.sympify(), circ_param_sym))
|
576
576
|
|
577
577
|
for param in template_param.parameters:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: qiskit
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.2
|
4
4
|
Summary: An open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
|
5
5
|
Author-email: Qiskit Development Team <qiskit@us.ibm.com>
|
6
6
|
License: Apache 2.0
|