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

Multi-Controlled CX & Custom basis transpilation #13562

Open
glanzz opened this issue Dec 12, 2024 · 1 comment
Open

Multi-Controlled CX & Custom basis transpilation #13562

glanzz opened this issue Dec 12, 2024 · 1 comment
Labels
documentation Something is not clear or an error documentation mod: transpiler Issues and PRs related to Transpiler

Comments

@glanzz
Copy link

glanzz commented Dec 12, 2024

Environment

  • Qiskit version: 1.3.0
  • Python version: 3.11
  • Operating system: Apple M2

What is happening?

Preset pass manager with custom basis gate fails to transpile circuits containing multi-controlled CX operation.
The measurement results of the decomposed mcx gate and the actual show clear divergence. The script below reproduces the issue.

How can we reproduce the issue?

from qiskit import QuantumCircuit
import numpy as np
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

qc = QuantumCircuit(7)
qc.mcx([0,2,3],4)

init = QuantumCircuit(7)
init.x(1)
init.x(3)
init.x(4)
init.x(5)
init.x(6)

inverse=qc.inverse()
decompose_pass_manager=generate_preset_pass_manager(basis_gates=["x", "y", "z", "cx", "swap", "s", "h", "ccx", "rx"], optimization_level=1)
decomposed = decompose_pass_manager.run(qc)

qc = init.compose(qc)
decomposed = init.compose(decomposed)

qc.measure_all()
decomposed.measure_all()


backend = AerSimulator()
for j,q in enumerate([qc, decomposed]):
  for i in range(4):
    pass_manager=generate_preset_pass_manager(backend=backend, optimization_level=i)
    transpiled_circuit = pass_manager.run(q)
    job = backend.run(transpiled_circuit, shots=11000)
    results = job.result().get_counts()
    print(f"Circuit {j}|Optimisation Level: {i}, Results: {results}")

What should happen?

                   ░ ┌─┐                  
   q_0: ───────■───░─┤M├──────────────────
        ┌───┐  │   ░ └╥┘┌─┐               
   q_1: ┤ X ├──┼───░──╫─┤M├───────────────
        └───┘  │   ░  ║ └╥┘┌─┐            
   q_2: ───────■───░──╫──╫─┤M├────────────
        ┌───┐  │   ░  ║  ║ └╥┘┌─┐         
   q_3: ┤ X ├──■───░──╫──╫──╫─┤M├─────────
        ├───┤┌─┴─┐ ░  ║  ║  ║ └╥┘┌─┐      
   q_4: ┤ X ├┤ X ├─░──╫──╫──╫──╫─┤M├──────
        ├───┤└───┘ ░  ║  ║  ║  ║ └╥┘┌─┐   
   q_5: ┤ X ├──────░──╫──╫──╫──╫──╫─┤M├───
        ├───┤      ░  ║  ║  ║  ║  ║ └╥┘┌─┐
   q_6: ┤ X ├──────░──╫──╫──╫──╫──╫──╫─┤M├
        └───┘      ░  ║  ║  ║  ║  ║  ║ └╥┘
meas: 7/══════════════╩══╩══╩══╩══╩══╩══╩═
                      0  1  2  3  4  5  6 
Circuit 0|Optimisation Level: 0, Results: {'1111010': 11000}
Circuit 0|Optimisation Level: 1, Results: {'1111010': 11000}
Circuit 0|Optimisation Level: 2, Results: {'1111010': 11000}
Circuit 0|Optimisation Level: 3, Results: {'1111010': 11000}
Circuit 1|Optimisation Level: 0, Results: {'1101010': 11000}
Circuit 1|Optimisation Level: 1, Results: {'1101010': 11000}
Circuit 1|Optimisation Level: 2, Results: {'1101010': 11000}
Circuit 1|Optimisation Level: 3, Results: {'1101010': 11000}

The decomposed and the actual circuit show clear output divergences after measurements.

Any suggestions?

No response

@glanzz glanzz added the bug Something isn't working label Dec 12, 2024
@Cryoris
Copy link
Contributor

Cryoris commented Dec 13, 2024

The default transpilation flow assumes that the initial qubit states are $|0\rangle$, which enables us to do certain optimizations by using idle qubits as auxiliary qubits. For example, the MCX gate can be decomposed more efficiently (i.e. less depth and less CX gates) if we have sufficient clean auxiliary qubits (in state $|0\rangle$). This means that you must take care when composing transpiled circuits, since it is no longer true that the initial qubit state is $|0\rangle$. In other words: default transpilation is guaranteed to give you the correct measurement results, but does not necessarily preserve the unitary. We should maybe emphasize this more in the docs.

If you want to preserve the unitary, you can simply give tell the pass manager that qubits_initially_zero=False. Concretely, if you update your code to

decompose_pass_manager=generate_preset_pass_manager(basis_gates=["x", "y", "z", "cx", "swap", "s", "h", "ccx", "rx"], optimization_level=1, qubits_initially_zero=False)

it should work.

As general guideline however, I would suggest to compose the circuit completely and only transpile in the end. This allows the transpiler to make the best choices and optimizations.

@Cryoris Cryoris added documentation Something is not clear or an error documentation and removed bug Something isn't working labels Dec 13, 2024
@ShellyGarion ShellyGarion added the mod: transpiler Issues and PRs related to Transpiler label Dec 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Something is not clear or an error documentation mod: transpiler Issues and PRs related to Transpiler
Projects
None yet
Development

No branches or pull requests

3 participants