Repository: microsoft/qsharp
Pull request #2328 (2025-04-23 00:50:44)

Capitalize the Rotation gate names in trace circuits
The rotation gates created by the circuit builder were getting lower-case names: `rx`, `rxx` for example. These are inconsistent with the other gate names like `H` and `X`. It is also inconsistent with the Q# equivalent operations `Rx(theta, q)`.

This PR changes the output of the circuit builder to have upper-case names for the rotation gates, making them consistent with the other gate names and the Q# operations.


Patch:
diff --git a/compiler/qsc_circuit/src/builder.rs b/compiler/qsc_circuit/src/builder.rs
index cbd28737..af0685d5 100644
--- a/compiler/qsc_circuit/src/builder.rs
+++ b/compiler/qsc_circuit/src/builder.rs
@@ -88,35 +88,35 @@ impl Backend for Builder {
 
     fn rx(&mut self, theta: f64, q: usize) {
         let q = self.map(q);
-        self.push_gate(rotation_gate("rx", theta, [q]));
+        self.push_gate(rotation_gate("Rx", theta, [q]));
     }
 
     fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
         let q0 = self.map(q0);
         let q1 = self.map(q1);
-        self.push_gate(rotation_gate("rxx", theta, [q0, q1]));
+        self.push_gate(rotation_gate("Rxx", theta, [q0, q1]));
     }
 
     fn ry(&mut self, theta: f64, q: usize) {
         let q = self.map(q);
-        self.push_gate(rotation_gate("ry", theta, [q]));
+        self.push_gate(rotation_gate("Ry", theta, [q]));
     }
 
     fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
         let q0 = self.map(q0);
         let q1 = self.map(q1);
-        self.push_gate(rotation_gate("ryy", theta, [q0, q1]));
+        self.push_gate(rotation_gate("Ryy", theta, [q0, q1]));
     }
 
     fn rz(&mut self, theta: f64, q: usize) {
         let q = self.map(q);
-        self.push_gate(rotation_gate("rz", theta, [q]));
+        self.push_gate(rotation_gate("Rz", theta, [q]));
     }
 
     fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
         let q0 = self.map(q0);
         let q1 = self.map(q1);
-        self.push_gate(rotation_gate("rzz", theta, [q0, q1]));
+        self.push_gate(rotation_gate("Rzz", theta, [q0, q1]));
     }
 
     fn sadj(&mut self, q: usize) {
diff --git a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs
index 65878ff0..d7ccc58e 100644
--- a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs
+++ b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs
@@ -232,7 +232,7 @@ function __MultiplyAngleByBigInt__(angle : __Angle__, factor : BigInt) : __Angle
     let (value, size) = angle!;
     let value : BigInt = Std.Convert.IntAsBigInt(value);
     let value = (value * factor) % Std.Convert.IntAsBigInt(1 <<< size);
-    let value = Std.Convert.BoolArrayAsInt(Std.Convert.BigIntAsBoolArray(value, size));
+    let value = Std.Convert.BigIntAsInt(value);
     new __Angle__ { Value = value, Size = size }
 }
 
diff --git a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs
index 22dfb0c0..eb5eee6a 100644
--- a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs
+++ b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs
@@ -5,10 +5,7 @@ import Std.Math.AbsI;
 
 /// The POW function is used to implement the `pow` modifier in QASM for integers.
 operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj + Ctl), target : 'T) : Unit {
-    let op = if N > 0 { () => op(target) } else { () => Adjoint op(target) };
-    for _ in 1..AbsI(N) {
-        op()
-    }
+    ApplyOperationPowerA(N, op, target);
 }
 
 /// The ``BARRIER`` function is used to implement the `barrier` statement in QASM.
diff --git a/library/std/src/Std/Canon.qs b/library/std/src/Std/Canon.qs
index 98751cc6..1eccc6fb 100644
--- a/library/std/src/Std/Canon.qs
+++ b/library/std/src/Std/Canon.qs
@@ -583,6 +583,22 @@ operation ApplyXorInPlaceL(value : BigInt, target : Qubit[]) : Unit is Adj + Ctl
     }
     adjoint self;
 }
+
+/// # Summary
+/// Applies operation `op` to the `target` `power` times.
+/// If `power` is negative, the adjoint of `op` is used.
+/// If `power` is 0, the operation `op` is not applied.
+operation ApplyOperationPowerA<'T>(
+    power : Int,
+    op : 'T => Unit is Adj,
+    target : 'T
+) : Unit is Adj {
+    let u = if power >= 0 { op } else { Adjoint op };
+    for _ in 1..AbsI(power) {
+        u(target);
+    }
+}
+
 /// # Summary
 /// Relabels the qubits in the `current` array with the qubits in the `updated` array. The `updated` array
 /// must be a valid permutation of the `current` array.
@@ -647,4 +663,5 @@ export
     SwapReverseRegister,
     ApplyXorInPlace,
     ApplyXorInPlaceL,
+    ApplyOperationPowerA,
     Relabel;
diff --git a/library/std/src/Std/Convert.qs b/library/std/src/Std/Convert.qs
index 58d8953f..721dc94e 100644
--- a/library/std/src/Std/Convert.qs
+++ b/library/std/src/Std/Convert.qs
@@ -162,6 +162,34 @@ function BigIntAsBoolArray(number : BigInt, bits : Int) : Bool[] {
     result
 }
 
+/// # Summary
+/// Converts a BigInt number into Int. Raises an error if the number is too large to fit.
+///
+/// # Input
+/// ## number
+/// A BigInt number to be converted.
+///
+/// # Output
+/// Int representation of a number.
+function BigIntAsInt(number : BigInt) : Int {
+    let max = 9_223_372_036_854_775_807L;
+    let min = -9_223_372_036_854_775_808L;
+    Fact(number >= min and number <= max, $"`number`=number is too big to fit into Int.");
+
+    mutable result = 0;
+    mutable powL = 1L;
+    mutable pow = 1;
+    for _ in 0..63 {
+        if number &&& powL != 0L {
+            result |||= pow;
+        }
+        powL <<<= 1;
+        pow <<<= 1;
+    }
+
+    result
+}
+
 /// # Summary
 /// Produces a non-negative integer from a string of Results in little-endian format.
 ///
@@ -289,9 +317,10 @@ export
     IntAsBoolArray,
     BoolArrayAsBigInt,
     BigIntAsBoolArray,
+    BigIntAsInt,
     ResultArrayAsInt,
     ResultArrayAsBoolArray,
     BoolArrayAsResultArray,
     ComplexAsComplexPolar,
     ComplexPolarAsComplex,
-    DoubleAsStringWithPrecision;
\ No newline at end of file
+    DoubleAsStringWithPrecision;


Test patch:
diff --git a/compiler/qsc/src/interpret/circuit_tests.rs b/compiler/qsc/src/interpret/circuit_tests.rs
index a2832ab7..5543bb0a 100644
--- a/compiler/qsc/src/interpret/circuit_tests.rs
+++ b/compiler/qsc/src/interpret/circuit_tests.rs
@@ -148,9 +148,9 @@ fn rotation_gate() {
         .circuit(CircuitEntryPoint::EntryPoint, false)
         .expect("circuit generation should succeed");
 
-    expect![[r"
-        q_0    ─ rx(1.5708) ──
-    "]]
+    expect![[r#"
+        q_0    ─ Rx(1.5708) ──
+    "#]]
     .assert_eq(&circ.to_string());
 }
 
@@ -883,10 +883,10 @@ fn operation_with_long_gates_properly_aligned() {
     expect![[r#"
         q_0    ── H ────────────────────────────────────── ● ──────── M ────────────────────────────────── ● ─────────
                                                            │          ╘════════════════════════════════════╪══════════
-        q_1    ── H ──────── X ─────── ry(1.0000) ──────── X ───────────────────────────── rxx(1.0000) ────┼───── M ──
+        q_1    ── H ──────── X ─────── Ry(1.0000) ──────── X ───────────────────────────── Rxx(1.0000) ────┼───── M ──
                                                                                                 ┆          │      ╘═══
-        q_2    ── H ─── rx(1.0000) ──────── H ─────── rx(1.0000) ──── H ─── rx(1.0000) ─────────┆──────────┼──────────
-        q_3    ─────────────────────────────────────────────────────────────────────────── rxx(1.0000) ─── X ──── M ──
+        q_2    ── H ─── Rx(1.0000) ──────── H ─────── Rx(1.0000) ──── H ─── Rx(1.0000) ─────────┆──────────┼──────────
+        q_3    ─────────────────────────────────────────────────────────────────────────── Rxx(1.0000) ─── X ──── M ──
                                                                                                                   ╘═══
     "#]]
     .assert_eq(&circ.to_string());
@@ -919,12 +919,12 @@ fn operation_with_subsequent_qubits_gets_horizontal_lines() {
         .expect("circuit generation should succeed");
 
     expect![[r#"
-        q_0    ─ rxx(1.0000) ─
+        q_0    ─ Rxx(1.0000) ─
                       ┆
-        q_1    ─ rxx(1.0000) ─
-        q_2    ─ rxx(1.0000) ─
+        q_1    ─ Rxx(1.0000) ─
+        q_2    ─ Rxx(1.0000) ─
                       ┆
-        q_3    ─ rxx(1.0000) ─
+        q_3    ─ Rxx(1.0000) ─
     "#]]
     .assert_eq(&circ.to_string());
 }
@@ -953,9 +953,9 @@ fn operation_with_subsequent_qubits_no_double_rows() {
         .expect("circuit generation should succeed");
 
     expect![[r#"
-        q_0    ─ rxx(1.0000) ── rxx(1.0000) ─
+        q_0    ─ Rxx(1.0000) ── Rxx(1.0000) ─
                       ┆              ┆
-        q_1    ─ rxx(1.0000) ── rxx(1.0000) ─
+        q_1    ─ Rxx(1.0000) ── Rxx(1.0000) ─
     "#]]
     .assert_eq(&circ.to_string());
 }
@@ -989,12 +989,12 @@ fn operation_with_subsequent_qubits_no_added_rows() {
         .expect("circuit generation should succeed");
 
     expect![[r#"
-        q_0    ─ rxx(1.0000) ─── M ──
+        q_0    ─ Rxx(1.0000) ─── M ──
                       ┆          ╘═══
-        q_1    ─ rxx(1.0000) ────────
-        q_2    ─ rxx(1.0000) ─── M ──
+        q_1    ─ Rxx(1.0000) ────────
+        q_2    ─ Rxx(1.0000) ─── M ──
                       ┆          ╘═══
-        q_3    ─ rxx(1.0000) ────────
+        q_3    ─ Rxx(1.0000) ────────
     "#]]
     .assert_eq(&circ.to_string());
 }
diff --git a/compiler/qsc_partial_eval/src/tests/qubits.rs b/compiler/qsc_partial_eval/src/tests/qubits.rs
index cb537f2c..f9985a4c 100644
--- a/compiler/qsc_partial_eval/src/tests/qubits.rs
+++ b/compiler/qsc_partial_eval/src/tests/qubits.rs
@@ -377,6 +377,6 @@ fn qubit_relabel_in_dynamic_block_triggers_capability_error() {
 
     assert_error(
         &error,
-        &expect!["CapabilityError(UseOfDynamicQubit(Span { lo: 59760, hi: 59773 }))"],
+        &expect!["CapabilityError(UseOfDynamicQubit(Span { lo: 60173, hi: 60186 }))"],
     );
 }
diff --git a/library/src/tests/canon.rs b/library/src/tests/canon.rs
index 7133ebf2..a7afda85 100644
--- a/library/src/tests/canon.rs
+++ b/library/src/tests/canon.rs
@@ -556,3 +556,18 @@ fn check_apply_xor_in_place_l() {
         &Value::Int(953),
     );
 }
+
+#[test]
+fn check_apply_operation_power_a() {
+    test_expression(
+        {
+            "{
+            use q = Qubit();
+            ApplyOperationPowerA(12, Rx(Std.Math.PI()/16.0, _), q);
+            ApplyOperationPowerA(-3, Rx(Std.Math.PI()/4.0, _), q);
+            M(q)
+        }"
+        },
+        &Value::RESULT_ZERO,
+    );
+}
diff --git a/library/src/tests/convert.rs b/library/src/tests/convert.rs
index 415dbe0a..b209711e 100644
--- a/library/src/tests/convert.rs
+++ b/library/src/tests/convert.rs
@@ -170,6 +170,37 @@ fn check_bigint_as_bool_array() {
     );
 }
 
+#[test]
+fn check_big_int_as_int_0() {
+    test_expression("Std.Convert.BigIntAsInt(0L)", &Value::Int(0));
+}
+
+#[test]
+fn check_big_int_as_int_1() {
+    test_expression("Std.Convert.BigIntAsInt(1L)", &Value::Int(1));
+}
+
+#[test]
+fn check_big_int_as_int_n1() {
+    test_expression("Std.Convert.BigIntAsInt(-1L)", &Value::Int(-1));
+}
+
+#[test]
+fn check_big_int_as_int_max() {
+    test_expression(
+        "Std.Convert.BigIntAsInt(9_223_372_036_854_775_807L)",
+        &Value::Int(i64::MAX),
+    );
+}
+
+#[test]
+fn check_big_int_as_int_min() {
+    test_expression(
+        "Std.Convert.BigIntAsInt(-9_223_372_036_854_775_808L)",
+        &Value::Int(i64::MIN),
+    );
+}
+
 #[test]
 fn check_bool_array_as_big_int() {
     test_expression(