Repository: microsoft/qsharp
Pull request #2230 (2025-03-13 21:47:32)

Decompose measurement in QIR Base profile processing
This change simplifies the decomposition of `M` and `Measure` in the stdlib to no longer conditionally compile to different versions based on qubit reuse capability, instead offloading that to QIR processing, which already has similar logic.

One effect of this is simulation and circuit generation will match user expectation and patterns in the Q# code rather than more closely showing what occurs on hardware.

github-actions[bot]: _Change in memory usage detected by benchmark._
## Memory Report for 517a0e1a3bfdcb3cd20aa1ec587651807c91fc10

| Test                        | This Branch | On Main  | Difference |
|-----------------------------|-------------|----------| ---------- |
| compile core + standard lib | 19840641 bytes | 19841233 bytes | -592 bytes
github-actions[bot]: _Change in memory usage detected by benchmark._
## Memory Report for 88ad901d1a3fdd9f1737c1366d79d7a9321df61d

| Test                        | This Branch | On Main  | Difference |
|-----------------------------|-------------|----------| ---------- |
| compile core + standard lib | 19840641 bytes | 19841233 bytes | -592 bytes
minestarks: Forgot to add before I signed off: Can we update the UI to remove the target profile information since it's no longer relevant?
swernli: > Forgot to add before I signed off: Can we update the circuit UI to remove the target profile information since it's no longer relevant?

Well, it's no longer relevant for most bits but it could still be relevant for conditionally compiled code from librarys or the user's own code. Since it's not too obtrusive, I'd be in favor of leaving it. But I could be convinced ;).

Patch:
diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs
index ce9f1977..829c073e 100644
--- a/compiler/qsc/src/interpret.rs
+++ b/compiler/qsc/src/interpret.rs
@@ -759,7 +759,6 @@ impl Interpreter {
             sim.chained.finish()
         } else {
             let mut sim = CircuitBuilder::new(CircuitConfig {
-                base_profile: self.capabilities.is_empty(),
                 max_operations: CircuitConfig::DEFAULT_MAX_OPERATIONS,
             });
 
@@ -1004,14 +1003,6 @@ fn sim_circuit_backend() -> BackendChain<SparseSim, CircuitBuilder> {
     BackendChain::new(
         SparseSim::new(),
         CircuitBuilder::new(CircuitConfig {
-            // When using in conjunction with the simulator,
-            // the circuit builder should *not* perform base profile
-            // decompositions, in order to match the simulator's behavior.
-            //
-            // Note that conditional compilation (e.g. @Config(Base) attributes)
-            // will still respect the selected profile. This also
-            // matches the behavior of the simulator.
-            base_profile: false,
             max_operations: CircuitConfig::DEFAULT_MAX_OPERATIONS,
         }),
     )
diff --git a/compiler/qsc_circuit/src/builder.rs b/compiler/qsc_circuit/src/builder.rs
index 9359a408..7dce08b6 100644
--- a/compiler/qsc_circuit/src/builder.rs
+++ b/compiler/qsc_circuit/src/builder.rs
@@ -56,48 +56,34 @@ impl Backend for Builder {
     }
 
     fn m(&mut self, q: usize) -> Self::ResultType {
-        if self.config.base_profile {
-            // defer the measurement and reset the qubit
-            self.remapper.mreset(q)
-        } else {
-            let mapped_q = self.map(q);
-            // In the Circuit schema, result id is per-qubit
-            let res_id = self.num_measurements_for_qubit(mapped_q);
-            let id = self.remapper.m(q);
+        let mapped_q = self.map(q);
+        // In the Circuit schema, result id is per-qubit
+        let res_id = self.num_measurements_for_qubit(mapped_q);
+        let id = self.remapper.m(q);
 
-            self.push_gate(measurement_gate(mapped_q.0, res_id));
-            id
-        }
+        self.push_gate(measurement_gate(mapped_q.0, res_id));
+        id
     }
 
     fn mresetz(&mut self, q: usize) -> Self::ResultType {
-        if self.config.base_profile {
-            // defer the measurement
-            self.remapper.mreset(q)
-        } else {
-            let mapped_q = self.map(q);
-            // In the Circuit schema, result id is per-qubit
-            let res_id = self.num_measurements_for_qubit(mapped_q);
-            // We don't actually need the Remapper since we're not
-            // remapping any qubits, but it's handy for keeping track of measurements
-            let id = self.remapper.m(q);
-
-            // Ideally MResetZ would be atomic but we don't currently have
-            // a way to visually represent that. So decompose it into
-            // a measurement and a reset gate.
-            self.push_gate(measurement_gate(mapped_q.0, res_id));
-            self.push_gate(gate(KET_ZERO, [mapped_q]));
-            id
-        }
+        let mapped_q = self.map(q);
+        // In the Circuit schema, result id is per-qubit
+        let res_id = self.num_measurements_for_qubit(mapped_q);
+        // We don't actually need the Remapper since we're not
+        // remapping any qubits, but it's handy for keeping track of measurements
+        let id = self.remapper.m(q);
+
+        // Ideally MResetZ would be atomic but we don't currently have
+        // a way to visually represent that. So decompose it into
+        // a measurement and a reset gate.
+        self.push_gate(measurement_gate(mapped_q.0, res_id));
+        self.push_gate(gate(KET_ZERO, [mapped_q]));
+        id
     }
 
     fn reset(&mut self, q: usize) {
-        if self.config.base_profile {
-            self.remapper.reset(q);
-        } else {
-            let mapped_q = self.map(q);
-            self.push_gate(gate(KET_ZERO, [mapped_q]));
-        }
+        let mapped_q = self.map(q);
+        self.push_gate(gate(KET_ZERO, [mapped_q]));
     }
 
     fn rx(&mut self, theta: f64, q: usize) {
@@ -267,18 +253,6 @@ impl Builder {
     }
 
     fn finish_circuit(&self, mut circuit: Circuit) -> Circuit {
-        // add deferred measurements
-        if self.config.base_profile {
-            for (qubit, _) in &self.remapper.qubit_measurement_counts {
-                if self.max_ops_exceeded || circuit.operations.len() >= self.config.max_operations {
-                    break;
-                }
-
-                // guaranteed one measurement per qubit, so result is always 0
-                circuit.operations.push(measurement_gate(qubit.0, 0));
-            }
-        }
-
         // add qubit declarations
         for i in 0..self.remapper.num_qubits() {
             let num_measurements = self.num_measurements_for_qubit(WireId(i));
@@ -408,16 +382,6 @@ impl Remapper {
         id
     }
 
-    fn mreset(&mut self, q: usize) -> usize {
-        let id = self.m(q);
-        self.reset(q);
-        id
-    }
-
-    fn reset(&mut self, q: usize) {
-        self.qubit_map.remove(q);
-    }
-
     fn qubit_allocate(&mut self) -> usize {
         let id = self.next_qubit_id;
         self.next_qubit_id += 1;
diff --git a/compiler/qsc_circuit/src/circuit.rs b/compiler/qsc_circuit/src/circuit.rs
index da101293..33507087 100644
--- a/compiler/qsc_circuit/src/circuit.rs
+++ b/compiler/qsc_circuit/src/circuit.rs
@@ -79,8 +79,6 @@ pub struct Qubit {
 
 #[derive(Clone, Debug, Copy, Default)]
 pub struct Config {
-    /// Perform Base Profile decompositions
-    pub base_profile: bool,
     /// Maximum number of operations the builder will add to the circuit
     pub max_operations: usize,
 }
diff --git a/library/std/src/Std/Intrinsic.qs b/library/std/src/Std/Intrinsic.qs
index 949587b6..44a5dd56 100644
--- a/library/std/src/Std/Intrinsic.qs
+++ b/library/std/src/Std/Intrinsic.qs
@@ -265,38 +265,6 @@ operation I(target : Qubit) : Unit is Adj + Ctl {
 /// ```qsharp
 /// Measure([PauliZ], [qubit]);
 /// ```
-@Config(QubitReset)
-operation M(qubit : Qubit) : Result {
-    __quantum__qis__m__body(qubit)
-}
-
-/// # Summary
-/// Performs a measurement of a single qubit in the
-/// Pauli _Z_ basis.
-///
-/// # Input
-/// ## qubit
-/// Qubit to be measured.
-///
-/// # Output
-/// `Zero` if the +1 eigenvalue is observed, and `One` if
-/// the -1 eigenvalue is observed.
-///
-/// # Remarks
-/// The output result is given by
-/// the distribution
-/// $$
-/// \begin{align}
-///     \Pr(\texttt{Zero} | \ket{\psi}) =
-///         \braket{\psi | 0} \braket{0 | \psi}.
-/// \end{align}
-/// $$
-///
-/// Equivalent to:
-/// ```qsharp
-/// Measure([PauliZ], [qubit]);
-/// ```
-@Config(not QubitReset)
 operation M(qubit : Qubit) : Result {
     Measure([PauliZ], [qubit])
 }
@@ -326,7 +294,6 @@ operation M(qubit : Qubit) : Result {
 /// $N$ is the `Length(bases)`.
 /// That is, measurement returns a `Result` $d$ such that the eigenvalue of the
 /// observed measurement effect is $(-1)^d$.
-@Config(QubitReset)
 operation Measure(bases : Pauli[], qubits : Qubit[]) : Result {
     if Length(bases) != Length(qubits) {
         fail "Arrays 'bases' and 'qubits' must be of the same length.";
@@ -350,50 +317,6 @@ operation Measure(bases : Pauli[], qubits : Qubit[]) : Result {
     }
 }
 
-/// # Summary
-/// Performs a joint measurement of one or more qubits in the
-/// specified Pauli bases.
-///
-/// If the basis array and qubit array are different lengths, then the
-/// operation will fail.
-///
-/// # Input
-/// ## bases
-/// Array of single-qubit Pauli values indicating the tensor product
-/// factors on each qubit.
-/// ## qubits
-/// Register of qubits to be measured.
-///
-/// # Output
-/// `Zero` if the +1 eigenvalue is observed, and `One` if
-/// the -1 eigenvalue is observed.
-///
-/// # Remarks
-/// The probability of getting `Zero` is
-/// $\bra{\psi} \frac{I + P_0 \otimes \ldots \otimes P_{N-1}}{2} \ket{\psi}$
-/// where $P_i$ is the $i$-th element of `bases`, and where
-/// $N$ is the `Length(bases)`.
-/// That is, measurement returns a `Result` $d$ such that the eigenvalue of the
-/// observed measurement effect is $(-1)^d$.
-@Config(not QubitReset)
-operation Measure(bases : Pauli[], qubits : Qubit[]) : Result {
-    if Length(bases) != Length(qubits) {
-        fail "Arrays 'bases' and 'qubits' must be of the same length.";
-    }
-    // Because Base Profile does not allow qubit reuse, we always allocate a new qubit
-    // and use entanglement to measure the state while collapsing the original target(s) and
-    // leaving it available for later operations.
-    use aux = Qubit();
-    within {
-        H(aux);
-    } apply {
-        for i in 0..Length(bases) - 1 {
-            EntangleForJointMeasure(bases[i], aux, qubits[i]);
-        }
-    }
-    __quantum__qis__mresetz__body(aux)
-}
-
 /// # Summary
 /// Applies a rotation about the given Pauli axis.
 ///
diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs
index 0ff71024..38d5b650 100644
--- a/pip/src/interpreter.rs
+++ b/pip/src/interpreter.rs
@@ -89,8 +89,8 @@ fn _native<'a>(py: Python<'a>, m: &Bound<'a, PyModule>) -> PyResult<()> {
 }
 
 // This ordering must match the _native.pyi file.
-#[derive(Clone, Copy, PartialEq)]
-#[pyclass(eq, eq_int)]
+#[derive(Clone, Copy, Default, PartialEq)]
+#[pyclass(eq, eq_int, module = "qsharp._native")]
 #[allow(non_camel_case_types)]
 /// A Q# target profile.
 ///
@@ -100,6 +100,7 @@ pub(crate) enum TargetProfile {
     /// Target supports the minimal set of capabilities required to run a quantum program.
     ///
     /// This option maps to the Base Profile as defined by the QIR specification.
+    #[default]
     Base,
     /// Target supports the Adaptive profile with the integer computation extension.
     ///
@@ -122,6 +123,32 @@ pub(crate) enum TargetProfile {
 
 #[pymethods]
 impl TargetProfile {
+    #[new]
+    // We need to define `new` so that instances of `TargetProfile` can be created by Python
+    pub(crate) fn new() -> Self {
+        Self::default()
+    }
+
+    // called and the returned object is pickled as the contents for the instance
+    #[allow(clippy::trivially_copy_pass_by_ref)]
+    fn __getstate__(&self) -> PyResult<isize> {
+        Ok(self.__pyo3__int__())
+    }
+
+    // called with the unpickled state and the instance is updated in place
+    // This is what requires `new` to be implemented as we can't hydrate an
+    // unininitialized instance in Python.
+    fn __setstate__(&mut self, state: i32) -> PyResult<()> {
+        (*self) = match state {
+            0 => Self::Base,
+            1 => Self::Adaptive_RI,
+            2 => Self::Adaptive_RIF,
+            3 => Self::Unrestricted,
+            _ => return Err(PyValueError::new_err("invalid state")),
+        };
+        Ok(())
+    }
+
     #[allow(clippy::trivially_copy_pass_by_ref)]
     fn __str__(&self) -> String {
         Into::<Profile>::into(*self).to_str().to_owned()
@@ -162,8 +189,8 @@ impl From<TargetProfile> for Profile {
 }
 
 // This ordering must match the _native.pyi file.
-#[derive(Clone, Copy, PartialEq)]
-#[pyclass(eq, eq_int)]
+#[derive(Clone, Copy, Default, PartialEq)]
+#[pyclass(eq, eq_int, module = "qsharp._native")]
 #[allow(non_camel_case_types)]
 /// Represents the output semantics for OpenQASM 3 compilation.
 /// Each has implications on the output of the compilation
@@ -173,6 +200,7 @@ pub(crate) enum OutputSemantics {
     /// is all of the classical registers, in reverse order
     /// in which they were added to the circuit with each
     /// bit within each register in reverse order.
+    #[default]
     Qiskit,
     /// [OpenQASM 3 has two output modes](https://openqasm.com/language/directives.html#input-output)
     /// - If the programmer provides one or more `output` declarations, then
@@ -184,6 +212,34 @@ pub(crate) enum OutputSemantics {
     ResourceEstimation,
 }
 
+#[pymethods]
+impl OutputSemantics {
+    #[new]
+    // We need to define `new` so that instances of `TargetProfile` can be created by Python
+    pub(crate) fn new() -> Self {
+        Self::default()
+    }
+
+    // called and the returned object is pickled as the contents for the instance
+    #[allow(clippy::trivially_copy_pass_by_ref)]
+    fn __getstate__(&self) -> PyResult<isize> {
+        Ok(self.__pyo3__int__())
+    }
+
+    // called with the unpickled state and the instance is updated in place
+    // This is what requires `new` to be implemented as we can't hydrate an
+    // unininitialized instance in Python.
+    fn __setstate__(&mut self, state: i32) -> PyResult<()> {
+        (*self) = match state {
+            0 => Self::Qiskit,
+            1 => Self::OpenQasm,
+            2 => Self::ResourceEstimation,
+            _ => return Err(PyValueError::new_err("invalid state")),
+        };
+        Ok(())
+    }
+}
+
 impl From<OutputSemantics> for qsc::qasm3::OutputSemantics {
     fn from(output_semantics: OutputSemantics) -> Self {
         match output_semantics {
@@ -195,8 +251,8 @@ impl From<OutputSemantics> for qsc::qasm3::OutputSemantics {
 }
 
 // This ordering must match the _native.pyi file.
-#[derive(Clone, PartialEq)]
-#[pyclass(eq)]
+#[derive(Clone, Copy, Default, PartialEq)]
+#[pyclass(eq, eq_int, module = "qsharp._native")]
 #[allow(non_camel_case_types)]
 /// Represents the type of compilation output to create
 pub enum ProgramType {
@@ -204,6 +260,7 @@ pub enum ProgramType {
     /// file. Inputs are lifted to the operation params. Output are lifted to
     /// the operation return type. The operation is marked as `@EntryPoint`
     /// as long as there are no input parameters.
+    #[default]
     File,
     /// Programs are compiled to a standalone function. Inputs are lifted to
     /// the operation params. Output are lifted to the operation return type.
@@ -215,6 +272,34 @@ pub enum ProgramType {
     Fragments,
 }
 
+#[pymethods]
+impl ProgramType {
+    #[new]
+    // We need to define `new` so that instances of `TargetProfile` can be created by Python
+    pub(crate) fn new() -> Self {
+        Self::default()
+    }
+
+    // called and the returned object is pickled as the contents for the instance
+    #[allow(clippy::trivially_copy_pass_by_ref)]
+    fn __getstate__(&self) -> PyResult<isize> {
+        Ok(self.__pyo3__int__())
+    }
+
+    // called with the unpickled state and the instance is updated in place
+    // This is what requires `new` to be implemented as we can't hydrate an
+    // unininitialized instance in Python.
+    fn __setstate__(&mut self, state: i32) -> PyResult<()> {
+        (*self) = match state {
+            0 => Self::File,
+            1 => Self::Operation,
+            2 => Self::Fragments,
+            _ => return Err(PyValueError::new_err("invalid state")),
+        };
+        Ok(())
+    }
+}
+
 impl From<ProgramType> for qsc::qasm3::ProgramType {
     fn from(output_semantics: ProgramType) -> Self {
         match output_semantics {
@@ -598,7 +683,7 @@ impl Interpreter {
             source,
             &operation_name,
             &resolver,
-            program_type.clone(),
+            program_type,
             output_semantics,
             false,
         )?;
diff --git a/samples/algorithms/BellState.qs b/samples/algorithms/BellState.qs
index 5af59f54..ec944aaa 100644
--- a/samples/algorithms/BellState.qs
+++ b/samples/algorithms/BellState.qs
@@ -27,7 +27,7 @@ operation Main() : (Result, Result)[] {
         prepare(register);
         Message($"Bell state {label}:");
         DumpMachine();
-        set measurements += [(MResetZ(register[0]), MResetZ(register[1]))];
+        measurements += [(MResetZ(register[0]), MResetZ(register[1]))];
     }
     return measurements;
 }
diff --git a/samples/algorithms/BernsteinVazirani.qs b/samples/algorithms/BernsteinVazirani.qs
index 70877115..4e6da5cb 100644
--- a/samples/algorithms/BernsteinVazirani.qs
+++ b/samples/algorithms/BernsteinVazirani.qs
@@ -46,7 +46,7 @@ operation Main() : Int[] {
         );
 
         Message($"Successfully decoded bit string as int: {decodedInteger}");
-        set decodedIntegers += [decodedInteger];
+        decodedIntegers += [decodedInteger];
     }
 
     return decodedIntegers;
diff --git a/samples/algorithms/DeutschJozsa.qs b/samples/algorithms/DeutschJozsa.qs
index 3c127d67..f41f3b8b 100644
--- a/samples/algorithms/DeutschJozsa.qs
+++ b/samples/algorithms/DeutschJozsa.qs
@@ -43,7 +43,7 @@ operation Main() : (String, Bool)[] {
 
         let isConstantStr = isConstant ? "constant" | "balanced";
         Message($"{name} is {isConstantStr}");
-        set results += [(name, isConstant)];
+        results += [(name, isConstant)];
     }
 
     return results;
@@ -105,7 +105,7 @@ operation DeutschJozsa(Uf : ((Qubit[], Qubit) => Unit), n : Int) : Bool {
     mutable result = true;
     for q in queryRegister {
         if MResetZ(q) == One {
-            set result = false;
+            result = false;
         }
     }
 
diff --git a/samples/algorithms/DotProductViaPhaseEstimation.qs b/samples/algorithms/DotProductViaPhaseEstimation.qs
index 51490713..2c10db93 100644
--- a/samples/algorithms/DotProductViaPhaseEstimation.qs
+++ b/samples/algorithms/DotProductViaPhaseEstimation.qs
@@ -117,7 +117,7 @@ operation IterativePhaseEstimation(
         set MeasureControlReg w/= (Measurements - 1 - index) <- MResetZ(ControlReg);
         if MeasureControlReg[Measurements - 1 - index] == One {
             //Assign bitValue based on previous measurement
-            set bitValue += 2^(index);
+            bitValue += 2^(index);
         }
     }
     return bitValue;
diff --git a/samples/algorithms/HiddenShift.qs b/samples/algorithms/HiddenShift.qs
index ebb7159d..91755632 100644
--- a/samples/algorithms/HiddenShift.qs
+++ b/samples/algorithms/HiddenShift.qs
@@ -33,7 +33,7 @@ operation Main() : Int[] {
         );
         let hiddenShift = ResultArrayAsInt(hiddenShiftBitString);
         Message($"Found {shift} successfully!");
-        set hiddenShifts += [hiddenShift];
+        hiddenShifts += [hiddenShift];
     }
 
     // Note: returned array should match shifts array
diff --git a/samples/algorithms/QRNG.qs b/samples/algorithms/QRNG.qs
index c8f16624..c17e1aac 100644
--- a/samples/algorithms/QRNG.qs
+++ b/samples/algorithms/QRNG.qs
@@ -25,7 +25,7 @@ operation GenerateRandomNumberInRange(max : Int) : Int {
     mutable bits = [];
     let nBits = BitSizeI(max);
     for idxBit in 1..nBits {
-        set bits += [GenerateRandomBit()];
+        bits += [GenerateRandomBit()];
     }
     let sample = ResultArrayAsInt(bits);
 
diff --git a/samples/algorithms/Shor.qs b/samples/algorithms/Shor.qs
index ef3ce407..c5f3c29b 100644
--- a/samples/algorithms/Shor.qs
+++ b/samples/algorithms/Shor.qs
@@ -62,7 +62,7 @@ operation FactorSemiprimeInteger(number : Int) : (Int, Int) {
 
             // Set the flag and factors values if the continued
             // fractions classical algorithm succeeds.
-            set (foundFactors, factors) = MaybeFactorsFromPeriod(number, generator, period);
+            (foundFactors, factors) = MaybeFactorsFromPeriod(number, generator, period);
         }
         // In this case, we guessed a divisor by accident.
         else {
@@ -72,10 +72,10 @@ operation FactorSemiprimeInteger(number : Int) : (Int, Int) {
 
             // Set the flag `foundFactors` to true, indicating that we
             // succeeded in finding factors.
-            set foundFactors = true;
-            set factors = (gcd, number / gcd);
+            foundFactors = true;
+            factors = (gcd, number / gcd);
         }
-        set attempt = attempt + 1;
+        attempt = attempt + 1;
         if (attempt > 100) {
             fail "Failed to find factors: too many attempts!";
         }
@@ -283,7 +283,7 @@ operation EstimateFrequency(generator : Int, modulus : Int, bitsize : Int) : Int
         H(c);
         if M(c) == One {
             X(c); // Reset
-            set frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
+            frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
         }
     }
 
diff --git a/samples/algorithms/Teleportation.qs b/samples/algorithms/Teleportation.qs
index 007f9789..5beb71ea 100644
--- a/samples/algorithms/Teleportation.qs
+++ b/samples/algorithms/Teleportation.qs
@@ -42,7 +42,7 @@ operation Main() : Result[] {
         // Measure target in the corresponding basis and reset the qubits to
         // continue teleporting more messages.
         let result = Measure([basis], [target]);
-        set results += [result];
+        results += [result];
         ResetAll([message, target]);
     }
 
diff --git a/samples/algorithms/ThreeQubitRepetitionCode.qs b/samples/algorithms/ThreeQubitRepetitionCode.qs
index fb71828f..882dd224 100644
--- a/samples/algorithms/ThreeQubitRepetitionCode.qs
+++ b/samples/algorithms/ThreeQubitRepetitionCode.qs
@@ -30,7 +30,7 @@ operation Main() : (Bool, Int) {
             let (parity01, parity12) = MeasureBitFlipSyndrome(encodedRegister);
             let bitFlipReverted = RevertBitFlip(encodedRegister, parity01, parity12);
             if (bitFlipReverted) {
-                set bitFlipCount += 1;
+                bitFlipCount += 1;
             }
         }
     }
@@ -82,7 +82,7 @@ operation RevertBitFlip(register : Qubit[], parity01 : Result, parity12 : Result
         if parity12 == One {
             X(register[2]);
         } else {
-            set result = false;
+            result = false;
         }
     }
     return result;
diff --git a/samples/estimation/ShorRE.qs b/samples/estimation/ShorRE.qs
index 6804a888..73e579d9 100644
--- a/samples/estimation/ShorRE.qs
+++ b/samples/estimation/ShorRE.qs
@@ -79,7 +79,7 @@ operation EstimateFrequency(
             R1Frac(frequencyEstimate, bitsPrecision - 1 - idx, c);
         }
         if MResetZ(c) == One {
-            set frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
+            frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
         }
     }
 
diff --git a/samples/language/Range.qs b/samples/language/Range.qs
index afea53dc..5dbbd4b8 100644
--- a/samples/language/Range.qs
+++ b/samples/language/Range.qs
@@ -42,7 +42,7 @@ function Main() : Range {
     // The array [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100].
     mutable array = [];
     for i in 0..10 {
-        set array += [i^2];
+        array += [i^2];
     }
     Message($"Array: {array}");
 
diff --git a/samples/language/RepeatUntilLoops.qs b/samples/language/RepeatUntilLoops.qs
index e3b1f9f4..00faea39 100644
--- a/samples/language/RepeatUntilLoops.qs
+++ b/samples/language/RepeatUntilLoops.qs
@@ -12,7 +12,7 @@ operation Main() : Unit {
     // Repeat-Until loop
     mutable x = 0;
     repeat {
-        set x += 1;
+        x += 1;
     } until x > 3;
 
     // Repeat-Until loop with fixup
diff --git a/samples/language/Variables.qs b/samples/language/Variables.qs
index 8e93d59f..d6a12c1e 100644
--- a/samples/language/Variables.qs
+++ b/samples/language/Variables.qs
@@ -13,10 +13,13 @@ function Main() : Unit {
     mutable mutableInt = 43;
     Message($"Mutable Int: {mutableInt}");
 
-    // Mutable variables can be mutated with the `set` keyword:
-    set mutableInt -= 1;
+    // Mutable variables can be mutated or reassigned.
+    mutableInt -= 1;
     Message($"Mutable Int after mutation: {mutableInt}");
 
+    mutableInt = 10;
+    Message($"Mutable Int after reassignment: {mutableInt}");
+
     // This is not mutation, rather, this is declaring a new variable
     // entirely.
     let immutableInt = 43;
@@ -30,7 +33,7 @@ function Main() : Unit {
 
     // The below line copies `point`, moves the X coordinate by +1.0,
     // and reassign the new `Point3d` to `point`.
-    set point = new Point3d { ...point, X = point.X + 1.0 };
+    point = new Point3d { ...point, X = point.X + 1.0 };
 
 }
 
diff --git a/samples/language/WhileLoops.qs b/samples/language/WhileLoops.qs
index a082b5b1..0f8b6cdf 100644
--- a/samples/language/WhileLoops.qs
+++ b/samples/language/WhileLoops.qs
@@ -10,6 +10,6 @@
 function Main() : Unit {
     mutable x = 0;
     while x < 3 {
-        set x += 1;
+        x += 1;
     }
 }
diff --git a/vscode/package.json b/vscode/package.json
index 0e59642e..76d82c19 100644
--- a/vscode/package.json
+++ b/vscode/package.json
@@ -240,6 +240,11 @@
           "command": "qsharp-vscode.workspacesAdd",
           "when": "view == quantum-workspaces",
           "group": "navigation"
+        },
+        {
+          "command": "qsharp-vscode.copilotClear",
+          "when": "view == quantum-copilot",
+          "group": "navigation"
         }
       ],
       "view/item/context": [
@@ -386,6 +391,13 @@
         "title": "Remove workspace connection",
         "icon": "$(remove)"
       },
+      {
+        "command": "qsharp-vscode.copilotClear",
+        "category": "Q#",
+        "title": "Clear Quantum Copilot chat",
+        "enablement": "!qdkCopilotIsBusy",
+        "icon": "$(clear-all)"
+      },
       {
         "command": "qsharp-vscode.workspacePythonCode",
         "category": "Q#",
diff --git a/vscode/src/copilot/copilot.ts b/vscode/src/copilot/copilot.ts
index 16f9754c..d0428e35 100644
--- a/vscode/src/copilot/copilot.ts
+++ b/vscode/src/copilot/copilot.ts
@@ -21,6 +21,7 @@ import { OpenAIChatService } from "./openAiChatService";
 import { getRandomGuid } from "../utils";
 import { EventType, sendTelemetryEvent, UserFlowStatus } from "../telemetry";
 import { knownToolNameOrDefault } from "./azqTools";
+import { commands } from "vscode";
 
 export class Copilot {
   private service: IChatService;
@@ -59,7 +60,14 @@ export class Copilot {
     }
     this.messages.push({ role: "user", content: userMessage });
 
-    await this.completeChat();
+    // Set the context so the 'clear' command is disabled when processing a response.
+    await commands.executeCommand("setContext", `qdkCopilotIsBusy`, true);
+    try {
+      await this.completeChat();
+    } finally {
+      // Reset the busy context when the chat is done.
+      await commands.executeCommand("setContext", `qdkCopilotIsBusy`, false);
+    }
   }
 
   /**
diff --git a/vscode/src/copilot/webview/copilot.css b/vscode/src/copilot/webview/copilot.css
index f9252e43..803e13b9 100644
--- a/vscode/src/copilot/webview/copilot.css
+++ b/vscode/src/copilot/webview/copilot.css
@@ -4,8 +4,8 @@
 body {
   padding: 12px;
   height: 100%;
-  /*  match the native vscode chat style */
-  font-family: "Segoe WPC", "Segoe UI", "sans-serif";
+  /*  system-ui works great on all platforms and browsers these days */
+  font-family: system-ui;
   font-size: 13px;
   background-color: var(--vscode-chat-list-background);
 }
diff --git a/vscode/src/copilot/webviewViewProvider.ts b/vscode/src/copilot/webviewViewProvider.ts
index 305ec265..dc61c30e 100644
--- a/vscode/src/copilot/webviewViewProvider.ts
+++ b/vscode/src/copilot/webviewViewProvider.ts
@@ -3,6 +3,7 @@
 
 import { log } from "qsharp-lang";
 import {
+  commands,
   CancellationToken,
   ExtensionContext,
   Uri,
@@ -13,6 +14,7 @@ import {
 } from "vscode";
 import { Copilot, CopilotUpdateHandler } from "./copilot";
 import { CopilotCommand } from "./shared";
+import { qsharpExtensionId } from "../common";
 
 export function registerCopilotPanel(context: ExtensionContext): void {
   const provider = new CopilotWebviewViewProvider(context.extensionUri);
@@ -25,6 +27,11 @@ export function registerCopilotPanel(context: ExtensionContext): void {
       },
     ),
   );
+  context.subscriptions.push(
+    commands.registerCommand(`${qsharpExtensionId}.copilotClear`, async () => {
+      provider.clearChat();
+    }),
+  );
 }
 
 class CopilotWebviewViewProvider implements WebviewViewProvider {
@@ -99,6 +106,10 @@ class CopilotWebviewViewProvider implements WebviewViewProvider {
     }
   }
 
+  clearChat() {
+    this.copilot?.restartChat([]);
+  }
+
   handleMessageFromWebview(message: CopilotCommand) {
     if (!this.copilot) {
       return;


Test patch:
diff --git a/compiler/qsc/src/interpret/circuit_tests.rs b/compiler/qsc/src/interpret/circuit_tests.rs
index 53bb19d4..0baba2e5 100644
--- a/compiler/qsc/src/interpret/circuit_tests.rs
+++ b/compiler/qsc/src/interpret/circuit_tests.rs
@@ -202,11 +202,10 @@ fn m_base_profile() {
         .circuit(CircuitEntryPoint::EntryPoint, false)
         .expect("circuit generation should succeed");
 
-    expect![[r"
-        q_0    ── H ──── Z ────────────────
-        q_1    ── H ──── ● ──── H ──── M ──
-                                       ╘═══
-    "]]
+    expect![[r#"
+        q_0    ── H ──── M ──
+                         ╘═══
+    "#]]
     .assert_eq(&circ.to_string());
 }
 
@@ -287,10 +286,10 @@ fn mresetz_base_profile() {
         .circuit(CircuitEntryPoint::EntryPoint, false)
         .expect("circuit generation should succeed");
 
-    expect![[r"
-        q_0    ── H ──── M ──
-                         ╘═══
-    "]]
+    expect![[r#"
+        q_0    ── H ──── M ──── |0〉 ──
+                         ╘════════════
+    "#]]
     .assert_eq(&circ.to_string());
 }
 
@@ -597,14 +596,12 @@ fn operation_with_qubits_base_profile() {
         .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
         .expect("circuit generation should succeed");
 
-    expect![[r"
-        q_0    ── H ──── ● ──── Z ──────────────────────────────
-        q_1    ───────── X ─────┼──────────── Z ────────────────
-        q_2    ── H ─────────── ● ──── H ─────┼───── M ─────────
-                                              │      ╘══════════
-        q_3    ── H ───────────────────────── ● ──── H ──── M ──
-                                                            ╘═══
-    "]]
+    expect![[r#"
+        q_0    ── H ──── ● ──── M ──
+                         │      ╘═══
+        q_1    ───────── X ──── M ──
+                                ╘═══
+    "#]]
     .assert_eq(&circ.to_string());
 }
 
@@ -1067,33 +1064,22 @@ mod debugger_stepping {
             Profile::Base,
         );
 
-        // Surprising but expected: Reset gates would *not* normally
-        // be generated in Base Profile, but they are here, since
-        // when running in tandem with the simulator, the resulting
-        // circuit is intended to match the calls into the simulator.
-        //
-        // Note the circuit still looks different than what would be
-        // generated in Unrestricted Profile for the same code,
-        // due to conditional compilation in the standard library.
-        expect![["
+        expect![[r#"
             step:
             step:
             q_0
             step:
             q_0    ── H ──
             step:
-            q_0    ── H ──── Z ─────────────────────────
-            q_1    ── H ──── ● ──── H ──── M ──── |0〉 ──
-                                           ╘════════════
+            q_0    ── H ──── M ──
+                             ╘═══
             step:
-            q_0    ── H ──── Z ──── |0〉 ──────────────────
-            q_1    ── H ──── ● ───── H ───── M ──── |0〉 ──
-                                             ╘════════════
+            q_0    ── H ──── M ──── |0〉 ──
+                             ╘════════════
             step:
-            q_0    ── H ──── Z ──── |0〉 ──────────────────
-            q_1    ── H ──── ● ───── H ───── M ──── |0〉 ──
-                                             ╘════════════
-        "]]
+            q_0    ── H ──── M ──── |0〉 ──
+                             ╘════════════
+        "#]]
         .assert_eq(&circs);
     }
 
diff --git a/compiler/qsc/src/interpret/tests.rs b/compiler/qsc/src/interpret/tests.rs
index 86fa3771..99942ba5 100644
--- a/compiler/qsc/src/interpret/tests.rs
+++ b/compiler/qsc/src/interpret/tests.rs
@@ -884,21 +884,17 @@ mod given_interpreter {
 
                 define void @ENTRYPOINT__main() #0 {
                 block_0:
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+                  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
+                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
                   call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
                   ret void
                 }
 
-                declare void @__quantum__qis__h__body(%Qubit*)
-
-                declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
+                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
                 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
-                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+                declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
                 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="1" }
                 attributes #1 = { "irreversible" }
@@ -1083,21 +1079,17 @@ mod given_interpreter {
 
                 define void @ENTRYPOINT__main() #0 {
                 block_0:
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+                  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
+                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
                   call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
                   ret void
                 }
 
-                declare void @__quantum__qis__h__body(%Qubit*)
-
-                declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
+                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
                 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
-                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+                declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
                 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="1" }
                 attributes #1 = { "irreversible" }
@@ -1131,21 +1123,17 @@ mod given_interpreter {
 
                 define void @ENTRYPOINT__main() #0 {
                 block_0:
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+                  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
+                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
                   call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
                   ret void
                 }
 
-                declare void @__quantum__qis__h__body(%Qubit*)
-
-                declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
+                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
                 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
-                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+                declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
                 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="1" }
                 attributes #1 = { "irreversible" }
@@ -1211,21 +1199,17 @@ mod given_interpreter {
 
                 define void @ENTRYPOINT__main() #0 {
                 block_0:
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+                  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
+                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
                   call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
                   ret void
                 }
 
-                declare void @__quantum__qis__h__body(%Qubit*)
-
-                declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
+                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
                 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
-                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+                declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
                 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="1" }
                 attributes #1 = { "irreversible" }
@@ -1271,21 +1255,17 @@ mod given_interpreter {
 
                 define void @ENTRYPOINT__main() #0 {
                 block_0:
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
-                  call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
-                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+                  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
+                  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
                   call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
                   ret void
                 }
 
-                declare void @__quantum__qis__h__body(%Qubit*)
-
-                declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
+                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
                 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
-                declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+                declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
                 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="1" }
                 attributes #1 = { "irreversible" }
diff --git a/compiler/qsc_circuit/src/builder/tests.rs b/compiler/qsc_circuit/src/builder/tests.rs
index 52636374..03f1bf09 100644
--- a/compiler/qsc_circuit/src/builder/tests.rs
+++ b/compiler/qsc_circuit/src/builder/tests.rs
@@ -6,10 +6,7 @@ use expect_test::expect;
 
 #[test]
 fn exceed_max_operations() {
-    let mut builder = Builder::new(Config {
-        base_profile: false,
-        max_operations: 2,
-    });
+    let mut builder = Builder::new(Config { max_operations: 2 });
 
     let q = builder.qubit_allocate();
 
@@ -31,10 +28,7 @@ fn exceed_max_operations() {
 
 #[test]
 fn exceed_max_operations_deferred_measurements() {
-    let mut builder = Builder::new(Config {
-        base_profile: true, // deferred measurements
-        max_operations: 2,
-    });
+    let mut builder = Builder::new(Config { max_operations: 2 });
 
     let q = builder.qubit_allocate();
 
@@ -48,11 +42,10 @@ fn exceed_max_operations_deferred_measurements() {
 
     // The current behavior is to silently truncate the circuit
     // if it exceeds the maximum allowed number of operations.
-    // The measurement will be dropped.
+    // The second X will be dropped.
     expect![[r#"
-        q_0    ── X ──
-
-        q_1    ── X ──
+        q_0    ── X ──── M ──
+                         ╘═══
     "#]]
     .assert_eq(&circuit.to_string());
 }
diff --git a/pip/tests-integration/test_base_qir.py b/pip/tests-integration/test_base_qir.py
index fd06879f..13d4649a 100644
--- a/pip/tests-integration/test_base_qir.py
+++ b/pip/tests-integration/test_base_qir.py
@@ -32,22 +32,23 @@ def test_compile_qir_input_data() -> None:
     qir = operation._repr_qir_()
     assert isinstance(qir, bytes)
     module = Module.from_ir(Context(), qir.decode(), "module")
-    assert len(module.functions) == 5
+    assert len(module.functions) == 3
     assert module.functions[0].name == "ENTRYPOINT__main"
     func = module.functions[0]
     assert len(func.basic_blocks) == 1
-    assert len(func.basic_blocks[0].instructions) == 6
+    assert len(func.basic_blocks[0].instructions) == 3
     call_m = func.basic_blocks[0].instructions[0]
     assert isinstance(call_m, Call)
-    assert call_m.callee.name == "__quantum__qis__h__body"
-    assert len(call_m.args) == 1
-    assert qubit_id(call_m.args[0]) == 1
-    record_res = func.basic_blocks[0].instructions[4]
+    assert call_m.callee.name == "__quantum__qis__m__body"
+    assert len(call_m.args) == 2
+    assert qubit_id(call_m.args[0]) == 0
+    assert result_id(call_m.args[1]) == 0
+    record_res = func.basic_blocks[0].instructions[1]
     assert isinstance(record_res, Call)
     assert len(record_res.args) == 2
     assert record_res.callee.name == "__quantum__rt__result_record_output"
     assert result_id(record_res.args[0]) == 0
-    assert func.basic_blocks[0].instructions[5].opcode == Opcode.RET
+    assert func.basic_blocks[0].instructions[2].opcode == Opcode.RET
 
 
 @pytest.mark.skipif(not PYQIR_AVAILABLE, reason=SKIP_REASON)
@@ -87,7 +88,7 @@ def test_compile_qir_all_gates() -> None:
     assert module.functions[0].name == "ENTRYPOINT__main"
     func = module.functions[0]
     assert len(func.basic_blocks) == 1
-    assert len(func.basic_blocks[0].instructions) == 28
+    assert len(func.basic_blocks[0].instructions) == 26
 
     def check_call(i: int, name: str, num_args: int) -> None:
         call = func.basic_blocks[0].instructions[i]
@@ -114,14 +115,12 @@ def test_compile_qir_all_gates() -> None:
     check_call(16, "__quantum__qis__y__body", 1)
     check_call(17, "__quantum__qis__z__body", 1)
     check_call(18, "__quantum__qis__swap__body", 2)
-    check_call(19, "__quantum__qis__h__body", 1)
-    check_call(20, "__quantum__qis__cz__body", 2)
-    check_call(21, "__quantum__qis__h__body", 1)
-    check_call(22, "__quantum__qis__m__body", 2)
-    check_call(23, "__quantum__qis__m__body", 2)
-    check_call(24, "__quantum__rt__tuple_record_output", 2)
-    check_call(25, "__quantum__rt__result_record_output", 2)
-    check_call(26, "__quantum__rt__result_record_output", 2)
+    check_call(19, "__quantum__qis__cx__body", 2)
+    check_call(20, "__quantum__qis__m__body", 2)
+    check_call(21, "__quantum__qis__m__body", 2)
+    check_call(22, "__quantum__rt__tuple_record_output", 2)
+    check_call(23, "__quantum__rt__result_record_output", 2)
+    check_call(24, "__quantum__rt__result_record_output", 2)
 
     assert required_num_qubits(module.functions[0]) == 5
     assert required_num_results(module.functions[0]) == 2
diff --git a/pip/tests/test_enums.py b/pip/tests/test_enums.py
new file mode 100644
index 00000000..839075b3
--- /dev/null
+++ b/pip/tests/test_enums.py
@@ -0,0 +1,106 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+from textwrap import dedent
+import pytest
+import qsharp
+import qsharp.code
+import qsharp.utils
+from contextlib import redirect_stdout
+import io
+
+from qsharp import TargetProfile
+# pull in from native module for tests so that we don't have to install qiskit
+# using the interop module
+from qsharp._native import OutputSemantics, ProgramType
+
+
+def test_target_profile_int_values_match_enum_values() -> None:
+    assert 0 == TargetProfile.Base
+    assert 1 == TargetProfile.Adaptive_RI
+    assert 2 == TargetProfile.Adaptive_RIF
+    assert 3 == TargetProfile.Unrestricted
+
+
+def test_target_profile_serialization() -> None:
+    input = [
+        TargetProfile.Base,
+        TargetProfile.Adaptive_RI,
+        TargetProfile.Adaptive_RIF,
+        TargetProfile.Unrestricted,
+    ]
+    import pickle
+
+    ser = pickle.dumps(input)
+    de = pickle.loads(ser)
+    assert de == input
+
+
+def test_target_profile_str_values_match_enum_values() -> None:
+    target_profile = TargetProfile.Base
+    str_value = str(target_profile)
+    assert str_value == "Base"
+    target_profile = TargetProfile.Adaptive_RI
+    str_value = str(target_profile)
+    assert str_value == "Adaptive_RI"
+    target_profile = TargetProfile.Adaptive_RIF
+    str_value = str(target_profile)
+    assert str_value == "Adaptive_RIF"
+    target_profile = TargetProfile.Unrestricted
+    str_value = str(target_profile)
+    assert str_value == "Unrestricted"
+
+
+def test_target_profile_from_str_match_enum_values() -> None:
+    target_profile = TargetProfile.Base
+    str_value = str(target_profile)
+    assert TargetProfile.from_str(str_value) == target_profile
+    target_profile = TargetProfile.Adaptive_RI
+    str_value = str(target_profile)
+    assert TargetProfile.from_str(str_value) == target_profile
+    target_profile = TargetProfile.Adaptive_RIF
+    str_value = str(target_profile)
+    assert TargetProfile.from_str(str_value) == target_profile
+    target_profile = TargetProfile.Unrestricted
+    str_value = str(target_profile)
+    assert TargetProfile.from_str(str_value) == target_profile
+    with pytest.raises(ValueError):
+        TargetProfile.from_str("Invalid")
+
+
+def test_output_semantics_int_values_match_enum_values() -> None:
+    assert 0 == OutputSemantics.Qiskit
+    assert 1 == OutputSemantics.OpenQasm
+    assert 2 == OutputSemantics.ResourceEstimation
+
+
+def test_output_semantics_serialization() -> None:
+    input = [
+        OutputSemantics.Qiskit,
+        OutputSemantics.OpenQasm,
+        OutputSemantics.ResourceEstimation,
+    ]
+    import pickle
+
+    ser = pickle.dumps(input)
+    de = pickle.loads(ser)
+    assert de == input
+
+
+def test_program_type_int_values_match_enum_values() -> None:
+    assert 0 == ProgramType.File
+    assert 1 == ProgramType.Operation
+    assert 2 == ProgramType.Fragments
+
+
+def test_program_type_serialization() -> None:
+    input = [
+        ProgramType.File,
+        ProgramType.Operation,
+        ProgramType.Fragments,
+    ]
+    import pickle
+
+    ser = pickle.dumps(input)
+    de = pickle.loads(ser)
+    assert de == input
diff --git a/pip/tests/test_qsharp.py b/pip/tests/test_qsharp.py
index 233caadd..7590c944 100644
--- a/pip/tests/test_qsharp.py
+++ b/pip/tests/test_qsharp.py
@@ -498,38 +498,6 @@ def test_run_with_invalid_shots_produces_error() -> None:
         assert False
 
 
-def test_target_profile_str_values_match_enum_values() -> None:
-    target_profile = qsharp.TargetProfile.Base
-    str_value = str(target_profile)
-    assert str_value == "Base"
-    target_profile = qsharp.TargetProfile.Adaptive_RI
-    str_value = str(target_profile)
-    assert str_value == "Adaptive_RI"
-    target_profile = qsharp.TargetProfile.Adaptive_RIF
-    str_value = str(target_profile)
-    assert str_value == "Adaptive_RIF"
-    target_profile = qsharp.TargetProfile.Unrestricted
-    str_value = str(target_profile)
-    assert str_value == "Unrestricted"
-
-
-def test_target_profile_from_str_match_enum_values() -> None:
-    target_profile = qsharp.TargetProfile.Base
-    str_value = str(target_profile)
-    assert qsharp.TargetProfile.from_str(str_value) == target_profile
-    target_profile = qsharp.TargetProfile.Adaptive_RI
-    str_value = str(target_profile)
-    assert qsharp.TargetProfile.from_str(str_value) == target_profile
-    target_profile = qsharp.TargetProfile.Adaptive_RIF
-    str_value = str(target_profile)
-    assert qsharp.TargetProfile.from_str(str_value) == target_profile
-    target_profile = qsharp.TargetProfile.Unrestricted
-    str_value = str(target_profile)
-    assert qsharp.TargetProfile.from_str(str_value) == target_profile
-    with pytest.raises(ValueError):
-        qsharp.TargetProfile.from_str("Invalid")
-
-
 def test_callables_exposed_into_env() -> None:
     qsharp.init()
     qsharp.eval("function Four() : Int { 4 }")
diff --git a/samples_test/src/tests/language.rs b/samples_test/src/tests/language.rs
index 4cd55b69..04773b6b 100644
--- a/samples_test/src/tests/language.rs
+++ b/samples_test/src/tests/language.rs
@@ -334,12 +334,14 @@ pub const VARIABLES_EXPECT: Expect = expect![[r#"
     Immutable Int: 42
     Mutable Int: 43
     Mutable Int after mutation: 42
+    Mutable Int after reassignment: 10
     Shadowed Immutable Int: 0
     ()"#]];
 pub const VARIABLES_EXPECT_DEBUG: Expect = expect![[r#"
     Immutable Int: 42
     Mutable Int: 43
     Mutable Int after mutation: 42
+    Mutable Int after reassignment: 10
     Shadowed Immutable Int: 0
     ()"#]];
 pub const WHILELOOPS_EXPECT: Expect = expect!["()"];