Skip to content

Basis translation can fail if run in parallel #13504

@Cryoris

Description

Environment

  • Qiskit version: Qiskit 1.3.0 and main
  • Python version: 3.11.9
  • Operating system: macOS

What is happening?

This is the problem underlying Qiskit/qiskit-addon-cutting#714.

Running basis translation in parallel (see snippet below) causes the following failure

[...]
  File "/Users/jul/Qiskit/qiskit/qiskit/transpiler/passes/basis/basis_translator.py", line 129, in run
    return base_run(
           ^^^^^^^^^
ValueError: An invalid parameter was provided. 

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

Traceback (most recent call last):
  File "/Users/jul/IBM/snippets/repro.py", line 25, in <module>
    pm.run([qc_a, qc_b]) # This fails with the error
    ^^^^^^^^^^^^^^^^^^^^
[...]

  File "/opt/homebrew/Cellar/python@3.11/3.11.9/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
ValueError: An invalid parameter was provided.

This seems to be due to the recent port of the basis translator to Rust (see #12246 for an overview). Dissecting the code, the error seems to be triggered by compose_transforms, so it might be worth checking if this already existed after #13137 and before the rest of the basis translator was moved to Rust.

How can we reproduce the issue?

From @mtreinish:

I just came up with a minimal reproduce:

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.circuit.library import RXGate

backend = GenericBackendV2(5)
pm = generate_preset_pass_manager(1, backend)

param = Parameter('a')
gate = RXGate(param)

qc_a = QuantumCircuit(1)
qc_a.append(gate, [0])
qc_b = QuantumCircuit(1)
qc_b.append(gate, [0])

pm.run(qc_a)
pm.run(qc_b)
pm.run([qc_a, qc_b]) # This fails with the error

Note that this only happens with multiprocessing turned on. On Mac this is turned off per default but can be enabled with export QISKIT_PARALLEL=TRUE.

What should happen?

The above should run, as it did in 1.2.4.

Any suggestions?

One possible solution is to delete the cached py_op upon parameter assignment, i.e. replace

borrowed
.bind(py)
.getattr(params_attr)?
.set_item(parameter, new_param)?;
borrowed.bind(py).setattr("_definition", py.None())?
by

previous.py_op = OnceLock::new();  // or OnceCell on Qiskit 1.3.0

But this comes at a ~5% performance overhead on my machine, benchmarked on some utility scale circuits. Maybe there's another way to avoid this overhead?

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions