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

Reset optimization in the transpiler #5127

Closed
zachschoenfeld33 opened this issue Sep 24, 2020 · 6 comments · Fixed by #10591
Closed

Reset optimization in the transpiler #5127

zachschoenfeld33 opened this issue Sep 24, 2020 · 6 comments · Fixed by #10591
Labels
type: enhancement It's working, but needs polishing

Comments

@zachschoenfeld33
Copy link
Contributor

zachschoenfeld33 commented Sep 24, 2020

What is the expected enhancement?

Modification of the behavior of the reset instruction in the transpiler.

There is a standard transpiler pass RemoveResetInZeroState, which understandably deletes a reset when the qubit is already in the ground state. According to documentation, this should be applied for optimization_level >= 1.

However, it does not always act as expected. Consider

qc = QuantumCircuit(1, 1)
qc.x(0)
qc.x(0)
qc.reset(0)

print(qc)
print(transpile(qc, optimization_level=0))
print(transpile(qc, optimization_level=1))
print(transpile(qc, optimization_level=2))
print(transpile(qc, optimization_level=3))

This gives the output

     ┌───┐┌───┐     
q_0: ┤ X ├┤ X ├─|0>─
     └───┘└───┘     
c: 1/═══════════════
                    
     ┌───┐┌───┐     
q_0: ┤ X ├┤ X ├─|0>─
     └───┘└───┘     
c: 1/═══════════════
                    
     ┌───┐┌───┐     
q_0: ┤ X ├┤ X ├─|0>─
     └───┘└───┘     
c: 1/═══════════════
                    
          
q_0: ─|0>─
          
c: 1/═════
          
     
q_0: 
     
c: 1/

We would expect that level 1 removes the two x gates in line w/ the RemoveResetInZeroState, but it does not.

Furthermore, when using a backend, interchanging of reset instructions for swap gates can be unpredictable.

Consider the following circuits which use a single reset on 3 different qubits. This is run on the public 5q device ibmq_ourense

IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q', group='open', project='main')
backend = provider.get_backend('ibmq_ourense')

def multiq_custom_circ(n_resets):
    """Multi qubit circuit on qubits 0, 1, 2 w/ n_resets reset instructions used."""
    qc = QuantumCircuit(3,3)
    qc.h(0)
    qc.h(1)
    qc.h(2)
    for _ in range(n_resets):
        qc.reset(0)
    qc.cx(1,0)
    for _ in range(n_resets):
        qc.reset(2)
    qc.cx(0,2)
    for _ in range(n_resets):
        qc.reset(1)
    qc.x(1)
    qc.measure([0,1,2], [0,1,2])
    return qc

qc = multiq_custom_circ(1)
print(qc)
print(transpile(qc, backend, optimization_level=0))
print(transpile(qc, backend, optimization_level=1))
print(transpile(qc, backend, optimization_level=2))
print(transpile(qc, backend, optimization_level=3))

The result is

     ┌───┐     ┌───┐               ┌─┐   
q_0: ┤ H ├─|0>─┤ X ├──■────────────┤M├───
     ├───┤     └─┬─┘  │       ┌───┐└╥┘┌─┐
q_1: ┤ H ├───────■────┼───|0>─┤ X ├─╫─┤M├
     ├───┤          ┌─┴─┐ ┌─┐ └───┘ ║ └╥┘
q_2: ┤ H ├─|0>──────┤ X ├─┤M├───────╫──╫─
     └───┘          └───┘ └╥┘       ║  ║ 
c: 3/══════════════════════╩════════╩══╩═
                           2        0  1 
               ┌──────────┐     ┌───┐                                    ░ ┌─┐»
      q_0 -> 0 ┤ U2(0,pi) ├─|0>─┤ X ├─────────────────■──────────────────░─┤M├»
               ├──────────┤     └─┬─┘     ┌───┐     ┌─┴─┐                ░ └╥┘»
      q_1 -> 1 ┤ U2(0,pi) ├───────■────■──┤ X ├──■──┤ X ├────────────────░──╫─»
               ├──────────┤          ┌─┴─┐└─┬─┘┌─┴─┐└───┘┌─────────────┐ ░  ║ »
      q_2 -> 2 ┤ U2(0,pi) ├─|0>──────┤ X ├──■──┤ X ├─|0>─┤ U3(pi,0,pi) ├─░──╫─»
               └──────────┘          └───┘     └───┘     └─────────────┘ ░  ║ »
ancilla_0 -> 3 ──────────────────────────────────────────────────────────░──╫─»
                                                                         ░  ║ »
ancilla_1 -> 4 ──────────────────────────────────────────────────────────░──╫─»
                                                                         ░  ║ »
          c: 3/═════════════════════════════════════════════════════════════╩═»
                                                                            0 »
«                     
«      q_0 -> 0 ──────
«                  ┌─┐
«      q_1 -> 1 ───┤M├
«               ┌─┐└╥┘
«      q_2 -> 2 ┤M├─╫─
«               └╥┘ ║ 
«ancilla_0 -> 3 ─╫──╫─
«                ║  ║ 
«ancilla_1 -> 4 ─╫──╫─
«                ║  ║ 
«          c: 3/═╩══╩═
«                1  2 
               ┌──────────┐               ┌───┐          ┌─────────────┐ ░    »
      q_2 -> 0 ┤ U2(0,pi) ├─|0>────────■──┤ X ├──■───|0>─┤ U3(pi,0,pi) ├─░────»
               ├──────────┤          ┌─┴─┐└─┬─┘┌─┴─┐┌───┐└─────────────┘ ░    »
      q_1 -> 1 ┤ U2(0,pi) ├───────■──┤ X ├──■──┤ X ├┤ X ├────────────────░────»
               ├──────────┤     ┌─┴─┐└───┘     └───┘└─┬─┘                ░ ┌─┐»
      q_0 -> 2 ┤ U2(0,pi) ├─|0>─┤ X ├─────────────────■──────────────────░─┤M├»
               └──────────┘     └───┘                                    ░ └╥┘»
ancilla_0 -> 3 ──────────────────────────────────────────────────────────░──╫─»
                                                                         ░  ║ »
ancilla_1 -> 4 ──────────────────────────────────────────────────────────░──╫─»
                                                                         ░  ║ »
          c: 3/═════════════════════════════════════════════════════════════╩═»
                                                                            0 »
«               ┌─┐   
«      q_2 -> 0 ┤M├───
«               └╥┘┌─┐
«      q_1 -> 1 ─╫─┤M├
«                ║ └╥┘
«      q_0 -> 2 ─╫──╫─
«                ║  ║ 
«ancilla_0 -> 3 ─╫──╫─
«                ║  ║ 
«ancilla_1 -> 4 ─╫──╫─
«                ║  ║ 
«          c: 3/═╩══╩═
«                1  2 
               ┌──────────┐               ┌─────────────┐   ┌─┐
      q_1 -> 0 ┤ U2(0,pi) ├───────■───|0>─┤ U3(pi,0,pi) ├───┤M├
               ├──────────┤     ┌─┴─┐     └─────┬─┬─────┘   └╥┘
      q_0 -> 1 ┤ U2(0,pi) ├─|0>─┤ X ├──■────────┤M├──────────╫─
               ├──────────┤     └───┘┌─┴─┐      └╥┘      ┌─┐ ║ 
      q_2 -> 2 ┤ U2(0,pi) ├─|0>──────┤ X ├───────╫───────┤M├─╫─
               └──────────┘          └───┘       ║       └╥┘ ║ 
ancilla_0 -> 3 ──────────────────────────────────╫────────╫──╫─
                                                 ║        ║  ║ 
ancilla_1 -> 4 ──────────────────────────────────╫────────╫──╫─
                                                 ║        ║  ║ 
          c: 3/══════════════════════════════════╩════════╩══╩═
                                                 0        2  1 
               ┌──────────┐               ┌─────────────┐   ┌─┐
      q_1 -> 0 ┤ U2(0,pi) ├───────■───|0>─┤ U3(pi,0,pi) ├───┤M├
               ├──────────┤     ┌─┴─┐     └─────┬─┬─────┘   └╥┘
      q_0 -> 1 ┤ U2(0,pi) ├─|0>─┤ X ├──■────────┤M├──────────╫─
               ├──────────┤     └───┘┌─┴─┐      └╥┘      ┌─┐ ║ 
      q_2 -> 2 ┤ U2(0,pi) ├─|0>──────┤ X ├───────╫───────┤M├─╫─
               └──────────┘          └───┘       ║       └╥┘ ║ 
ancilla_0 -> 3 ──────────────────────────────────╫────────╫──╫─
                                                 ║        ║  ║ 
ancilla_1 -> 4 ──────────────────────────────────╫────────╫──╫─
                                                 ║        ║  ║ 
          c: 3/══════════════════════════════════╩════════╩══╩═
                                                 0        2  1 

We observe that for levels 0, 1, the reset on 1 is changed w/ a swap and moved to another qubit. However, on levels 2,3 this does not occur. I would expect that the optimization increase for these higher levels.

Suggestions and Discussion

Regarding the pass, RemoveResetInZeroState, I believe we should make it easier for users to turn this off. Advanced users adding their own reset instructions may not want them optimized away (for instance, they may be testing the backend reset fidelity). There is a flag init_qubits in the assembler which defines whether qubits are initialized, but I think we need something more general to define whether or not resets should be optimized away.

The other issue is defining and adding clarity to the unrolling on reset instructions as shown above. It would be nice if users could choose to shut this unrolling off and maintain the exact ordering and location of reset's that they sent in initially. Some documentation of how this process works would also be beneficial.

@zachschoenfeld33 zachschoenfeld33 added the type: enhancement It's working, but needs polishing label Sep 24, 2020
@zachschoenfeld33
Copy link
Contributor Author

@andrewwack if you have any suggestions you'd like to add based on your experience feel free...

@zachschoenfeld33
Copy link
Contributor Author

@mtreinish

@mtreinish
Copy link
Member

So I think what this comes down to is we have a init_qubits flag on assemble which is basically a way of saying for any backends that support it don't implicitly initialize the qubits for us, let us the user handle it. I'm thinking we probably should bubble that construct up to the QuantumCircuit class level. Right now we're kind of in a weird place where we don't draw a circuit as being at |0> by default since we added the circuit library and made things easily composable but the implicit assumption when you run a circuit is that all the bits are at |0> when the circuit starts. With that in mind we probably want the transpiler to keep running the remove reset in zero state pass, but I agree there are users and use cases where that's not the case.

What I think we should do here is probably add a similar init_qubits (maybe another name) attribute to the QuantumCircuit class. If that is set then the transpiler knows not to run RemoveResetInZeroState. We also can update assemble to respect the flag too (and use the kwarg to override whatever is set in the circuit).

The other alternative I see is exposing a boolean flag in the PassManagerConfig for toggling the passs as part of the default pass managers and adding a kwarg on transpile/execute for disabling it. But my preference would be to handle this at the circuit level, because it feels like we're punting it to the user to set the flag in a bunch of places if they want to handle resets on their own.

@zachschoenfeld33
Copy link
Contributor Author

I like this approach in the QuantumCircuit class. However, the pass I assume also applies to reset's at other points in the circuit, so perhaps the flag should be something like opt_resets? Just don't want to be specific to initialization (but the init_qubits flag should stay because users should be able to control initialization).

@mtreinish
Copy link
Member

Well, I'm thinking if it's a QuantumCircuit flag it should be about initialization. Something like opt_resets feels more like a transpiler flag than a circuit attribute because it's basically just saying to the transpile() function don't run the RemoveResetInZero pass. I was thinking we want to have the attribute on QuantumCircuit because it can inform how the circuit is expected to be executed through the entire execution path. In other words it tells transpile() to not run RemoveResetInZero and it tells assemble() to set init_qubits in the Qobj object config while also potentially informing local decisions for other functions that use circuits (like whether we draw |0> in the circuit drawer). That being said I don't think the 2 options are mutually exclusive and we can add a circuit attribute and a PassManagerConfig option if there is a need for both.

@zachschoenfeld33
Copy link
Contributor Author

Sure, that makes sense. In that case, I think we'd need a second option so that it doesn't occur anywhere else in the circuit.

It would also be nice if the reset didn't get replaced w/ swap operations in the transpiler so they maintained the order input into the qobj. Is that something that would be a possibility?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement It's working, but needs polishing
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants