Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
4.85k stars 2.29k forks source link

``qiskit.synthesis.MatrixExponential`` breaks if evolution time is a bound ``Parameter`` #7507

Closed Cryoris closed 4 months ago

Cryoris commented 2 years ago

Environment

What is happening?

Using the PauliEvolutionGate with a parameterized time and the MatrixExponential as synthesis method breaks. The reason is that the evolution time, after being bound, is not cast to a float but remains a ParameterExpression with 0 free parameters. That type is incompatible with SciPy which is used to compute the matrix exponential.

How can we reproduce the issue?

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.synthesis import SuzukiTrotter, MatrixExponential
from qiskit.opflow import I, X, Y, Z
from qiskit.quantum_info import Operator

# evolution time and operator we evolve
time = Parameter("t")
op = (I ^ X ^ Y) + (I ^ Y ^ X) - (Z ^ Z ^ I)

# evolution gate
synth = MatrixExponential()
evo = PauliEvolutionGate(op, time=time, synthesis=synth)

# plug into circuit
circuit = QuantumCircuit(op.num_qubits)
circuit.append(evo, range(op.num_qubits))
print(circuit.draw())

# bind time to some value and obtain matrix
value = 0.23
bound = circuit.bind_parameters([value])  # or {time: value}

print(bound.decompose())

produces

Traceback (most recent call last):
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/circuit/parameterexpression.py", line 444, in __float__
    return float(self._symbol_expr)
  File "symengine_wrapper.pyx", line 1143, in symengine.lib.symengine_wrapper.Basic.__float__
  File "symengine_wrapper.pyx", line 968, in symengine.lib.symengine_wrapper.Basic.n
  File "symengine_wrapper.pyx", line 4278, in symengine.lib.symengine_wrapper.evalf
RuntimeError: Not Implemented

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "evoop.py", line 24, in <module>
    print(bound.decompose())
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/circuit/quantumcircuit.py", line 1528, in decompose
    decomposed_dag = pass_.run(circuit_to_dag(self))
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/transpiler/passes/basis/decompose.py", line 90, in run
    if node.op.definition is None:
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/circuit/instruction.py", line 232, in definition
    self._define()
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/circuit/library/pauli_evolution.py", line 105, in _define
    self.definition = self.synthesis.synthesize(self)
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/synthesis/evolution/matrix_synthesis.py", line 43, in synthesize
    exp = expm(-1j * time * matrix)
  File "/Users/jul/opt/miniconda3/envs/py37/lib/python3.7/site-packages/scipy/linalg/matfuncs.py", line 255, in expm
    return scipy.sparse.linalg.expm(A)
  File "/Users/jul/opt/miniconda3/envs/py37/lib/python3.7/site-packages/scipy/sparse/linalg/matfuncs.py", line 591, in expm
    return _expm(A, use_exact_onenorm='auto')
  File "/Users/jul/opt/miniconda3/envs/py37/lib/python3.7/site-packages/scipy/sparse/linalg/matfuncs.py", line 626, in _expm
    A = A.astype(float)
  File "/Users/jul/Qiskit/qiskit-terra/qiskit/circuit/parameterexpression.py", line 450, in __float__
    ) from exc
TypeError: ParameterExpression with unbound parameters (set()) cannot be cast to a float.

What should happen?

Properly evolve.

Any suggestions?

See #7508

jakelishman commented 4 months ago

With slightly modified code to account for changes in Qiskit since this issue:

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.synthesis import SuzukiTrotter, MatrixExponential
from qiskit.quantum_info import Operator, SparsePauliOp

# evolution time and operator we evolve
time = Parameter("t")
op = SparsePauliOp.from_list([("IXY", 1), ("IYX", 1), ("ZZI", -1)])

# evolution gate
synth = MatrixExponential()
evo = PauliEvolutionGate(op, time=time, synthesis=synth)

# plug into circuit
circuit = QuantumCircuit(op.num_qubits)
circuit.append(evo, range(op.num_qubits))
print(circuit.draw())

# bind time to some value and obtain matrix
value = 0.23
bound = circuit.assign_parameters([value])  # or {time: value}

print(bound.decompose())

this no longer seems to be reproducible with Qiskit 1.0, so I'll close the issue.