Skip to content

TranspilerError in presets when circuit with stretch is compiled to Target with alignment constraints #14264

Open
@jakelishman

Description

@jakelishman

Environment

  • Qiskit version: 2.0.0
  • Python version: 3.13
  • Operating system: macOS

What is happening?

If a circuit containing delays that use stretches is compiled with the preset pipeline for a backend that has alignment constraints, we run InstructionDurationCheck, which then attempts to modulo all durations with the alignment, failing if there's an expression-based delay.

How can we reproduce the issue?

IBM Eagles have alignment concerns that trigger the pass to run, so using ibm_sherbrooke as an example:

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.transpiler import generate_preset_pass_manager

backend = QiskitRuntimeService().backend("ibm_sherbrooke")

circuit = QuantumCircuit(2)
a = circuit.add_stretch("a")
circuit.delay(a, 0)
circuit.x(1)
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

raises

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[1], line 14
     11 circuit.measure_all()
     13 pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
---> 14 isa_circuit = pm.run(circuit)

File ~/code/qiskit/terra/qiskit/transpiler/passmanager.py:451, in StagedPassManager.run(self, circuits, output_name, callback, num_processes, property_set)
    441 def run(
    442     self,
    443     circuits: _CircuitsT,
   (...)
    448     property_set: dict[str, object] | None = None,
    449 ) -> _CircuitsT:
    450     self._update_passmanager()
--> 451     return super().run(circuits, output_name, callback, num_processes=num_processes)

File ~/code/qiskit/terra/qiskit/transpiler/passmanager.py:474, in _replace_error.<locals>.wrapper(*meth_args, **meth_kwargs)
    471 @wraps(meth)
    472 def wrapper(*meth_args, **meth_kwargs):
    473     try:
--> 474         return meth(*meth_args, **meth_kwargs)
    475     except TranspilerError:
    476         # If it's already a `TranspilerError` subclass, don't erase the extra information.
    477         raise

File ~/code/qiskit/terra/qiskit/transpiler/passmanager.py:233, in PassManager.run(self, circuits, output_name, callback, num_processes, property_set)
    230 if callback is not None:
    231     callback = _legacy_style_callback(callback)
--> 233 return super().run(
    234     in_programs=circuits,
    235     callback=callback,
    236     output_name=output_name,
    237     num_processes=num_processes,
    238     property_set=property_set,
    239 )

File ~/code/qiskit/terra/qiskit/passmanager/passmanager.py:238, in BasePassManager.run(self, in_programs, callback, num_processes, property_set, **kwargs)
    234 # If we're not going to run in parallel, we want to avoid spending time `dill` serializing
    235 # ourselves, since that can be quite expensive.
    236 if len(in_programs) == 1 or not should_run_in_parallel(num_processes):
    237     out = [
--> 238         _run_workflow(
    239             program=program,
    240             pass_manager=self,
    241             callback=callback,
    242             initial_property_set=property_set,
    243             **kwargs,
    244         )
    245         for program in in_programs
    246     ]
    247     if len(in_programs) == 1 and not is_list:
    248         return out[0]

File ~/code/qiskit/terra/qiskit/passmanager/passmanager.py:312, in _run_workflow(program, pass_manager, initial_property_set, **kwargs)
    305 passmanager_ir = pass_manager._passmanager_frontend(
    306     input_program=program,
    307     **kwargs,
    308 )
    309 property_set = (
    310     PropertySet() if initial_property_set is None else PropertySet(initial_property_set)
    311 )
--> 312 passmanager_ir, final_state = flow_controller.execute(
    313     passmanager_ir=passmanager_ir,
    314     state=PassManagerState(workflow_status=initial_status, property_set=property_set),
    315     callback=kwargs.get("callback", None),
    316 )
    317 # The `property_set` has historically been returned as a mutable attribute on `PassManager`
    318 # This makes us non-reentrant (though `PassManager` would be dependent on its internal tasks to
    319 # be re-entrant if that was required), but is consistent with previous interfaces.  We're still
    320 # safe to be called in a serial loop, again assuming internal tasks are re-runnable.  The
    321 # conversion to the backend language is also allowed to use the property set, so it must be set
    322 # before calling it.
    323 pass_manager.property_set = final_state.property_set

File ~/code/qiskit/terra/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 ~/code/qiskit/terra/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 ~/code/qiskit/terra/qiskit/transpiler/passes/scheduling/alignments/check_durations.py:68, in InstructionDurationCheck.run(self, dag)
     66 for delay_node in dag.op_nodes(Delay):
     67     dur = delay_node.op.duration
---> 68     if not (dur % self.acquire_align == 0 and dur % self.pulse_align == 0):
     69         self.property_set["reschedule_required"] = True
     70         return

TypeError: unsupported operand type(s) for %: 'qiskit._accelerate.circuit.classical.expr.Stretch' and 'int'

What should happen?

If there's stretches, we likely have to assume that there'll be later postprocessing of the compilation result, so shouldn't attempt to reschedule it ourselves.

Any suggestions?

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingmod: transpilerIssues and PRs related to Transpiler

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions