Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
5.11k stars 2.35k forks source link

Extracting permutation of classical registers from Quantum Circuit #5350

Closed yourball closed 1 year ago

yourball commented 3 years ago

Hi,

during routing/mapping passes input quantum registers could be permuted, the permutation logical -> physical qregs is saved in quantum_circuit._layout property and could be easily accessed by a user.

Similarly, classical cregs also become permuted after routing/mapping: physical cregs->logical cregs. This permutation can be extracted when applying measure gates quantum_circuit.measure_all(). E.g. when two cregs (0 and 1) are permuted than the measure gates will look like: measure qreg[0] creg[1]; measure qreg[1] creg[0].

However, when measure gates are not applied, there is still could be implicit permutation of cregs after mapping/routing pass. It seems that there is no simple way to access information about cregs permutation, which could be inconvenient in some cases. For example this information is necessary to check the equivalence of original and transpiled circuits. Is it possible to add this information as a property of QuantumCircuit object? Or there is some other way to access this information?

Thanks a lot!

alvinjoseph7 commented 3 years ago

The plot_gate_map() from qiskit.visualization can be used to view the original layout. And transpiled circuit can be viewed via plot_circuit_layout() which also belongs to qiskit.visualization. In this way, 2 plots will be displayed and both will have to be compared. It's doable,but I'm open to a simpler solution if it exists.

kdk commented 3 years ago

Hi @yourball, cregs will not be modified by the routing or layout process. Can you provide an example of the behavior you're seeing and the behavior you'd expect?

ajavadia commented 3 years ago

The circuit._layout gives the initial layout (i.e. the initial permutation of qubits before the circuit starts). I think this issue is asking for a convenient way to view the final layout (i.e. the final permutation, which is usually absorbed into the measurements and reorders their clbit targets). I agree it could be handy to access this.

nonhermitian commented 3 years ago

This is a topic for the hackathon next week: https://github.com/qiskit-community/qiskit-hackathon-korea-21/issues/2

yourball commented 3 years ago

The circuit._layout gives the initial layout (i.e. the initial permutation of qubits before the circuit starts). I think this issue is asking for a convenient way to view the final layout (i.e. the final permutation, which is usually absorbed into the measurements and reorders their clbit targets). I agree it could be handy to access this.

@ajavadia Thanks, that is the exactly what I mean! It is useful to access the information about virtual permutation of qubits at the end of the circuit even when there are no measurement gates. For example this is important for equivalence checking of circuits before/after optimization.

nonhermitian commented 3 years ago

It is not strictly needed in that case, although it makes it much easier. Instead of checking equiv to identity with possible phase, you look for equiv up to a permutation matrix.

ajavadia commented 3 years ago

I recently wrote a function for my own usage, but something like this could be helpful.

def unpermute(circ_mapped):
    """
    Apply left and right permutations to remove effect of layout and measurement retargeting.
    """
    empty_circ = QuantumCircuit(circ_mapped.num_qubits)

    lo = circ_mapped._layout
    tokenswapper = LayoutTransformation(coupling_map=CouplingMap.from_line(8),
                                        to_layout=lo,
                                        from_layout=Layout.generate_trivial_layout(lo.get_registers().pop()))
    pm = PassManager(tokenswapper)
    initial_permutation = pm.run(empty_circ)
    initial_permutation.barrier()

    final_perm = [0] * 8
    for op, qubits, clbits in circ_mapped.data:
        if op.name == 'measure':
            final_perm[qubits[0].index] = clbits[0].index

    tokenswapper = LayoutTransformation(coupling_map=CouplingMap.from_line(8),
                                        to_layout=Layout({v: p for v, p in zip(circ_mapped.qubits, final_perm)}),
                                        from_layout=Layout.generate_trivial_layout(lo.get_registers().pop()))
    pm = PassManager(tokenswapper)
    empty_circ.barrier()
    final_permutation = pm.run(empty_circ)

    circ_left_permuted = initial_permutation.compose(circ_mapped.remove_final_measurements(inplace=False))
    circ_left_right_permuted = circ_left_permuted.compose(final_permutation)
    return circ_left_right_permuted

A circuit mapped by Qiskit: image

Calling unpermute() function on it models the left layout and right measurement reordering as explicit permutations: image

I used the TokenSwapper pass but using the qiskit.circuit.library.Permutation is easier (but it needs this bug fix: https://github.com/Qiskit/qiskit-terra/pull/5813)

1ucian0 commented 3 years ago

Could PR https://github.com/Qiskit/qiskit-terra/pull/5280 help here?

from qiskit import QuantumCircuit
from qiskit.compiler import transpile
from qiskit.transpiler import CouplingMap

circ = QuantumCircuit(3)
circ.h(0)
circ.cx(0,2)
print(circ)

coupling_map = CouplingMap([[0,1], [1,2]])

transpiled = transpile(circ, coupling_map=coupling_map, routing_method='basic', initial_layout=[0,1,2], restore_layout='initial')
print(transpiled)
     ┌───┐     
q_0: ┤ H ├──■──
     └───┘  │  
q_1: ───────┼──
          ┌─┴─┐
q_2: ─────┤ X ├
          └───┘
         ┌───┐           
q_0 -> 0 ┤ H ├─X───────X─
         └───┘ │       │ 
q_1 -> 1 ──────X───■───X─
                 ┌─┴─┐   
q_2 -> 2 ────────┤ X ├───
                 └───┘   
nonhermitian commented 3 years ago

This is done for the StochasticSwap in #6827. Others can be modified in a similar manner.