4
$\begingroup$

I'm having a problem in IBM Qiskit with my qubit identities switching places during transpilation. I've been told by developers on the Qiskit slack server that there's currently no built-in way to fix this problem. I'm wondering if anyone has found one themselves.

The issue is that I'd like to perform an algorithm like so:

  1. Prepare in an initial state
  2. Apply a circuit U repeatedly n_iter times
  3. Measure

I also want this algorithm to:

  • Be reasonably efficient with gates (specifically noisy CNOTs)
  • Have the same error for U for each iteration of it

This turns out to be a headche. When U is complicated, it becomes difficult for the transpiler to efficiently decompose it to native gates. This difficulty goes up for U*U and U*U*U since they're longer. So if I were to transpile the whole algorithm at once (with many U's), it certainly won't give an efficient gate decomposition, and won't have the same error for each iteration of U.

The solution I thought would be reasonable is to transpile U on its own, then compose it with itself. But this hits a problem. The transpiler likes to switch qubit identities (and add global phase, and maybe more?) while searching for a more efficient circuit. So if I follow my algorithm as stated above, U will not line up correctly with the prep or measurement, and my results will be wrong.

I've been told by developers on the slack that there's currently no built-in way to fix this. The transpiler does not report a final_layout of the qubits.

I've been trying to determine the final_layout by hand by comparing the transpiler output to my intended circuit plus swap gates (up to a global phase), but I'm even failing at this.

Does anyone have either working code to determine the final_layout, or a better approach to implementing this algorithm?

EDIT: Code and more info is included in my partial answer below

EDIT 2: See further clarification here: Qubit identities get swapped in IBM Qiskit (This issue is remarkably difficult to communicate clearly)

$\endgroup$
8
  • $\begingroup$ If I understand correctly, then maybe you can try to add a barrier between each application of $U$. This will make sure Qiskit will only optimize the part within the barrier, which is just $U$, and it would be consistent. This will allow you to have the same error for $U$ for each iteration. Now, you can also specify the qubit layout that you want to use instead of letting Qiskit picking it for you. $\endgroup$
    – KAJ226
    Oct 30, 2020 at 4:35
  • $\begingroup$ @KAJ226 In my experience this approach neither makes the error consistent nor particularly close to minimal. I assume the randomness of the optimization is to blame for this. $\endgroup$
    – Max
    Oct 30, 2020 at 6:01
  • 1
    $\begingroup$ Could you share some code? For example, what's the unitary U and what settings in transpiler have you tried? $\endgroup$
    – tsgeorgios
    Oct 30, 2020 at 7:38
  • $\begingroup$ if you fix transpile(..., seed_transpiler=42, ...), does it help? $\endgroup$
    – luciano
    Oct 30, 2020 at 11:27
  • 1
    $\begingroup$ You can technically skip the transpiler step as long as your circuit is valid to run on the device (only basis gates, valid cnot connections, etc). If you're running the same circuit n times, you might be able to run the transpiler on just one instance of the circuit, see how it compiles your gates into basis gates, and then recreate the circuit with those basis gates. Then you can create the full circuit with your n instances, and just pass it directly into assemble() and then backend.run() $\endgroup$ Jan 11, 2021 at 15:47

2 Answers 2

1
$\begingroup$

If i understand your question correctly, i guess you could get, after transpiling $U$, the final layout from _layout property of a circuit and remap the qubits in initial state preparation circuit $ V $ to enforce this layout. If you want your measurement results to respect the initial ordering of qubits, you could re-order the classical register, i.e if qubit 0 is qubit 1 in the final circuit, store the measurement of qubit 1 in classical bit 0.

Finally, you can execute the circuit. The only catch is that you may need another transpilation step in case the layout is not compatible with $ V $ but if $U$ is much more 'complicated' than $V$, it should be fine.

Here is some working code.

from qiskit import transpile, execute, Aer, IBMQ

from qiskit.circuit import QuantumCircuit, ClassicalRegister
from qiskit.circuit.random import random_circuit

from qiskit.transpiler import PassManager
from qiskit.transpiler.passes.layout import ApplyLayout, SetLayout

from qiskit.quantum_info import Statevector
from qiskit.quantum_info.random import random_unitary

qasm  = Aer.get_backend('qasm_simulator')

IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
device = provider.get_backend('ibmq_santiago')

gates = device.configuration().basis_gates
coupling_map = device.configuration().coupling_map

n = 3
n_iter = 2

# init state
V = QuantumCircuit(n)
V.h(0)
for i in range(n - 1):
    V.cx(i, i + 1)
    
# random U
U = random_circuit(n, depth=5)

# full algorithm - no optimization - just for reference
qc = U.repeat(n_iter)
qc.compose(V, front=True, inplace=True)
qc.measure_all()

# transpile U
Utr = transpile(U, 
                basis_gates=gates, 
                coupling_map=coupling_map, 
                optimization_level=3)

layout = Utr._layout
mapping = layout.get_virtual_bits() # a map from virtual to physical qubits

# enforce layout in V
passes_ = [SetLayout(layout), ApplyLayout()]
pm = PassManager(passes_)
Vm = pm.run(V)

# full algorithm
circ = Utr.repeat(n_iter)
circ.compose(Vm, front=True, inplace=True)

# add measurements
cr = ClassicalRegister(n)
circ.add_register(cr)

# re-order classical registers 
for q in U.qubits:
    circ.measure(mapping[q], 
                 cr[q.index])


def simulate(qc):
    return execute(qc, qasm).result().get_counts()

print(simulate(qc))
---
{'000': 484, '001': 17, '010': 6, '011': 47, '100': 10, '110': 19, '111': 441}

print(simulate(circ))
---
{'000': 496, '001': 23, '010': 7, '011': 37, '100': 12, '101': 1, '110': 14, '111': 434}
$\endgroup$
1
  • $\begingroup$ Thank you for the response, I didn't know one could access _layout, good to know. However it turns out I misunderstood the problem and that simply keeping a consistent layout wouldn't solve the problem -- see my answer. $\endgroup$
    – Max
    Nov 17, 2020 at 1:17
1
$\begingroup$

I found a partial answer that I'll share to clarify what the problem is, and provide a hacky fix.

I finally realized that transpiling a circuit piece (without a measurement) doesn’t seem to execute the circuit on a permuted version of the qubits like I thought, but rather it ends up executing the circuit and then a permutation at the end. Comparing to what happens when measurements are present, it seems like transpile assumes measurements are present even if they aren’t. It then allows the measurement step to “absorb” some cnots in the form of swap gates, just swapping which measurement maps to what. This is smart when measurements are present, but when they aren’t it causes the circuit to just swap qubits at the end of the circuit, incorrectly.

I also found a strange partial fix for this: It seems that if I add swaps that undo the error then transpile a second time, the second transpile won’t wrongly delete the swaps. Unsure why this works.

EDIT: Here is code demonstrating the problem:

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=sant, optimization_level=3)
qc_foo_trans.draw()

Notice the transpiled circuit has turned the 0-2 swap into a 1-2 swap (or something similar for your random seed), absorbing the difference into the measurement. Now comment out the measurement step and re-run. The 0-2 still becomes a 1-2, despite the lack of measurement, making the transpiled circuit incorrect.

A developer has also confirmed for me that this is likely a bug. I reported it through their system early 2021 but have not heard anything.

$\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.