Open
Description
Environment
- Qiskit version: 2.0.0
- Python version: 3.13
- Operating system: macOS
What is happening?
If a circuit containing delays that use stretch
es 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