Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TranspilerError with Unconventional Physical Qubit Indices in Layout #13536

Open
vili-1 opened this issue Dec 6, 2024 · 9 comments
Open

TranspilerError with Unconventional Physical Qubit Indices in Layout #13536

vili-1 opened this issue Dec 6, 2024 · 9 comments
Labels
bug Something isn't working mod: transpiler Issues and PRs related to Transpiler

Comments

@vili-1
Copy link

vili-1 commented Dec 6, 2024

Environment

  • Qiskit version: 1.2.4
  • Python version: 3.12.6
  • Operating system: macOS Sequoia 15.1

What is happening?

Providing unconventional physical qubit indices (e.g., -1, 1000000000) in the initial_layout for stress testing results in a TranspilerError without a clear explanation. The error states that the layout is “not full,” which is misleading as all virtual qubits are mapped. This behaviour limits testing scenarios where users intentionally supply extreme indices.

Full Traceback:

Traceback (most recent call last):
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 464, in wrapper
    return meth(*meth_args, **meth_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 226, in run
    return super().run(
           ^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py", line 232, in run
    _run_workflow(program=program, pass_manager=self, callback=callback, **kwargs)
  File "~/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py", line 292, in _run_workflow
    passmanager_ir, final_state = flow_controller.execute(
                                  ^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 218, in execute
    passmanager_ir, state = next_task.execute(
                            ^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 218, in execute
    passmanager_ir, state = next_task.execute(
                            ^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 218, in execute
    passmanager_ir, state = next_task.execute(
                            ^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/basepasses.py", line 195, in execute
    new_dag, state = super().execute(
                     ^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 98, in execute
    ret = self.run(passmanager_ir)
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passes/layout/apply_layout.py", line 56, in run
    raise TranspilerError("The 'layout' must be full (with ancilla).")
qiskit.transpiler.exceptions.TranspilerError: "The 'layout' must be full (with ancilla)."

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

Traceback (most recent call last):
  File "~/quantum_circuit_builder.py", line 2002, in <module>
    main()
  File "~/quantum_circuit_builder.py", line 1953, in main
    try_angles_qiskit()
  File "~/quantum_circuit_builder.py", line 1876, in try_angles_qiskit
    transpiled_qc = transpile(qc_test, simulator, initial_layout=layout)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/compiler/transpiler.py", line 391, in transpile
    out_circuits = pm.run(circuits, callback=callback, num_processes=num_processes)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 441, in run
    return super().run(circuits, output_name, callback, num_processes=num_processes)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 466, in wrapper
    raise TranspilerError(ex.message) from ex
qiskit.transpiler.exceptions.TranspilerError: "The 'layout' must be full (with ancilla)."
~ % python3 quantum_circuit_builder.py
Installing qiskit-aer...
Running in test mode..
Traceback (most recent call last):
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 464, in wrapper
    return meth(*meth_args, **meth_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 226, in run
    return super().run(
           ^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py", line 232, in run
    _run_workflow(program=program, pass_manager=self, callback=callback, **kwargs)
  File "~/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py", line 292, in _run_workflow
    passmanager_ir, final_state = flow_controller.execute(
                                  ^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 218, in execute
    passmanager_ir, state = next_task.execute(
                            ^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 218, in execute
    passmanager_ir, state = next_task.execute(
                            ^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 218, in execute
    passmanager_ir, state = next_task.execute(
                            ^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/basepasses.py", line 195, in execute
    new_dag, state = super().execute(
                     ^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py", line 98, in execute
    ret = self.run(passmanager_ir)
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passes/layout/apply_layout.py", line 56, in run
    raise TranspilerError("The 'layout' must be full (with ancilla).")
qiskit.transpiler.exceptions.TranspilerError: "The 'layout' must be full (with ancilla)."

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

Traceback (most recent call last):
  File "~/quantum_circuit_builder.py", line 2002, in <module>
    main()
  File "~/quantum_circuit_builder.py", line 1953, in main
    try_angles_qiskit()
  File "~/quantum_circuit_builder.py", line 1876, in try_angles_qiskit
    transpiled_qc = transpile(qc_test, simulator, initial_layout=layout)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/compiler/transpiler.py", line 391, in transpile
    out_circuits = pm.run(circuits, callback=callback, num_processes=num_processes)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 441, in run
    return super().run(circuits, output_name, callback, num_processes=num_processes)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py", line 466, in wrapper
    raise TranspilerError(ex.message) from ex
qiskit.transpiler.exceptions.TranspilerError: "The 'layout' must be full (with ancilla)."

How can we reproduce the issue?

from qiskit import QuantumCircuit, transpile, Aer
from qiskit.transpiler import Layout

qc_test = QuantumCircuit(2, 2)  # 2 qubits, 2 classical bits
qc_test.h(0)
qc_test.cx(0, 1)
qc_test.measure_all()

simulator = Aer.get_backend('qasm_simulator')
layout = Layout({qc_test.qubits[0]: -1, qc_test.qubits[1]: 1000000000})  # Extreme indices
transpiled_qc = transpile(qc_test, simulator, initial_layout=layout)

What should happen?

  1. Clear error messages should identify the problem with the layout. For example: "Physical qubit index -1 is invalid; indices must be non-negative and within the backend's qubit range."
  2. An option to bypass strict validation (for testing purposes) would allow intentional stress testing of the simulator and transpiler.

Any suggestions?

  • Documentation: Update the initial_layout documentation to clarify that the layout must map all logical qubits and any ancilla for compatibility.
  • Relax Constraints for Virtual Backends: Consider allowing partial layouts for virtual backends where ancilla qubits are not strictly necessary. For example, automatically handling unmapped qubits or issuing a warning instead of raising an error.
@vili-1 vili-1 added the bug Something isn't working label Dec 6, 2024
@jakelishman
Copy link
Member

Why would we add an option to pass bad types into the transpiler? This kind of error is exactly protection against things going wrong more mysteriously further down - good library design doesn't have type checking within every single function, it normalises user input as it's encountered, then can assume the invariants that were enforced once, rather than wasting the user's time doing it over and over.

These "unconventional" physical indices are simply incorrect physical indices per Qiskit's data model, which specifies that physical qubit indices in a layout are the integers 0 to n-1. The message can potentially be improved slightly, but the error is correct.

The standard way to pass initial_layout is as a list of integers, which is the virtual mapping - no ancillas are needed in this case. We can potentially expand that to work with Layout too, but I can't remember if there's a reason that's harder.

Fwiw, it's not necessarily good practice for a Python project to optimise for somebody "stress testing" the input types to every function. The allowed types are documented, and typically you should use static code analysis like Mypy to check typing correctness, not expect that every function spends runtime enforcing it - this penalises the happy paths for mistakes that are mostly only made deliberately (as you are), or are obvious as soon as the first exception is raised.

@vili-1
Copy link
Author

vili-1 commented Dec 11, 2024

I really appreciate the insight into Qiskit’s design philosophy. It makes sense that the focus is on performance and optimising for the “happy path,” which I’m sure is what the majority of users need.

That said, since we’re a group of academic researchers stress testing the framework (definitely not the “typical user” 😅), we’re deliberately pushing edge cases to test how robustly unexpected inputs are handled.

I also appreciate you mentioning that initial_layout could potentially be expanded to work better with Layout and that the error message could be improved slightly. Those sound like great steps to make the experience even smoother.

By the way, could you point us to the documentation you mentioned about the allowed types for initial_layout? It’d be super helpful for guiding our future tests.

Thanks again for all your time and effort - it’s clear a lot of thought goes into making Qiskit work so well, and these discussions help us better understand the design choices.

@ShellyGarion ShellyGarion added the mod: transpiler Issues and PRs related to Transpiler label Dec 15, 2024
@karineek
Copy link

Exploring hyperparameters is a common practice in software engineering methods, not only in the context of reliability and security but also for methods like search-based optimization and genetic algorithms. Consequently, knowing sensible values for these parameters is crucial for automating optimization processes.

In this case, it would be helpful if documentation clarified the following:

  • "-1" is invalid: we cannot use negative indices because they don’t correspond to any physical qubit.
  • Large indices (e.g., 100000000): These are fine for the simulator because, unlike the hardware, it doesn't impose strict limits on the number of qubits. However, it will be good to know the maximal number tested, or what the spec is expecting as a maximal value the quantum platform can handle.

Additionally, support for a wider range of "good values," even those less commonly used, will be super useful for such methods.

@jakelishman
Copy link
Member

jakelishman commented Dec 16, 2024

When you're deliberately pushing incorrect types, it's worth considering whether these are things that are actually likely to occur by mistake, or whether they're only going to happen if somebody does it deliberately very wrong, and what it would cost to insert checks in those situations.

Python as a language is almost always used via an interpreter, and the typing is highly dynamic. This means it's always going to be possible to pass something bad to a function, whereas in a static and strict type system, a compiler might reject it from even building. We don't have that luxury in Python, so any time we want to do a type check, we have to trade off the fact that it needs to happen at run-time, not compile-time, so it penalises all users, even ones who are following the documentation. We typically make this trade-off by ensuring that high-level user-facing functions normalise and type check inputs (directly with isinstance or indirectly by regular Python duck-typing assumptions), and then let the lower-level functions all assume that the data is in valid formats, and undergo undefined behaviour if it isn't. For top performance, we may also choose to expose lower-level functions directly to users, with caveats that the user needs to be more vigilant, because there are far fewer guardrails.

(edit: this comment is mostly in response to the bits about typing, not the bits about using the correct types but passing invalid values in those - it's a problem when we don't gracefully handle values of a type without warning.)

@jakelishman
Copy link
Member

jakelishman commented Dec 16, 2024

@karineek: what hyperparameters are you referring to? Physical qubit indices aren't "parameters" to be tuned, they're labels for the actual qubits that exist on a physical device. Similarly, what "wider range of good values" are you meaning here? The allowed values are "however many physical qubits the device has", a Layout just has to contain a map point for each physical qubit (despite the error message, the transpiler has expanded it with ancillas to fill out the layout, but see below).

I agree that the documentation of the Layout class is lacking, and could be improved. The class is very old, and nowadays is barely used for anything by Qiskit itself.

Documentation of the initial_layout option is with the rest of the tranpsile documentation.


I suspect that the main trouble here is actually that nothing is (directly) checking that initial_layout only contains physical qubits that exist in the backend. The check that's erroring can only pass if it does, but regardless, the actual trouble is that the simulator instance you're passing will only report itself as supporting 30-ish qubits, so it doesn't have a qubit 10,000,000 (or whatever).

@karineek
Copy link

From a software engineering perspective, all interface parameters, including Layout indices (also circuit size etc), are subject to auto-exploration.

There are three points here: documentation, simulator support and what is sensible for the hardware. I struggle with the documentation and the simulator, as it is unclear for example if 50, or 100 is a good number.

At some point, maybe a bit above the currently known hardware limit, the value is no longer sensible, but this is exactly what we are missing here. I think 30 is a conservative value and not the actual possible max value. On a GPU, I know it is around 40-50, but this can change at any time.

@jakelishman
Copy link
Member

Yes, you are entirely free to explore changing the values you pass in and auto-optimising those, just like lots of successful projects have done using Qiskit.

The maximum number of qubits reported by the simulator is set by the simulator. The example above instantiated a CPU-bound, RAM-based statevector simulator, and will have determined the maximum number of qubits based on those constraints. If you'd instantiated a different simulator, the constraints would have been different, because it would have been a completely different device. You can see what number of qubits the backed supports by looking at the BackendV2.num_qubits property (simulator.num_qubits in the example above). On my machine, with my amount of RAM, that simulator reports having 31 qubits. It might be a little different on your machine, if you've got more/less RAM than I do. The simulator you're using is not a part of Qiskit SDK (this repo), and the Qiskit compiler doesn't treat simulators specially - to us, a simulator is just like any other backend we're targetting, and it's up to the simulator to provide a hardware-like description of itself and what it has available.

If you used, for example, qiskit_ibm_runtime.fake_provider.FakeTorino as your backend instead, it would report having 127 qubits, like the real hardware device has. If you choose to make your own custom Target, you can put any number of qubits in there, if you define the device topology and available instructions, etc. I know that people have successfully used Qiskit to compile to (imaginary) 10,000+ qubit devices - you just need to have a backend descriptor (a Target) to tell the compiler about it.

Beyond that, your inputs to transpile need to be logically consistent with each other. If your backend only has 31 qubits, a layout that attempts to map the 10,000,000th is not a valid input - the allowed sets of values to the tranpsile function are interdependent, just like how you can't ask a QuantumCircuit to put an X gate on virtual qubit 10,000,000 if you said your circuit was only going to use 31 qubits.

The transpiler is entirely correct to raise an error in the example given at the top. The only real troubles that I see are that the error message could be clearer and have happened sooner in the pipeline, and that we don't have a huge amount of centralised documentation about the common data representations of the transpiler. Both of those can be improved.

@karineek
Copy link

Thanks for the detailed explanation! I think adding a check much earlier sounds like a good solution.

More specific documentation is super useful especially since the analysis of these documents commonly is done by Gen-AI tools, which might miss the context you added here. In the end, we are trying to build models that can sensibly explore these systems.

@eliarbel
Copy link
Contributor

@vili-1 @karineek I'm wondering, can you please elaborate more on the use-case you are after here? Is the end goal a specific quantum problem you are trying to solve with Qiskit (and IBM Quantum) or is it more of a generic testing/fuzzing/automation story?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working mod: transpiler Issues and PRs related to Transpiler
Projects
None yet
Development

No branches or pull requests

5 participants