Skip to content

Commit db207db

Browse files
authored
Support intrinsic SX gate (#2338)
This adds full support for an intrinsic `SX` gate by including it in the "canonical" gate set used by the QDK. This includes support across QIR generation, circuits, resource estimation, and Qiskit interop.
1 parent 9851719 commit db207db

File tree

16 files changed

+890
-57
lines changed

16 files changed

+890
-57
lines changed

compiler/qsc_circuit/src/builder.rs

+5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ impl Backend for Builder {
129129
self.push_gate(gate("S", [q]));
130130
}
131131

132+
fn sx(&mut self, q: usize) {
133+
let q = self.map(q);
134+
self.push_gate(gate("SX", [q]));
135+
}
136+
132137
fn swap(&mut self, q0: usize, q1: usize) {
133138
let q0 = self.map(q0);
134139
let q1 = self.map(q1);

compiler/qsc_circuit/src/circuit_to_qsharp.rs

+2-29
Original file line numberDiff line numberDiff line change
@@ -203,35 +203,8 @@ fn generate_unitary_call(
203203
qubits: &FxHashMap<usize, String>,
204204
indent: &str,
205205
) -> String {
206-
// "SX" will generate three operations: H, X and H
207-
if unitary.gate == "SX" {
208-
let h_str = operation_call(
209-
&Unitary {
210-
gate: "H".to_string(),
211-
args: vec![],
212-
children: vec![],
213-
targets: unitary.targets.clone(),
214-
controls: unitary.controls.clone(),
215-
is_adjoint: false,
216-
},
217-
qubits,
218-
);
219-
let s_str = operation_call(
220-
&Unitary {
221-
gate: "S".to_string(),
222-
args: vec![],
223-
children: vec![],
224-
targets: unitary.targets.clone(),
225-
controls: unitary.controls.clone(),
226-
is_adjoint: unitary.is_adjoint,
227-
},
228-
qubits,
229-
);
230-
format!("{indent}{h_str};\n{indent}{s_str};\n{indent}{h_str};\n")
231-
} else {
232-
let operation_str = operation_call(unitary, qubits);
233-
format!("{indent}{operation_str};\n")
234-
}
206+
let operation_str = operation_call(unitary, qubits);
207+
format!("{indent}{operation_str};\n")
235208
}
236209

237210
fn generate_ket_call(ket: &Ket, qubits: &FxHashMap<usize, String>, indent: &str) -> String {

compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -611,9 +611,7 @@ fn circuit_with_sqrt_x_gate() {
611611
}
612612
H(qs[0]);
613613
Z(qs[0]);
614-
H(qs[1]);
615-
S(qs[1]);
616-
H(qs[1]);
614+
SX(qs[1]);
617615
Z(qs[1]);
618616
}
619617
@@ -660,9 +658,7 @@ fn circuit_with_ctrl_adj_sqrt_x_gate() {
660658
}
661659
H(qs[0]);
662660
Z(qs[0]);
663-
Controlled H([qs[1]], qs[0]);
664-
Controlled Adjoint S([qs[1]], qs[0]);
665-
Controlled H([qs[1]], qs[0]);
661+
Controlled Adjoint SX([qs[1]], qs[0]);
666662
Z(qs[1]);
667663
}
668664

compiler/qsc_eval/src/backend.rs

+15
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ pub trait Backend {
6565
fn s(&mut self, _q: usize) {
6666
unimplemented!("s gate");
6767
}
68+
fn sx(&mut self, _q: usize) {
69+
unimplemented!("sx gate");
70+
}
6871
fn swap(&mut self, _q0: usize, _q1: usize) {
6972
unimplemented!("swap gate");
7073
}
@@ -295,6 +298,13 @@ impl Backend for SparseSim {
295298
self.apply_noise(q);
296299
}
297300

301+
fn sx(&mut self, q: usize) {
302+
self.sim.h(q);
303+
self.sim.s(q);
304+
self.sim.h(q);
305+
self.apply_noise(q);
306+
}
307+
298308
fn swap(&mut self, q0: usize, q1: usize) {
299309
self.sim.swap_qubit_ids(q0, q1);
300310
self.apply_noise(q0);
@@ -593,6 +603,11 @@ where
593603
self.main.s(q);
594604
}
595605

606+
fn sx(&mut self, q: usize) {
607+
self.chained.sx(q);
608+
self.main.sx(q);
609+
}
610+
596611
fn swap(&mut self, q0: usize, q1: usize) {
597612
self.chained.swap(q0, q1);
598613
self.main.swap(q0, q1);

compiler/qsc_eval/src/intrinsic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ pub(crate) fn call(
183183
"__quantum__qis__h__body" => one_qubit_gate(|q| sim.h(q), arg, arg_span),
184184
"__quantum__qis__s__body" => one_qubit_gate(|q| sim.s(q), arg, arg_span),
185185
"__quantum__qis__s__adj" => one_qubit_gate(|q| sim.sadj(q), arg, arg_span),
186+
"__quantum__qis__sx__body" => one_qubit_gate(|q| sim.sx(q), arg, arg_span),
186187
"__quantum__qis__t__body" => one_qubit_gate(|q| sim.t(q), arg, arg_span),
187188
"__quantum__qis__t__adj" => one_qubit_gate(|q| sim.tadj(q), arg, arg_span),
188189
"__quantum__qis__x__body" => one_qubit_gate(|q| sim.x(q), arg, arg_span),

compiler/qsc_eval/src/intrinsic/tests.rs

+37
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ impl Backend for CustomSim {
9393
self.sim.s(q);
9494
}
9595

96+
fn sx(&mut self, q: usize) {
97+
self.sim.h(q);
98+
self.sim.s(q);
99+
self.sim.h(q);
100+
}
101+
96102
fn swap(&mut self, q0: usize, q1: usize) {
97103
self.sim.swap(q0, q1);
98104
}
@@ -1228,6 +1234,37 @@ fn sadj_qubit_already_released_fails() {
12281234
);
12291235
}
12301236

1237+
#[test]
1238+
fn sx() {
1239+
check_intrinsic_result(
1240+
"",
1241+
indoc! {r#"{
1242+
use q1 = Qubit();
1243+
QIR.Intrinsic.__quantum__qis__sx__body(q1);
1244+
if Microsoft.Quantum.Diagnostics.CheckZero(q1) {
1245+
fail "Qubit should be in one state.";
1246+
}
1247+
QIR.Intrinsic.__quantum__qis__sx__body(q1);
1248+
QIR.Intrinsic.__quantum__qis__sx__body(q1);
1249+
QIR.Intrinsic.__quantum__qis__sx__body(q1);
1250+
Microsoft.Quantum.Diagnostics.CheckZero(q1)
1251+
}"#},
1252+
&expect!["true"],
1253+
);
1254+
}
1255+
1256+
#[test]
1257+
fn sx_qubit_already_released_fails() {
1258+
check_intrinsic_result(
1259+
"",
1260+
indoc! {"{
1261+
let q = { use q = Qubit(); q };
1262+
QIR.Intrinsic.__quantum__qis__sx__body(q)
1263+
}"},
1264+
&expect!["qubit used after release"],
1265+
);
1266+
}
1267+
12311268
#[test]
12321269
fn t() {
12331270
check_intrinsic_result(

compiler/qsc_partial_eval/src/tests/intrinsics.rs

+20
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,26 @@ fn call_to_intrinsic_adjoint_s_adds_callable_and_generates_instruction() {
191191
);
192192
}
193193

194+
#[test]
195+
fn call_to_intrinsic_sx_adds_callable_and_generates_instruction() {
196+
check_call_to_single_qubit_instrinsic_adds_callable_and_generates_instruction(
197+
"__quantum__qis__sx__body",
198+
&expect![[r#"
199+
Callable:
200+
name: __quantum__qis__sx__body
201+
call_type: Regular
202+
input_type:
203+
[0]: Qubit
204+
output_type: <VOID>
205+
body: <NONE>"#]],
206+
&expect![[r#"
207+
Block:
208+
Call id(1), args( Qubit(0), )
209+
Call id(2), args( Integer(0), Pointer, )
210+
Return"#]],
211+
);
212+
}
213+
194214
#[test]
195215
fn call_to_intrinsic_t_adds_callable_and_generates_instruction() {
196216
check_call_to_single_qubit_instrinsic_adds_callable_and_generates_instruction(

compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs

+1-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ operation tdg(qubit : Qubit) : Unit is Adj + Ctl {
120120
}
121121

122122
operation sx(qubit : Qubit) : Unit is Adj + Ctl {
123-
Rx(Std.Math.PI() / 2., qubit);
124-
Adjoint R(PauliI, Std.Math.PI() / 2., qubit);
123+
SX(qubit);
125124
}
126125

127126
operation rx(theta : Angle, qubit : Qubit) : Unit is Adj + Ctl {

compiler/qsc_rca/src/tests/intrinsics.rs

+25
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,31 @@ fn check_rca_for_quantum_qis_s_adj() {
910910
);
911911
}
912912

913+
#[test]
914+
fn check_rca_for_quantum_qis_sx_body() {
915+
let compilation_context = CompilationContext::default();
916+
check_callable_compute_properties(
917+
&compilation_context.fir_store,
918+
compilation_context.get_compute_properties(),
919+
"__quantum__qis__sx__body",
920+
&expect![
921+
r#"
922+
Callable: CallableComputeProperties:
923+
body: ApplicationsGeneratorSet:
924+
inherent: Quantum: QuantumProperties:
925+
runtime_features: RuntimeFeatureFlags(0x0)
926+
value_kind: Element(Static)
927+
dynamic_param_applications:
928+
[0]: [Parameter Type Element] Quantum: QuantumProperties:
929+
runtime_features: RuntimeFeatureFlags(UseOfDynamicQubit)
930+
value_kind: Element(Static)
931+
adj: <none>
932+
ctl: <none>
933+
ctl-adj: <none>"#
934+
],
935+
);
936+
}
937+
913938
#[test]
914939
fn check_rca_for_quantum_qis_t_body() {
915940
let compilation_context = CompilationContext::default();

0 commit comments

Comments
 (0)