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

Disentangle SWAP with fsm #1297

Open
wants to merge 3 commits into
base: qstate_mlir_interpreter_fsm
Choose a base branch
from

Conversation

ritu-thombre99
Copy link

Context:

Disentangle SWAP gates using finite state machine pass implemented in #1154

Description of the Change:

  1. Added disentangle-SWAP pass
  2. TODO: Add tests to check the disentangle-SWAP pass

Benefits:

Reduce the number of CNOT gates required to implement SWAP to a few CNOTs and single-qubit gates for basis states

Related issue: #1268

@ritu-thombre99 ritu-thombre99 changed the title Disentangle cnot with fsm Disentangle SWAP with fsm Nov 9, 2024
// See the License for the specific language governing permissions and
// limitations under the License.

// This algorithm is taken from https://arxiv.org/pdf/2012.07711, table 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SWAP gate is table 6


///////////////////////////

PropagateSimpleStatesAnalysis &pssa = getAnalysis<PropagateSimpleStatesAnalysis>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a tutorial for the basic catalyst mlir structure, and how to write a mlir pass. The tutorial covers more than what we need to know here. Here we just need to know two things:

  1. The canScheduleOn method above determines that the pass will only be applied to function operations, i.e. things that look like func.func ... {...} in the IR
  2. The runOnOperation method here determines what transformations will be performed on the target operations.

In other words, for each function, the runOnOperation method happens once.

Let's take a look at the construction of the PropagateSimpleStatesAnalysis object. The object carries a map, which records the abstract enum qubit state of each qubit value. The write into the map happens upon the analysis object's construction. If the analysis object is only constructed once, the map will be written into only once.

Say you perform some transformation that adds a new gate. At the time of the analysis object's creation above (when the pass is run on the function, but before the walk visitor has created the new gate op), the new gate wouldn't have existed yet, so it wouldn't be in the map.

However, looking at your code, it seems like you haven't created any gates yet. It would be helpful to see your test case! Note that the existing PropagateSimpleStatesAnalysis is only my first draft so there could be bugs : )

What might be helpful is, since the assertion failure is saying some qubit values don't have corresponding states in the map, before the assertion line, we can print out the operation being analyzed, and print out the map, to see what's going on. In mlir you can print via the following:

Thing thing_I_want_to_print;
llvm::errs() << thing_I_want_to_print;

Don't hesitate to let me know if you get any progress!

@ritu-thombre99
Copy link
Author

ritu-thombre99 commented Nov 11, 2024

Thanks @paul0403.

I run into the same error when I run disentangle-CNOT pass for qubits in state |1>,|+>,|->.

Here's my input .mlir code to prepare CNOT with both qubits in |1>

module {
  func.func @my_circuit(%in_qubit1: !quantum.bit, %in_qubit2: !quantum.bit) -> (!quantum.bit, !quantum.bit) {
    %0 = quantum.custom "PauliX"() %in_qubit1 : !quantum.bit
    %1 = quantum.custom "PauliX"() %in_qubit2 : !quantum.bit
    %2,%3 = quantum.custom "CNOT"() %in_qubit1,%in_qubit2 : !quantum.bit, !quantum.bit
    return %2,%3 : !quantum.bit, !quantum.bit
  }
}

Even disentangle-CNOT pass is only working for |0> basis state.

The tests for disentangle-CNOT, however, run fine. Maybe I'm writing my input .mlir files in an incorrect way.

I also tested disentangle-CNOT pass for circuits with no CNOT, got the same error again.

@paul0403
Copy link
Contributor

paul0403 commented Nov 11, 2024

Thanks @paul0403.

I run into the same error when I run disentangle-CNOT pass for qubits in state |1>,|+>,|->.

Here's my input .mlir code to prepare CNOT with both qubits in |1>

module {
  func.func @my_circuit(%in_qubit1: !quantum.bit, %in_qubit2: !quantum.bit) -> (!quantum.bit, !quantum.bit) {
    %0 = quantum.custom "PauliX"() %in_qubit1 : !quantum.bit
    %1 = quantum.custom "PauliX"() %in_qubit2 : !quantum.bit
    %2,%3 = quantum.custom "CNOT"() %in_qubit1,%in_qubit2 : !quantum.bit, !quantum.bit
    return %2,%3 : !quantum.bit, !quantum.bit
  }
}

Even disentangle-CNOT pass is only working for |0> basis state.

The tests for disentangle-CNOT, however, run fine. Maybe I'm writing my input .mlir files in an incorrect way.

I also tested disentangle-CNOT pass for circuits with no CNOT, got the same error again.

Aha! Looking at my "todo" comment on the proapagate state analysis, you will see what qubit values get set to the "zero" enum state. Knowing that now, what do you think the course of action should be?

(Thanks for finding this bug! 💯)

@ritu-thombre99
Copy link
Author

ritu-thombre99 commented Nov 12, 2024

Fixed it :)

I decided to call PSSA only when op is available (instead of referenced from getAnalysis), and get the qubitValues directly from the class method getQubitValues:

PropagateSimpleStatesAnalysis pssa = PropagateSimpleStatesAnalysis(op);
llvm::DenseMap<Value, QubitState> qubitValues = pssa.getQubitValues();

Seems to be working for non-SWAP gates and also when both qubits in the SWAP are in the same basis state.

@ritu-thombre99
Copy link
Author

One more question

image

Here's what the output after removing SWAP on (|+>,|+>) looks like.
Is this correct or should the last return statement be return out_qubits,out_qubits_0?

@ritu-thombre99
Copy link
Author

It took very long time to figure out but at this moment in time, both of these passes would not work without having extract at the beginning of the function to extract qubits from the registers.

I'll use this structure for now (i.e. have the extracts in the beginning)

@paul0403
Copy link
Contributor

It took very long time to figure out but at this moment in time, both of these passes would not work without having extract at the beginning of the function to extract qubits from the registers.

I'll use this structure for now (i.e. have the extracts in the beginning)

Yes! This was deliberate, since the first occurrence of a qubit in a function doesn't actually mean it's the starting qubit on a wire: the function can be called by some other circuit function as a subroutine!

Excellent work, I'll take a look later : )

@ritu-thombre99
Copy link
Author

ritu-thombre99 commented Nov 12, 2024

I got all the cases where SWAP is replaced by 1 single qubit gate on each qubit.

Now trying to figure out the way to insert additional operations on the same wire.

@paul0403
Copy link
Contributor

Hi @ritu-thombre99 , impressive work! 💯

I think we are good for now. The catalyst IR actually handles control a bit differently because of the existence of qml.ctrl. The conventional control bit of a CNOT isn't actually a ControlQubit of the gate, but just a regular input/output qubit. But it seems like you figured this out yourself, so good job!

If you have time and want to add some tests that's also good with me, but I think what you've done is very good already! (Sorry for the issue being a bit long)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants