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
5.04k stars 2.32k forks source link

Passmanager fails to transpile a basic StatePreparation gate #12984

Closed mmlouamri closed 3 weeks ago

mmlouamri commented 3 weeks ago

Environment

What is happening?

I am trying to execute the following circuit:

from qiskit import QuantumCircuit, __version__ as qversion
from qiskit.circuit.library import StatePreparation
from qiskit_aer import AerSimulator, __version__ as qaversion
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

print(qversion, '/', qaversion) # 1.2.0 / 0.14.2

pm = generate_preset_pass_manager(backend=AerSimulator(), optimization_level=3)

qc =  QuantumCircuit(1)
qc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)

pm.run(qc)

And I am getting the following error:

---------------------------------------------------------------------------
QiskitError                               Traceback (most recent call last)
Cell In[21], line 13
     10 qc =  QuantumCircuit(1)
     11 qc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)
---> 13 pm.run(qc)

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:441, in StagedPassManager.run(self, circuits, output_name, callback, num_processes)
    433 def run(
    434     self,
    435     circuits: _CircuitsT,
   (...)
    438     num_processes: int = None,
    439 ) -> _CircuitsT:
    440     self._update_passmanager()
--> 441     return super().run(circuits, output_name, callback, num_processes=num_processes)

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:464, in _replace_error.<locals>.wrapper(*meth_args, **meth_kwargs)
    461 @wraps(meth)
    462 def wrapper(*meth_args, **meth_kwargs):
    463     try:
--> 464         return meth(*meth_args, **meth_kwargs)
    465     except PassManagerError as ex:
    466         raise TranspilerError(ex.message) from ex

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:226, in PassManager.run(self, circuits, output_name, callback, num_processes)
    223 if callback is not None:
    224     callback = _legacy_style_callback(callback)
--> 226 return super().run(
    227     in_programs=circuits,
    228     callback=callback,
    229     output_name=output_name,
    230     num_processes=num_processes,
    231 )

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py:232, in BasePassManager.run(self, in_programs, callback, num_processes, **kwargs)
    228 # If we're not going to run in parallel, we want to avoid spending time `dill` serializing
    229 # ourselves, since that can be quite expensive.
    230 if len(in_programs) == 1 or not should_run_in_parallel(num_processes):
    231     out = [
--> 232         _run_workflow(program=program, pass_manager=self, callback=callback, **kwargs)
    233         for program in in_programs
    234     ]
    235     if len(in_programs) == 1 and not is_list:
    236         return out[0]

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py:292, in _run_workflow(program, pass_manager, **kwargs)
    286 initial_status = WorkflowStatus()
    288 passmanager_ir = pass_manager._passmanager_frontend(
    289     input_program=program,
    290     **kwargs,
    291 )
--> 292 passmanager_ir, final_state = flow_controller.execute(
    293     passmanager_ir=passmanager_ir,
    294     state=PassManagerState(
    295         workflow_status=initial_status,
    296         property_set=PropertySet(),
    297     ),
    298     callback=kwargs.get("callback", None),
    299 )
    300 # The `property_set` has historically been returned as a mutable attribute on `PassManager`
    301 # This makes us non-reentrant (though `PassManager` would be dependent on its internal tasks to
    302 # be re-entrant if that was required), but is consistent with previous interfaces.  We're still
    303 # safe to be called in a serial loop, again assuming internal tasks are re-runnable.  The
    304 # conversion to the backend language is also allowed to use the property set, so it must be set
    305 # before calling it.
    306 pass_manager.property_set = final_state.property_set

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py:218, in BaseController.execute(self, passmanager_ir, state, callback)
    216     return passmanager_ir, state
    217 while True:
--> 218     passmanager_ir, state = next_task.execute(
    219         passmanager_ir=passmanager_ir,
    220         state=state,
    221         callback=callback,
    222     )
    223     try:
    224         # Sending the object through the generator implies the custom controllers
    225         # can always rely on the latest data to choose the next task to run.
    226         next_task = task_generator.send(state)

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/basepasses.py:195, in TransformationPass.execute(self, passmanager_ir, state, callback)
    189 def execute(
    190     self,
    191     passmanager_ir: PassManagerIR,
    192     state: PassManagerState,
    193     callback: Callable = None,
    194 ) -> tuple[PassManagerIR, PassManagerState]:
--> 195     new_dag, state = super().execute(
    196         passmanager_ir=passmanager_ir,
    197         state=state,
    198         callback=callback,
    199     )
    201     if state.workflow_status.previous_run == RunState.SUCCESS:
    202         if isinstance(new_dag, DAGCircuit):
    203             # Copy calibration data from the original program

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py:98, in GenericPass.execute(self, passmanager_ir, state, callback)
     96 try:
     97     if self not in state.workflow_status.completed_passes:
---> 98         ret = self.run(passmanager_ir)
     99         run_state = RunState.SUCCESS
    100     else:

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:443, in HighLevelSynthesis.run(self, dag)
    440 if self._definitely_skip_node(node, qubits):
    441     continue
--> 443 decomposition, modified = self._recursively_handle_op(node.op, qubits)
    445 if not modified:
    446     continue

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:551, in HighLevelSynthesis._recursively_handle_op(self, op, qubits)
    547         return op, False
    549 try:
    550     # extract definition
--> 551     definition = op.definition
    552 except TypeError as err:
    553     raise TranspilerError(
    554         f"HighLevelSynthesis was unable to extract definition for {op.name}: {err}"
    555     ) from err

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/instruction.py:312, in Instruction.definition(self)
    310 """Return definition in terms of other basic gates."""
    311 if self._definition is None:
--> 312     self._define()
    313 return self._definition

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/library/data_preparation/state_preparation.py:122, in StatePreparation._define(self)
    120     self.definition = self._define_from_int()
    121 else:
--> 122     self.definition = self._define_synthesis_isom()

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/library/data_preparation/state_preparation.py:176, in StatePreparation._define_synthesis_isom(self)
    173 q = QuantumRegister(self.num_qubits, "q")
    174 initialize_circuit = QuantumCircuit(q, name="init_def")
--> 176 isom = Isometry(self._params_arg, 0, 0)
    177 initialize_circuit.append(isom, q[:])
    179 # invert the circuit to create the desired vector from zero (assuming
    180 # the qubits are in the zero state)

File ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/library/generalized_gates/isometry.py:112, in Isometry.__init__(self, isometry, num_ancillas_zero, num_ancillas_dirty, epsilon)
    108     raise QiskitError(
    109         "The input matrix has more columns than rows and hence it can't be an isometry."
    110     )
    111 if not is_isometry(isometry, self._epsilon):
--> 112     raise QiskitError(
    113         "The input matrix has non orthonormal columns and hence it is not an isometry."
    114     )
    116 num_qubits = int(n) + num_ancillas_zero + num_ancillas_dirty
    118 super().__init__("isometry", num_qubits, 0, [isometry])

QiskitError: 'The input matrix has non orthonormal columns and hence it is not an isometry.'

As far as I remember, similar code used to work on previous versions of Qiskit.

How can we reproduce the issue?

Here is a minimal code snippet to reproduce the error:

from qiskit import QuantumCircuit, __version__ as qversion
from qiskit.circuit.library import StatePreparation
from qiskit_aer import AerSimulator, __version__ as qaversion
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

print(qversion, '/', qaversion) # 1.2.0 / 0.14.2

pm = generate_preset_pass_manager(backend=AerSimulator(), optimization_level=3)

qc =  QuantumCircuit(1)
qc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)

pm.run(qc)

What should happen?

The transpilation of the StatePreparation gate :)

Any suggestions?

No response

Cryoris commented 3 weeks ago

That's indeed a bug -- the StatePreparation code passes the unnormalized vector to Isometry, even though normalize is set to True.

Edit: https://github.com/Qiskit/qiskit/pull/12988 should fix it