From adb3a79217c7dd2c812a6cbddc796152e2104cae Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:15:15 -0800 Subject: [PATCH] Add custom operations sample (#1995) This PR adds a new language sample showing how to use custom measurements and a more comprehensive sample showing how to use custom operations (intrinsics and measurements). --------- Co-authored-by: Stefan J. Wernli --- samples/algorithms/MajoranaQubits/qsharp.json | 1 + .../algorithms/MajoranaQubits/src/GateSet.qs | 88 +++++++++++++++++++ .../MajoranaQubits/src/HardwareIntrinsics.qs | 27 ++++++ samples/algorithms/MajoranaQubits/src/Main.qs | 30 +++++++ samples/language/CustomMeasurements.qs | 27 ++++++ samples_test/src/tests/language.rs | 2 + 6 files changed, 175 insertions(+) create mode 100644 samples/algorithms/MajoranaQubits/qsharp.json create mode 100644 samples/algorithms/MajoranaQubits/src/GateSet.qs create mode 100644 samples/algorithms/MajoranaQubits/src/HardwareIntrinsics.qs create mode 100644 samples/algorithms/MajoranaQubits/src/Main.qs create mode 100644 samples/language/CustomMeasurements.qs diff --git a/samples/algorithms/MajoranaQubits/qsharp.json b/samples/algorithms/MajoranaQubits/qsharp.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/samples/algorithms/MajoranaQubits/qsharp.json @@ -0,0 +1 @@ +{} diff --git a/samples/algorithms/MajoranaQubits/src/GateSet.qs b/samples/algorithms/MajoranaQubits/src/GateSet.qs new file mode 100644 index 0000000000..3396842f2b --- /dev/null +++ b/samples/algorithms/MajoranaQubits/src/GateSet.qs @@ -0,0 +1,88 @@ +/// A set of gates built upon the custom measurements +/// provided by the hardware provider. +/// +/// Source: +/// [1] Surface code compilation via edge-disjoint paths +/// https://arxiv.org/pdf/2110.11493 + +/// Apply a CNOT gate to the given qubits. +/// Source: [1] Figure 3. +operation CNOT(control : Qubit, target : Qubit) : Unit { + // Prepare an ancilla qubit in the |+⟩ state. + use ancilla = Qubit(); + PrepareX(ancilla); + + let a = Mzz(control, ancilla); + let b = Mxx(ancilla, target); + let c = Mz(ancilla); + Reset(ancilla); + + if b == One { + Z(control); + } + + if a != c { + X(target); + } +} + + +/// Prepare a qubit in the |+⟩ state. +operation PrepareX(q : Qubit) : Unit { + if Mx(q) == One { + Z(q); + } +} + +/// Prepare a qubit in the |0⟩ state. +operation PrepareZ(q : Qubit) : Unit { + if Mz(q) == One { + X(q); + } +} + +/// Prepare a Bell Pair. +/// Source: [1] Figure 18a. +operation BellPair(q1 : Qubit, q2 : Qubit) : Unit { + // Collapse the qubits onto the Pauli-Z basis. + Mz(q1); + Mz(q2); + + // If they are not aligned in the Pauli-X basis, phase flip one of them. + if Mxx(q1, q2) == One { + Z(q2); + } +} + +/// Measure a Bell Pair. +/// Source: [1] Figure 18b. +/// Below is a map showing how the Bell states map to the Result pairs: +/// |𝚽⁺⟩ -> (Zero, Zero) +/// |𝚿⁺⟩ -> (Zero, One) +/// |𝚽⁻⟩ -> (One, Zero) +/// |𝚿⁻⟩ -> (One, One) +operation BellMeasurement(q1 : Qubit, q2 : Qubit) : (Result, Result) { + let z = Mzz(q1, q2); + let x = Mxx(q1, q2); + (x, z) +} + +/// User friendly wrapper around the Mx hardware gate. +operation Mx(q : Qubit) : Result { + HardwareIntrinsics.__quantum__qis__mx__body(q) +} + +/// User friendly wrapper around the Mz hardware gate. +operation Mz(q : Qubit) : Result { + HardwareIntrinsics.__quantum__qis__mz__body(q) +} + +/// User friendly wrapper around the Mxx hardware gate. +operation Mxx(q1 : Qubit, q2 : Qubit) : Result { + HardwareIntrinsics.__quantum__qis__mxx__body(q1, q2) +} + +/// User friendly wrapper around the Mzz hardware gate. +operation Mzz(q1 : Qubit, q2 : Qubit) : Result { + HardwareIntrinsics.__quantum__qis__mzz__body(q1, q2) +} diff --git a/samples/algorithms/MajoranaQubits/src/HardwareIntrinsics.qs b/samples/algorithms/MajoranaQubits/src/HardwareIntrinsics.qs new file mode 100644 index 0000000000..0921939c71 --- /dev/null +++ b/samples/algorithms/MajoranaQubits/src/HardwareIntrinsics.qs @@ -0,0 +1,27 @@ +/// A set of custom measurements exposed from a hardware +/// provider using Majorana Qubits. + +@Measurement() +@SimulatableIntrinsic() +operation __quantum__qis__mx__body(q : Qubit) : Result { + H(q); + M(q) +} + +@Measurement() +@SimulatableIntrinsic() +operation __quantum__qis__mz__body(q : Qubit) : Result { + M(q) +} + +@Measurement() +@SimulatableIntrinsic() +operation __quantum__qis__mxx__body(q1 : Qubit, q2 : Qubit) : Result { + Std.Intrinsic.Measure([PauliX, PauliX], [q1, q2]) +} + +@Measurement() +@SimulatableIntrinsic() +operation __quantum__qis__mzz__body(q1 : Qubit, q2 : Qubit) : Result { + Std.Intrinsic.Measure([PauliZ, PauliZ], [q1, q2]) +} diff --git a/samples/algorithms/MajoranaQubits/src/Main.qs b/samples/algorithms/MajoranaQubits/src/Main.qs new file mode 100644 index 0000000000..4047d7c931 --- /dev/null +++ b/samples/algorithms/MajoranaQubits/src/Main.qs @@ -0,0 +1,30 @@ +/// # Sample +/// Majorana Qubits +/// +/// # Description +/// In hardware providing majorana qubits, common quantum operations +/// are implemented using measurements and Pauli corrections. This +/// sample shows a hypotetical hardware provider exposing some custom +/// gates to Q# and a small library built on top of it. + +/// Sample program using custom gates from a hardware provider. +operation Main() : (Result, Result) { + // Create a Bell Pair in the |𝚽⁺⟩ state. + use qs = Qubit[2]; + GateSet.BellPair(qs[0], qs[1]); + + // Applying X to any of the qubits will result in the |𝚿⁺⟩ Bell state. + // X(qs[0]); // Uncomment to try + + // Applying Z to any of the qubits will result in the |𝚽⁻⟩ Bell state. + // Z(qs[0]); // Uncomment to try + + // Applying X and Z to the pair will result in the |𝚿⁻⟩ Bell state. + // Note that they can be applied to the same Qubit. + // Z(qs[0]); // Uncomment to try + // X(qs[0]); + + let res = GateSet.BellMeasurement(qs[0], qs[1]); + ResetAll(qs); + res +} diff --git a/samples/language/CustomMeasurements.qs b/samples/language/CustomMeasurements.qs new file mode 100644 index 0000000000..8addd2d654 --- /dev/null +++ b/samples/language/CustomMeasurements.qs @@ -0,0 +1,27 @@ +// # Sample +// Custom Operations +// +// # Description +// The @Measurement attribute in Q# allows you to define custom measurements +// that are lowered to QIR in the same way the `M` measurement in the standard +// library is lowered. That means an `"irreversible"` attribute is added to +// the callable declaration and the output results are moved to the paramaters +// and treated as result registers. +// +// # Who is this for? +// The target audience are library authors targeting specific hardware. + +/// Try running the command `Q#: Get QIR for the current Q# program` +/// in VS-Code's Command Palette. +operation Main() : Result { + use q = Qubit(); + H(q); + __quantum__qis__mx__body(q) +} + +@Measurement() +@SimulatableIntrinsic() +operation __quantum__qis__mx__body(q : Qubit) : Result { + H(q); + M(q) +} diff --git a/samples_test/src/tests/language.rs b/samples_test/src/tests/language.rs index 4b51b10169..61b7137eca 100644 --- a/samples_test/src/tests/language.rs +++ b/samples_test/src/tests/language.rs @@ -144,6 +144,8 @@ pub const COPYANDUPDATEOPERATOR_EXPECT_DEBUG: Expect = expect![[r#" Updated array: [10, 11, 100, 13] Updated array: [10, 100, 12, 200] ()"#]]; +pub const CUSTOMMEASUREMENTS_EXPECT: Expect = expect!["Zero"]; +pub const CUSTOMMEASUREMENTS_EXPECT_DEBUG: Expect = expect!["Zero"]; pub const DATATYPES_EXPECT: Expect = expect![[r#" Binary BigInt: 42 Octal BigInt: 42