Skip to content

Commit 4623b9b

Browse files
authored
Circuit diagrams: handle integer comparisons, add SX gate (#2982)
- add the SX gate to the supported primitives - handle comparison operators more gracefully. <img width="949" height="628" alt="image" src="https://github.com/user-attachments/assets/84c94042-e663-44a8-8ed9-faa66e9ac01a" /> from: ```qsharp operation Main() : Result { use q = Qubit(); use reg = Qubit[4]; ApplyToEach(H, reg); let num = MeasureInteger(reg); if num < 8 { X(q); } else { Z(q); } ResetAll(reg); MResetZ(q) } ``` --------- Co-authored-by: Mine Starks <>
1 parent 910cdd8 commit 4623b9b

2 files changed

Lines changed: 128 additions & 11 deletions

File tree

source/compiler/qsc_circuit/src/rir_to_circuit.rs

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -573,14 +573,32 @@ fn process_icmp_variables(
573573
) -> Result<(), Error> {
574574
let expr_left = expr_from_operand(variables, operand)?;
575575
let expr_right = expr_from_operand(variables, operand1)?;
576-
let expr = eq_expr(expr_left, expr_right)?;
577-
match condition_code {
578-
ConditionCode::Eq => store_expr_in_variable(variables, variable, expr),
579-
ConditionCode::Ne => store_expr_in_variable(variables, variable, expr.negate()),
580-
condition_code => Err(Error::UnsupportedFeature(format!(
581-
"unsupported condition code in icmp: {condition_code:?}"
582-
))),
583-
}
576+
let expr = match condition_code {
577+
ConditionCode::Eq => eq_expr(expr_left, expr_right)?,
578+
ConditionCode::Ne => eq_expr(expr_left, expr_right)?.negate(),
579+
ConditionCode::Slt => Expr::Bool(BoolExpr::BinOp(
580+
expr_left.into(),
581+
expr_right.into(),
582+
ComparisonOp::Lt,
583+
)),
584+
ConditionCode::Sgt => Expr::Bool(BoolExpr::BinOp(
585+
expr_left.into(),
586+
expr_right.into(),
587+
ComparisonOp::Gt,
588+
)),
589+
ConditionCode::Sle => Expr::Bool(BoolExpr::BinOp(
590+
expr_left.into(),
591+
expr_right.into(),
592+
ComparisonOp::Le,
593+
)),
594+
ConditionCode::Sge => Expr::Bool(BoolExpr::BinOp(
595+
expr_left.into(),
596+
expr_right.into(),
597+
ComparisonOp::Ge,
598+
)),
599+
};
600+
store_expr_in_variable(variables, variable, expr)?;
601+
Ok(())
584602
}
585603

586604
fn process_fcmp_variables(
@@ -595,7 +613,34 @@ fn process_fcmp_variables(
595613
let expr = match condition_code {
596614
FcmpConditionCode::False => BoolExpr::LiteralBool(false),
597615
FcmpConditionCode::True => BoolExpr::LiteralBool(true),
598-
cmp => BoolExpr::BinOp(expr_left.into(), expr_right.into(), cmp.to_string()),
616+
FcmpConditionCode::OrderedAndEqual | FcmpConditionCode::UnorderedOrEqual => {
617+
BoolExpr::BinOp(expr_left.into(), expr_right.into(), ComparisonOp::Eq)
618+
}
619+
FcmpConditionCode::OrderedAndNotEqual | FcmpConditionCode::UnorderedOrNotEqual => {
620+
BoolExpr::BinOp(expr_left.into(), expr_right.into(), ComparisonOp::Ne)
621+
}
622+
FcmpConditionCode::OrderedAndLessThan | FcmpConditionCode::UnorderedOrLessThan => {
623+
BoolExpr::BinOp(expr_left.into(), expr_right.into(), ComparisonOp::Lt)
624+
}
625+
FcmpConditionCode::OrderedAndGreaterThan | FcmpConditionCode::UnorderedOrGreaterThan => {
626+
BoolExpr::BinOp(expr_left.into(), expr_right.into(), ComparisonOp::Gt)
627+
}
628+
FcmpConditionCode::OrderedAndLessThanOrEqual
629+
| FcmpConditionCode::UnorderedOrLessThanOrEqual => {
630+
BoolExpr::BinOp(expr_left.into(), expr_right.into(), ComparisonOp::Le)
631+
}
632+
FcmpConditionCode::OrderedAndGreaterThanOrEqual
633+
| FcmpConditionCode::UnorderedOrGreaterThanOrEqual => {
634+
BoolExpr::BinOp(expr_left.into(), expr_right.into(), ComparisonOp::Ge)
635+
}
636+
FcmpConditionCode::Ordered | FcmpConditionCode::Unordered => {
637+
// These don't map to a simple binary comparison; treat as opaque.
638+
return store_expr_in_variable(
639+
variables,
640+
variable,
641+
Expr::Rich(RichExpr::FunctionOf(vec![expr_left, expr_right])),
642+
);
643+
}
599644
};
600645
store_expr_in_variable(variables, variable, Expr::Bool(expr))?;
601646
Ok(())
@@ -620,7 +665,7 @@ fn eq_expr(expr_left: Expr, expr_right: Expr) -> Result<Expr, Error> {
620665
filter: (true, false, false, true), // 00 and 11
621666
})
622667
}
623-
(left, right) => Expr::Rich(RichExpr::FunctionOf(vec![left, right])),
668+
(left, right) => Expr::Bool(BoolExpr::BinOp(left.into(), right.into(), ComparisonOp::Eq)),
624669
})
625670
}
626671

@@ -660,6 +705,42 @@ enum Expr {
660705
Bool(BoolExpr),
661706
}
662707

708+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
709+
enum ComparisonOp {
710+
Eq,
711+
Ne,
712+
Lt,
713+
Gt,
714+
Le,
715+
Ge,
716+
}
717+
718+
impl ComparisonOp {
719+
fn negate(self) -> Self {
720+
match self {
721+
Self::Eq => Self::Ne,
722+
Self::Ne => Self::Eq,
723+
Self::Lt => Self::Ge,
724+
Self::Gt => Self::Le,
725+
Self::Le => Self::Gt,
726+
Self::Ge => Self::Lt,
727+
}
728+
}
729+
}
730+
731+
impl Display for ComparisonOp {
732+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
733+
match self {
734+
Self::Eq => write!(f, "=="),
735+
Self::Ne => write!(f, "!="),
736+
Self::Lt => write!(f, "<"),
737+
Self::Gt => write!(f, ">"),
738+
Self::Le => write!(f, "<="),
739+
Self::Ge => write!(f, ">="),
740+
}
741+
}
742+
}
743+
663744
#[derive(Debug, Clone, PartialEq)]
664745
enum BoolExpr {
665746
Result(usize),
@@ -670,7 +751,7 @@ enum BoolExpr {
670751
filter: (bool, bool, bool, bool),
671752
},
672753
LiteralBool(bool),
673-
BinOp(Box<Expr>, Box<Expr>, String),
754+
BinOp(Box<Expr>, Box<Expr>, ComparisonOp),
674755
}
675756

676757
/// These could be of type boolean, we just don't necessary know
@@ -695,6 +776,9 @@ impl Expr {
695776
filter: (!f00, !f01, !f10, !f11),
696777
})
697778
}
779+
Expr::Bool(BoolExpr::BinOp(left, right, op)) => {
780+
Expr::Bool(BoolExpr::BinOp(left.clone(), right.clone(), op.negate()))
781+
}
698782
expr => Expr::Rich(RichExpr::FunctionOf(expr.flat_exprs())),
699783
}
700784
}
@@ -1328,6 +1412,7 @@ fn known_gate_spec(callable_name: &str) -> Option<GateSpec<'static>> {
13281412
"__quantum__qis__z__body" => GateSpec::single_qubit_gate("Z"),
13291413
"__quantum__qis__s__body" => GateSpec::single_qubit_gate("S"),
13301414
"__quantum__qis__s__adj" => GateSpec::single_qubit_gate_adjoint("S"),
1415+
"__quantum__qis__sx__body" => GateSpec::single_qubit_gate("SX"),
13311416
"__quantum__qis__t__body" => GateSpec::single_qubit_gate("T"),
13321417
"__quantum__qis__t__adj" => GateSpec::single_qubit_gate_adjoint("T"),
13331418
"__quantum__qis__h__body" => GateSpec::single_qubit_gate("H"),

source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,3 +968,35 @@ fn result_to_result_comparison() {
968968
"#]],
969969
);
970970
}
971+
972+
#[test]
973+
fn integer_comparison() {
974+
check_trace(
975+
indoc! {"
976+
operation Main() : Unit {
977+
use q = Qubit();
978+
use reg = Qubit[4];
979+
ApplyToEach(H, reg);
980+
let num = MeasureInteger(reg);
981+
if num < 8 {
982+
X(q);
983+
} else {
984+
Y(q);
985+
}
986+
}
987+
"},
988+
"A.Main()",
989+
&expect![[r#"
990+
Main@A.qs:3:4 -> ApplyToEach@qsharp-library-source:Std/Canon.qs:29:4 -> loop: register@qsharp-library-source:Std/Canon.qs:29:25[1] -> (1)@qsharp-library-source:Std/Canon.qs:30:8 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_1), controls=())
991+
Main@A.qs:3:4 -> ApplyToEach@qsharp-library-source:Std/Canon.qs:29:4 -> loop: register@qsharp-library-source:Std/Canon.qs:29:25[2] -> (2)@qsharp-library-source:Std/Canon.qs:30:8 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_2), controls=())
992+
Main@A.qs:3:4 -> ApplyToEach@qsharp-library-source:Std/Canon.qs:29:4 -> loop: register@qsharp-library-source:Std/Canon.qs:29:25[3] -> (3)@qsharp-library-source:Std/Canon.qs:30:8 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_3), controls=())
993+
Main@A.qs:3:4 -> ApplyToEach@qsharp-library-source:Std/Canon.qs:29:4 -> loop: register@qsharp-library-source:Std/Canon.qs:29:25[4] -> (4)@qsharp-library-source:Std/Canon.qs:30:8 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_4), controls=())
994+
Main@A.qs:4:14 -> MeasureInteger@qsharp-library-source:Std/Measurement.qs:155:4 -> loop: 0..nBits - 1@qsharp-library-source:Std/Measurement.qs:155:26[1] -> (1)@qsharp-library-source:Std/Measurement.qs:156:12 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_1, c_0)
995+
Main@A.qs:4:14 -> MeasureInteger@qsharp-library-source:Std/Measurement.qs:155:4 -> loop: 0..nBits - 1@qsharp-library-source:Std/Measurement.qs:155:26[2] -> (2)@qsharp-library-source:Std/Measurement.qs:156:12 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_2, c_1)
996+
Main@A.qs:4:14 -> MeasureInteger@qsharp-library-source:Std/Measurement.qs:155:4 -> loop: 0..nBits - 1@qsharp-library-source:Std/Measurement.qs:155:26[3] -> (3)@qsharp-library-source:Std/Measurement.qs:156:12 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_3, c_2)
997+
Main@A.qs:4:14 -> MeasureInteger@qsharp-library-source:Std/Measurement.qs:155:4 -> loop: 0..nBits - 1@qsharp-library-source:Std/Measurement.qs:155:26[4] -> (4)@qsharp-library-source:Std/Measurement.qs:156:12 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_4, c_3)
998+
Main@A.qs:5:4[true] -> if: (f(c_0, c_1, c_2, c_3)) < (8)@A.qs:6:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=())
999+
Main@A.qs:5:4[false] -> if: (f(c_0, c_1, c_2, c_3)) >= (8)@A.qs:8:8 -> Y@qsharp-library-source:Std/Intrinsic.qs:1082:8 -> gate(Y, targets=(q_0), controls=())
1000+
"#]],
1001+
);
1002+
}

0 commit comments

Comments
 (0)