Skip to content

LightCone pass gives index errors unexpectedly #15021

@caleb-johnson

Description

@caleb-johnson

Environment

  • Qiskit version: 2.1.2
  • Python version: 3.12.9
  • Operating system: macOS

What is happening?

I am trying to run the LightCone pass but am getting index errors once the initial lightcone grows to a certain size. Full traceback below:

PanicException                            Traceback (most recent call last)
Cell In[24], line 15
      6 indices = [0, 1, 2, 3, 4, 9, 10, 12, 13]
      7 pm = PassManager(
      8     [
      9         LightCone(
   (...)
     13     ]
     14 )
---> 15 reduced_circ = pm.run(qc)

File [~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:482](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py#line=481), in _replace_error.<locals>.wrapper(*meth_args, **meth_kwargs)
    479 @wraps(meth)
    480 def wrapper(*meth_args, **meth_kwargs):
    481     try:
--> 482         return meth(*meth_args, **meth_kwargs)
    483     except TranspilerError:
    484         # If it's already a `TranspilerError` subclass, don't erase the extra information.
    485         raise

File [~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:241](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py#line=240), in PassManager.run(self, circuits, output_name, callback, num_processes, property_set)
    238 if callback is not None:
    239     callback = _legacy_style_callback(callback)
--> 241 return super().run(
    242     in_programs=circuits,
    243     callback=callback,
    244     output_name=output_name,
    245     num_processes=num_processes,
    246     property_set=property_set,
    247 )

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

File [~/venvs/blox/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py:218](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py#line=217), 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 [~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/basepasses.py:167](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/basepasses.py#line=166), in TransformationPass.execute(self, passmanager_ir, state, callback)
    161 def execute(
    162     self,
    163     passmanager_ir: PassManagerIR,
    164     state: PassManagerState,
    165     callback: Callable = None,
    166 ) -> tuple[PassManagerIR, PassManagerState]:
--> 167     new_dag, state = super().execute(
    168         passmanager_ir=passmanager_ir,
    169         state=state,
    170         callback=callback,
    171     )
    173     if state.workflow_status.previous_run == RunState.SUCCESS:
    174         if not isinstance(new_dag, DAGCircuit):

File [~/venvs/blox/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py:98](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py#line=97), 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 [~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/passes/optimization/light_cone.py:121](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/transpiler/passes/optimization/light_cone.py#line=120), in LightCone.run(self, dag)
    114 if max_num_qubits > 10:
    115     warnings.warn(
    116         "LightCone pass is checking commutation of"
    117         f"operators of size {max_num_qubits}."
    118         "This operation can be slow.",
    119         category=RuntimeWarning,
    120     )
--> 121 commute_bool = scc.commute(
    122     op[0], op[1], [], node.op, node.qargs, [], max_num_qubits=max_num_qubits
    123 )
    124 if not commute_bool:
    125     # If the current node does not commute, update the light-cone
    126     lightcone_qubits.update(node.qargs)

File [~/venvs/blox/lib/python3.12/site-packages/qiskit/circuit/commutation_checker.py:101](http://localhost:8888/lab/workspaces/~/venvs/blox/lib/python3.12/site-packages/qiskit/circuit/commutation_checker.py#line=100), in CommutationChecker.commute(self, op1, qargs1, cargs1, op2, qargs2, cargs2, max_num_qubits, approximation_degree)
     69 def commute(
     70     self,
     71     op1: Operation,
   (...)
     78     approximation_degree: float = 1.0,
     79 ) -> bool:
     80     """
     81     Checks if two Operations commute. The return value of `True` means that the operations
     82     truly commute, and the return value of `False` means that either the operations do not
   (...)
     99         bool: whether two operations commute.
    100     """
--> 101     return self.cc.commute(
    102         op1, qargs1, cargs1, op2, qargs2, cargs2, max_num_qubits, approximation_degree
    103     )

PanicException: index out of bounds: the len is 26 but the index is 26

How can we reproduce the issue?

import numpy as np
from qiskit import QuantumCircuit
from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.passes.optimization.light_cone import LightCone

qc = QuantumCircuit(18)
qc.rx(np.pi / 3, range(0, qc.num_qubits))
bit_terms = "ZZZZZZZZ"
indices = [0, 1, 2, 3, 4, 9, 10, 12]
pm = PassManager(
    [
        LightCone(
            bit_terms=bit_terms,
            indices=indices,
        )
    ]
)
reduced_circ = pm.run(qc)

The above runs succesfully. Adding an extra qubit (13), as in the snippet below, causes a failure.

import numpy as np
from qiskit import QuantumCircuit
from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.passes.optimization.light_cone import LightCone

qc = QuantumCircuit(18)
qc.rx(np.pi / 3, range(0, qc.num_qubits))
bit_terms = "ZZZZZZZZZ"
indices = [0, 1, 2, 3, 4, 9, 10, 12, 13]
pm = PassManager(
    [
        LightCone(
            bit_terms=bit_terms,
            indices=indices,
        )
    ]
)
reduced_circ = pm.run(qc)

What should happen?

The pass should run without failure in both of the above cases

Any suggestions?

This happens on a larger qubit number for a non-trivial circuit, but it breaks given these same qubit id's in both the minimal snippet above and the non-trivial case in my workflow

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions