qiskit 2.0.3__cp39-abi3-macosx_11_0_arm64.whl → 2.1.0__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.
Files changed (180) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +19 -1
  3. qiskit/_accelerate.abi3.so +0 -0
  4. qiskit/circuit/__init__.py +104 -20
  5. qiskit/circuit/_add_control.py +57 -31
  6. qiskit/circuit/_classical_resource_map.py +4 -0
  7. qiskit/circuit/annotation.py +504 -0
  8. qiskit/circuit/classical/expr/__init__.py +1 -1
  9. qiskit/circuit/classical/expr/expr.py +104 -446
  10. qiskit/circuit/classical/expr/visitors.py +6 -0
  11. qiskit/circuit/classical/types/types.py +7 -130
  12. qiskit/circuit/controlflow/box.py +32 -7
  13. qiskit/circuit/delay.py +11 -9
  14. qiskit/circuit/library/arithmetic/adders/adder.py +4 -4
  15. qiskit/circuit/library/arithmetic/multipliers/multiplier.py +2 -2
  16. qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +8 -4
  17. qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py +23 -15
  18. qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py +22 -14
  19. qiskit/circuit/library/arithmetic/quadratic_form.py +6 -0
  20. qiskit/circuit/library/arithmetic/weighted_adder.py +43 -24
  21. qiskit/circuit/library/basis_change/qft.py +2 -2
  22. qiskit/circuit/library/blueprintcircuit.py +6 -0
  23. qiskit/circuit/library/boolean_logic/inner_product.py +2 -2
  24. qiskit/circuit/library/boolean_logic/quantum_and.py +2 -2
  25. qiskit/circuit/library/boolean_logic/quantum_or.py +3 -3
  26. qiskit/circuit/library/boolean_logic/quantum_xor.py +2 -2
  27. qiskit/circuit/library/data_preparation/_z_feature_map.py +2 -2
  28. qiskit/circuit/library/data_preparation/_zz_feature_map.py +2 -2
  29. qiskit/circuit/library/data_preparation/pauli_feature_map.py +2 -2
  30. qiskit/circuit/library/fourier_checking.py +2 -2
  31. qiskit/circuit/library/generalized_gates/diagonal.py +5 -1
  32. qiskit/circuit/library/generalized_gates/gms.py +5 -1
  33. qiskit/circuit/library/generalized_gates/linear_function.py +2 -2
  34. qiskit/circuit/library/generalized_gates/permutation.py +5 -1
  35. qiskit/circuit/library/generalized_gates/uc.py +1 -1
  36. qiskit/circuit/library/generalized_gates/unitary.py +21 -2
  37. qiskit/circuit/library/graph_state.py +2 -2
  38. qiskit/circuit/library/grover_operator.py +2 -2
  39. qiskit/circuit/library/hidden_linear_function.py +2 -2
  40. qiskit/circuit/library/iqp.py +2 -2
  41. qiskit/circuit/library/n_local/efficient_su2.py +2 -2
  42. qiskit/circuit/library/n_local/evolved_operator_ansatz.py +1 -1
  43. qiskit/circuit/library/n_local/excitation_preserving.py +7 -9
  44. qiskit/circuit/library/n_local/n_local.py +4 -3
  45. qiskit/circuit/library/n_local/pauli_two_design.py +2 -2
  46. qiskit/circuit/library/n_local/real_amplitudes.py +2 -2
  47. qiskit/circuit/library/n_local/two_local.py +2 -2
  48. qiskit/circuit/library/overlap.py +2 -2
  49. qiskit/circuit/library/pauli_evolution.py +3 -2
  50. qiskit/circuit/library/phase_estimation.py +2 -2
  51. qiskit/circuit/library/standard_gates/dcx.py +11 -12
  52. qiskit/circuit/library/standard_gates/ecr.py +21 -24
  53. qiskit/circuit/library/standard_gates/equivalence_library.py +232 -96
  54. qiskit/circuit/library/standard_gates/global_phase.py +5 -6
  55. qiskit/circuit/library/standard_gates/h.py +22 -45
  56. qiskit/circuit/library/standard_gates/i.py +1 -1
  57. qiskit/circuit/library/standard_gates/iswap.py +13 -31
  58. qiskit/circuit/library/standard_gates/p.py +19 -26
  59. qiskit/circuit/library/standard_gates/r.py +11 -17
  60. qiskit/circuit/library/standard_gates/rx.py +21 -45
  61. qiskit/circuit/library/standard_gates/rxx.py +7 -22
  62. qiskit/circuit/library/standard_gates/ry.py +21 -39
  63. qiskit/circuit/library/standard_gates/ryy.py +13 -28
  64. qiskit/circuit/library/standard_gates/rz.py +18 -35
  65. qiskit/circuit/library/standard_gates/rzx.py +7 -22
  66. qiskit/circuit/library/standard_gates/rzz.py +7 -19
  67. qiskit/circuit/library/standard_gates/s.py +44 -39
  68. qiskit/circuit/library/standard_gates/swap.py +25 -38
  69. qiskit/circuit/library/standard_gates/sx.py +34 -41
  70. qiskit/circuit/library/standard_gates/t.py +18 -27
  71. qiskit/circuit/library/standard_gates/u.py +8 -24
  72. qiskit/circuit/library/standard_gates/u1.py +28 -52
  73. qiskit/circuit/library/standard_gates/u2.py +9 -9
  74. qiskit/circuit/library/standard_gates/u3.py +24 -40
  75. qiskit/circuit/library/standard_gates/x.py +190 -336
  76. qiskit/circuit/library/standard_gates/xx_minus_yy.py +12 -50
  77. qiskit/circuit/library/standard_gates/xx_plus_yy.py +13 -52
  78. qiskit/circuit/library/standard_gates/y.py +19 -23
  79. qiskit/circuit/library/standard_gates/z.py +31 -38
  80. qiskit/circuit/parameter.py +14 -5
  81. qiskit/circuit/parameterexpression.py +109 -75
  82. qiskit/circuit/quantumcircuit.py +172 -99
  83. qiskit/circuit/quantumcircuitdata.py +1 -0
  84. qiskit/circuit/random/__init__.py +37 -2
  85. qiskit/circuit/random/utils.py +445 -56
  86. qiskit/circuit/tools/pi_check.py +5 -13
  87. qiskit/compiler/transpiler.py +1 -1
  88. qiskit/converters/circuit_to_instruction.py +2 -2
  89. qiskit/dagcircuit/dagnode.py +8 -3
  90. qiskit/primitives/__init__.py +2 -2
  91. qiskit/primitives/base/base_estimator.py +2 -2
  92. qiskit/primitives/containers/data_bin.py +0 -3
  93. qiskit/primitives/containers/observables_array.py +192 -108
  94. qiskit/primitives/primitive_job.py +29 -10
  95. qiskit/providers/fake_provider/generic_backend_v2.py +2 -0
  96. qiskit/qasm3/__init__.py +106 -12
  97. qiskit/qasm3/ast.py +15 -1
  98. qiskit/qasm3/exporter.py +59 -36
  99. qiskit/qasm3/printer.py +12 -0
  100. qiskit/qpy/__init__.py +182 -6
  101. qiskit/qpy/binary_io/circuits.py +256 -24
  102. qiskit/qpy/binary_io/parse_sympy_repr.py +5 -0
  103. qiskit/qpy/binary_io/schedules.py +12 -32
  104. qiskit/qpy/binary_io/value.py +36 -18
  105. qiskit/qpy/common.py +11 -3
  106. qiskit/qpy/formats.py +17 -1
  107. qiskit/qpy/interface.py +52 -12
  108. qiskit/qpy/type_keys.py +7 -1
  109. qiskit/quantum_info/__init__.py +10 -0
  110. qiskit/quantum_info/operators/__init__.py +1 -0
  111. qiskit/quantum_info/operators/symplectic/__init__.py +1 -0
  112. qiskit/quantum_info/operators/symplectic/clifford_circuits.py +26 -0
  113. qiskit/quantum_info/operators/symplectic/pauli.py +2 -2
  114. qiskit/result/sampled_expval.py +3 -1
  115. qiskit/synthesis/__init__.py +10 -0
  116. qiskit/synthesis/arithmetic/__init__.py +1 -1
  117. qiskit/synthesis/arithmetic/adders/__init__.py +1 -0
  118. qiskit/synthesis/arithmetic/adders/draper_qft_adder.py +6 -2
  119. qiskit/synthesis/arithmetic/adders/rv_ripple_carry_adder.py +156 -0
  120. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +14 -126
  121. qiskit/synthesis/discrete_basis/solovay_kitaev.py +161 -121
  122. qiskit/synthesis/evolution/lie_trotter.py +10 -7
  123. qiskit/synthesis/evolution/product_formula.py +10 -7
  124. qiskit/synthesis/evolution/qdrift.py +10 -7
  125. qiskit/synthesis/evolution/suzuki_trotter.py +10 -7
  126. qiskit/synthesis/multi_controlled/__init__.py +4 -0
  127. qiskit/synthesis/multi_controlled/mcx_synthesis.py +402 -178
  128. qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py +14 -15
  129. qiskit/synthesis/qft/qft_decompose_lnn.py +7 -25
  130. qiskit/synthesis/unitary/qsd.py +80 -9
  131. qiskit/transpiler/__init__.py +10 -3
  132. qiskit/transpiler/instruction_durations.py +2 -20
  133. qiskit/transpiler/passes/__init__.py +5 -2
  134. qiskit/transpiler/passes/layout/dense_layout.py +26 -6
  135. qiskit/transpiler/passes/layout/disjoint_utils.py +1 -166
  136. qiskit/transpiler/passes/layout/sabre_layout.py +22 -3
  137. qiskit/transpiler/passes/layout/sabre_pre_layout.py +1 -1
  138. qiskit/transpiler/passes/layout/vf2_layout.py +49 -13
  139. qiskit/transpiler/passes/layout/vf2_utils.py +10 -0
  140. qiskit/transpiler/passes/optimization/__init__.py +1 -1
  141. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +2 -1
  142. qiskit/transpiler/passes/optimization/optimize_clifford_t.py +68 -0
  143. qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +3 -9
  144. qiskit/transpiler/passes/routing/sabre_swap.py +4 -2
  145. qiskit/transpiler/passes/routing/star_prerouting.py +106 -81
  146. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  147. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
  148. qiskit/transpiler/passes/scheduling/padding/__init__.py +1 -0
  149. qiskit/transpiler/passes/scheduling/padding/context_aware_dynamical_decoupling.py +876 -0
  150. qiskit/transpiler/passes/synthesis/__init__.py +1 -0
  151. qiskit/transpiler/passes/synthesis/clifford_unitary_synth_plugin.py +123 -0
  152. qiskit/transpiler/passes/synthesis/hls_plugins.py +494 -93
  153. qiskit/transpiler/passes/synthesis/plugin.py +4 -0
  154. qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +27 -22
  155. qiskit/transpiler/passmanager_config.py +3 -0
  156. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +149 -28
  157. qiskit/transpiler/preset_passmanagers/common.py +101 -0
  158. qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +6 -0
  159. qiskit/transpiler/preset_passmanagers/level3.py +2 -2
  160. qiskit/transpiler/target.py +15 -2
  161. qiskit/utils/optionals.py +6 -5
  162. qiskit/visualization/circuit/_utils.py +5 -3
  163. qiskit/visualization/circuit/latex.py +9 -2
  164. qiskit/visualization/circuit/matplotlib.py +26 -4
  165. qiskit/visualization/circuit/qcstyle.py +9 -157
  166. qiskit/visualization/dag/__init__.py +13 -0
  167. qiskit/visualization/dag/dagstyle.py +103 -0
  168. qiskit/visualization/dag/styles/__init__.py +13 -0
  169. qiskit/visualization/dag/styles/color.json +10 -0
  170. qiskit/visualization/dag/styles/plain.json +5 -0
  171. qiskit/visualization/dag_visualization.py +169 -98
  172. qiskit/visualization/style.py +223 -0
  173. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/METADATA +7 -6
  174. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/RECORD +178 -169
  175. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/entry_points.txt +6 -0
  176. qiskit/synthesis/discrete_basis/commutator_decompose.py +0 -265
  177. qiskit/synthesis/discrete_basis/gate_sequence.py +0 -421
  178. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/WHEEL +0 -0
  179. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/licenses/LICENSE.txt +0 -0
  180. {qiskit-2.0.3.dist-info → qiskit-2.1.0.dist-info}/top_level.txt +0 -0
@@ -18,16 +18,16 @@ from __future__ import annotations
18
18
  from dataclasses import dataclass
19
19
  from enum import IntEnum
20
20
  from typing import Callable, Union
21
-
22
21
  import numbers
23
22
  import operator
24
23
 
25
24
  import numpy
26
- import symengine
27
25
 
28
- from qiskit.circuit.exceptions import CircuitError
29
- from qiskit.exceptions import QiskitError
30
26
  from qiskit.utils.optionals import HAS_SYMPY
27
+ from qiskit.circuit.exceptions import CircuitError
28
+ import qiskit._accelerate.circuit
29
+
30
+ SymbolExpr = qiskit._accelerate.circuit.ParameterExpression
31
31
 
32
32
  # This type is redefined at the bottom to insert the full reference to "ParameterExpression", so it
33
33
  # can safely be used by runtime type-checkers like Sphinx. Mypy does not need this because it
@@ -126,15 +126,23 @@ class ParameterExpression:
126
126
  symbol_map (Dict[Parameter, [ParameterExpression, float, or int]]):
127
127
  Mapping of :class:`Parameter` instances to the :class:`sympy.Symbol`
128
128
  serving as their placeholder in expr.
129
- expr (sympy.Expr): Expression of :class:`sympy.Symbol` s.
129
+ expr (SymbolExpr or str): Expression with Rust's SymbolExprPy or string
130
130
  """
131
131
  # NOTE: `Parameter.__init__` does not call up to this method, since this method is dependent
132
132
  # on `Parameter` instances already being initialized enough to be hashable. If changing
133
133
  # this method, check that `Parameter.__init__` and `__setstate__` are still valid.
134
- self._parameter_symbols = symbol_map
135
- self._parameter_keys = frozenset(p._hash_key() for p in self._parameter_symbols)
136
- self._symbol_expr = expr
134
+ if isinstance(expr, SymbolExpr):
135
+ self._parameter_symbols = symbol_map
136
+ self._symbol_expr = expr
137
+ else:
138
+ self._symbol_expr = SymbolExpr.Expression(expr)
139
+ self._parameter_symbols = {}
140
+ # reconstruct symbols from input parameters
141
+ for param in symbol_map.keys():
142
+ self._parameter_symbols[param] = SymbolExpr.Symbol(param.name)
137
143
  self._name_map: dict | None = None
144
+ self._parameter_keys = frozenset(p._hash_key() for p in self._parameter_symbols)
145
+
138
146
  self._standalone_param = False
139
147
  if _qpy_replay is not None:
140
148
  self._qpy_replay = _qpy_replay
@@ -162,7 +170,7 @@ class ParameterExpression:
162
170
  new_replay = self._qpy_replay.copy()
163
171
  new_replay.append(new_op)
164
172
  conjugated = ParameterExpression(
165
- self._parameter_symbols, symengine.conjugate(self._symbol_expr), _qpy_replay=new_replay
173
+ self._parameter_symbols, self._symbol_expr.conjugate(), _qpy_replay=new_replay
166
174
  )
167
175
  return conjugated
168
176
 
@@ -213,9 +221,9 @@ class ParameterExpression:
213
221
  symbol_values = {}
214
222
  for parameter, value in parameter_values.items():
215
223
  if (param_expr := self._parameter_symbols.get(parameter)) is not None:
216
- symbol_values[param_expr] = value
224
+ symbol_values[str(param_expr)] = value
217
225
 
218
- bound_symbol_expr = self._symbol_expr.subs(symbol_values)
226
+ bound_symbol_expr = self._symbol_expr.bind(symbol_values)
219
227
 
220
228
  # Don't use sympy.free_symbols to count remaining parameters here.
221
229
  # sympy will in some cases reduce the expression and remove even
@@ -279,14 +287,14 @@ class ParameterExpression:
279
287
  new_parameter_symbols = {
280
288
  p: s for p, s in self._parameter_symbols.items() if p not in parameter_map
281
289
  }
282
- symbol_type = symengine.Symbol
290
+ symbol_type = SymbolExpr.Symbol
283
291
 
284
292
  # If new_param is an expr, we'll need to construct a matching sympy expr
285
293
  # but with our sympy symbols instead of theirs.
286
294
  symbol_map = {}
287
295
  for old_param, new_param in parameter_map.items():
288
296
  if (old_symbol := self._parameter_symbols.get(old_param)) is not None:
289
- symbol_map[old_symbol] = new_param._symbol_expr
297
+ symbol_map[str(old_symbol)] = new_param._symbol_expr
290
298
  for p in new_param.parameters:
291
299
  new_parameter_symbols[p] = symbol_type(p.name)
292
300
 
@@ -399,7 +407,13 @@ class ParameterExpression:
399
407
  return out_expr
400
408
 
401
409
  def gradient(self, param) -> Union["ParameterExpression", complex]:
402
- """Get the derivative of a parameter expression w.r.t. a specified parameter expression.
410
+ """Get the derivative of a real parameter expression w.r.t. a specified parameter.
411
+
412
+ .. note::
413
+
414
+ This method assumes that the parameter expression represents a **real expression only**.
415
+ Calling this method on a parameter expression that contains complex values, or binding
416
+ complex values to parameters in the expression is undefined behavior.
403
417
 
404
418
  Args:
405
419
  param (Parameter): Parameter w.r.t. which we want to take the derivative
@@ -422,24 +436,20 @@ class ParameterExpression:
422
436
 
423
437
  # Compute the gradient of the parameter expression w.r.t. param
424
438
  key = self._parameter_symbols[param]
425
- expr_grad = symengine.Derivative(self._symbol_expr, key)
439
+ expr_grad = self._symbol_expr.derivative(key)
426
440
 
427
441
  # generate the new dictionary of symbols
428
442
  # this needs to be done since in the derivative some symbols might disappear (e.g.
429
443
  # when deriving linear expression)
430
444
  parameter_symbols = {}
431
445
  for parameter, symbol in self._parameter_symbols.items():
432
- if symbol in expr_grad.free_symbols:
446
+ if symbol.name in expr_grad.symbols():
433
447
  parameter_symbols[parameter] = symbol
434
448
  # If the gradient corresponds to a parameter expression then return the new expression.
435
449
  if len(parameter_symbols) > 0:
436
450
  return ParameterExpression(parameter_symbols, expr=expr_grad, _qpy_replay=qpy_replay)
437
451
  # If no free symbols left, return a complex or float gradient
438
- expr_grad_cplx = complex(expr_grad)
439
- if expr_grad_cplx.imag != 0:
440
- return expr_grad_cplx
441
- else:
442
- return float(expr_grad)
452
+ return expr_grad.value()
443
453
 
444
454
  def __add__(self, other):
445
455
  return self._apply_operation(operator.add, other, op_code=_OPCode.ADD)
@@ -492,54 +502,49 @@ class ParameterExpression:
492
502
 
493
503
  def sin(self):
494
504
  """Sine of a ParameterExpression"""
495
- return self._call(symengine.sin, op_code=_OPCode.SIN)
505
+ return self._call(SymbolExpr.sin, op_code=_OPCode.SIN)
496
506
 
497
507
  def cos(self):
498
508
  """Cosine of a ParameterExpression"""
499
- return self._call(symengine.cos, op_code=_OPCode.COS)
509
+ return self._call(SymbolExpr.cos, op_code=_OPCode.COS)
500
510
 
501
511
  def tan(self):
502
512
  """Tangent of a ParameterExpression"""
503
- return self._call(symengine.tan, op_code=_OPCode.TAN)
513
+ return self._call(SymbolExpr.tan, op_code=_OPCode.TAN)
504
514
 
505
515
  def arcsin(self):
506
516
  """Arcsin of a ParameterExpression"""
507
- return self._call(symengine.asin, op_code=_OPCode.ASIN)
517
+ return self._call(SymbolExpr.asin, op_code=_OPCode.ASIN)
508
518
 
509
519
  def arccos(self):
510
520
  """Arccos of a ParameterExpression"""
511
- return self._call(symengine.acos, op_code=_OPCode.ACOS)
521
+ return self._call(SymbolExpr.acos, op_code=_OPCode.ACOS)
512
522
 
513
523
  def arctan(self):
514
524
  """Arctan of a ParameterExpression"""
515
- return self._call(symengine.atan, op_code=_OPCode.ATAN)
525
+ return self._call(SymbolExpr.atan, op_code=_OPCode.ATAN)
516
526
 
517
527
  def exp(self):
518
528
  """Exponential of a ParameterExpression"""
519
- return self._call(symengine.exp, op_code=_OPCode.EXP)
529
+ return self._call(SymbolExpr.exp, op_code=_OPCode.EXP)
520
530
 
521
531
  def log(self):
522
532
  """Logarithm of a ParameterExpression"""
523
- return self._call(symengine.log, op_code=_OPCode.LOG)
533
+ return self._call(SymbolExpr.log, op_code=_OPCode.LOG)
524
534
 
525
535
  def sign(self):
526
536
  """Sign of a ParameterExpression"""
527
- return self._call(symengine.sign, op_code=_OPCode.SIGN)
537
+ return self._call(SymbolExpr.sign, op_code=_OPCode.SIGN)
528
538
 
529
539
  def __repr__(self):
530
540
  return f"{self.__class__.__name__}({str(self)})"
531
541
 
532
542
  def __str__(self):
533
- from sympy import sympify, sstr
534
-
535
- if not isinstance(self._symbol_expr, symengine.Basic):
536
- raise QiskitError("Invalid ParameterExpression")
537
-
538
- return sstr(sympify(self._symbol_expr), full_prec=False)
543
+ return str(self._symbol_expr)
539
544
 
540
545
  def __complex__(self):
541
546
  try:
542
- return complex(self._symbol_expr)
547
+ return complex(self._symbol_expr.value())
543
548
  # TypeError is for sympy, RuntimeError for symengine
544
549
  except (TypeError, RuntimeError) as exc:
545
550
  if self.parameters:
@@ -551,7 +556,7 @@ class ParameterExpression:
551
556
 
552
557
  def __float__(self):
553
558
  try:
554
- return float(self._symbol_expr)
559
+ return float(self._symbol_expr.value())
555
560
  # TypeError is for sympy, RuntimeError for symengine
556
561
  except (TypeError, RuntimeError) as exc:
557
562
  if self.parameters:
@@ -571,7 +576,7 @@ class ParameterExpression:
571
576
 
572
577
  def __int__(self):
573
578
  try:
574
- return int(self._symbol_expr)
579
+ return int(self._symbol_expr.value())
575
580
  # TypeError is for backwards compatibility, RuntimeError is raised by symengine
576
581
  except RuntimeError as exc:
577
582
  if self.parameters:
@@ -595,7 +600,7 @@ class ParameterExpression:
595
600
 
596
601
  def __abs__(self):
597
602
  """Absolute of a ParameterExpression"""
598
- return self._call(symengine.Abs, _OPCode.ABS)
603
+ return self._call(SymbolExpr.abs, _OPCode.ABS)
599
604
 
600
605
  def abs(self):
601
606
  """Absolute of a ParameterExpression"""
@@ -613,28 +618,19 @@ class ParameterExpression:
613
618
  if isinstance(other, ParameterExpression):
614
619
  if self.parameters != other.parameters:
615
620
  return False
616
- from sympy import sympify
617
-
618
- if not isinstance(self._symbol_expr, symengine.Basic):
619
- raise QiskitError("Invalid ParameterExpression")
620
621
 
621
- return sympify(self._symbol_expr).equals(sympify(other._symbol_expr))
622
+ return self._symbol_expr == other._symbol_expr
622
623
  elif isinstance(other, numbers.Number):
623
- return len(self.parameters) == 0 and complex(self._symbol_expr) == other
624
+ return self._symbol_expr == other
624
625
  return False
625
626
 
626
627
  def is_real(self):
627
628
  """Return whether the expression is real"""
628
- if not self._symbol_expr.is_real and self._symbol_expr.is_real is not None:
629
- # Symengine returns false for is_real on the expression if
630
- # there is a imaginary component (even if that component is 0),
631
- # but the parameter will evaluate as real. Check that if the
632
- # expression's is_real attribute returns false that we have a
633
- # non-zero imaginary
634
- if self._symbol_expr.imag == 0.0:
635
- return True
636
- return False
637
- return self._symbol_expr.is_real
629
+ try:
630
+ val = self._symbol_expr.value()
631
+ return not isinstance(val, complex)
632
+ except RuntimeError:
633
+ return None
638
634
 
639
635
  def numeric(self) -> int | float | complex:
640
636
  """Return a Python number representing this object, using the most restrictive of
@@ -667,27 +663,11 @@ class ParameterExpression:
667
663
  raise TypeError(
668
664
  f"Expression with unbound parameters '{self.parameters}' is not numeric"
669
665
  )
670
- if self._symbol_expr.is_integer:
671
- # Integer evaluation is reliable, as far as we know.
672
- return int(self._symbol_expr)
673
- # We've come across several ways in which symengine's general-purpose evaluators
674
- # introduce spurious imaginary components can get involved in the output. The most
675
- # reliable strategy "try it and see" while forcing real floating-point evaluation.
676
- try:
677
- real_expr = self._symbol_expr.evalf(real=True)
678
- except RuntimeError:
679
- # Symengine returns `complex` if any imaginary floating-point enters at all, even if
680
- # the result is zero. The best we can do is detect that and decay to a float.
681
- out = complex(self._symbol_expr)
682
- return out.real if out.imag == 0.0 else out
683
- return float(real_expr)
666
+ return self._symbol_expr.value()
684
667
 
685
668
  @HAS_SYMPY.require_in_call
686
669
  def sympify(self):
687
- """Return symbolic expression as a raw Sympy or Symengine object.
688
-
689
- Symengine is used preferentially; if both are available, the result will always be a
690
- ``symengine`` object. Symengine is a separate library but has integration with Sympy.
670
+ """Return symbolic expression as a raw Sympy object.
691
671
 
692
672
  .. note::
693
673
 
@@ -695,7 +675,61 @@ class ParameterExpression:
695
675
  Symegine expressions in its parameters, because they do not contain the tracking
696
676
  information used in circuit-parameter binding and assignment.
697
677
  """
698
- return self._symbol_expr
678
+ import sympy
679
+
680
+ output = None
681
+ for inst in self._qpy_replay:
682
+ if isinstance(inst, _SUBS):
683
+ sympy_binds = {}
684
+ for old, new in inst.binds.items():
685
+ if isinstance(new, ParameterExpression):
686
+ new = new.sympify()
687
+ sympy_binds[old.sympify()] = new
688
+ output = output.subs(sympy_binds, simultaneous=True)
689
+ continue
690
+
691
+ if isinstance(inst.lhs, ParameterExpression):
692
+ lhs = inst.lhs.sympify()
693
+ elif inst.lhs is None:
694
+ lhs = output
695
+ else:
696
+ lhs = inst.lhs
697
+
698
+ method_str = _OP_CODE_MAP[inst.op]
699
+ if inst.op in {0, 1, 2, 3, 4, 13, 15, 18, 19, 20}:
700
+ if inst.rhs is None:
701
+ rhs = output
702
+ elif isinstance(inst.rhs, ParameterExpression):
703
+ rhs = inst.rhs.sympify()
704
+ else:
705
+ rhs = inst.rhs
706
+
707
+ if (
708
+ not isinstance(lhs, sympy.Basic)
709
+ and isinstance(rhs, sympy.Basic)
710
+ and inst.op in [0, 2]
711
+ ):
712
+ if inst.op == 0:
713
+ method_str = "__radd__"
714
+ elif inst.op == 2:
715
+ method_str = "__rmul__"
716
+ output = getattr(rhs, method_str)(lhs)
717
+ elif inst.op == _OPCode.GRAD:
718
+ output = getattr(lhs, "diff")(rhs)
719
+ else:
720
+ output = getattr(lhs, method_str)(rhs)
721
+ else:
722
+ if inst.op == _OPCode.ACOS:
723
+ output = getattr(sympy, "acos")(lhs)
724
+ elif inst.op == _OPCode.ASIN:
725
+ output = getattr(sympy, "asin")(lhs)
726
+ elif inst.op == _OPCode.ATAN:
727
+ output = getattr(sympy, "atan")(lhs)
728
+ elif inst.op == _OPCode.ABS:
729
+ output = getattr(sympy, "Abs")(lhs)
730
+ else:
731
+ output = getattr(sympy, method_str)(lhs)
732
+ return output
699
733
 
700
734
 
701
735
  # Redefine the type so external imports get an evaluated reference; Sphinx needs this to understand