1
$\begingroup$

Edit: Improved code a bit

Edit 2: After corresponding with a Qiskit developer, this issue was considered not a bug per se but a missing functionality that has not yet been implemented (and will be difficult to do so). The proscribed solution is to not transpile partial circuits. In practice I find one can transpile partial circuits then check for qubit identity swapping and fix as needed. The correspondence is here: https://github.com/Qiskit/qiskit-terra/issues/7472

This is a condensed description of the IBM bug that was a problem here, to make it more clear.

The following code compiles to a correct circuit when the measurement is present, but not when the measurement is absent:

from qiskit.compiler import transpile
from qiskit import QuantumCircuit, IBMQ
provider = IBMQ.load_account()
lima = provider.get_backend('ibmq_lima')
n_q = 3
qc_foo = QuantumCircuit(n_q, n_q)
qubit_list = [0,1,2]
qc_foo.swap(0,1)
qc_foo.swap(0,2)
qc_foo.barrier()
qc_foo.measure(qubit_list, qubit_list)
qc_foo_trans = transpile(qc_foo, backend=lima, optimization_level=3
                         , initial_layout=qubit_list, seed_transpiler=101)
qc_foo_trans.draw()

With measurement the circuit is:

enter image description here

And without measurement (comment out .measure line in code):

enter image description here

Notice the latter circuit is not equivalent to the intended circuit. The reason is that in both cases part of the work is moved to the measurements by swapping them i.e. q1->c0 and q0->c1. But when there are no measurements, that extra work disappears and the final qubit identities are wrong. It is further strange that a barrier does not stop this in either case.

This is an especially big problem when trying to perform a circuit multiple times in a row before measurement. In that case the circuit itself has to be correct, and simple solutions like remapping state prep or measurements won't help.

(This is mostly a bug report, to make the problem more visible to Qiskit contributors. But temporary solutions are welcome.)

$\endgroup$

2 Answers 2

1
$\begingroup$

This looks like expected behavior by optimizer at level 3. Whatever backend you chose, level 3 is noise-adaptive, and most probably qubits chosen as 1 and 2 are the least noisy to perform the swap on, and later reinterpret the result during measurement.

The qubit identities are not wrong in itself - the optimizer/transpiler will remember its mapping of logical to physical qubits and adjust the code accordingly. So if ever you actually measure the result it would be correct as expected.

$\endgroup$
2
  • $\begingroup$ If this is intended behavior, there should at least be a way to turn it off without sacrificing gate optimization. What if someone wants to repeat a circuit ten times before measuring? It’d be silly to input the 10x length circuit to the optimizer rather than optimize once and repeat, as the latter would both have fewer gates and take less compute time. But this qubit swapping bug makes optimizing the 1x circuit impossible, as it assumes I’ll measure immediately rather than repeating and so swaps identities. Surely what I’m describing is a common use case? $\endgroup$
    – Max
    Jan 2, 2022 at 4:25
  • $\begingroup$ Additionally — I don’t think your claim is true that the qubit identities are remembered. When I add measurements later, the identities come out wrong. $\endgroup$
    – Max
    Jan 2, 2022 at 4:26
0
$\begingroup$

I don't think Qiskit currently provides a straightforward way to exclude a certain transpiler pass.

You can construct a pass manager that contains only the passes you want then use it.

my_pass_manager = PassManager()
my_pass_manager.append(my_list_of_passes)
qc_foo_trans = transpile(qc_foo, backend=lima, pass_manager=my_pass_manager)

However, this would be a tedious task. A quicker solution is to monkey patch the transpiler pass (OptimizeSwapBeforeMeasure in your case) to alter its behavior

from qiskit.transpiler.passes.optimization import OptimizeSwapBeforeMeasure

# Monkey patching the transpiler pass class:
def doNothing(self, dag):
    return dag

OptimizeSwapBeforeMeasure.run = doNothing

# Now, call transpile as usual
qc_foo_trans = transpile(qc_foo, backend=lima, optimization_level=3)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.