Skip to content

Defining a custom gate called qft in QASM apparently collides with qiskit QFT in v1.2 #13120

@lochsh

Description

@lochsh

Environment

  • Qiskit version: 1.2.0
  • Python version: 3.10
  • Operating system: Ubuntu 22.04

What is happening?

If I transpile this qasm:

OPENQASM 2.0;
include "qelib1.inc";

gate qft q0,q1 { h q1; barrier q0,q1; crz(pi/2) q0,q1; h q0; barrier q0,q1; }
qreg q30[2];
qft q30[0],q30[1];

then I get a TranspilerError:

Python 3.10.12 (main, Jul 29 2024, 16:56:48) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.27.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import qiskit

In [2]: import qiskit.qasm2

In [3]: qc = qiskit.qasm2.load("example.qasm")

In [4]: qiskit.transpile(qc)
backtrace
---------------------------------------------------------------------------
TranspilerError                           Traceback (most recent call last)
File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passmanager.py:464, in _replace_error.<locals>.wrapper(*meth_args, **meth_kwargs)
    463 try:
--> 464     return meth(*meth_args, **meth_kwargs)
    465 except PassManagerError as ex:

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passmanager.py:226, in PassManager.run(self, circuits, output_name, callback, num_processes)
    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 ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/passmanager/passmanager.py:231, in BasePassManager.run(self, in_programs, callback, num_processes, **kwargs)
    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:

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/passmanager/passmanager.py:232, in <listcomp>(.0)
    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:

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/passmanager/passmanager.py:292, in _run_workflow(program, pass_manager, **kwargs)
    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.

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/passmanager/base_tasks.py:218, in BaseController.execute(self, passmanager_ir, state, callback)
    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.

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/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:

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/passmanager/base_tasks.py:98, in GenericPass.execute(self, passmanager_ir, state, callback)
     97 if self not in state.workflow_status.completed_passes:
---> 98     ret = self.run(passmanager_ir)
     99     run_state = RunState.SUCCESS

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:443, in HighLevelSynthesis.run(self, dag)
    441     continue
--> 443 decomposition, modified = self._recursively_handle_op(node.op, qubits)
    445 if not modified:

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:527, in HighLevelSynthesis._recursively_handle_op(self, op, qubits)
    523 # WARNING: if adding new things in here, ensure that `_definitely_skip_node` is also
    524 # up-to-date.
    525
    526 # Try to apply plugin mechanism
--> 527 decomposition = self._synthesize_op_using_plugins(op, qubits)
    528 if decomposition is not None:

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:625, in HighLevelSynthesis._synthesize_op_using_plugins(self, op, qubits)
    623     plugin_method = plugin_specifier
--> 625 decomposition = plugin_method.run(
    626     op,
    627     coupling_map=self._coupling_map,
    628     target=self._target,
    629     qubits=qubits,
    630     **plugin_args,
    631 )
    633 # The synthesis methods that are not suited for the given higher-level-object
    634 # will return None.

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:999, in QFTSynthesisFull.run(self, high_level_object, coupling_map, target, qubits, **options)
    998 if not isinstance(high_level_object, QFTGate):
--> 999     raise TranspilerError(
   1000         "The synthesis plugin 'qft.full` only applies to objects of type QFTGate."
   1001     )
   1003 reverse_qubits = options.get("reverse_qubits", False)

TranspilerError: "The synthesis plugin 'qft.full` only applies to objects of type QFTGate."

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

TranspilerError                           Traceback (most recent call last)
Cell In[4], line 1
----> 1 qiskit.transpile(qc)

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/compiler/transpiler.py:391, in transpile(circuits, backend, basis_gates, inst_map, coupling_map, backend_properties, initial_layout, layout_method, routing_method, translation_method, scheduling_method, instruction_durations, dt, approximation_degree, timing_constraints, seed_transpiler, optimization_level, callback, output_name, unitary_synthesis_method, unitary_synthesis_plugin_config, target, hls_config, init_method, optimization_method, ignore_backend_supplied_default_methods, num_processes)
    363 # Edge cases require using the old model (loose constraints) instead of building a target,
    364 # but we don't populate the passmanager config with loose constraints unless it's one of
    365 # the known edge cases to control the execution path.
    366 pm = generate_preset_pass_manager(
    367     optimization_level,
    368     target=target,
   (...)
    388     dt=dt,
    389 )
--> 391 out_circuits = pm.run(circuits, callback=callback, num_processes=num_processes)
    393 for name, circ in zip(output_name, out_circuits):
    394     circ.name = name

File ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/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 ~/.cache/pypoetry/virtualenvs/my-project-HV3wIkQ7-py3.10/lib/python3.10/site-packages/qiskit/transpiler/passmanager.py:466, in _replace_error.<locals>.wrapper(*meth_args, **meth_kwargs)
    464     return meth(*meth_args, **meth_kwargs)
    465 except PassManagerError as ex:
--> 466     raise TranspilerError(ex.message) from ex
TranspilerError: "The synthesis plugin 'qft.full` only applies to objects of type QFTGate."

If I rename the custom gate defined in the QASM file to qft_, then transpilation succeeds. It seems like the name of the custom gate is colliding with qiskit's built-in QFT gate? The error does not occur in version 1.1 of qiskit.

How can we reproduce the issue?

QASM file contents, assume file is called example.qasm

OPENQASM 2.0;
include "qelib1.inc";

gate qft q0,q1 { h q1; barrier q0,q1; crz(pi/2) q0,q1; h q0; barrier q0,q1; }
qreg q30[2];
qft q30[0],q30[1];

Python to reproduce with qiskit 1.2:

import qiskit
import qiskit.qasm2

qc = qiskit.qasm2.load("example.qasm")
qiskit.transpile(qc)

What should happen?

Transpilation should succeed and not be dependent on the name of the custom gate, if the gate name is a valid QASM identifier.

Any suggestions?

No response

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions