Design
For this example:
def controlled_gate_circuit(angle):
qp.PauliX(0) #Flip to |1> for RX to act on the second wire
qp.ctrl(qp.RX, control = (0), control_values=(1))(angle, wires=[1])
return qp.state()
Current Flow
The mlir generated is:
qref.custom "PauliX"() %1 : !qref.bit
%2 = qref.get %0[ 0] : !qref.reg<2> -> !qref.bit
qref.ctrl(%2) ctrlvals(%true) {
%5 = qref.get %0[ 1] : !qref.reg<2> -> !qref.bit
%extracted = tensor.extract %arg0[] : tensor<f64>
qref.custom "RX"(%extracted) %5 : !qref.bit
}
Instead of this going through the convert-to-value-semantics turning into:
%out_ctrl_qubits, %results = quantum.ctrl(%3) ctrlvals(%true) (%4) : !quantum.bit -> !quantum.bit {
^bb0(%arg1: !quantum.bit):
%extracted = tensor.extract %arg0[] : tensor<f64>
%out_qubits_0 = quantum.custom "RX"(%extracted) %arg1 : !quantum.bit
quantum.yield %out_qubits_0 : !quantum.bit
}
which gets folded into this in the ctrl-lowering pass
%out_qubits_0, %out_ctrl_qubits = quantum.custom "RX"(%extracted) %2 ctrls(%out_qubits) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit
We want to move this process earlier in the pipeline in reference semantics as it is easier and more intuitive there.
New Flow
Now we want to go directly from the original mlir:
qref.custom "PauliX"() %1 : !qref.bit
%2 = qref.get %0[ 0] : !qref.reg<2> -> !qref.bit
qref.ctrl(%2) ctrlvals(%true) {
%5 = qref.get %0[ 1] : !qref.reg<2> -> !qref.bit
%extracted = tensor.extract %arg0[] : tensor<f64>
qref.custom "RX"(%extracted) %5 : !qref.bit
}
To the Qref instructions:
%5 = qref.get %0[ 1] : !qref.reg<2> -> !qref.bit
%extracted = tensor.extract %arg0[] : tensor<f64>
qref.custom "RX"(%extracted) %5 ctrls(%2) ctrlvals(%true) : !qref.bit
Then after convert-to-value-semantics is called,
qref.custom "RX"(%extracted) %5 ctrls(%2) ctrlvals(%true) : !qref.bit
gets folded into
%out_qubits_0, %out_ctrl_qubits = quantum.custom "RX"(%extracted) %q1 ctrls(%q0) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit
The test for this behaviour is in TestFlatCircuits.mlir:
// CHECK: [[ROT:%.+]], [[ROTctrl:%.+]] = quantum.custom "some_gate"(%arg0, %arg1) [[RX]] adj ctrls([[CNOT]]#1) ctrlvals(%arg2) : !quantum.bit ctrls !quantum.bit
qref.custom "some_gate"(%arg0, %arg1) %q0 adj ctrls (%q1) ctrlvals (%arg2) : !qref.bit ctrls !qref.bit
The idea is to call --convert-to-reference-semantics on entering the control-lowering pass, do the lowering described above, then call --convert-to-value-semantics on exit.
Then for any pass that breaks, call conver-to-value-semantics at the beginning to fix it.
Design
For this example:
Current Flow
The mlir generated is:
Instead of this going through the
convert-to-value-semanticsturning into:which gets folded into this in the
ctrl-loweringpassWe want to move this process earlier in the pipeline in reference semantics as it is easier and more intuitive there.
New Flow
Now we want to go directly from the original mlir:
To the Qref instructions:
Then after
convert-to-value-semanticsis called,gets folded into
The test for this behaviour is in
TestFlatCircuits.mlir:The idea is to call
--convert-to-reference-semanticson entering the control-lowering pass, do the lowering described above,then call.--convert-to-value-semanticson exitThen for any pass that breaks, call
conver-to-value-semanticsat the beginning to fix it.