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 CHANGED
@@ -1 +1 @@
1
- 1.4.1
1
+ 1.4.2
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
@@ -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:
@@ -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(file_obj, version, metadata_deserializer)
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(file_obj, version, metadata_deserializer=None, use_symengine=False):
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, version, vectors, metadata_deserializer
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(file_obj.read(header.constraints_size))
130
- valid_amp_conditions = _loads_symbolic_expr(file_obj.read(header.valid_amp_conditions_size))
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(file_obj.read(header.envelope_size), use_symengine)
206
- constraints = _loads_symbolic_expr(file_obj.read(header.constraints_size), use_symengine)
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(data_bytes, _read_symbolic_pulse, version=version)
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, _read_symbolic_pulse_v6, version=version, use_symengine=use_symengine
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(file_obj, version, metadata_deserializer, use_symengine)
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, deserializer=_loads_operand, version=version, use_symengine=use_symengine
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(file_obj, version, metadata_deserializer=None, use_symengine=False):
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(file_obj, version, metadata_deserializer, use_symengine)
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():
@@ -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
- expr_ = symengine.sympify(parse_expr(file_obj.read(data.expr_size).decode(common.ENCODE)))
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
- from sympy.parsing.sympy_parser import parse_expr
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
@@ -37,6 +37,8 @@ _1q_inverses = {
37
37
  "tdg": "t",
38
38
  "s": "sdg",
39
39
  "sdg": "s",
40
+ "sx": "sxdg",
41
+ "sxdg": "sx",
40
42
  }
41
43
 
42
44
  _1q_gates = {
@@ -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
- circ_param_sym = parse_expr(str(circuit_param))
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.1
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