Repository: microsoft/qsharp
Pull request #1972 (2024-10-22 23:28:02)

Initial Python telemetry
Python feature usage telemetry.


Patch:
diff --git a/compiler/qsc_codegen/src/qir.rs b/compiler/qsc_codegen/src/qir.rs
index 45f7c04f..e80fcc06 100644
--- a/compiler/qsc_codegen/src/qir.rs
+++ b/compiler/qsc_codegen/src/qir.rs
@@ -504,8 +504,11 @@ impl ToQir<String> for rir::Callable {
             return format!(
                 "declare {output_type} @{}({input_type}){}",
                 self.name,
-                if self.call_type == rir::CallableType::Measurement {
-                    // Measurement callables are a special case that needs the irreversable attribute.
+                if matches!(
+                    self.call_type,
+                    rir::CallableType::Measurement | rir::CallableType::Reset
+                ) {
+                    // These callables are a special case that need the irreversable attribute.
                     " #1"
                 } else {
                     ""
diff --git a/compiler/qsc_doc_gen/src/display.rs b/compiler/qsc_doc_gen/src/display.rs
index 4a8b1d67..f579b40d 100644
--- a/compiler/qsc_doc_gen/src/display.rs
+++ b/compiler/qsc_doc_gen/src/display.rs
@@ -158,7 +158,7 @@ impl HirCallableDecl<'_> {
     fn get_param_offset(&self) -> u32 {
         let offset = match self.decl.kind {
             hir::CallableKind::Function => "function".len(),
-            hir::CallableKind::Operation | hir::CallableKind::Measurement => "operation".len(),
+            hir::CallableKind::Operation => "operation".len(),
         } + 1 // this is for the space between keyword and name
         + self.decl.name.name.len()
         + display_type_params(&self.decl.generics).len();
@@ -172,7 +172,7 @@ impl Display for HirCallableDecl<'_> {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
         let kind = match self.decl.kind {
             hir::CallableKind::Function => "function",
-            hir::CallableKind::Operation | hir::CallableKind::Measurement => "operation",
+            hir::CallableKind::Operation => "operation",
         };
 
         write!(f, "{} {}", kind, self.decl.name.name)?;
diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs
index f3f6188a..68f8f7b4 100644
--- a/compiler/qsc_doc_gen/src/generate_docs.rs
+++ b/compiler/qsc_doc_gen/src/generate_docs.rs
@@ -350,6 +350,18 @@ enum PackageKind {
     Core,
 }
 
+impl Display for PackageKind {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+        let s = match &self {
+            PackageKind::UserCode => "__Main__",
+            PackageKind::AliasedPackage(alias) => alias,
+            PackageKind::StandardLibrary => "__Std__",
+            PackageKind::Core => "__Core__",
+        };
+        write!(f, "{s}")
+    }
+}
+
 impl Display for Metadata {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
         let kind = match &self.kind {
@@ -366,11 +378,19 @@ title: {}
 ms.date: {{TIMESTAMP}}
 ms.topic: {}
 qsharp.kind: {}
+qsharp.package: {}
 qsharp.namespace: {}
 qsharp.name: {}
 qsharp.summary: \"{}\"
 ---",
-            self.uid, self.title, self.topic, kind, self.namespace, self.name, self.summary
+            self.uid,
+            self.title,
+            self.topic,
+            kind,
+            self.package,
+            self.namespace,
+            self.name,
+            self.summary
         )
     }
 }
@@ -406,7 +426,7 @@ fn get_metadata(
             display.hir_callable_decl(decl).to_string(),
             match &decl.kind {
                 CallableKind::Function => MetadataKind::Function,
-                CallableKind::Operation | CallableKind::Measurement => MetadataKind::Operation,
+                CallableKind::Operation => MetadataKind::Operation,
             },
         )),
         ItemKind::Ty(ident, udt) => Some((
diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs
index 844976dd..0da9c148 100644
--- a/compiler/qsc_fir/src/fir.rs
+++ b/compiler/qsc_fir/src/fir.rs
@@ -756,6 +756,8 @@ pub struct CallableDecl {
     pub functors: FunctorSetValue,
     /// The callable implementation.
     pub implementation: CallableImpl,
+    /// The attributes of the callable, (e.g.: Measurement or Reset).
+    pub attrs: Vec<Attr>,
 }
 
 impl CallableDecl {
@@ -1521,6 +1523,8 @@ pub enum Attr {
     EntryPoint,
     /// Indicates that a callable is a measurement.
     Measurement,
+    /// Indicates that a callable is a reset.
+    Reset,
 }
 
 /// A field.
@@ -1591,15 +1595,13 @@ pub enum CallableKind {
     Function,
     /// An operation.
     Operation,
-    /// A measurement.
-    Measurement,
 }
 
 impl Display for CallableKind {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         match self {
             CallableKind::Function => f.write_str("function"),
-            CallableKind::Operation | CallableKind::Measurement => f.write_str("operation"),
+            CallableKind::Operation => f.write_str("operation"),
         }
     }
 }
diff --git a/compiler/qsc_fir/src/ty.rs b/compiler/qsc_fir/src/ty.rs
index 2e70685d..831a3e6b 100644
--- a/compiler/qsc_fir/src/ty.rs
+++ b/compiler/qsc_fir/src/ty.rs
@@ -259,7 +259,7 @@ impl Display for Arrow {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         let arrow = match self.kind {
             CallableKind::Function => "->",
-            CallableKind::Operation | CallableKind::Measurement => "=>",
+            CallableKind::Operation => "=>",
         };
         write!(f, "({} {arrow} {}", self.input, self.output)?;
         if self.functors != FunctorSet::Value(FunctorSetValue::Empty) {
diff --git a/compiler/qsc_frontend/src/closure.rs b/compiler/qsc_frontend/src/closure.rs
index b24d31a0..62bd69b1 100644
--- a/compiler/qsc_frontend/src/closure.rs
+++ b/compiler/qsc_frontend/src/closure.rs
@@ -151,6 +151,7 @@ pub(super) fn lift(
         adj: None,
         ctl: None,
         ctl_adj: None,
+        attrs: Vec::default(),
     };
 
     (free_vars, callable)
diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs
index 2b881027..788f2ec1 100644
--- a/compiler/qsc_frontend/src/lower.rs
+++ b/compiler/qsc_frontend/src/lower.rs
@@ -30,10 +30,10 @@ pub(super) enum Error {
     #[error("invalid attribute arguments: expected {0}")]
     #[diagnostic(code("Qsc.LowerAst.InvalidAttrArgs"))]
     InvalidAttrArgs(String, #[label] Span),
-    #[error("invalid use of the Measurement attribute on a function")]
+    #[error("invalid use of the {0} attribute on a function")]
     #[diagnostic(help("try declaring the callable as an operation"))]
-    #[diagnostic(code("Qsc.LowerAst.InvalidMeasurementAttrOnFunction"))]
-    InvalidMeasurementAttrOnFunction(#[label] Span),
+    #[diagnostic(code("Qsc.LowerAst.InvalidAttrOnFunction"))]
+    InvalidAttrOnFunction(String, #[label] Span),
     #[error("missing callable body")]
     #[diagnostic(code("Qsc.LowerAst.MissingBody"))]
     MissingBody(#[label] Span),
@@ -368,6 +368,15 @@ impl With<'_> {
                     None
                 }
             },
+            Ok(hir::Attr::Reset) => match &*attr.arg.kind {
+                ast::ExprKind::Tuple(args) if args.is_empty() => Some(hir::Attr::Reset),
+                _ => {
+                    self.lowerer
+                        .errors
+                        .push(Error::InvalidAttrArgs("()".to_string(), attr.arg.span));
+                    None
+                }
+            },
             Err(()) => {
                 self.lowerer.errors.push(Error::UnknownAttr(
                     attr.name.name.to_string(),
@@ -429,30 +438,35 @@ impl With<'_> {
             adj,
             ctl,
             ctl_adj,
+            attrs: attrs.to_vec(),
+        }
+    }
+
+    fn check_invalid_attrs_on_function(&mut self, attrs: &[hir::Attr], span: Span) {
+        const INVALID_ATTRS: [hir::Attr; 2] = [hir::Attr::Measurement, hir::Attr::Reset];
+
+        for invalid_attr in &INVALID_ATTRS {
+            if attrs.contains(invalid_attr) {
+                self.lowerer.errors.push(Error::InvalidAttrOnFunction(
+                    format!("{invalid_attr:?}"),
+                    span,
+                ));
+            }
         }
     }
 
     fn lower_callable_kind(
         &mut self,
         kind: ast::CallableKind,
-        attrs: &[qsc_hir::hir::Attr],
+        attrs: &[hir::Attr],
         span: Span,
     ) -> hir::CallableKind {
-        if attrs.contains(&qsc_hir::hir::Attr::Measurement) {
-            match kind {
-                ast::CallableKind::Operation => hir::CallableKind::Measurement,
-                ast::CallableKind::Function => {
-                    self.lowerer
-                        .errors
-                        .push(Error::InvalidMeasurementAttrOnFunction(span));
-                    hir::CallableKind::Function
-                }
-            }
-        } else {
-            match kind {
-                ast::CallableKind::Operation => hir::CallableKind::Operation,
-                ast::CallableKind::Function => hir::CallableKind::Function,
+        match kind {
+            ast::CallableKind::Function => {
+                self.check_invalid_attrs_on_function(attrs, span);
+                hir::CallableKind::Function
             }
+            ast::CallableKind::Operation => hir::CallableKind::Operation,
         }
     }
 
diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs
index a39caa7e..fd2799fa 100644
--- a/compiler/qsc_hir/src/hir.rs
+++ b/compiler/qsc_hir/src/hir.rs
@@ -391,6 +391,8 @@ pub struct CallableDecl {
     pub ctl: Option<SpecDecl>,
     /// The controlled adjoint specialization.
     pub ctl_adj: Option<SpecDecl>,
+    /// The attributes of the callable, (e.g.: Measurement or Reset).
+    pub attrs: Vec<Attr>,
 }
 
 impl CallableDecl {
@@ -1352,8 +1354,11 @@ pub enum Attr {
     /// and any implementation should be ignored.
     SimulatableIntrinsic,
     /// Indicates that a callable is a measurement. This means that the operation will be marked as
-    /// "irreversible" in the generated QIR.
+    /// "irreversible" in the generated QIR, and output Result types will be moved to the arguments.
     Measurement,
+    /// Indicates that a callable is a reset. This means that the operation will be marked as
+    /// "irreversible" in the generated QIR.
+    Reset,
 }
 
 impl FromStr for Attr {
@@ -1366,6 +1371,7 @@ impl FromStr for Attr {
             "Unimplemented" => Ok(Self::Unimplemented),
             "SimulatableIntrinsic" => Ok(Self::SimulatableIntrinsic),
             "Measurement" => Ok(Self::Measurement),
+            "Reset" => Ok(Self::Reset),
             _ => Err(()),
         }
     }
@@ -1439,15 +1445,13 @@ pub enum CallableKind {
     Function,
     /// An operation.
     Operation,
-    /// A measurement.
-    Measurement,
 }
 
 impl Display for CallableKind {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         match self {
             CallableKind::Function => f.write_str("function"),
-            CallableKind::Operation | CallableKind::Measurement => f.write_str("operation"),
+            CallableKind::Operation => f.write_str("operation"),
         }
     }
 }
diff --git a/compiler/qsc_hir/src/ty.rs b/compiler/qsc_hir/src/ty.rs
index 9fc01095..0ea18ed9 100644
--- a/compiler/qsc_hir/src/ty.rs
+++ b/compiler/qsc_hir/src/ty.rs
@@ -73,7 +73,7 @@ impl Ty {
             Ty::Arrow(arrow) => {
                 let arrow_symbol = match arrow.kind {
                     CallableKind::Function => "->",
-                    CallableKind::Operation | CallableKind::Measurement => "=>",
+                    CallableKind::Operation => "=>",
                 };
 
                 let functors = match arrow.functors {
@@ -370,7 +370,7 @@ impl Display for Arrow {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         let arrow = match self.kind {
             CallableKind::Function => "->",
-            CallableKind::Operation | CallableKind::Measurement => "=>",
+            CallableKind::Operation => "=>",
         };
         write!(f, "({} {arrow} {}", self.input, self.output)?;
         if self.functors != FunctorSet::Value(FunctorSetValue::Empty) {
diff --git a/compiler/qsc_lowerer/src/lib.rs b/compiler/qsc_lowerer/src/lib.rs
index 44a46aac..7495daf5 100644
--- a/compiler/qsc_lowerer/src/lib.rs
+++ b/compiler/qsc_lowerer/src/lib.rs
@@ -282,6 +282,7 @@ impl Lowerer {
             };
             CallableImpl::Spec(specialized_implementation)
         };
+        let attrs = lower_attrs(&decl.attrs);
 
         self.assigner.reset_local();
         self.locals.clear();
@@ -299,6 +300,7 @@ impl Lowerer {
             output,
             functors,
             implementation,
+            attrs,
         }
     }
 
@@ -888,6 +890,7 @@ fn lower_attrs(attrs: &[hir::Attr]) -> Vec<fir::Attr> {
         .filter_map(|attr| match attr {
             hir::Attr::EntryPoint => Some(fir::Attr::EntryPoint),
             hir::Attr::Measurement => Some(fir::Attr::Measurement),
+            hir::Attr::Reset => Some(fir::Attr::Reset),
             hir::Attr::SimulatableIntrinsic | hir::Attr::Unimplemented | hir::Attr::Config => None,
         })
         .collect()
@@ -973,7 +976,6 @@ fn lower_callable_kind(kind: hir::CallableKind) -> fir::CallableKind {
     match kind {
         hir::CallableKind::Function => fir::CallableKind::Function,
         hir::CallableKind::Operation => fir::CallableKind::Operation,
-        hir::CallableKind::Measurement => fir::CallableKind::Measurement,
     }
 }
 
diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs
index e362968a..b759aca1 100644
--- a/compiler/qsc_partial_eval/src/lib.rs
+++ b/compiler/qsc_partial_eval/src/lib.rs
@@ -258,6 +258,7 @@ impl<'a> PartialEvaluator<'a> {
         &self,
         store_item_id: StoreItemId,
         callable_decl: &CallableDecl,
+        call_type: CallableType,
     ) -> Callable {
         let callable_package = self.package_store.get(store_item_id.package);
         let name = callable_decl.name.name.to_string();
@@ -275,7 +276,7 @@ impl<'a> PartialEvaluator<'a> {
         let call_type = if name.eq("__quantum__qis__reset__body") {
             CallableType::Reset
         } else {
-            CallableType::Regular
+            call_type
         };
         Callable {
             name,
@@ -1310,8 +1311,17 @@ impl<'a> PartialEvaluator<'a> {
         args_span: PackageSpan,        // For diagnostic purposes only.
         callee_expr_span: PackageSpan, // For diagnostic puprposes only.
     ) -> Result<Value, Error> {
-        if matches!(callable_decl.kind, qsc_fir::fir::CallableKind::Measurement) {
-            return self.measure_qubits(callable_decl, args_value, args_span);
+        if callable_decl.attrs.contains(&fir::Attr::Measurement) {
+            return Ok(self.measure_qubits(callable_decl, args_value));
+        }
+        if callable_decl.attrs.contains(&fir::Attr::Reset) {
+            return self.eval_expr_call_to_intrinsic_qis(
+                store_item_id,
+                callable_decl,
+                args_value,
+                callee_expr_span,
+                CallableType::Reset,
+            );
         }
 
         // There are a few special cases regarding intrinsic callables. Identify them and handle them properly.
@@ -1351,6 +1361,7 @@ impl<'a> PartialEvaluator<'a> {
                 callable_decl,
                 args_value,
                 callee_expr_span,
+                CallableType::Regular,
             ),
         }
     }
@@ -1361,6 +1372,7 @@ impl<'a> PartialEvaluator<'a> {
         callable_decl: &CallableDecl,
         args_value: Value,
         callee_expr_span: PackageSpan,
+        call_type: CallableType,
     ) -> Result<Value, Error> {
         // Intrinsic callables that make it to this point are expected to be unitary.
         if callable_decl.output != Ty::UNIT {
@@ -1374,7 +1386,7 @@ impl<'a> PartialEvaluator<'a> {
         }
 
         // Check if the callable is already in the program, and if not add it.
-        let callable = self.create_intrinsic_callable(store_item_id, callable_decl);
+        let callable = self.create_intrinsic_callable(store_item_id, callable_decl, call_type);
         let callable_id = self.get_or_insert_callable(callable);
 
         // Resove the call arguments, create the call instruction and insert it to the current block.
@@ -2264,12 +2276,7 @@ impl<'a> PartialEvaluator<'a> {
         Value::Qubit(qubit)
     }
 
-    fn measure_qubits(
-        &mut self,
-        callable_decl: &CallableDecl,
-        args_value: Value,
-        args_span: PackageSpan, // For diagnostic purposes only.
-    ) -> Result<Value, Error> {
+    fn measure_qubits(&mut self, callable_decl: &CallableDecl, args_value: Value) -> Value {
         let mut input_type = Vec::new();
         let mut operands = Vec::new();
         let mut results_values = Vec::new();
@@ -2282,20 +2289,14 @@ impl<'a> PartialEvaluator<'a> {
             Value::Tuple(values) => {
                 for value in &*values {
                     let Value::Qubit(qubit) = value else {
-                        return Err(Error::EvaluationFailed(
-                            "the arguments of a measurement should be Qubits".to_string(),
-                            args_span,
-                        ));
+                        panic!("by this point a qsc_pass should have checked that all arguments are Qubits")
                     };
                     input_type.push(qsc_rir::rir::Ty::Qubit);
                     operands.push(self.map_eval_value_to_rir_operand(&Value::Qubit(*qubit)));
                 }
             }
             _ => {
-                return Err(Error::EvaluationFailed(
-                    "the arguments of a measurement should be Qubits".to_string(),
-                    args_span,
-                ))
+                panic!("by this point a qsc_pass should have checked that all arguments are Qubits")
             }
         }
 
@@ -2317,18 +2318,12 @@ impl<'a> PartialEvaluator<'a> {
                         operands.push(result_operand);
                         results_values.push(result_value);
                     } else {
-                        return Err(Error::EvaluationFailed(
-                            "the arguments of a measurement should be Qubits".to_string(),
-                            args_span,
-                        ));
+                        panic!("by this point a qsc_pass should have checked that all outputs are Results")
                     }
                 }
             }
             _ => {
-                return Err(Error::EvaluationFailed(
-                    "the outputs of a measurement should be Results".to_string(),
-                    args_span,
-                ));
+                panic!("by this point a qsc_pass should have checked that all outputs are Results")
             }
         }
 
@@ -2348,8 +2343,8 @@ impl<'a> PartialEvaluator<'a> {
 
         match results_values.len() {
             0 => panic!("unexpected unitary measurement"),
-            1 => Ok(results_values[0].clone()),
-            2.. => Ok(Value::Tuple(results_values.into())),
+            1 => results_values[0].clone(),
+            2.. => Value::Tuple(results_values.into()),
         }
     }
 
diff --git a/compiler/qsc_passes/src/lib.rs b/compiler/qsc_passes/src/lib.rs
index 76cc40e9..c20a3816 100644
--- a/compiler/qsc_passes/src/lib.rs
+++ b/compiler/qsc_passes/src/lib.rs
@@ -13,6 +13,7 @@ mod logic_sep;
 mod loop_unification;
 mod measurement;
 mod replace_qubit_allocation;
+mod reset;
 mod spec_gen;
 
 use callable_limits::CallableLimits;
@@ -49,6 +50,7 @@ pub enum Error {
     ConjInvert(conjugate_invert::Error),
     EntryPoint(entry_point::Error),
     Measurement(measurement::Error),
+    Reset(reset::Error),
     SpecGen(spec_gen::Error),
 }
 
@@ -108,6 +110,7 @@ impl PassContext {
         Validator::default().visit_package(package);
 
         let measurement_decl_errors = measurement::validate_measurement_declarations(package);
+        let reset_decl_errors = reset::validate_reset_declarations(package);
 
         let entry_point_errors = generate_entry_expr(package, assigner, package_type);
         Validator::default().visit_package(package);
@@ -126,6 +129,7 @@ impl PassContext {
             .chain(conjugate_errors.into_iter().map(Error::ConjInvert))
             .chain(entry_point_errors)
             .chain(measurement_decl_errors.into_iter().map(Error::Measurement))
+            .chain(reset_decl_errors.into_iter().map(Error::Reset))
             .collect()
     }
 
diff --git a/compiler/qsc_passes/src/measurement.rs b/compiler/qsc_passes/src/measurement.rs
index f9a29349..d1a87928 100644
--- a/compiler/qsc_passes/src/measurement.rs
+++ b/compiler/qsc_passes/src/measurement.rs
@@ -15,7 +15,9 @@ use thiserror::Error;
 
 #[derive(Clone, Debug, Diagnostic, Error)]
 pub enum Error {
-    #[error("a callable with the measurement attribute should take at least one argument of type")]
+    #[error(
+        "a callable with the measurement attribute should take at least one argument of type Qubit"
+    )]
     #[diagnostic(code("Qsc.Measurement.NoArguments"))]
     NoArguments(#[label] Span),
 
diff --git a/compiler/qsc_passes/src/reset.rs b/compiler/qsc_passes/src/reset.rs
new file mode 100644
index 00000000..9c2c0e5d
--- /dev/null
+++ b/compiler/qsc_passes/src/reset.rs
@@ -0,0 +1,101 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#[cfg(test)]
+mod tests;
+
+use miette::Diagnostic;
+use qsc_data_structures::span::Span;
+use qsc_hir::{
+    hir::{Attr, CallableDecl, Item, ItemKind, Package, SpecBody, SpecGen},
+    ty::{Prim, Ty},
+    visit::Visitor,
+};
+use thiserror::Error;
+
+#[derive(Clone, Debug, Diagnostic, Error)]
+pub enum Error {
+    #[error("a callable with the reset attribute should take at least one argument of type Qubit")]
+    #[diagnostic(code("Qsc.Reset.NoArguments"))]
+    NoArguments(#[label] Span),
+
+    #[error("a callable with the reset attribute should only take arguments of type Qubit")]
+    #[diagnostic(code("Qsc.Reset.NonQubitArgument"))]
+    NonQubitArgument(#[label] Span),
+
+    #[error("a callable with the reset attribute should have output of type Unit")]
+    #[diagnostic(code("Qsc.Reset.NonResultOutput"))]
+    NonUnitOutput(#[label] Span),
+
+    #[error("a callable with the reset attribute should be an intrinsic")]
+    #[diagnostic(code("Qsc.Reset.NotIntrinsic"))]
+    NotIntrinsic(#[label] Span),
+}
+
+/// For each reset declaration check that:
+///  1. It takes at least one argument.
+///  2. It only takes Qubits as arguments.
+///  3. It only outputs Results.
+///  4. It is an intrinsic.
+pub(super) fn validate_reset_declarations(package: &Package) -> Vec<Error> {
+    let mut validator = ResetValidator { errors: Vec::new() };
+    validator.visit_package(package);
+    validator.errors
+}
+
+fn validate_reset_declaration(decl: &CallableDecl, attrs: &[Attr], errors: &mut Vec<Error>) {
+    // 1. Check that the declaration takes at least one argument.
+    if decl.input.ty == Ty::UNIT {
+        errors.push(Error::NoArguments(decl.input.span));
+    }
+
+    // 2. Check that the declaration only takes Qubits as arguments.
+    match &decl.input.ty {
+        Ty::Prim(Prim::Qubit) => (),
+        Ty::Tuple(types) => {
+            for ty in types {
+                if !matches!(ty, Ty::Prim(Prim::Qubit)) {
+                    errors.push(Error::NonQubitArgument(decl.input.span));
+                    // break so that we don't repeat the same error multiple times
+                    break;
+                }
+            }
+        }
+        _ => errors.push(Error::NonQubitArgument(decl.input.span)),
+    }
+
+    // 3. Check that the declaration only outputs Unit.
+    if decl.output != Ty::UNIT {
+        errors.push(Error::NonUnitOutput(decl.span));
+    }
+
+    // 4. Check that the declaration is an intrinsic.
+    if !decl_is_intrinsic(decl, attrs) {
+        errors.push(Error::NotIntrinsic(decl.name.span));
+    }
+}
+
+/// Returns `true` if a declaration is an intrinsic. A declaration is
+/// an intrinsic if it has `body intrinsic;` in its body or if it has
+/// the `@SimulatableIntrinsic()` attribute.
+fn decl_is_intrinsic(decl: &CallableDecl, attrs: &[Attr]) -> bool {
+    matches!(decl.body.body, SpecBody::Gen(SpecGen::Intrinsic))
+        || attrs
+            .iter()
+            .any(|attr| matches!(attr, Attr::SimulatableIntrinsic))
+}
+
+/// A helper structure to find and validate reset callables in a Package.
+struct ResetValidator {
+    errors: Vec<Error>,
+}
+
+impl<'a> Visitor<'a> for ResetValidator {
+    fn visit_item(&mut self, item: &'a Item) {
+        if let ItemKind::Callable(callable) = &item.kind {
+            if item.attrs.contains(&Attr::Reset) {
+                validate_reset_declaration(callable, &item.attrs, &mut self.errors);
+            }
+        }
+    }
+}
diff --git a/compiler/qsc_rca/src/core.rs b/compiler/qsc_rca/src/core.rs
index ce945a79..e5050815 100644
--- a/compiler/qsc_rca/src/core.rs
+++ b/compiler/qsc_rca/src/core.rs
@@ -15,10 +15,11 @@ use qsc_data_structures::{functors::FunctorApp, index_map::IndexMap};
 use qsc_fir::{
     extensions::InputParam,
     fir::{
-        BinOp, Block, BlockId, CallableDecl, CallableImpl, CallableKind, Expr, ExprId, ExprKind,
-        FieldAssign, Global, Ident, Item, ItemKind, LocalVarId, Mutability, Package, PackageId,
-        PackageLookup, PackageStore, PackageStoreLookup, Pat, PatId, PatKind, Res, SpecDecl,
-        SpecImpl, Stmt, StmtId, StmtKind, StoreExprId, StoreItemId, StorePatId, StringComponent,
+        Attr, BinOp, Block, BlockId, CallableDecl, CallableImpl, CallableKind, Expr, ExprId,
+        ExprKind, FieldAssign, Global, Ident, Item, ItemKind, LocalVarId, Mutability, Package,
+        PackageId, PackageLookup, PackageStore, PackageStoreLookup, Pat, PatId, PatKind, Res,
+        SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StoreExprId, StoreItemId, StorePatId,
+        StringComponent,
     },
     ty::{Arrow, FunctorSetValue, Prim, Ty},
     visit::{walk_stmt, Visitor},
@@ -1155,7 +1156,7 @@ impl<'a> Analyzer<'a> {
             CallableKind::Function => {
                 derive_intrinsic_function_application_generator_set(callable_context)
             }
-            CallableKind::Operation | CallableKind::Measurement => {
+            CallableKind::Operation => {
                 derive_instrinsic_operation_application_generator_set(callable_context)
             }
         };
@@ -1239,6 +1240,7 @@ impl<'a> Analyzer<'a> {
             callable_decl.kind,
             input_params,
             callable_decl.output.clone(),
+            &callable_decl.attrs,
         );
 
         // Continue with the analysis differently depending on whether the callable is an intrinsic or not.
@@ -1788,7 +1790,12 @@ impl<'a> Visitor<'a> for Analyzer<'a> {
         let package = self.package_store.get(package_id);
         let input_params = package.derive_callable_input_params(decl);
         let current_callable_context = self.get_current_item_context_mut();
-        current_callable_context.set_callable_context(decl.kind, input_params, decl.output.clone());
+        current_callable_context.set_callable_context(
+            decl.kind,
+            input_params,
+            decl.output.clone(),
+            &decl.attrs,
+        );
         self.visit_callable_impl(&decl.implementation);
     }
 
@@ -2129,12 +2136,14 @@ impl ItemContext {
         kind: CallableKind,
         input_params: Vec<InputParam>,
         output_type: Ty,
+        attrs: &[Attr],
     ) {
         assert!(self.callable_context.is_none());
         self.callable_context = Some(CallableContext {
             kind,
             input_params,
             output_type,
+            attrs: attrs.to_vec(),
         });
     }
 
@@ -2148,6 +2157,7 @@ struct CallableContext {
     pub kind: CallableKind,
     pub input_params: Vec<InputParam>,
     pub output_type: Ty,
+    pub attrs: Vec<Attr>,
 }
 
 struct SpecContext {
@@ -2238,10 +2248,7 @@ fn array_param_application_from_runtime_features(
 fn derive_instrinsic_operation_application_generator_set(
     callable_context: &CallableContext,
 ) -> ApplicationGeneratorSet {
-    assert!(matches!(
-        callable_context.kind,
-        CallableKind::Operation | CallableKind::Measurement
-    ));
+    assert!(matches!(callable_context.kind, CallableKind::Operation));
 
     // The value kind of intrinsic operations is inherently dynamic if their output is not `Unit` or `Qubit`.
     let value_kind = if callable_context.output_type == Ty::UNIT
@@ -2252,13 +2259,17 @@ fn derive_instrinsic_operation_application_generator_set(
         ValueKind::new_dynamic_from_type(&callable_context.output_type)
     };
 
+    let mut inherent_runtime_features = RuntimeFeatureFlags::empty();
+    if callable_context.attrs.contains(&Attr::Measurement) {
+        inherent_runtime_features |= RuntimeFeatureFlags::CallToCustomMeasurement;
+    }
+    if callable_context.attrs.contains(&Attr::Reset) {
+        inherent_runtime_features |= RuntimeFeatureFlags::CallToCustomReset;
+    }
+
     // The compute kind of intrinsic operations is always quantum.
     let inherent_compute_kind = ComputeKind::Quantum(QuantumProperties {
-        runtime_features: if matches!(callable_context.kind, CallableKind::Operation) {
-            RuntimeFeatureFlags::empty()
-        } else {
-            RuntimeFeatureFlags::CustomMeasurement
-        },
+        runtime_features: inherent_runtime_features,
         value_kind,
     });
 
@@ -2384,9 +2395,6 @@ fn derive_runtime_features_for_value_kind_associated_to_type(
         match arrow.kind {
             CallableKind::Function => RuntimeFeatureFlags::UseOfDynamicArrowFunction,
             CallableKind::Operation => RuntimeFeatureFlags::UseOfDynamicArrowOperation,
-            CallableKind::Measurement => {
-                panic!("measurements are intrinsics, and arrow kinds cannot be intrinsics");
-            }
         }
     }
 
diff --git a/compiler/qsc_rca/src/cyclic_callables.rs b/compiler/qsc_rca/src/cyclic_callables.rs
index 202828b3..c4e762dd 100644
--- a/compiler/qsc_rca/src/cyclic_callables.rs
+++ b/compiler/qsc_rca/src/cyclic_callables.rs
@@ -90,9 +90,6 @@ impl<'a> Analyzer<'a> {
                 &input_params,
                 &callable.output,
             ),
-            CallableKind::Measurement => {
-                panic!("measurements are intrinsics, and intrinsics cannot have specializations")
-            }
         };
 
         // Find the specialization.
diff --git a/compiler/qsc_rca/src/errors.rs b/compiler/qsc_rca/src/errors.rs
index 120810e9..dc6ddb65 100644
--- a/compiler/qsc_rca/src/errors.rs
+++ b/compiler/qsc_rca/src/errors.rs
@@ -167,6 +167,12 @@ pub enum Error {
     #[diagnostic(code("Qsc.CapabilitiesCk.CallToCustomMeasurement"))]
     CallToCustomMeasurement(#[label] Span),
 
+    #[error("cannot call a custom reset")]
+    #[diagnostic(help("cannot call a custom reset in the configured target profile"))]
+    #[diagnostic(url("https://aka.ms/qdk.qir#call-to-custom-reset"))]
+    #[diagnostic(code("Qsc.CapabilitiesCk.CallToCustomReset"))]
+    CallToCustomReset(#[label] Span),
+
     #[error("cannot access an array using a dynamic index")]
     #[diagnostic(help("accessing an array using an index that depends on a measurement result is not supported by the configured target profile"))]
     #[diagnostic(url("https://aka.ms/qdk.qir#use-of-dynamic-array-index"))]
@@ -308,9 +314,12 @@ pub fn generate_errors_from_runtime_features(
     if runtime_features.contains(RuntimeFeatureFlags::UseOfAdvancedOutput) {
         errors.push(Error::UseOfAdvancedOutput(span));
     }
-    if runtime_features.contains(RuntimeFeatureFlags::CustomMeasurement) {
+    if runtime_features.contains(RuntimeFeatureFlags::CallToCustomMeasurement) {
         errors.push(Error::CallToCustomMeasurement(span));
     }
+    if runtime_features.contains(RuntimeFeatureFlags::CallToCustomReset) {
+        errors.push(Error::CallToCustomReset(span));
+    }
     errors
 }
 
diff --git a/compiler/qsc_rca/src/lib.rs b/compiler/qsc_rca/src/lib.rs
index a62fff64..f331aaec 100644
--- a/compiler/qsc_rca/src/lib.rs
+++ b/compiler/qsc_rca/src/lib.rs
@@ -810,8 +810,10 @@ bitflags! {
         const UseOfDynamicResult = 1 << 26;
         /// Use of a dynamic tuple variable.
         const UseOfDynamicTuple = 1 << 27;
-        /// A custom measurement was declared using the @Measurement attribute.
-        const CustomMeasurement = 1 << 28;
+        /// A callee expression to a measurement.
+        const CallToCustomMeasurement = 1 << 28;
+        /// A callee expression to a reset.
+        const CallToCustomReset = 1 << 29;
     }
 }
 
@@ -914,9 +916,13 @@ impl RuntimeFeatureFlags {
         if self.contains(RuntimeFeatureFlags::UseOfDynamicTuple) {
             capabilities |= TargetCapabilityFlags::HigherLevelConstructs;
         }
-        if self.contains(RuntimeFeatureFlags::CustomMeasurement) {
+        if self.contains(RuntimeFeatureFlags::CallToCustomMeasurement) {
             capabilities |= TargetCapabilityFlags::Adaptive;
         }
+        if self.contains(RuntimeFeatureFlags::CallToCustomReset) {
+            capabilities |= TargetCapabilityFlags::Adaptive;
+            capabilities |= TargetCapabilityFlags::QubitReset;
+        }
         capabilities
     }
 
diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs
index 2f4fa282..bf6b2ffa 100644
--- a/language_service/src/completion.rs
+++ b/language_service/src/completion.rs
@@ -132,6 +132,7 @@ fn collect_hardcoded_words(expected: WordKinds) -> Vec<Completion> {
                     Completion::new("EntryPoint".to_string(), CompletionItemKind::Interface),
                     Completion::new("Config".to_string(), CompletionItemKind::Interface),
                     Completion::new("Measurement".to_string(), CompletionItemKind::Interface),
+                    Completion::new("Reset".to_string(), CompletionItemKind::Interface),
                 ]);
             }
             HardcodedIdentKind::Size => {
diff --git a/pip/README.md b/pip/README.md
index 29fe1adc..6784349f 100644
--- a/pip/README.md
+++ b/pip/README.md
@@ -39,6 +39,13 @@ operation BellState() : Unit {
 BellState()
 ```
 
+## Telemetry
+
+This library sends telemetry. Minimal anonymous data is collected to help measure feature usage and performance.
+All telemetry events can be seen in the source file [telemetry_events.py](https://github.com/microsoft/qsharp/tree/main/pip/qsharp/telemetry_events.py).
+
+To disable sending telemetry from this package, set the environment variable `QSHARP_PYTHON_TELEMETRY=none`
+
 ## Support
 
 For more information about the Azure Quantum Development Kit, visit [https://aka.ms/AQ/Documentation](https://aka.ms/AQ/Documentation).
diff --git a/pip/qsharp/__init__.py b/pip/qsharp/__init__.py
index 1d068b86..3b9984cf 100644
--- a/pip/qsharp/__init__.py
+++ b/pip/qsharp/__init__.py
@@ -1,6 +1,7 @@
 # Copyright (c) Microsoft Corporation.
 # Licensed under the MIT License.
 
+from . import telemetry_events
 from ._qsharp import (
     init,
     eval,
@@ -20,6 +21,8 @@ from ._qsharp import (
     PhaseFlipNoise,
 )
 
+telemetry_events.on_import()
+
 from ._native import Result, Pauli, QSharpError, TargetProfile
 
 # IPython notebook specific features
diff --git a/pip/qsharp/_qsharp.py b/pip/qsharp/_qsharp.py
index 87fe7c3b..d9016219 100644
--- a/pip/qsharp/_qsharp.py
+++ b/pip/qsharp/_qsharp.py
@@ -1,6 +1,7 @@
 # Copyright (c) Microsoft Corporation.
 # Licensed under the MIT License.
 
+from . import telemetry_events
 from ._native import (
     Interpreter,
     TargetProfile,
@@ -23,8 +24,10 @@ from typing import (
 from .estimator._estimator import EstimatorResult, EstimatorParams
 import json
 import os
+from time import monotonic
 
 _interpreter = None
+_config = None
 
 # Check if we are running in a Jupyter notebook to use the IPython display function
 _in_jupyter = False
@@ -161,6 +164,7 @@ def init(
     from ._http import fetch_github
 
     global _interpreter
+    global _config
 
     if isinstance(target_name, str):
         target = target_name.split(".")[0].lower()
@@ -198,9 +202,10 @@ def init(
         fetch_github,
     )
 
+    _config = Config(target_profile, language_features, manifest_contents, project_root)
     # Return the configuration information to provide a hint to the
     # language service through the cell output.
-    return Config(target_profile, language_features, manifest_contents, project_root)
+    return _config 
 
 
 def get_interpreter() -> Interpreter:
@@ -286,6 +291,9 @@ def run(
     if shots < 1:
         raise QSharpError("The number of shots must be greater than 0.")
 
+    telemetry_events.on_run(shots)
+    start_time = monotonic()
+
     results: List[ShotResult] = []
 
     def print_output(output: Output) -> None:
@@ -317,6 +325,9 @@ def run(
         # compilation.
         entry_expr = None
 
+    durationMs = (monotonic() - start_time) * 1000
+    telemetry_events.on_run_end(durationMs, shots)
+
     if save_events:
         return results
     else:
@@ -366,10 +377,15 @@ def compile(entry_expr: str) -> QirInputData:
             file.write(str(program))
     """
     ipython_helper()
-
+    start = monotonic()
+    global _config
+    target_profile = _config._config.get("targetProfile", "unspecified")
+    telemetry_events.on_compile(target_profile)
     ll_str = get_interpreter().qir(entry_expr)
-    return QirInputData("main", ll_str)
-
+    res = QirInputData("main", ll_str)
+    durationMs = (monotonic() - start) * 1000
+    telemetry_events.on_compile_end(durationMs, target_profile)
+    return res
 
 def circuit(
     entry_expr: Optional[str] = None, *, operation: Optional[str] = None
@@ -421,9 +437,18 @@ def estimate(
 
     params = _coerce_estimator_params(params)
     param_str = json.dumps(params)
-
+    telemetry_events.on_estimate()
+    start = monotonic()
     res_str = get_interpreter().estimate(entry_expr, param_str)
     res = json.loads(res_str)
+
+    try:
+        qubits = res[0]["logicalCounts"]["numQubits"]
+    except (KeyError, IndexError):
+        qubits = "unknown"
+
+    durationMs = (monotonic() - start) * 1000
+    telemetry_events.on_estimate_end(durationMs, qubits)
     return EstimatorResult(res)
 
 
diff --git a/pip/qsharp/telemetry.py b/pip/qsharp/telemetry.py
new file mode 100644
index 00000000..a13a1dca
--- /dev/null
+++ b/pip/qsharp/telemetry.py
@@ -0,0 +1,305 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+"""
+This module sends telemetry directly to Azure Monitor using a similar mechanism and
+format to the Azure Monitor OpenTelemetry Python SDK. It only supports custom metrics of
+type "counter" and "histogram" for now. It's goal is to be minimal in size and dependencies,
+and easy to read to understand exactly what data is being sent.
+
+To use this API, simply call `log_telemetry` with the metric name, value, and any other
+optional properties. The telemetry will be batched and sent at a regular intervals (60 sec),
+and when the process is about to exit.
+
+Disable qsharp Python telemetry by setting the environment variable `QSHARP_PYTHON_TELEMETRY=none`.
+"""
+
+import atexit
+import json
+import locale
+import logging
+import os
+import platform
+import time
+import urllib.request
+import warnings
+
+from datetime import datetime, timezone
+from queue import SimpleQueue, Empty
+from threading import Thread
+from typing import Any, Dict, Literal, List, TypedDict, Union
+
+logger = logging.getLogger(__name__)
+
+QSHARP_VERSION = "0.0.0.dev0"
+
+AIKEY = os.environ.get("QSHARP_PYTHON_AI_KEY") or "95d25b22-8b6d-448e-9677-78ad4047a95a"
+AIURL = (
+    os.environ.get("QSHARP_PYTHON_AI_URL")
+    or "https://westus2-2.in.applicationinsights.azure.com//v2.1/track"
+)
+
+# Environment variables take precedence, else disable telemetry for non 'stable' builds
+QSHARP_PYTHON_TELEMETRY = (os.environ.get("QSHARP_PYTHON_TELEMETRY") or "").lower()
+TELEMETRY_ENABLED = (
+    True
+    if QSHARP_PYTHON_TELEMETRY in ["1", "true", "enabled"]
+    else (
+        False
+        if QSHARP_PYTHON_TELEMETRY in ["0", "false", "disabled", "none"]
+        else (not "dev" in QSHARP_VERSION)
+    )
+)
+
+BATCH_INTERVAL_SEC = int(os.environ.get("QSHARP_PYTHON_TELEMETRY_INTERVAL") or 60)
+
+
+# The below is taken from the Azure Monitor Python SDK
+def _getlocale() -> str:
+    try:
+        with warnings.catch_warnings():
+            # Workaround for https://github.com/python/cpython/issues/82986 by continuing to use getdefaultlocale() even though it has been deprecated.
+            # Ignore the deprecation warnings to reduce noise
+            warnings.simplefilter("ignore", category=DeprecationWarning)
+            return locale.getdefaultlocale()[0] or ""
+    except AttributeError:
+        # Use this as a fallback if locale.getdefaultlocale() doesn't exist (>Py3.13)
+        return locale.getlocale()[0] or ""
+
+
+# Minimal device information to include with telemetry
+AI_DEVICE_LOCALE = _getlocale()
+AI_DEVICE_OS_VERSION = platform.version()
+
+
+class Metric(TypedDict):
+    """Used internally for objects in the telemetry queue"""
+
+    name: str
+    value: float
+    count: int
+    properties: Dict[str, Any]
+    type: str
+
+
+class PendingMetric(Metric):
+    """Used internally to aggregate metrics before sending"""
+
+    min: float
+    max: float
+
+
+# Maintain a collection of custom metrics to log, stored by metric name with a list entry
+# for each unique set of properties per metric name
+pending_metrics: Dict[str, List[PendingMetric]] = {}
+
+# The telemetry queue is used to send telemetry from the main thread to the telemetry thread
+# This simplifies any thread-safety concerns, and avoids the need for locks, etc.
+telemetry_queue: SimpleQueue[Union[Literal["exit"], Metric]] = SimpleQueue()
+
+
+def log_telemetry(
+    name: str,
+    value: float,
+    count: int = 1,
+    properties: Dict[str, Any] = {},
+    type: Literal["counter", "histogram"] = "counter",
+) -> None:
+    """
+    Logs a custom metric with the name provided. Properties are optional and can be used to
+    capture additional context about the metric (but should be a relatively static set of values, as
+    each unique set of properties will be sent as a separate metric and creates a separate 'dimension'
+    in the backend telemetry store).
+
+    The type can be either 'counter' or 'histogram'. A 'counter' is a simple value that is summed
+    over time, such as how many times an event occurs, while a 'histogram' is used to track 'quantative'
+    values, such as the distribution of values over time, e.g., the duration of an operation.
+
+    Example usage for a counter:
+
+        log_telemetry("qir_generated", 1, properties={"profile": "base", "qsharp.version": "1.9.0"})
+
+    Example usage for a histogram:
+
+        log_telemetry("simulation_duration", 123.45, type="histogram")
+
+    """
+    if not TELEMETRY_ENABLED:
+        return
+
+    obj: Metric = {
+        "name": name,
+        "value": value,
+        "count": count,
+        "properties": {**properties, "qsharp.version": QSHARP_VERSION},
+        "type": type,
+    }
+
+    logger.debug("Queuing telemetry: %s", obj)
+    telemetry_queue.put(obj)
+
+
+def _add_to_pending(metric: Metric):
+    """Used by the telemetry thread to aggregate metrics before sending"""
+
+    if metric["type"] not in ["counter", "histogram"]:
+        raise Exception("Metric must be of type counter or histogram")
+
+    # Get or create the entry list for this name
+    name_entries = pending_metrics.setdefault(metric["name"], [])
+
+    # Try to find the entry with matching properties
+    # This relies on the fact dicts with matching keys/values compare equal in Python
+    prop_entry = next(
+        (
+            entry
+            for entry in name_entries
+            if entry["properties"] == metric["properties"]
+        ),
+        None,
+    )
+    if prop_entry is None:
+        new_entry: PendingMetric = {
+            **metric,
+            "min": metric["value"],
+            "max": metric["value"],
+        }
+        name_entries.append(new_entry)
+    else:
+        if prop_entry["type"] != metric["type"]:
+            raise Exception("Cannot mix counter and histogram for the same metric name")
+        prop_entry["value"] += metric["value"]
+        prop_entry["count"] += metric["count"]
+        prop_entry["min"] = min(prop_entry["min"], metric["value"])
+        prop_entry["max"] = max(prop_entry["max"], metric["value"])
+
+
+def _pending_to_payload() -> List[Dict[str, Any]]:
+    """Converts the pending metrics to the JSON payload for Azure Monitor"""
+
+    result_array: List[Dict[str, Any]] = []
+    formatted_time = (
+        datetime.now(timezone.utc)
+        .isoformat(timespec="microseconds")
+        .replace("+00:00", "Z")
+    )
+    for name in pending_metrics:
+        for unique_props in pending_metrics[name]:
+            # The below matches the entry format for Azure Monitor REST API
+            entry: Dict[str, Any] = {
+                "ver": 1,
+                "name": "Microsoft.ApplicationInsights.Metric",
+                "time": formatted_time,
+                "sampleRate": 100.0,
+                "iKey": AIKEY,
+                "tags": {
+                    "ai.device.locale": AI_DEVICE_LOCALE,
+                    "ai.device.osVersion": AI_DEVICE_OS_VERSION,
+                },
+                "data": {
+                    "baseType": "MetricData",
+                    "baseData": {
+                        "ver": 2,
+                        "metrics": [
+                            {
+                                "name": unique_props["name"],
+                                "value": unique_props["value"],
+                                "count": unique_props["count"],
+                            }
+                        ],
+                        "properties": unique_props["properties"],
+                    },
+                },
+            }
+            # Histogram values differ only in that they have min/max values also
+            if unique_props["type"] == "histogram":
+                entry["data"]["baseData"]["metrics"][0]["min"] = unique_props["min"]
+                entry["data"]["baseData"]["metrics"][0]["max"] = unique_props["max"]
+
+            result_array.append(entry)
+
+    return result_array
+
+
+def _post_telemetry() -> bool:
+    """Posts the pending telemetry to Azure Monitor"""
+
+    if len(pending_metrics) == 0:
+        return True
+
+    payload = json.dumps(_pending_to_payload()).encode("utf-8")
+    logger.debug("Sending telemetry request: %s", payload)
+    try:
+        request = urllib.request.Request(AIURL, data=payload, method="POST")
+        request.add_header("Content-Type", "application/json")
+        with urllib.request.urlopen(request, timeout=10) as response:
+            logger.debug("Telemetry response: %s", response.status)
+            # On a successful post, clear the pending list. (Else they will be included on the next retry)
+            pending_metrics.clear()
+            return True
+
+    except Exception:
+        logger.exception(
+            "Failed to post telemetry. Pending metrics will be retried at the next interval."
+        )
+        return False
+
+
+# This is the thread that aggregates and posts telemetry at a regular interval.
+# The main thread will signal the thread loop to exit when the process is about to exit.
+def _telemetry_thread_start():
+    next_post_sec: Union[float, None] = None
+
+    def on_metric(msg: Metric):
+        nonlocal next_post_sec
+
+        # Add to the pending batch to send next
+        _add_to_pending(msg)
+
+        # Schedule the next post if we don't have one scheduled
+        if next_post_sec == None:
+            next_post_sec = time.monotonic() + BATCH_INTERVAL_SEC
+
+    while True:
+        try:
+            # Block if no timeout, else wait a maximum of time until the next post is due
+            timeout: Union[float, None] = None
+            if next_post_sec:
+                timeout = max(next_post_sec - time.monotonic(), 0)
+            msg = telemetry_queue.get(timeout=timeout)
+
+            if msg == "exit":
+                logger.debug("Exiting telemetry thread")
+                if not _post_telemetry():
+                    logger.error("Failed to post telemetry on exit")
+                return
+            else:
+                on_metric(msg)
+                # Loop until the queue has been drained. This will cause the 'Empty' exception
+                # below once the queue is empty and it's time to post
+                continue
+        except Empty:
+            # No more telemetry within timeout, so write what we have pending
+            _ = _post_telemetry()
+
+        # If we get here, it's after a post attempt. Pending will still have items if the attempt
+        # failed, so updated the time for the next attempt in that case.
+        if len(pending_metrics) == 0:
+            next_post_sec = None
+        else:
+            next_post_sec = time.monotonic() + BATCH_INTERVAL_SEC
+
+
+# When the process is about to exit, notify the telemetry thread to flush, and wait max 3 sec before exiting anyway
+def _on_exit():
+    logger.debug("In on_exit handler")
+    telemetry_queue.put("exit")
+    # Wait at most 3 seconds for the telemetry thread to flush and exit
+    telemetry_thread.join(timeout=3)
+
+
+# Mark the telemetry thread as a deamon thread, else it will keep the process alive when the main thread exits
+if TELEMETRY_ENABLED:
+    telemetry_thread = Thread(target=_telemetry_thread_start, daemon=True)
+    telemetry_thread.start()
+    atexit.register(_on_exit)
diff --git a/pip/qsharp/telemetry_events.py b/pip/qsharp/telemetry_events.py
new file mode 100644
index 00000000..c69693d6
--- /dev/null
+++ b/pip/qsharp/telemetry_events.py
@@ -0,0 +1,87 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+from .telemetry import log_telemetry
+import math
+from typing import Union
+
+# For metrics such as duration, we want to capture things like how many shots or qubits in
+# the additional properties. However properties shouldn't be 'continuous' values, as they
+# create new 'dimensions' on the backend, which is limited, thus we want to bucket these properties.
+
+# See some of the notes at: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/metrics-custom-overview#design-limitations-and-considerations
+
+
+def get_shots_bucket(shots: int) -> int:
+    if shots <= 1:
+        return 1
+    elif shots >= 1000000:
+        # Limit the buckets upper bound
+        return 1000000
+    else:
+        # Bucket into nearest (rounded up) power of 10, e.g. 75 -> 100, 450 -> 1000, etc.
+        return 10 ** math.ceil(math.log10(shots))
+
+
+# gets the order of magnitude for the number of qubits
+def get_qubits_bucket(qubits: Union[str, int]) -> str:
+    if qubits == "unknown":
+        return "unknown"
+    qubits = int(qubits)
+    if qubits <= 1:
+        return "1"
+    elif qubits >= 50:
+        return "50"
+    else:
+        # integer divide by 5 to get nearest 5
+        return str(qubits // 5 * 5)
+
+
+def on_import() -> None:
+    log_telemetry("qsharp.import", 1)
+
+
+def on_run(shots: int) -> None:
+    log_telemetry(
+        "qsharp.run",
+        1,
+        properties={"shots": get_shots_bucket(shots)},
+    )
+
+
+def on_run_end(durationMs: float, shots: int) -> None:
+    log_telemetry(
+        "qsharp.run.durationMs",
+        durationMs,
+        properties={"shots": get_shots_bucket(shots)},
+        type="histogram",
+    )
+
+
+def on_compile(profile: str) -> None:
+    log_telemetry("qsharp.compile", 1, properties={"profile": profile})
+
+
+def on_compile_end(durationMs: float, profile: str) -> None:
+    log_telemetry(
+        "qsharp.compile.durationMs",
+        durationMs,
+        properties={"profile": profile},
+        type="histogram",
+    )
+
+
+def on_estimate() -> None:
+    log_telemetry(
+        "qsharp.estimate",
+        1,
+    )
+
+
+def on_estimate_end(durationMs: float, qubits: Union[str, int]) -> None:
+    log_telemetry(
+        "qsharp.estimate.durationMs",
+        durationMs,
+        properties={"qubits": get_qubits_bucket(qubits)},
+        type="histogram",
+    )
diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs
index 8e34d096..78b4b00a 100644
--- a/pip/src/interpreter.rs
+++ b/pip/src/interpreter.rs
@@ -645,8 +645,8 @@ impl StateDumpData {
     }
 }
 
-#[derive(Clone, Copy, PartialEq)]
-#[pyclass(eq, eq_int)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[pyclass(eq, eq_int, ord)]
 /// A Q# measurement result.
 pub(crate) enum Result {
     Zero,
diff --git a/samples/notebooks/noise.ipynb b/samples/notebooks/noise.ipynb
new file mode 100644
index 00000000..c4fde081
--- /dev/null
+++ b/samples/notebooks/noise.ipynb
@@ -0,0 +1,208 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Simulating Pauli noise\n",
+    "This notebook shows how to run simulations with Pauli noise, such as bit-flip or depolarizing noise.\n",
+    "\n",
+    "First, make sure prerequisites are available. Packages `qsharp` and `qsharp_widgets` must be already installed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import qsharp\n",
+    "import qsharp_widgets"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Simulation with noise\n",
+    "\n",
+    "Define a simple program that creates a Bell state on two qubits and measures both qubits."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "vscode": {
+     "languageId": "qsharp"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%qsharp\n",
+    "\n",
+    "operation BellPair() : Result[] {\n",
+    "    use q = Qubit[2];\n",
+    "    H(q[0]);\n",
+    "    CNOT(q[0], q[1]);\n",
+    "    MResetEachZ(q)\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Run 20 shots without noise and display results."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = qsharp.run(\"BellPair()\", 20)\n",
+    "results"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note that measurements always agree within a shot as expected. Now run 20 shots of the same program with 10% [depolarizing noise](https://en.wikipedia.org/wiki/Quantum_depolarizing_channel). Depolarizing noise is applied to each gate and each measurement."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = qsharp.run(\"BellPair()\", 20, noise=qsharp.DepolarizingNoise(0.1))\n",
+    "results"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note that measurements do not always agree within the shot."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Histograms\n",
+    "\n",
+    "Define a program to prepare a cat state on five qubits and measure each qubit."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "vscode": {
+     "languageId": "qsharp"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%qsharp\n",
+    "\n",
+    "operation Cat5() : Result[] {\n",
+    "    use q = Qubit[5];\n",
+    "    H(q[0]);\n",
+    "    ApplyCNOTChain(q);\n",
+    "    MResetEachZ(q)\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, run this program without noise. Roughly half of the outcomes should be $\\ket{00000}$ and another half should be $\\ket{11111}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "result = qsharp.run(\"Cat5()\", 1000)\n",
+    "qsharp_widgets.Histogram(result)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now, run the same program with bit-flip noise of 1%, 5%, 10%, 25%."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for p in [0.01, 0.05, 0.1, 0.25]:\n",
+    "    result = qsharp.run(\"Cat5()\", 1000, noise=qsharp.BitFlipNoise(p))\n",
+    "    display(f\"Noise probability = {p}\")\n",
+    "    display(qsharp_widgets.Histogram(result))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can see that with 1% noise, cat state can still be clearly seen, but when noise approaches 25%, the cat state is indistinguishable from noise."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Arbitrary Pauli noise\n",
+    "\n",
+    "Standard bit-flip, phase-flip, and [depolarizing](https://en.wikipedia.org/wiki/Quantum_depolarizing_channel) noise are available, but arbitrary Pauli noise is also possible. The following example runs the same Cat5 program. First it applies noise with 20% probability (bit-flip half the time and phase-flip half the time). In a second experiment it applies Pauli-Y noise with 10% probability."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "result = qsharp.run(\"Cat5()\", 1000, noise=(0.1, 0.0, 0.1))\n",
+    "display(qsharp_widgets.Histogram(result))\n",
+    "result = qsharp.run(\"Cat5()\", 1000, noise=(0.0, 0.1, 0.0))\n",
+    "display(qsharp_widgets.Histogram(result))"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/samples/notebooks/sample.ipynb b/samples/notebooks/sample.ipynb
index afe2336e..3e6a8fc4 100644
--- a/samples/notebooks/sample.ipynb
+++ b/samples/notebooks/sample.ipynb
@@ -57,36 +57,67 @@
       "outputs": [
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|1⟩</span>\n",
+              "  <td>\n",
+              "    <span>|1⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |1\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
@@ -96,11 +127,13 @@
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<p>The result of the measurement is One</p>"
-            ],
             "text/plain": [
               "The result of the measurement is One"
             ]
@@ -108,6 +141,11 @@
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
             "text/plain": [
@@ -154,13 +192,90 @@
       },
       "outputs": [
         {
-          "name": "stdout",
-          "output_type": "stream",
-          "text": [
-            "STATE:\n",
-            "|1⟩: 1.0000+0.0000𝑖\n",
-            "The result of the measurement is One\n"
-          ]
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
+        {
+          "data": {
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
+              "  <thead>\n",
+              "    <tr>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
+              "    </tr>\n",
+              "  </thead>\n",
+              "  <tbody>\n",
+              "    <tr>\n",
+              "  <td>\n",
+              "    <span>|1⟩</span>\n",
+              "  </td>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
+              "  </td>\n",
+              "  <td>\n",
+              "    <progress max=\"100\" value=\"100\"></progress>\n",
+              "    <span>100.0000%</span>\n",
+              "  </td>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
+              "  </td>\n",
+              "</tr>\n",
+              "\n",
+              "  </tbody>\n",
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |1\\rangle$"
+            ],
+            "text/plain": [
+              "STATE:\n",
+              "|1⟩: 1.0000+0.0000𝑖"
+            ]
+          },
+          "metadata": {},
+          "output_type": "display_data"
+        },
+        {
+          "data": {
+            "text/plain": [
+              "The result of the measurement is One"
+            ]
+          },
+          "metadata": {},
+          "output_type": "display_data"
         },
         {
           "data": {
@@ -191,6 +306,11 @@
       "id": "50383f8a",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "name": "stdout",
           "output_type": "stream",
@@ -221,52 +341,57 @@
       "id": "33fd3c4d",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "\u001b[31mQsc.Resolve.NotFound\u001b[0m\n",
+            "Qsc.Resolve.NotFound\n",
             "\n",
-            "  \u001b[31m×\u001b[0m name error\n",
-            "\u001b[31m  ╰─▶ \u001b[0m`Bar` not found\n",
-            "   ╭─[\u001b[36;1;4mline_3\u001b[0m:2:1]\n",
-            " \u001b[2m2\u001b[0m │ operation Foo() : Unit {\n",
-            " \u001b[2m3\u001b[0m │     Bar();\n",
-            "   · \u001b[35;1m    ───\u001b[0m\n",
-            " \u001b[2m4\u001b[0m │     Baz();\n",
-            "   ╰────\n",
-            "\u001b[31mQsc.Resolve.NotFound\u001b[0m\n",
+            "  x name error\n",
+            "  `-> `Bar` not found\n",
+            "   ,-[line_3:3:5]\n",
+            " 2 | operation Foo() : Unit {\n",
+            " 3 |     Bar();\n",
+            "   :     ^^^\n",
+            " 4 |     Baz();\n",
+            "   `----\n",
+            "Qsc.Resolve.NotFound\n",
             "\n",
-            "  \u001b[31m×\u001b[0m name error\n",
-            "\u001b[31m  ╰─▶ \u001b[0m`Baz` not found\n",
-            "   ╭─[\u001b[36;1;4mline_3\u001b[0m:3:1]\n",
-            " \u001b[2m3\u001b[0m │     Bar();\n",
-            " \u001b[2m4\u001b[0m │     Baz();\n",
-            "   · \u001b[35;1m    ───\u001b[0m\n",
-            " \u001b[2m5\u001b[0m │ }\n",
-            "   ╰────\n",
-            "\u001b[31mQsc.TypeCk.AmbiguousTy\u001b[0m\n",
+            "  x name error\n",
+            "  `-> `Baz` not found\n",
+            "   ,-[line_3:4:5]\n",
+            " 3 |     Bar();\n",
+            " 4 |     Baz();\n",
+            "   :     ^^^\n",
+            " 5 | }\n",
+            "   `----\n",
+            "Qsc.TypeCk.AmbiguousTy\n",
             "\n",
-            "  \u001b[31m×\u001b[0m type error\n",
-            "\u001b[31m  ╰─▶ \u001b[0minsufficient type information to infer type\n",
-            "   ╭─[\u001b[36;1;4mline_3\u001b[0m:2:1]\n",
-            " \u001b[2m2\u001b[0m │ operation Foo() : Unit {\n",
-            " \u001b[2m3\u001b[0m │     Bar();\n",
-            "   · \u001b[35;1m    ─────\u001b[0m\n",
-            " \u001b[2m4\u001b[0m │     Baz();\n",
-            "   ╰────\n",
-            "\u001b[36m  help: \u001b[0mprovide a type annotation\n",
-            "\u001b[31mQsc.TypeCk.AmbiguousTy\u001b[0m\n",
+            "  x type error\n",
+            "  `-> insufficient type information to infer type\n",
+            "   ,-[line_3:3:5]\n",
+            " 2 | operation Foo() : Unit {\n",
+            " 3 |     Bar();\n",
+            "   :     ^^^^^\n",
+            " 4 |     Baz();\n",
+            "   `----\n",
+            "  help: provide a type annotation\n",
+            "Qsc.TypeCk.AmbiguousTy\n",
             "\n",
-            "  \u001b[31m×\u001b[0m type error\n",
-            "\u001b[31m  ╰─▶ \u001b[0minsufficient type information to infer type\n",
-            "   ╭─[\u001b[36;1;4mline_3\u001b[0m:3:1]\n",
-            " \u001b[2m3\u001b[0m │     Bar();\n",
-            " \u001b[2m4\u001b[0m │     Baz();\n",
-            "   · \u001b[35;1m    ─────\u001b[0m\n",
-            " \u001b[2m5\u001b[0m │ }\n",
-            "   ╰────\n",
-            "\u001b[36m  help: \u001b[0mprovide a type annotation\n",
+            "  x type error\n",
+            "  `-> insufficient type information to infer type\n",
+            "   ,-[line_3:4:5]\n",
+            " 3 |     Bar();\n",
+            " 4 |     Baz();\n",
+            "   :     ^^^^^\n",
+            " 5 | }\n",
+            "   `----\n",
+            "  help: provide a type annotation\n",
             "\n"
           ]
         }
@@ -301,24 +426,29 @@
       "id": "e70a95d9",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "name": "stdout",
           "output_type": "stream",
           "text": [
             "Error: Qubit0 released while not in |0⟩ state\n",
             "Call stack:\n",
-            "    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n",
+            "    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs\n",
             "    at Foo in line_4\n",
-            "\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n",
+            "Qsc.Eval.ReleasedQubitNotZero\n",
             "\n",
-            "  \u001b[31m×\u001b[0m runtime error\n",
-            "\u001b[31m  ╰─▶ \u001b[0mQubit0 released while not in |0⟩ state\n",
-            "   ╭─[\u001b[36;1;4mline_4\u001b[0m:1:1]\n",
-            " \u001b[2m1\u001b[0m │ operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n",
-            "   · \u001b[35;1m                         ────────┬───────\u001b[0m\n",
-            "   ·                                  \u001b[35;1m╰── \u001b[35;1mQubit0\u001b[0m\u001b[0m\n",
-            "   ╰────\n",
-            "\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n",
+            "  x runtime error\n",
+            "  `-> Qubit0 released while not in |0⟩ state\n",
+            "   ,-[line_4:1:26]\n",
+            " 1 | operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n",
+            "   :                          ^^^^^^^^|^^^^^^^\n",
+            "   :                                  `-- Qubit0\n",
+            "   `----\n",
+            "  help: qubits should be returned to the |0⟩ state before being released to\n",
             "        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
             "\n"
           ]
@@ -345,16 +475,21 @@
       "id": "d40d86cb",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "ename": "QSharpError",
-          "evalue": "Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Foo in line_5\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit1 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_5\u001b[0m:1:1]\n \u001b[2m1\u001b[0m │ operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n   · \u001b[35;1m                         ────────┬───────\u001b[0m\n   ·                                  \u001b[35;1m╰── \u001b[35;1mQubit1\u001b[0m\u001b[0m\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
+          "evalue": "Error: Qubit0 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs\n    at Foo in line_5\nQsc.Eval.ReleasedQubitNotZero\n\n  x runtime error\n  `-> Qubit0 released while not in |0⟩ state\n   ,-[line_5:1:26]\n 1 | operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n   :                          ^^^^^^^^|^^^^^^^\n   :                                  `-- Qubit0\n   `----\n  help: qubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
           "output_type": "error",
           "traceback": [
             "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
             "\u001b[0;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
-            "Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mqsharp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43meval\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43moperation Foo() : Unit \u001b[39;49m\u001b[38;5;124;43m{\u001b[39;49m\u001b[38;5;124;43m use q = Qubit(); X(q) } Foo()\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
-            "File \u001b[0;32m~/Programming/qsharp/pip/qsharp/_qsharp.py:120\u001b[0m, in \u001b[0;36meval\u001b[0;34m(source)\u001b[0m\n\u001b[1;32m    117\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcallback\u001b[39m(output: Output) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m    118\u001b[0m     \u001b[38;5;28mprint\u001b[39m(output)\n\u001b[0;32m--> 120\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minterpret\u001b[49m\u001b[43m(\u001b[49m\u001b[43msource\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallback\u001b[49m\u001b[43m)\u001b[49m\n",
-            "\u001b[0;31mQSharpError\u001b[0m: Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Foo in line_5\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit1 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_5\u001b[0m:1:1]\n \u001b[2m1\u001b[0m │ operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n   · \u001b[35;1m                         ────────┬───────\u001b[0m\n   ·                                  \u001b[35;1m╰── \u001b[35;1mQubit1\u001b[0m\u001b[0m\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n"
+            "Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m qsharp\u001b[38;5;241m.\u001b[39meval(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moperation Foo() : Unit \u001b[39m\u001b[38;5;124m{\u001b[39m\u001b[38;5;124m use q = Qubit(); X(q) } Foo()\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
+            "File \u001b[0;32m~/.local/lib/python3.12/site-packages/qsharp/_qsharp.py:241\u001b[0m, in \u001b[0;36meval\u001b[0;34m(source)\u001b[0m\n\u001b[1;32m    238\u001b[0m             \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[1;32m    239\u001b[0m     \u001b[38;5;28mprint\u001b[39m(output, flush\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m--> 241\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m get_interpreter()\u001b[38;5;241m.\u001b[39minterpret(source, callback)\n",
+            "\u001b[0;31mQSharpError\u001b[0m: Error: Qubit0 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs\n    at Foo in line_5\nQsc.Eval.ReleasedQubitNotZero\n\n  x runtime error\n  `-> Qubit0 released while not in |0⟩ state\n   ,-[line_5:1:26]\n 1 | operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n   :                          ^^^^^^^^|^^^^^^^\n   :                                  `-- Qubit0\n   `----\n  help: qubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n"
           ]
         }
       ],
@@ -383,66 +518,102 @@
       "outputs": [
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|110⟩</span>\n",
+              "  <td>\n",
+              "    <span>|0⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |0\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
-              "|110⟩: 1.0000+0.0000𝑖"
+              "|0⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "ename": "QSharpCellError",
-          "evalue": "Error: Qubit2 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Bar in line_6\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit2 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_6\u001b[0m:2:1]\n \u001b[2m2\u001b[0m │ operation Bar() : Unit {\n \u001b[2m3\u001b[0m │     use q = Qubit();\n   · \u001b[35;1m    ────────┬───────\u001b[0m\n   ·             \u001b[35;1m╰── \u001b[35;1mQubit2\u001b[0m\u001b[0m\n \u001b[2m4\u001b[0m │     Microsoft.Quantum.Diagnostics.DumpMachine();\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
+          "evalue": "Error: Qubit0 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs\n    at Bar in line_6\nQsc.Eval.ReleasedQubitNotZero\n\n  x runtime error\n  `-> Qubit0 released while not in |0⟩ state\n   ,-[line_6:3:5]\n 2 | operation Bar() : Unit {\n 3 |     use q = Qubit();\n   :     ^^^^^^^^|^^^^^^^\n   :             `-- Qubit0\n 4 |     Microsoft.Quantum.Diagnostics.DumpMachine();\n   `----\n  help: qubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
           "output_type": "error",
           "traceback": [
-            "Error: Qubit2 released while not in |0⟩ state",
+            "Error: Qubit0 released while not in |0⟩ state",
             "Call stack:",
-            "    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs",
+            "    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs",
             "    at Bar in line_6",
-            "\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m",
+            "Qsc.Eval.ReleasedQubitNotZero",
             "",
-            "  \u001b[31m×\u001b[0m runtime error",
-            "\u001b[31m  ╰─▶ \u001b[0mQubit2 released while not in |0⟩ state",
-            "   ╭─[\u001b[36;1;4mline_6\u001b[0m:2:1]",
-            " \u001b[2m2\u001b[0m │ operation Bar() : Unit {",
-            " \u001b[2m3\u001b[0m │     use q = Qubit();",
-            "   · \u001b[35;1m    ────────┬───────\u001b[0m",
-            "   ·             \u001b[35;1m╰── \u001b[35;1mQubit2\u001b[0m\u001b[0m",
-            " \u001b[2m4\u001b[0m │     Microsoft.Quantum.Diagnostics.DumpMachine();",
-            "   ╰────",
-            "\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to",
+            "  x runtime error",
+            "  `-> Qubit0 released while not in |0⟩ state",
+            "   ,-[line_6:3:5]",
+            " 2 | operation Bar() : Unit {",
+            " 3 |     use q = Qubit();",
+            "   :     ^^^^^^^^|^^^^^^^",
+            "   :             `-- Qubit0",
+            " 4 |     Microsoft.Quantum.Diagnostics.DumpMachine();",
+            "   `----",
+            "  help: qubits should be returned to the |0⟩ state before being released to",
             "        satisfy the assumption that allocated qubits start in the |0⟩ state"
           ]
         }
@@ -479,9 +650,6 @@
       "outputs": [
         {
           "data": {
-            "text/html": [
-              "<p>Generating random bit... </p>"
-            ],
             "text/plain": [
               "Generating random bit... "
             ]
@@ -489,52 +657,90 @@
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|1111⟩</span>\n",
+              "  <td>\n",
+              "    <span>|1⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |1\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
-              "|1111⟩: 1.0000+0.0000𝑖"
+              "|1⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<p>Result: One</p>"
-            ],
             "text/plain": [
               "Result: One"
             ]
@@ -542,217 +748,374 @@
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|1111⟩</span>\n",
+              "  <td>\n",
+              "    <span>|0⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |0\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
-              "|1111⟩: 1.0000+0.0000𝑖"
+              "|0⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<p>Result: One</p>"
-            ],
             "text/plain": [
-              "Result: One"
+              "Result: Zero"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|1111⟩</span>\n",
+              "  <td>\n",
+              "    <span>|0⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |0\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
-              "|1111⟩: 1.0000+0.0000𝑖"
+              "|0⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<p>Result: One</p>"
-            ],
             "text/plain": [
-              "Result: One"
+              "Result: Zero"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|1111⟩</span>\n",
+              "  <td>\n",
+              "    <span>|0⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |0\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
-              "|1111⟩: 1.0000+0.0000𝑖"
+              "|0⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<p>Result: One</p>"
-            ],
             "text/plain": [
-              "Result: One"
+              "Result: Zero"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<table>\n",
+            "text/markdown": [
+              "<table class=\"qs-stateTable\">\n",
+              "  <style>\n",
+              "    .qs-stateTable thead tr {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "    .qs-stateTable th {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody {\n",
+              "      pointer-events: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td {\n",
+              "      text-align: left;\n",
+              "      border: none;\n",
+              "    }\n",
+              "    .qs-stateTable tbody td span {\n",
+              "      display: inline-block;\n",
+              "    }\n",
+              "    .qs-stateTable tbody tr:nth-child(even) {\n",
+              "      background-color: var(\n",
+              "        --vscode-list-hoverBackground,\n",
+              "        var(--jp-layout-color1, inherit)\n",
+              "      );\n",
+              "    }\n",
+              "  </style>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
-              "      <th style=\"text-align: left\">Amplitude</th>\n",
-              "      <th style=\"text-align: left\">Measurement Probability</th>\n",
-              "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
+              "      <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
+              "      <th>Amplitude</th>\n",
+              "      <th>Measurement Probability</th>\n",
+              "      <th colspan=\"2\">Phase</th>\n",
               "    </tr>\n",
               "  </thead>\n",
               "  <tbody>\n",
               "    <tr>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|1111⟩</span>\n",
+              "  <td>\n",
+              "    <span>|0⟩</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
+              "  <td>\n",
+              "    <span>1.0000+0.0000𝑖</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left\">\n",
+              "  <td>\n",
               "    <progress max=\"100\" value=\"100\"></progress>\n",
-              "    <span style=\"display: inline-block\">100.0000%</span>\n",
+              "    <span>100.0000%</span>\n",
               "  </td>\n",
-              "  <td style=\"text-align: left; transform: rotate(0.0000rad)\">↑</td>\n",
-              "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">0.0000</span>\n",
+              "  <td style=\"transform: rotate(0.0000rad)\">↑</td>\n",
+              "  <td>\n",
+              "    <span>0.0000</span>\n",
               "  </td>\n",
               "</tr>\n",
               "\n",
               "  </tbody>\n",
-              "</table>\n"
+              "</table>\n",
+              "\n",
+              "\n",
+              "$|\\psi\\rangle = |0\\rangle$"
             ],
             "text/plain": [
               "STATE:\n",
-              "|1111⟩: 1.0000+0.0000𝑖"
+              "|0⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
         },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
-            "text/html": [
-              "<p>Result: One</p>"
-            ],
             "text/plain": [
-              "Result: One"
+              "Result: Zero"
             ]
           },
           "metadata": {},
           "output_type": "display_data"
+        },
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
         }
       ],
       "source": [
@@ -813,10 +1176,15 @@
       "id": "9a9f5335",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "data": {
             "text/plain": [
-              "[One, Zero, Zero, Zero, Zero, Zero, Zero, One, Zero, Zero]"
+              "[One, Zero, One, Zero, One, Zero, One, One, Zero, One]"
             ]
           },
           "execution_count": 11,
@@ -840,7 +1208,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": null,
+      "execution_count": 12,
       "id": "7bd77379",
       "metadata": {},
       "outputs": [
@@ -848,17 +1216,21 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "Requirement already satisfied: matplotlib in /opt/homebrew/lib/python3.11/site-packages (3.8.2)\n",
-            "Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.2.0)\n",
-            "Requirement already satisfied: cycler>=0.10 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (0.12.1)\n",
-            "Requirement already satisfied: fonttools>=4.22.0 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (4.46.0)\n",
-            "Requirement already satisfied: kiwisolver>=1.3.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.4.5)\n",
-            "Requirement already satisfied: numpy<2,>=1.21 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.23.5)\n",
-            "Requirement already satisfied: packaging>=20.0 in /Users/swernli/Library/Python/3.11/lib/python/site-packages (from matplotlib) (23.2)\n",
-            "Requirement already satisfied: pillow>=8 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (10.1.0)\n",
-            "Requirement already satisfied: pyparsing>=2.3.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (3.1.1)\n",
-            "Requirement already satisfied: python-dateutil>=2.7 in /Users/swernli/Library/Python/3.11/lib/python/site-packages (from matplotlib) (2.8.2)\n",
-            "Requirement already satisfied: six>=1.5 in /opt/homebrew/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n",
+            "Defaulting to user installation because normal site-packages is not writeable\n",
+            "Requirement already satisfied: matplotlib in /home/vscode/.local/lib/python3.12/site-packages (3.8.2)\n",
+            "Requirement already satisfied: contourpy>=1.0.1 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (1.2.0)\n",
+            "Requirement already satisfied: cycler>=0.10 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (0.12.1)\n",
+            "Requirement already satisfied: fonttools>=4.22.0 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (4.47.2)\n",
+            "Requirement already satisfied: kiwisolver>=1.3.1 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (1.4.5)\n",
+            "Requirement already satisfied: numpy<2,>=1.21 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (1.26.3)\n",
+            "Requirement already satisfied: packaging>=20.0 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (23.2)\n",
+            "Requirement already satisfied: pillow>=8 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (10.2.0)\n",
+            "Requirement already satisfied: pyparsing>=2.3.1 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (3.1.1)\n",
+            "Requirement already satisfied: python-dateutil>=2.7 in /home/vscode/.local/lib/python3.12/site-packages (from matplotlib) (2.8.2)\n",
+            "Requirement already satisfied: six>=1.5 in /home/vscode/.local/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n",
+            "\n",
+            "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.2.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
+            "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
             "Note: you may need to restart the kernel to use updated packages.\n"
           ]
         }
@@ -869,13 +1241,13 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 12,
+      "execution_count": 13,
       "id": "83cee4e8",
       "metadata": {},
       "outputs": [
         {
           "data": {
-            "image/png": "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8skn4/TTT48RI0bE1q1bizEfAFBCCg6L2bNnx3e+850YN25cnHLKKXH77bfHYYcdFr/61a+KMR8AUELaFrLzRx99FOvXr4/a2trmbYccckicf/75sWrVqk88JpfLRS6Xa15vaGiIiIhsNtuaefeoKffP5M8JB5NivO/2B+912L1ivc//87z5fH6P+xUUFu+++240NjbGUUcd1WL7UUcdFS+88MInHlNXVxfTp0/fZXt1dXUhpwYSqJyzvycAiq3Y7/Pt27dHZWXlbh8vKCxao7a2NqZOndq83tTUFNu2bYvOnTtHWVlZsU/PfpLNZqO6ujrq6+ujoqJif48DFIn3+udHPp+P7du3R1VV1R73KygsjjzyyGjTpk1s2bKlxfYtW7ZEt27dPvGYTCYTmUymxbaOHTsWclpKWEVFhR828Dngvf75sKcrFf9R0M2b7dq1i/79+8ejjz7avK2pqSkeffTRGDRoUOETAgAHlYI/Cpk6dWqMHTs2BgwYEGeddVbMmTMndu7cGePGjSvGfABACSk4LL7xjW/EO++8EzfeeGNs3rw5vvjFL8aSJUt2uaGTz7dMJhM33XTTLh+DAQcX73X+V1n+0/7fCADAXvJdIQBAMsICAEhGWAAAyQgLACAZYcEeLV++PMrKyna7DB06dH+PCBRBfX19jB8/PqqqqqJdu3bRq1evuOaaa+K9997b36NxgBMW7NE555wTb7/99i7LHXfcEWVlZTFx4sRWPe9HH32UeFIglZdffjkGDBgQL730Utx3332xcePGuP3225t/GeK2bdv294gcwIQFe9SuXbvo1q1bi+X999+P6667Lq6//vq4+OKLIyLiueeei5qamujQoUMcddRR8a1vfSvefffd5ucZMmRIXHXVVTFlypQ48sgjY8SIERER8dhjj8VZZ50VmUwmunfvHtOmTYt//etf++W1Av82adKkaNeuXfzpT3+Kc889N3r27Bk1NTWxbNmyePPNN+OHP/xhRET07t07fvrTn8b48eOjvLw8evbsGXfeeWeL56qvr49LLrkkOnbsGJ06dYrRo0fHq6++uh9eFZ8VYUFBPvjggxg9enQMGTIkZsyY0bztvPPOizPOOCPWrVsXS5YsiS1btsQll1zS4th58+ZFu3btYuXKlXH77bfHm2++GV/72tdi4MCB8cwzz8TcuXPj7rvvjp/85Cf746UBEbFt27ZYunRpTJw4Mdq3b9/isW7dusWYMWNi0aJFzV+d/bOf/SwGDBgQTz31VEycODGuvPLK2LBhQ0REfPzxxzFixIgoLy+Pxx9/PFauXBkdOnSIr371q65aHszysJcaGxvzNTU1+T59+uSz2Wzz9hkzZuSHDx/eYt/6+vp8ROQ3bNiQz+fz+XPPPTd/xhlntNjn+uuvz5900kn5pqam5m233XZbvkOHDvnGxsYivhJgd1avXp2PiPzixYs/8fHZs2fnIyK/ZcuWfK9evfKXXXZZ82NNTU35rl275ufOnZvP5/P5X//617u8x3O5XL59+/b5pUuXFvV1sP8U/WvTOXhcf/31sWrVqlizZk2Ul5c3b3/mmWfiL3/5S3To0GGXYzZt2hQnnnhiRET079+/xWPPP/98DBo0KMrKypq3DR48OHbs2BFvvPFG9OzZs0ivBPg0+b38pcz9+vVr/nNZWVl069Yttm7dGhH//tmwcePGFj8vIiI+/PDD2LRpU7phOaAIC/bKwoUL45ZbbomHHnooTjjhhBaP7dixI0aNGhU333zzLsd17969+c+HH3540ecE9s3xxx8fZWVl8fzzz8dFF120y+PPP/98HHHEEdGlS5eIiPjCF77Q4vGysrJoamqKiH//bOjfv3/Mnz9/l+f5z/EcfIQFn+rpp5+OCRMmxKxZs5pvuvxvZ555Ztx///3Ru3fvaNt27/9K9enTJ+6///7I5/PNVy1WrlwZ5eXl0aNHj2TzA3uvc+fOMWzYsPjlL38Z1157bYv7LDZv3hzz58+Pyy+/vMWVxt0588wzY9GiRdG1a9eoqKgo5tgcQNy8yR69++67ceGFF8aQIUPisssui82bN7dY3nnnnZg0aVJs27YtLr300li7dm1s2rQpli5dGuPGjYvGxsbdPvfEiROjvr4+rr766njhhRfiwQcfjJtuuimmTp0ahxzirybsL7feemvkcrkYMWJErFixIurr62PJkiUxbNiwOProo2PmzJl79TxjxoyJI488MkaPHh2PP/54vPLKK7F8+fKYPHlyvPHGG0V+FewvfnqzRw899FC89tpr8fDDD0f37t13WQYOHBhVVVWxcuXKaGxsjOHDh8dpp50WU6ZMiY4dO+4xEI4++uh4+OGHY82aNXH66afHFVdcERMmTIgf/ehHn+ErBP7XCSecEOvWrYtjjz02LrnkkjjuuOPiu9/9bgwdOjRWrVoVnTp12qvnOeyww2LFihXRs2fP+PrXvx59+vSJCRMmxIcffugKxkHM16YDAMm4YgEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkvl/ckGCHgMKFIgAAAAASUVORK5CYII=",
+            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGdCAYAAABO2DpVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAWKElEQVR4nO3de4xUhdn48Wd1uyPK7iIIArKA1htesApoKG0Fq1KqBm2ijcFKkLRR1gsSkwptao3VxaSvsU3teomtTaNCGkNtqkLVFDAIFbAabZUKal0vgIruAm8cLTu/P5ru790iyMAzwODnk5yEOXPOnGcSZvebM2dnakqlUikAABLst6cHAAD2HcICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEhTu7sP2NnZGW+//XbU19dHTU3N7j48ALATSqVSbNy4MQYOHBj77bft8xK7PSzefvvtaGpq2t2HBQAStLW1xaBBg7Z5/24Pi/r6+oj492ANDQ27+/AAwE7o6OiIpqamrt/j27Lbw+I/b380NDQICwCoMp91GYOLNwGANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEhTdli89dZbcckll0SfPn2iR48eceKJJ8aKFSsqMRsAUGXK+q6QDz74IMaMGRPjxo2Lxx57LPr27RuvvPJKHHzwwZWaDwCoImWFxa233hpNTU3x61//umvd4Ycfnj4UAFCdynor5A9/+EOMHDkyLrzwwujXr1+cfPLJcc8992x3n2KxGB0dHd0WAGDfVNYZi1dffTVaW1tjxowZMWvWrFi+fHlcffXVUVdXF5MnT/7UfVpaWuLGG29MGRYgImLo9Y/s6RFgr/X67HP26PFrSqVSaUc3rquri5EjR8bTTz/dte7qq6+O5cuXx9KlSz91n2KxGMViset2R0dHNDU1RXt7ezQ0NOzC6MDnlbCAbatUWHR0dERjY+Nn/v4u662QAQMGxHHHHddt3bBhw+KNN97Y5j6FQiEaGhq6LQDAvqmssBgzZkysWrWq27p//OMfMWTIkNShAIDqVFZYXHvttbFs2bK45ZZbYvXq1fHAAw/E3XffHc3NzZWaDwCoImWFxahRo2LevHnx4IMPxgknnBA33XRT3H777TFp0qRKzQcAVJGy/iokIuLcc8+Nc889txKzAABVzneFAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkKassPjxj38cNTU13ZZjjz22UrMBAFWmttwdjj/++HjiiSf+/wPUlv0QAMA+quwqqK2tjf79+1diFgCgypV9jcUrr7wSAwcOjCOOOCImTZoUb7zxxna3LxaL0dHR0W0BAPZNZYXFaaedFvfdd1/Mnz8/Wltb47XXXouvfvWrsXHjxm3u09LSEo2NjV1LU1PTLg8NAOydakqlUmlnd/7www9jyJAhcdttt8XUqVM/dZtisRjFYrHrdkdHRzQ1NUV7e3s0NDTs7KGBz7Gh1z+yp0eAvdbrs8+pyON2dHREY2PjZ/7+3qUrL3v16hVHH310rF69epvbFAqFKBQKu3IYAKBK7NLnWGzatCnWrFkTAwYMyJoHAKhiZYXFddddF4sWLYrXX389nn766bjgggti//33j4svvrhS8wEAVaSst0LefPPNuPjii+P999+Pvn37xle+8pVYtmxZ9O3bt1LzAQBVpKywmDNnTqXmAAD2Ab4rBABIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIs0thMXv27KipqYnp06cnjQMAVLOdDovly5fHXXfdFcOHD8+cBwCoYjsVFps2bYpJkybFPffcEwcffHD2TABAldqpsGhubo5zzjknzjzzzOx5AIAqVlvuDnPmzIlnn302li9fvkPbF4vFKBaLXbc7OjrKPSQAUCXKCou2tra45ppr4vHHH48DDjhgh/ZpaWmJG2+8caeGK9fQ6x/ZLceBavX67HP29AjAPq6st0JWrlwZ69evj1NOOSVqa2ujtrY2Fi1aFD//+c+jtrY2tmzZstU+M2fOjPb29q6lra0tbXgAYO9S1hmLr3/96/HCCy90WzdlypQ49thj4/vf/37sv//+W+1TKBSiUCjs2pQAQFUoKyzq6+vjhBNO6LbuoIMOij59+my1HgD4/PHJmwBAmrL/KuS/LVy4MGEMAGBf4IwFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAacoKi9bW1hg+fHg0NDREQ0NDjB49Oh577LFKzQYAVJmywmLQoEExe/bsWLlyZaxYsSLOOOOMmDhxYvztb3+r1HwAQBWpLWfj8847r9vtm2++OVpbW2PZsmVx/PHHpw4GAFSfssLi/9qyZUv87ne/i82bN8fo0aO3uV2xWIxisdh1u6OjY2cPCQDs5cq+ePOFF16Inj17RqFQiMsvvzzmzZsXxx133Da3b2lpicbGxq6lqalplwYGAPZeZYfFMcccE88991z85S9/iSuuuCImT54cf//737e5/cyZM6O9vb1raWtr26WBAYC9V9lvhdTV1cWRRx4ZEREjRoyI5cuXx89+9rO46667PnX7QqEQhUJh16YEAKrCLn+ORWdnZ7drKACAz6+yzljMnDkzJkyYEIMHD46NGzfGAw88EAsXLowFCxZUaj4AoIqUFRbr16+PSy+9NN55551obGyM4cOHx4IFC+Kss86q1HwAQBUpKyzuvffeSs0BAOwDfFcIAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAacoKi5aWlhg1alTU19dHv3794vzzz49Vq1ZVajYAoMqUFRaLFi2K5ubmWLZsWTz++OPxySefxNlnnx2bN2+u1HwAQBWpLWfj+fPnd7t93333Rb9+/WLlypXxta99LXUwAKD6lBUW/629vT0iInr37r3NbYrFYhSLxa7bHR0du3JIAGAvttMXb3Z2dsb06dNjzJgxccIJJ2xzu5aWlmhsbOxampqadvaQAMBebqfDorm5OV588cWYM2fOdrebOXNmtLe3dy1tbW07e0gAYC+3U2+FXHnllfHHP/4xFi9eHIMGDdrutoVCIQqFwk4NBwBUl7LColQqxVVXXRXz5s2LhQsXxuGHH16puQCAKlRWWDQ3N8cDDzwQDz/8cNTX18fatWsjIqKxsTF69OhRkQEBgOpR1jUWra2t0d7eHmPHjo0BAwZ0LXPnzq3UfABAFSn7rRAAgG3xXSEAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQJqyw2Lx4sVx3nnnxcCBA6OmpiZ+//vfV2AsAKAalR0WmzdvjpNOOinuuOOOSswDAFSx2nJ3mDBhQkyYMKESswAAVc41FgBAmrLPWJSrWCxGsVjsut3R0VHpQwIAe0jFz1i0tLREY2Nj19LU1FTpQwIAe0jFw2LmzJnR3t7etbS1tVX6kADAHlLxt0IKhUIUCoVKHwYA2AuUHRabNm2K1atXd91+7bXX4rnnnovevXvH4MGDU4cDAKpL2WGxYsWKGDduXNftGTNmRETE5MmT47777ksbDACoPmWHxdixY6NUKlViFgCgyvkcCwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANLsVFjccccdMXTo0DjggAPitNNOi2eeeSZ7LgCgCpUdFnPnzo0ZM2bEDTfcEM8++2ycdNJJMX78+Fi/fn0l5gMAqkjZYXHbbbfFd7/73ZgyZUocd9xxceedd8aBBx4Yv/rVryoxHwBQRWrL2fjjjz+OlStXxsyZM7vW7bfffnHmmWfG0qVLP3WfYrEYxWKx63Z7e3tERHR0dOzMvNvVWfzf9MeEfUklXnd7gtc6bFulXuf/edxSqbTd7coKi/feey+2bNkShx56aLf1hx56aLz88sufuk9LS0vceOONW61vamoq59BAgsbb9/QEQKVV+nW+cePGaGxs3Ob9ZYXFzpg5c2bMmDGj63ZnZ2ds2LAh+vTpEzU1NZU+PHtIR0dHNDU1RVtbWzQ0NOzpcYAK8Vr//CiVSrFx48YYOHDgdrcrKywOOeSQ2H///WPdunXd1q9bty769+//qfsUCoUoFArd1vXq1aucw1LFGhoa/LCBzwGv9c+H7Z2p+I+yLt6sq6uLESNGxJNPPtm1rrOzM5588skYPXp0+RMCAPuUst8KmTFjRkyePDlGjhwZp556atx+++2xefPmmDJlSiXmAwCqSNlh8e1vfzvefffd+NGPfhRr166NL33pSzF//vytLujk861QKMQNN9yw1dtgwL7Fa53/VlP6rL8bAQDYQb4rBABIIywAgDTCAgBIIywAgDTCgu1auHBh1NTUbHMZN27cnh4RqIC2tra47LLLYuDAgVFXVxdDhgyJa665Jt5///09PRp7OWHBdn35y1+Od955Z6vlrrvuipqampg2bdpOPe7HH3+cPCmQ5dVXX42RI0fGK6+8Eg8++GCsXr067rzzzq4PQ9ywYcOeHpG9mLBgu+rq6qJ///7dlg8++CCuu+66mDVrVlx44YUREfHiiy/GhAkTomfPnnHooYfGd77znXjvvfe6Hmfs2LFx5ZVXxvTp0+OQQw6J8ePHR0TEokWL4tRTT41CoRADBgyI66+/Pv71r3/tkecK/Ftzc3PU1dXFn/70pzj99NNj8ODBMWHChHjiiSfirbfeih/84AcRETF06NC45ZZb4rLLLov6+voYPHhw3H333d0eq62tLS666KLo1atX9O7dOyZOnBivv/76HnhW7C7CgrJ8+OGHMXHixBg7dmzcdNNNXevOOOOMOPnkk2PFihUxf/78WLduXVx00UXd9v3Nb34TdXV1sWTJkrjzzjvjrbfeim9+85sxatSoeP7556O1tTXuvffe+MlPfrInnhoQERs2bIgFCxbEtGnTokePHt3u69+/f0yaNCnmzp3b9dXZ//M//xMjR46Mv/71rzFt2rS44oorYtWqVRER8cknn8T48eOjvr4+nnrqqViyZEn07NkzvvGNbzhruS8rwQ7asmVLacKECaVhw4aVOjo6utbfdNNNpbPPPrvbtm1tbaWIKK1atapUKpVKp59+eunkk0/uts2sWbNKxxxzTKmzs7Nr3R133FHq2bNnacuWLRV8JsC2LFu2rBQRpXnz5n3q/bfddlspIkrr1q0rDRkypHTJJZd03dfZ2Vnq169fqbW1tVQqlUq//e1vt3qNF4vFUo8ePUoLFiyo6PNgz6n416az75g1a1YsXbo0nnnmmaivr+9a//zzz8ef//zn6Nmz51b7rFmzJo4++uiIiBgxYkS3+1566aUYPXp01NTUdK0bM2ZMbNq0Kd58880YPHhwhZ4J8FlKO/ihzMOHD+/6d01NTfTv3z/Wr18fEf/+2bB69epuPy8iIj766KNYs2ZN3rDsVYQFO2TOnDnx05/+NB555JE46qijut23adOmOO+88+LWW2/dar8BAwZ0/fuggw6q+JzArjnyyCOjpqYmXnrppbjgggu2uv+ll16Kgw8+OPr27RsREV/4whe63V9TUxOdnZ0R8e+fDSNGjIj7779/q8f5z/7se4QFn+m5556LqVOnxuzZs7suuvy/TjnllHjooYdi6NChUVu74/+lhg0bFg899FCUSqWusxZLliyJ+vr6GDRoUNr8wI7r06dPnHXWWfHLX/4yrr322m7XWaxduzbuv//+uPTSS7udadyWU045JebOnRv9+vWLhoaGSo7NXsTFm2zXe++9F+eff36MHTs2Lrnkkli7dm235d13343m5ubYsGFDXHzxxbF8+fJYs2ZNLFiwIKZMmRJbtmzZ5mNPmzYt2tra4qqrroqXX345Hn744bjhhhtixowZsd9+/mvCnvKLX/wiisVijB8/PhYvXhxtbW0xf/78OOuss+Kwww6Lm2++eYceZ9KkSXHIIYfExIkT46mnnorXXnstFi5cGFdffXW8+eabFX4W7Cl+erNdjzzySPzzn/+MRx99NAYMGLDVMmrUqBg4cGAsWbIktmzZEmeffXaceOKJMX369OjVq9d2A+Gwww6LRx99NJ555pk46aST4vLLL4+pU6fGD3/4w934DIH/dtRRR8WKFSviiCOOiIsuuii++MUvxve+970YN25cLF26NHr37r1Dj3PggQfG4sWLY/DgwfGtb30rhg0bFlOnTo2PPvrIGYx9mK9NBwDSOGMBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAmv8HvX2m8Y1VbDQAAAAASUVORK5CYII=",
             "text/plain": [
               "<Figure size 640x480 with 1 Axes>"
             ]
@@ -903,7 +1275,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": null,
+      "execution_count": 14,
       "id": "eaf766f4",
       "metadata": {},
       "outputs": [
@@ -911,12 +1283,16 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "Requirement already satisfied: pandas in /opt/homebrew/lib/python3.11/site-packages (2.1.3)\n",
-            "Requirement already satisfied: numpy<2,>=1.23.2 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (1.23.5)\n",
-            "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/swernli/Library/Python/3.11/lib/python/site-packages (from pandas) (2.8.2)\n",
-            "Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2023.3.post1)\n",
-            "Requirement already satisfied: tzdata>=2022.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2023.3)\n",
-            "Requirement already satisfied: six>=1.5 in /opt/homebrew/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
+            "Defaulting to user installation because normal site-packages is not writeable\n",
+            "Requirement already satisfied: pandas in /home/vscode/.local/lib/python3.12/site-packages (2.2.0)\n",
+            "Requirement already satisfied: numpy<2,>=1.26.0 in /home/vscode/.local/lib/python3.12/site-packages (from pandas) (1.26.3)\n",
+            "Requirement already satisfied: python-dateutil>=2.8.2 in /home/vscode/.local/lib/python3.12/site-packages (from pandas) (2.8.2)\n",
+            "Requirement already satisfied: pytz>=2020.1 in /home/vscode/.local/lib/python3.12/site-packages (from pandas) (2023.3.post1)\n",
+            "Requirement already satisfied: tzdata>=2022.7 in /home/vscode/.local/lib/python3.12/site-packages (from pandas) (2023.4)\n",
+            "Requirement already satisfied: six>=1.5 in /home/vscode/.local/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
+            "\n",
+            "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.2.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
+            "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
             "Note: you may need to restart the kernel to use updated packages.\n"
           ]
         }
@@ -927,23 +1303,37 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 13,
+      "execution_count": 15,
       "id": "8095640a",
       "metadata": {},
       "outputs": [
+        {
+          "name": "stderr",
+          "output_type": "stream",
+          "text": [
+            "/tmp/ipykernel_15611/2211578213.py:1: DeprecationWarning: \n",
+            "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n",
+            "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n",
+            "but was not found to be installed on your system.\n",
+            "If this would cause problems for you,\n",
+            "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n",
+            "        \n",
+            "  import pandas\n"
+          ]
+        },
         {
           "data": {
             "text/plain": [
               "<Axes: >"
             ]
           },
-          "execution_count": 13,
+          "execution_count": 15,
           "metadata": {},
           "output_type": "execute_result"
         },
         {
           "data": {
-            "image/png": "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CjUbAFBi8gqLJ554Iqqrq2PFihXx97//PT755JMYM2ZM7Nixo1DzAQAlpFM+Oz/00EOt1ufPnx89evSI1atXxwknnJB0MACg9OQVFv+tvr4+IiIOOuigXe6Ty+Uil8u1rDc0NOzOKQGAdqzNYdHU1BRXXXVVjBo1Ko444ohd7ldbWxvTp09v62kASkK/6x4o9gjsQetnji/2CO1Wm78VUl1dHS+88EIsWrToC/erqamJ+vr6lqWurq6tpwQA2rk2XbG4/PLL469//WssXbo0evfu/YX7ZrPZyGazbRoOACgteYVFc3NzXHHFFXHvvffG448/Hv379y/UXABACcorLKqrq2PhwoVx//33R1lZWWzevDkiIioqKqJz584FGRAAKB15PWMxd+7cqK+vj+985zvRq1evluWee+4p1HwAQAnJ+1YIAMCueFcIAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQTN5hsXTp0jjjjDOisrIyMplM3HfffQUYCwAoRXmHxY4dO2LYsGFxyy23FGIeAKCEdcr3gHHjxsW4ceMKMQsAUOLyDot85XK5yOVyLesNDQ2FPiUAUCQFf3iztrY2KioqWpaqqqpCnxIAKJKCh0VNTU3U19e3LHV1dYU+JQBQJAW/FZLNZiObzRb6NABAO+DvWAAAyeR9xWL79u3x2muvtayvW7cunn322TjooIOiT58+SYcDAEpL3mGxatWqOPHEE1vWp0yZEhEREydOjPnz5ycbDAAoPXmHxXe+851obm4uxCwAQInzjAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJBMm8LilltuiX79+sV+++0Xxx13XDz99NOp5wIASlDeYXHPPffE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+vXrdxkW69ati4MOOmgPT0V75VshJPHYY4/FueeeG9OmTfvczxsbG30rBErU2LFj4/rrr4+PP/54p89yuVxMnTo1Tj311CJMRnvkVghJdOjQIebNmxfXXHNNnHTSSXHnnXe2egHZ22+/HZWVldHY2FjEKYG22LhxY4wYMSKy2WxUV1fH4MGDo7m5Of71r3/Fb37zm8jlcrFq1aqoqqoq9qi0A8KCJDp06BCbN2+O9957L84666zIZrNx//33t7ysSFhAaVu3bl1MmjQpHnnkkfjs10Ymk4lTTjkl5syZEwMHDizyhLQXwoIkPguLHj16RH19fVxwwQXx1FNPxT333BMnn3yysIC9xPvvvx9r166NiP+8O8SzFfw3z1iQXEVFRTzwwANxySWXxGmnnea77bAX6dKlSxx77LFx7LHHigo+l2+FkMR/v7k0k8nEzJkz46ijjoof/ehH8eijjxZpMgD2JFcsSGJXd9TOP//8ePLJJ+P555/fwxMBUAyuWJDEY489tsvLokcddVSsXr06HnjggT08FQB7moc3AYBk3AoBAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkMz/A0oPESovBdCBAAAAAElFTkSuQmCC",
+            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGvCAYAAAAHapzzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAXaklEQVR4nO3da4wVhfn48ecgckS7uyIIQlhRihVRwQpqCNWoqHi3NqmX0JRYa6KsV2JiqVG0xi6+qKUNFqmxJU1jMW1ibWrFCyLGAhYw3qsFBV2kiIruAo1HZff3onH//y1ge5Zn9+yBzyeZFzNnxnlMXPebmTk7hba2trYAAEjQq9IDAAC7D2EBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKTp3d0nbG1tjfXr10dNTU0UCoXuPj0A0AltbW2xefPmGDJkSPTqtfPrEt0eFuvXr4/6+vruPi0AkKCpqSmGDh2608+7PSxqamoi4t+D1dbWdvfpAYBOaGlpifr6+vbf4zvT7WHxxe2P2tpaYQEAVea/Pcbg4U0AII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSlB0W7777bnznO9+J/v37R9++fePoo4+OFStWdMVsAECVKetdIR999FFMmDAhTjnllHj00UfjwAMPjFWrVkW/fv26aj4AoIqUFRZ33XVX1NfXx69//ev2bYceemj6UABAdSrrVsif/vSnGDduXHz729+OgQMHxte//vW47777vvSYUqkULS0tHRYAYPdU1hWLt956K+bMmRPTpk2LH/7wh7F8+fK49tpro0+fPjFlypQdHtPY2Bi33357yrAAPdUhP3ik0iPQjdbOPKfSI/RYhba2trb/dec+ffrEuHHjYsmSJe3brr322li+fHksXbp0h8eUSqUolUrt6y0tLVFfXx/Nzc1RW1u7C6MD9BzCYs+yJ4ZFS0tL1NXV/dff32XdChk8eHCMGjWqw7Yjjjgi3nnnnZ0eUywWo7a2tsMCAOyeygqLCRMmxBtvvNFh2z/+8Y8YNmxY6lAAQHUqKyxuuOGGWLZsWfz4xz+O1atXxwMPPBC//OUvo6GhoavmAwCqSFlhcdxxx8VDDz0Uv/vd7+Koo46KO+64I2bNmhWTJ0/uqvkAgCpS1rdCIiLOPffcOPfcc7tiFgCgynlXCACQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGnKCovbbrstCoVCh2XkyJFdNRsAUGV6l3vAkUceGU8++eT/+wf0LvsfAQDspsqugt69e8dBBx3UFbMAAFWu7GcsVq1aFUOGDInhw4fH5MmT45133vnS/UulUrS0tHRYAIDdU1lhccIJJ8S8efNiwYIFMWfOnFizZk2ceOKJsXnz5p0e09jYGHV1de1LfX39Lg8NAPRMhba2trbOHvzxxx/HsGHD4u67747LL798h/uUSqUolUrt6y0tLVFfXx/Nzc1RW1vb2VMD9CiH/OCRSo9AN1o785xKj9DtWlpaoq6u7r/+/t6lJy/333//+NrXvharV6/e6T7FYjGKxeKunAYAqBK79HcstmzZEm+++WYMHjw4ax4AoIqVFRY33nhjLF68ONauXRtLliyJCy+8MPbaa6+49NJLu2o+AKCKlHUrZN26dXHppZfGhx9+GAceeGB84xvfiGXLlsWBBx7YVfMBAFWkrLCYP39+V80BAOwGvCsEAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEizS2Exc+bMKBQKcf311yeNAwBUs06HxfLly2Pu3LkxevTozHkAgCrWqbDYsmVLTJ48Oe67777o169f9kwAQJXqVFg0NDTEOeecE6eddlr2PABAFetd7gHz58+P559/PpYvX/4/7V8qlaJUKrWvt7S0lHtKAKBKlBUWTU1Ncd1118UTTzwR++yzz/90TGNjY9x+++2dGm53c8gPHqn0CHSjtTPPqfQIAN2urFshK1eujI0bN8axxx4bvXv3jt69e8fixYvj5z//efTu3Tu2bdu23THTp0+P5ubm9qWpqSlteACgZynrisXEiRPj5Zdf7rDtsssui5EjR8ZNN90Ue+2113bHFIvFKBaLuzYlAFAVygqLmpqaOOqoozps22+//aJ///7bbQcA9jz+8iYAkKbsb4X8p6effjphDABgd+CKBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGnKCos5c+bE6NGjo7a2Nmpra2P8+PHx6KOPdtVsAECVKSsshg4dGjNnzoyVK1fGihUr4tRTT40LLrggXn311a6aDwCoIr3L2fm8887rsH7nnXfGnDlzYtmyZXHkkUemDgYAVJ+ywuL/t23btvj9738fW7dujfHjx+90v1KpFKVSqX29paWls6cEAHq4sh/efPnll+MrX/lKFIvFuPLKK+Ohhx6KUaNG7XT/xsbGqKura1/q6+t3aWAAoOcqOywOP/zweOGFF+K5556Lq666KqZMmRKvvfbaTvefPn16NDc3ty9NTU27NDAA0HOVfSukT58+MWLEiIiIGDt2bCxfvjx+9rOfxdy5c3e4f7FYjGKxuGtTAgBVYZf/jkVra2uHZygAgD1XWVcspk+fHmeddVYcfPDBsXnz5njggQfi6aefjscee6yr5gMAqkhZYbFx48b47ne/G//85z+jrq4uRo8eHY899licfvrpXTUfAFBFygqL+++/v6vmAAB2A94VAgCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQJqywqKxsTGOO+64qKmpiYEDB8Y3v/nNeOONN7pqNgCgypQVFosXL46GhoZYtmxZPPHEE/HZZ5/FGWecEVu3bu2q+QCAKtK7nJ0XLFjQYX3evHkxcODAWLlyZZx00kmpgwEA1aessPhPzc3NERFxwAEH7HSfUqkUpVKpfb2lpWVXTgkA9GCdfniztbU1rr/++pgwYUIcddRRO92vsbEx6urq2pf6+vrOnhIA6OE6HRYNDQ3xyiuvxPz58790v+nTp0dzc3P70tTU1NlTAgA9XKduhVx99dXx5z//OZ555pkYOnTol+5bLBajWCx2ajgAoLqUFRZtbW1xzTXXxEMPPRRPP/10HHrooV01FwBQhcoKi4aGhnjggQfi4YcfjpqamtiwYUNERNTV1UXfvn27ZEAAoHqU9YzFnDlzorm5OU4++eQYPHhw+/Lggw921XwAQBUp+1YIAMDOeFcIAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJCm7LB45pln4rzzzoshQ4ZEoVCIP/7xj10wFgBQjcoOi61bt8aYMWPinnvu6Yp5AIAq1rvcA84666w466yzumIWAKDKecYCAEhT9hWLcpVKpSiVSu3rLS0tXX1KAKBCuvyKRWNjY9TV1bUv9fX1XX1KAKBCujwspk+fHs3Nze1LU1NTV58SAKiQLr8VUiwWo1gsdvVpAIAeoOyw2LJlS6xevbp9fc2aNfHCCy/EAQccEAcffHDqcABAdSk7LFasWBGnnHJK+/q0adMiImLKlCkxb968tMEAgOpTdlicfPLJ0dbW1hWzAABVzt+xAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAIE2nwuKee+6JQw45JPbZZ5844YQT4m9/+1v2XABAFSo7LB588MGYNm1azJgxI55//vkYM2ZMTJo0KTZu3NgV8wEAVaTssLj77rvjiiuuiMsuuyxGjRoV9957b+y7777xq1/9qivmAwCqSO9ydv70009j5cqVMX369PZtvXr1itNOOy2WLl26w2NKpVKUSqX29ebm5oiIaGlp6cy8Va219K9Kj0A32hP/G9+T+fnes+yJP99f/Du3tbV96X5lhcUHH3wQ27Zti0GDBnXYPmjQoHj99dd3eExjY2Pcfvvt222vr68v59RQdepmVXoCoKvsyT/fmzdvjrq6up1+XlZYdMb06dNj2rRp7eutra2xadOm6N+/fxQKha4+PRXW0tIS9fX10dTUFLW1tZUeB0jk53vP0tbWFps3b44hQ4Z86X5lhcWAAQNir732ivfee6/D9vfeey8OOuigHR5TLBajWCx22Lb//vuXc1p2A7W1tf7HA7spP997ji+7UvGFsh7e7NOnT4wdOzYWLlzYvq21tTUWLlwY48ePL39CAGC3UvatkGnTpsWUKVNi3Lhxcfzxx8esWbNi69atcdlll3XFfABAFSk7LC6++OJ4//3349Zbb40NGzbEMcccEwsWLNjugU6I+PetsBkzZmx3Owyofn6+2ZFC23/73ggAwP/Iu0IAgDTCAgBIIywAgDTCAgBIIywAgDTCgi6zbt26WLduXaXHABJ9/vnn8eSTT8bcuXNj8+bNERGxfv362LJlS4Uno6cQFqRqbW2NH/3oR1FXVxfDhg2LYcOGxf777x933HFHtLa2Vno8YBe8/fbbcfTRR8cFF1wQDQ0N8f7770dExF133RU33nhjhaejp+jyl5CxZ7n55pvj/vvvj5kzZ8aECRMiIuLZZ5+N2267LT755JO48847Kzwh0FnXXXddjBs3Ll588cXo379/+/YLL7wwrrjiigpORk/iD2SRasiQIXHvvffG+eef32H7ww8/HFOnTo133323QpMBu6p///6xZMmSOPzww6OmpiZefPHFGD58eKxduzZGjRoV//rXvyo9Ij2AWyGk2rRpU4wcOXK77SNHjoxNmzZVYCIgS2tra2zbtm277evWrYuampoKTERPJCxINWbMmJg9e/Z222fPnh1jxoypwERAljPOOCNmzZrVvl4oFGLLli0xY8aMOPvssys3GD2KWyGkWrx4cZxzzjlx8MEHx/jx4yMiYunSpdHU1BR/+ctf4sQTT6zwhEBnrVu3LiZNmhRtbW2xatWqGDduXKxatSoGDBgQzzzzTAwcOLDSI9IDCAvSrV+/Pu655554/fXXIyLiiCOOiKlTp8aQIUMqPBmwqz7//POYP39+vPTSS7Fly5Y49thjY/LkydG3b99Kj0YPISxI89lnn8WZZ54Z9957bxx22GGVHgeACvB1U9Lsvffe8dJLL1V6DKALrVq1KhYtWhQbN27c7m/T3HrrrRWaip7EFQtS3XDDDVEsFmPmzJmVHgVIdt9998VVV10VAwYMiIMOOigKhUL7Z4VCIZ5//vkKTkdPISxIdc0118RvfvObOOyww2Ls2LGx3377dfj87rvvrtBkwK4aNmxYTJ06NW666aZKj0IPJixIdcopp+z0s0KhEE899VQ3TgNkqq2tjRdeeCGGDx9e6VHowYQFAP+Tyy+/PI477ri48sorKz0KPZiHN+kSq1evjjfffDNOOumk6Nu3b7S1tXW4HwtUnxEjRsQtt9wSS5cujdGjR8fee+/d4fNrr722QpPRk7hiQaoPP/wwLrrooli0aFEUCoVYtWpVDB8+PL73ve9Fv3794ic/+UmlRwQ66dBDD93pZ4VCId56661unIaeyhULUt1www2x9957xzvvvBNHHHFE+/aLL744pk2bJiygiq1ZsyYiIj744IOIiBgwYEAlx6GH8q4QUj3++ONx1113xdChQztsP+yww+Ltt9+u0FTArvr444+joaEhBgwYEIMGDYpBgwbFgAED4uqrr47m5uZKj0cP4ooFqbZu3Rr77rvvdts3bdoUxWKxAhMBu2rTpk0xfvz4ePfdd2Py5MntVyNfe+21mDdvXixcuDCWLFkS/fr1q/Ck9ASesSDV2WefHWPHjo077rgjampq4qWXXophw4bFJZdcEq2trfGHP/yh0iMCZbr++utj4cKF8eSTT8agQYM6fLZhw4Y444wzYuLEifHTn/60QhPSkwgLUr3yyisxceLEOPbYY+Opp56K888/P1599dXYtGlT/PWvf42vfvWrlR4RKNMhhxwSc+fOjUmTJu3w8wULFsSVV14Za9eu7d7B6JGEBemam5tj9uzZ8eKLL7a//bChoSEGDx5c6dGATigWi/Hmm29u9+zUF9atWxcjRoyITz75pJsnoyfyjAUpJk6cGA0NDfGtb30r6urq4uabb+7w+QcffBDDhw/3dTSoQgMGDIi1a9fuNCzWrFkTBxxwQDdPRU/lWyGkWLRoUVx00UUxY8aMHX6+bds23wqBKjVp0qS4+eab49NPP93us1KpFLfcckuceeaZFZiMnsitEFL06tUr5s6dGzfeeGOceuqp8dvf/rbDC8jee++9GDJkSGzbtq2CUwKdsW7duhg3blwUi8VoaGiIkSNHRltbW/z973+PX/ziF1EqlWLFihVRX19f6VHpAYQFKXr16hUbNmyIDz/8MC644IIoFovx8MMPt7+sSFhAdVuzZk1MnTo1Hn/88fji10ahUIjTTz89Zs+eHSNGjKjwhPQUwoIUX4TFwIEDo7m5OS699NJ47rnn4sEHH4zTTjtNWMBu4qOPPopVq1ZFxL/fHeLZCv6TZyxIV1dXF4888khcccUVcfbZZ/tuO+xG+vXrF8cff3wcf/zxooId8q0QUvznm0sLhULMnDkzjjnmmPj+978fTz31VIUmA6A7uWJBip3dUbvkkkvi2WefjZdffrmbJwKgElyxIMWiRYt2eln0mGOOiZUrV8YjjzzSzVMB0N08vAkApHErBABIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDT/BzRqNf3Uih03AAAAAElFTkSuQmCC",
             "text/plain": [
               "<Figure size 640x480 with 1 Axes>"
             ]
@@ -971,20 +1361,25 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 14,
+      "execution_count": 16,
       "id": "f045ad9c",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "ename": "QSharpError",
-          "evalue": "\u001b[31mQsc.TypeCk.TyMismatch\u001b[0m\n\n  \u001b[31m×\u001b[0m type error\n\u001b[31m  ╰─▶ \u001b[0mexpected Unit, found String\n   ╭─[\u001b[36;1;4m<entry>\u001b[0m:1:1]\n \u001b[2m1\u001b[0m │ RandomBit(\"a\")\n   · \u001b[35;1m──────────────\u001b[0m\n   ╰────\n",
+          "evalue": "Qsc.TypeCk.TyMismatch\n\n  x type error\n  `-> expected Unit, found String\n   ,-[<entry>:1:1]\n 1 | RandomBit(\"a\")\n   : ^^^^^^^^^^^^^^\n   `----\n",
           "output_type": "error",
           "traceback": [
             "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
             "\u001b[0;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
-            "Cell \u001b[0;32mIn[14], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mqsharp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\"\"\u001b[39;49m\u001b[38;5;124;43mRandomBit(\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43ma\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m)\u001b[39;49m\u001b[38;5;124;43m\"\"\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m)\u001b[49m\n",
-            "File \u001b[0;32m~/Programming/qsharp/pip/qsharp/_qsharp.py:165\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events)\u001b[0m\n\u001b[1;32m    163\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    164\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 165\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    166\u001b[0m \u001b[43m        \u001b[49m\u001b[43mentry_expr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_save_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msave_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mprint_output\u001b[49m\n\u001b[1;32m    167\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    168\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    169\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
-            "\u001b[0;31mQSharpError\u001b[0m: \u001b[31mQsc.TypeCk.TyMismatch\u001b[0m\n\n  \u001b[31m×\u001b[0m type error\n\u001b[31m  ╰─▶ \u001b[0mexpected Unit, found String\n   ╭─[\u001b[36;1;4m<entry>\u001b[0m:1:1]\n \u001b[2m1\u001b[0m │ RandomBit(\"a\")\n   · \u001b[35;1m──────────────\u001b[0m\n   ╰────\n"
+            "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m qsharp\u001b[38;5;241m.\u001b[39mrun(\u001b[38;5;124m\"\"\"\u001b[39m\u001b[38;5;124mRandomBit(\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124ma\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\"\"\u001b[39m, \u001b[38;5;241m10\u001b[39m)\n",
+            "File \u001b[0;32m~/.local/lib/python3.12/site-packages/qsharp/_qsharp.py:307\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events, noise)\u001b[0m\n\u001b[1;32m    305\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m shot \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    306\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 307\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m get_interpreter()\u001b[38;5;241m.\u001b[39mrun(\n\u001b[1;32m    308\u001b[0m         entry_expr,\n\u001b[1;32m    309\u001b[0m         on_save_events \u001b[38;5;28;01mif\u001b[39;00m save_events \u001b[38;5;28;01melse\u001b[39;00m print_output,\n\u001b[1;32m    310\u001b[0m         noise,\n\u001b[1;32m    311\u001b[0m     )\n\u001b[1;32m    312\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    313\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
+            "\u001b[0;31mQSharpError\u001b[0m: Qsc.TypeCk.TyMismatch\n\n  x type error\n  `-> expected Unit, found String\n   ,-[<entry>:1:1]\n 1 | RandomBit(\"a\")\n   : ^^^^^^^^^^^^^^\n   `----\n"
           ]
         }
       ],
@@ -1002,7 +1397,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 15,
+      "execution_count": 17,
       "id": "9b85eb2d",
       "metadata": {
         "vscode": {
@@ -1027,20 +1422,25 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 16,
+      "execution_count": 18,
       "id": "2445df05",
       "metadata": {},
       "outputs": [
+        {
+          "data": {},
+          "metadata": {},
+          "output_type": "display_data"
+        },
         {
           "ename": "QSharpError",
-          "evalue": "Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Bad in line_9\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit1 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_9\u001b[0m:7:1]\n \u001b[2m7\u001b[0m │         // Do something bad, sometimes\n \u001b[2m8\u001b[0m │         use q2 = Qubit();\n   · \u001b[35;1m        ────────┬────────\u001b[0m\n   ·                 \u001b[35;1m╰── \u001b[35;1mQubit1\u001b[0m\u001b[0m\n \u001b[2m9\u001b[0m │         X(q2);\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
+          "evalue": "Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs\n    at Bad in line_9\nQsc.Eval.ReleasedQubitNotZero\n\n  x runtime error\n  `-> Qubit1 released while not in |0⟩ state\n   ,-[line_9:8:9]\n 7 |         // Do something bad, sometimes\n 8 |         use q2 = Qubit();\n   :         ^^^^^^^^|^^^^^^^^\n   :                 `-- Qubit1\n 9 |         X(q2);\n   `----\n  help: qubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n",
           "output_type": "error",
           "traceback": [
             "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
             "\u001b[0;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
-            "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mqsharp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mBad()\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m)\u001b[49m\n",
-            "File \u001b[0;32m~/Programming/qsharp/pip/qsharp/_qsharp.py:165\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events)\u001b[0m\n\u001b[1;32m    163\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    164\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 165\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    166\u001b[0m \u001b[43m        \u001b[49m\u001b[43mentry_expr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_save_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msave_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mprint_output\u001b[49m\n\u001b[1;32m    167\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    168\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    169\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
-            "\u001b[0;31mQSharpError\u001b[0m: Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Bad in line_9\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit1 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_9\u001b[0m:7:1]\n \u001b[2m7\u001b[0m │         // Do something bad, sometimes\n \u001b[2m8\u001b[0m │         use q2 = Qubit();\n   · \u001b[35;1m        ────────┬────────\u001b[0m\n   ·                 \u001b[35;1m╰── \u001b[35;1mQubit1\u001b[0m\u001b[0m\n \u001b[2m9\u001b[0m │         X(q2);\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n"
+            "Cell \u001b[0;32mIn[18], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m qsharp\u001b[38;5;241m.\u001b[39mrun(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBad()\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m10\u001b[39m)\n",
+            "File \u001b[0;32m~/.local/lib/python3.12/site-packages/qsharp/_qsharp.py:307\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events, noise)\u001b[0m\n\u001b[1;32m    305\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m shot \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    306\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 307\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m get_interpreter()\u001b[38;5;241m.\u001b[39mrun(\n\u001b[1;32m    308\u001b[0m         entry_expr,\n\u001b[1;32m    309\u001b[0m         on_save_events \u001b[38;5;28;01mif\u001b[39;00m save_events \u001b[38;5;28;01melse\u001b[39;00m print_output,\n\u001b[1;32m    310\u001b[0m         noise,\n\u001b[1;32m    311\u001b[0m     )\n\u001b[1;32m    312\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    313\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
+            "\u001b[0;31mQSharpError\u001b[0m: Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in qsharp-library-source:core/qir.qs\n    at Bad in line_9\nQsc.Eval.ReleasedQubitNotZero\n\n  x runtime error\n  `-> Qubit1 released while not in |0⟩ state\n   ,-[line_9:8:9]\n 7 |         // Do something bad, sometimes\n 8 |         use q2 = Qubit();\n   :         ^^^^^^^^|^^^^^^^^\n   :                 `-- Qubit1\n 9 |         X(q2);\n   `----\n  help: qubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n"
           ]
         }
       ],
@@ -1051,7 +1451,7 @@
   ],
   "metadata": {
     "kernelspec": {
-      "display_name": "Python 3 (ipykernel)",
+      "display_name": "Python 3",
       "language": "python",
       "name": "python3"
     },
@@ -1065,7 +1465,7 @@
       "name": "python",
       "nbconvert_exporter": "python",
       "pygments_lexer": "ipython3",
-      "version": "3.11.6"
+      "version": "3.12.1"
     }
   },
   "nbformat": 4,
diff --git a/version.py b/version.py
index 3e05e83c..8b9f5c14 100755
--- a/version.py
+++ b/version.py
@@ -54,6 +54,13 @@ update_file(
     r'version = "0.0.0"',
     r'version = "{}"'.format(pip_version),
 )
+
+update_file(
+    os.path.join(root_dir, "pip/qsharp/telemetry.py"),
+    r'QSHARP_VERSION = "0.0.0.dev0"',
+    r'QSHARP_VERSION = "{}"'.format(pip_version),
+)
+
 update_file(
     os.path.join(root_dir, "widgets/pyproject.toml"),
     r'version = "0.0.0"',
diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts
index f9d068d1..d58da7c8 100644
--- a/vscode/src/documentation.ts
+++ b/vscode/src/documentation.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-import { getCompilerWorker } from "qsharp-lang";
+import { getCompilerWorker, IDocFile } from "qsharp-lang";
 import { Uri } from "vscode";
 import { sendMessageToPanel } from "./webviewPanel";
 import { getActiveProgram } from "./programConfig";
@@ -27,20 +27,24 @@ export async function showDocumentationCommand(extensionUri: Uri) {
   const worker = getCompilerWorker(compilerWorkerScriptPath);
   const docFiles = await worker.getDocumentation(program.programConfig);
 
-  const documentation: string[] = [];
+  const documentation: IDocFile[] = [];
   for (const file of docFiles) {
     // Some files may contain information other than documentation
     // For example, table of content is a separate file in a special format
     // We check presence of qsharp.name in metadata to make sure we take
     // only files that contain documentation from some qsharp object.
     if (file.metadata.indexOf("qsharp.name:") >= 0) {
-      documentation.push(file.contents);
+      documentation.push(file);
     }
   }
 
+  let projectName = program.programConfig.projectName;
+  if (projectName.endsWith(".qs")) projectName = projectName.slice(0, -3);
+
   const message = {
     command: "showDocumentationCommand", // This is handled in webview.tsx onMessage
     fragmentsToRender: documentation,
+    projectName,
   };
 
   sendMessageToPanel(
diff --git a/vscode/src/telemetry.ts b/vscode/src/telemetry.ts
index f119253c..f3adc004 100644
--- a/vscode/src/telemetry.ts
+++ b/vscode/src/telemetry.ts
@@ -39,6 +39,7 @@ export enum EventType {
   ResourceEstimationEnd = "Qsharp.ResourceEstimationEnd",
   TriggerHistogram = "Qsharp.TriggerHistogram",
   HistogramStart = "Qsharp.HistogramStart",
+  NoisySimulation = "Qsharp.NoisySimulation",
   HistogramEnd = "Qsharp.HistogramEnd",
   FormatStart = "Qsharp.FormatStart",
   FormatEnd = "Qsharp.FormatEnd",
@@ -209,6 +210,10 @@ type EventTypes = {
     properties: { associationId: string };
     measurements: Empty;
   };
+  [EventType.NoisySimulation]: {
+    properties: { associationId: string };
+    measurements: Empty;
+  };
   [EventType.HistogramEnd]: {
     properties: { associationId: string };
     measurements: { timeToCompleteMs: number };
diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx
index 94627a16..1d6b8ec8 100644
--- a/vscode/src/webview/docview.tsx
+++ b/vscode/src/webview/docview.tsx
@@ -2,13 +2,435 @@
 // Licensed under the MIT License.
 
 import { Markdown } from "qsharp-lang/ux";
+import { useEffect, useState } from "preact/hooks";
 
-export function DocumentationView(props: { fragmentsToRender: string[] }) {
-  // Concatenate all documentation.
-  // The following adds an empty line and a horizontal line
-  // between documentation for different functions.
-  // We may consider filtering of fragments later.
-  const contentToRender = props.fragmentsToRender.join("<br/>\n\n---\n\n");
+export interface IDocFile {
+  filename: string;
+  metadata: string;
+  contents: string;
+}
+
+interface ItemDocs {
+  pkg: string;
+  namespace: string;
+  member: string;
+  content: string;
+}
+
+interface PageContents {
+  [name: string]: {
+    onclick: (evt: Event) => void;
+    content: string;
+    anchor: string;
+  };
+}
+
+function GetPageContents(
+  currPath: string,
+  docs: ItemDocs[],
+  setPath: any,
+): PageContents {
+  const contents: PageContents = {};
+
+  if (currPath === "") {
+    // Collect the set of all packages
+    docs.forEach((doc) => {
+      if (!(doc.pkg in contents)) {
+        contents[doc.pkg] = {
+          onclick: () => setPath(doc.pkg),
+          content: "",
+          anchor: "",
+        };
+      }
+    });
+  } else if (currPath.indexOf("/") === -1) {
+    // Render the list of namespaces in the current package
+    docs.forEach((doc) => {
+      if (doc.pkg === currPath) {
+        if (!(doc.namespace in contents)) {
+          contents[doc.namespace] = {
+            onclick: () => setPath(`${currPath}/${doc.namespace}`),
+            content: "",
+            anchor: "",
+          };
+        }
+      }
+    });
+  } else {
+    // Render the list of members in the current namespace
+    docs.forEach((doc) => {
+      if (
+        doc.pkg === currPath.split("/")[0] &&
+        doc.namespace === currPath.split("/")[1]
+      ) {
+        contents[doc.member] = {
+          onclick: (evt: Event) => {
+            evt.preventDefault();
+            scrollToElement(doc.member);
+          },
+          content: doc.content,
+          anchor: doc.member,
+        };
+      }
+    });
+  }
+  return contents;
+}
+
+function scrollToElement(id: string) {
+  const elem = id ? document.getElementById(id) : null;
+  if (!elem) {
+    window.scrollTo({
+      top: 0,
+      behavior: "instant",
+    });
+  } else {
+    const yOffset = -64; // Negative value to offset from the top
+    const yPosition =
+      elem.getBoundingClientRect().top + window.scrollY + yOffset;
+
+    window.scrollTo({
+      top: yPosition,
+      behavior: "instant",
+    });
+  }
+}
+
+interface SearchResult {
+  rank: number;
+  anchor: string;
+  header: string;
+  summary: string;
+}
+
+function getSearchResults(
+  searchText: string,
+  docs: ItemDocs[],
+): SearchResult[] {
+  const results: SearchResult[] = [];
+
+  // Search on member name first, then on namespace name, then on package name, then on content
+  // Prefer earlier matches (e.g. "X" should match "X" before "CX")
+
+  // RegExp groups
+  // 1. is the first header (e.g. 'Foo operation'), group
+  // 2. Is any text after the first header that precedes the next header
+  // 3. Ignore
+  // 4. Is the summary (if present)
+  const summaryRe =
+    /^(?:# )(.*?\n)\s*(.*?)(?=(##+)|$)(## Summary\r?\n.*?(?=(##+)|$))?/s;
+
+  docs.forEach((doc) => {
+    const reMatch = doc.content.match(summaryRe);
+    const summary = (reMatch?.[2] ?? "") + (reMatch?.[4] ?? "");
+    const header = doc.content.match(summaryRe)?.[1] ?? doc.member;
+
+    const lowerText = searchText.toLowerCase();
+    let rank = doc.member.toLowerCase().indexOf(lowerText) + 1;
+    if (rank) {
+      // Matches on larger parts of the name are more important
+      rank += doc.member.length / 100;
+    }
+
+    if (!rank) {
+      rank = (doc.namespace.toLowerCase().indexOf(lowerText) + 1) * 100;
+      if (rank) {
+        rank += doc.namespace.length;
+      }
+    }
+    if (!rank) {
+      rank = (doc.pkg.toLowerCase().indexOf(lowerText) + 1) * 1000;
+    }
+    if (!rank) {
+      rank = (doc.content.toLowerCase().indexOf(lowerText) + 1) * 10000;
+    }
+
+    if (rank) {
+      results.push({
+        rank,
+        anchor: `${doc.pkg}/${doc.namespace}/${doc.member}`,
+        header,
+        summary,
+      });
+      return;
+    }
+  });
+
+  return results.sort((a, b) => a.rank - b.rank);
+}
+
+function DocsPage(props: { fragmentsToRender: ItemDocs[] }) {
+  // currPath is of the format: "<pkg>/<namespace>/<member>", e.g.
+  // "Std/Canon/CCNOT" or "Std/Microsoft.Quantum.Diagnostics/AssertMeasurementEqual" or
+  // "Unsigned/Main/GetInt" or "Main/Particle/Particle". When at the top level, currPath is "".
+
+  const [currPath, setPath] = useState("");
+  const [searchText, setSearchText] = useState("");
+
+  useEffect(() => {
+    // If the member is navigated to, scroll to it after rendering
+    const member = currPath.split("/")[2];
+    scrollToElement(member);
+  }, [currPath]);
+
+  // Skip processing contents if searching
+  const contents =
+    searchText === ""
+      ? GetPageContents(currPath, props.fragmentsToRender, setPath)
+      : {};
+
+  const searchResults =
+    searchText === ""
+      ? []
+      : getSearchResults(searchText, props.fragmentsToRender);
+
+  // Used to bold the text links when overing
+  function overLi(e: MouseEvent) {
+    (e.target as HTMLElement).style.fontWeight = "600";
+    (e.target as HTMLElement).style.textDecoration = "underline";
+    (e.target as HTMLElement).style.cursor = "pointer";
+  }
+
+  function outLi(e: MouseEvent) {
+    (e.target as HTMLElement).style.fontWeight = "400";
+    (e.target as HTMLElement).style.textDecoration = "none";
+    (e.target as HTMLElement).style.cursor = "default";
+  }
+
+  // Whenever the breadcrumbs are clicked, go up one level
+  function onPathClick() {
+    setSearchText("");
+    if (currPath) {
+      const parts = currPath.split("/");
+      parts.pop();
+      setPath(parts.join("/"));
+    }
+  }
+
+  // Handle the user focusing or updating the search box
+  function onSearchFocus(e: FocusEvent) {
+    e.preventDefault();
+    if (e.target) {
+      const currText = (e.target as HTMLInputElement).value;
+      if (currText) setPath("");
+      setSearchText(currText);
+    }
+  }
+
+  function onSearchInput(e: InputEvent) {
+    e.preventDefault();
+    if (e.target) {
+      setPath("");
+      setSearchText((e.target as HTMLInputElement).value);
+    }
+  }
+
+  function searchResultClicked(anchor: string) {
+    setSearchText("");
+    setPath(anchor);
+  }
+
+  return (
+    <div
+      class="qs-docsPage"
+      style="width: 100%; padding-bottom: 1em; position: relative; background-color: var(--main-background); color: var(--main-color)"
+    >
+      <div
+        class="qs-docsHeader"
+        style="height: 3em; display: flex; justify-content: space-between; align-items: center; margin-top: 0px; padding-top: 1.5em; padding-bottom: 1em; position: fixed; top: 0; width: 95%; background-color: var(--main-background); z-index: 1;"
+      >
+        <div
+          onClick={() => {
+            setSearchText("");
+            setPath("");
+          }}
+          onMouseOver={overLi}
+          onMouseOut={outLi}
+        >
+          <svg
+            style="height: 2.25em; width: 2.25em; margin: 0.25em"
+            viewBox="0 0 100 100"
+          >
+            <g transform="translate(10,10)" fill="#888">
+              <path d="M10,50 l40,-40 l40,40 Z"></path>
+              <path d="M20,49 l0,30 l20,0 l0,-30Z"></path>
+              <path d="M60,49 l0,30 l20,0 l0,-30Z"></path>
+              <path d="M39,49 l0,8 l24,0 l0,-30Z"></path>
+              <path d="M77,40 l0,-20 l-10,0 l0,20"></path>
+            </g>
+          </svg>
+        </div>
+        <div
+          style="flex-grow: 1; font-size: 1.4em; margin-left: 0.5em;"
+          onClick={onPathClick}
+        >
+          {currPath
+            ? currPath.replaceAll("/", " > ")
+            : searchText
+              ? "Search results"
+              : "Q# API documentation"}
+        </div>
+        <div>
+          <input
+            type="text"
+            placeholder="Search..."
+            onFocus={onSearchFocus}
+            onInput={onSearchInput}
+          />
+        </div>
+        <div>
+          <svg
+            style="height: 2.25em; width: 2.25em; margin: 0.25em"
+            viewBox="0 0 100 100"
+          >
+            <g stroke="#888" fill="none" transform="translate(0,10)">
+              <circle stroke-width="6" cx="40" cy="35" r="25"></circle>
+              <path stroke-width="8" d="M52,57 l20,30"></path>
+            </g>
+          </svg>
+        </div>
+      </div>
+      {searchText ? (
+        <div
+          class="qs-searchResults"
+          style="margin: 2em; position: relative; top: 2em;"
+        >
+          {searchResults.map((result) => (
+            <div>
+              <hr />
+              <h1
+                onMouseOver={overLi}
+                onMouseOut={outLi}
+                onClick={() => searchResultClicked(result.anchor)}
+              >
+                {result.header}
+              </h1>
+              <Markdown markdown={result.summary} />
+            </div>
+          ))}
+        </div>
+      ) : (
+        <div
+          class="qs-docsContent"
+          style="margin: 2em; position: relative; top: 2em;"
+        >
+          <div
+            class="qs-index"
+            style="background: var(--vscode-textCodeBlock-background); padding: 0.1em"
+          >
+            <p style="font-size: 1.1em; font-weight: 600; margin: 0.8em;">
+              {currPath === ""
+                ? "Packages"
+                : currPath.indexOf("/") === -1
+                  ? "Namespaces"
+                  : "Members"}
+            </p>
+            <ul>
+              {Object.keys(contents).map((key) => (
+                <li
+                  onClick={contents[key].onclick}
+                  onMouseOver={overLi}
+                  onMouseOut={outLi}
+                >
+                  {key}
+                </li>
+              ))}
+            </ul>
+          </div>
+
+          {Object.keys(contents).map((key) =>
+            !contents[key].content ? null : (
+              <div id={contents[key].anchor} style="margin-top: 12px;">
+                <Markdown markdown={contents[key].content} />
+                <hr />
+              </div>
+            ),
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export function DocumentationView(props: {
+  fragmentsToRender: IDocFile[];
+  projectName: string;
+}) {
+  const docs: ItemDocs[] = [];
+
+  props.fragmentsToRender.forEach((doc) => {
+    if (!doc.metadata) {
+      return;
+    }
+    const pkg = doc.metadata.match(/^(?:qsharp\.package: )(.*)$/m)?.[1];
+    const namespace = doc.metadata.match(/^(?:qsharp\.namespace: )(.*)$/m)?.[1];
+    const member = doc.metadata.match(/^(?:qsharp\.name: )(.*)$/m)?.[1];
+
+    if (pkg && namespace && member) {
+      docs.push({
+        pkg:
+          pkg === "__Core__" || pkg === "__Std__"
+            ? "Std"
+            : pkg === "__Main__"
+              ? props.projectName
+              : pkg,
+        // For some reason Std namespaces include the package name. Remove it.
+        namespace: namespace.startsWith("Std.")
+          ? namespace.slice(4)
+          : namespace,
+        member,
+        content: doc.contents,
+      });
+    }
+  });
+
+  docs.sort((a, b) => {
+    if (a.pkg != b.pkg) {
+      // Sorted by __Main__, then reference packages, then __Std__ (which includes __Core__)
+      if (a.pkg === props.projectName || b.pkg === "Std") {
+        return -1;
+      } else if (b.pkg === props.projectName || a.pkg === "Std") {
+        return 1;
+      } else {
+        return a.pkg.localeCompare(b.pkg);
+      }
+    } else if (a.namespace != b.namespace) {
+      // Main namespace comes first and "Microsoft.Quantum.*" comes last
+      if (
+        a.namespace === props.projectName ||
+        b.namespace.startsWith("Microsoft.Quantum")
+      ) {
+        return -1;
+      } else if (
+        b.namespace === props.projectName ||
+        a.namespace.startsWith("Microsoft.Quantum")
+      ) {
+        return 1;
+      } else {
+        return a.namespace.localeCompare(b.namespace);
+      }
+    } else {
+      return a.member.localeCompare(b.member);
+    }
+  });
+
+  const style = document.createElement("style");
+  style.textContent = `
+body {
+  background-color: var(--main-background);
+  margin: 0;
+  padding: 0;
+}
+
+.markdown-body {
+  background-color: var(--main-background);
+}
+
+.markdown-body code {
+  color: var(--main-color);
+}
+`;
+  document.head.appendChild(style);
 
-  return <Markdown markdown={contentToRender} />;
+  return <DocsPage fragmentsToRender={docs} />;
 }
diff --git a/vscode/src/webview/tsconfig.json b/vscode/src/webview/tsconfig.json
index 0fe35e1e..5e5f6917 100644
--- a/vscode/src/webview/tsconfig.json
+++ b/vscode/src/webview/tsconfig.json
@@ -3,7 +3,7 @@
     "module": "CommonJS",
     "target": "ES2020",
     "noEmit": true,
-    "lib": ["DOM", "ES2020"],
+    "lib": ["DOM", "ES2021"],
     "rootDir": ".",
     "strict": true /* enable all strict type-checking options */,
     "jsx": "react-jsx",
diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx
index 92b686c8..3a4c3a3d 100644
--- a/vscode/src/webview/webview.tsx
+++ b/vscode/src/webview/webview.tsx
@@ -15,7 +15,7 @@ import {
   type ReData,
 } from "qsharp-lang/ux";
 import { HelpPage } from "./help";
-import { DocumentationView } from "./docview";
+import { DocumentationView, IDocFile } from "./docview";
 
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 // @ts-ignore - there are no types for this
@@ -52,7 +52,8 @@ type CircuitState = {
 
 type DocumentationState = {
   viewType: "documentation";
-  fragmentsToRender: string[];
+  fragmentsToRender: IDocFile[];
+  projectName: string;
 };
 
 type State =
@@ -186,6 +187,7 @@ function onMessage(event: any) {
         state = {
           viewType: "documentation",
           fragmentsToRender: message.fragmentsToRender,
+          projectName: message.projectName,
         };
       }
       break;
@@ -260,7 +262,12 @@ function App({ state }: { state: State }) {
       // too large in the others right now. Something to unify later.
       document.body.classList.add("markdown-body");
       document.body.style.fontSize = "0.8em";
-      return <DocumentationView fragmentsToRender={state.fragmentsToRender} />;
+      return (
+        <DocumentationView
+          fragmentsToRender={state.fragmentsToRender}
+          projectName={state.projectName}
+        />
+      );
     default:
       console.error("Unknown view type in state", state);
       return <div>Loading error</div>;
diff --git a/vscode/src/webviewPanel.ts b/vscode/src/webviewPanel.ts
index 790246aa..7344a6c6 100644
--- a/vscode/src/webviewPanel.ts
+++ b/vscode/src/webviewPanel.ts
@@ -345,6 +345,9 @@ export function registerWebViewCommands(context: ExtensionContext) {
         sendTelemetryEvent(EventType.HistogramStart, { associationId }, {});
 
         const noise = getPauliNoiseModel();
+        if (noise[0] != 0 || noise[1] != 0 || noise[2] != 0) {
+          sendTelemetryEvent(EventType.NoisySimulation, { associationId }, {});
+        }
         await worker.runWithPauliNoise(
           program.programConfig,
           "",


Test patch:
diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs
index 02c2a436..6c82359c 100644
--- a/compiler/qsc/src/codegen/tests.rs
+++ b/compiler/qsc/src/codegen/tests.rs
@@ -1080,7 +1080,7 @@ mod adaptive_ri_profile {
 
             declare void @__quantum__qis__x__body(%Qubit*)
 
-            declare void @__quantum__qis__reset__body(%Qubit*)
+            declare void @__quantum__qis__reset__body(%Qubit*) #1
 
             declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
 
@@ -1263,4 +1263,73 @@ mod adaptive_ri_profile {
             !10 = !{i32 1, !"multiple_target_branching", i1 false}
         "#]].assert_eq(&qir);
     }
+
+    #[test]
+    fn custom_reset_generates_correct_qir() {
+        let source = "namespace Test {
+            operation Main() : Result {
+                use q = Qubit();
+                __quantum__qis__custom_reset__body(q);
+                M(q)
+            }
+
+            @Reset()
+            operation __quantum__qis__custom_reset__body(target: Qubit) : Unit {
+                body intrinsic;
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive
+            | TargetCapabilityFlags::QubitReset
+            | TargetCapabilityFlags::IntegerComputations;
+
+        let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
+        let qir = get_qir(
+            sources,
+            language_features,
+            capabilities,
+            store,
+            &[(std_id, None)],
+        )
+        .expect("the input program set in the `source` variable should be valid Q#");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__custom_reset__body(%Qubit* inttoptr (i64 0 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__custom_reset__body(%Qubit*) #1
+
+            declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 true}
+            !5 = !{i32 1, !"qubit_resetting", i1 true}
+            !6 = !{i32 1, !"classical_floats", i1 false}
+            !7 = !{i32 1, !"backwards_branching", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]]
+        .assert_eq(&qir);
+    }
 }
diff --git a/compiler/qsc_doc_gen/src/generate_docs/tests.rs b/compiler/qsc_doc_gen/src/generate_docs/tests.rs
index 9ae10590..89d4d396 100644
--- a/compiler/qsc_doc_gen/src/generate_docs/tests.rs
+++ b/compiler/qsc_doc_gen/src/generate_docs/tests.rs
@@ -20,6 +20,7 @@ fn docs_generation() {
         ms.date: {TIMESTAMP}
         ms.topic: managed-reference
         qsharp.kind: function
+        qsharp.package: __Core__
         qsharp.namespace: Std.Core
         qsharp.name: Length
         qsharp.summary: "Returns the number of elements in the input array `a`."
diff --git a/compiler/qsc_frontend/src/lower/tests.rs b/compiler/qsc_frontend/src/lower/tests.rs
index f35ba17a..0e8bbd26 100644
--- a/compiler/qsc_frontend/src/lower/tests.rs
+++ b/compiler/qsc_frontend/src/lower/tests.rs
@@ -2221,7 +2221,8 @@ fn test_measurement_attr_on_function_issues_error() {
         "#},
         &expect![[r#"
             [
-                InvalidMeasurementAttrOnFunction(
+                InvalidAttrOnFunction(
+                    "Measurement",
                     Span {
                         lo: 49,
                         hi: 52,
@@ -2232,6 +2233,31 @@ fn test_measurement_attr_on_function_issues_error() {
     );
 }
 
+#[test]
+fn test_reset_attr_on_function_issues_error() {
+    check_errors(
+        indoc! {r#"
+            namespace Test {
+                @Reset()
+                function Foo(q: Qubit) : Unit {
+                    body intrinsic;
+                }
+            }
+        "#},
+        &expect![[r#"
+            [
+                InvalidAttrOnFunction(
+                    "Reset",
+                    Span {
+                        lo: 43,
+                        hi: 46,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
 #[test]
 fn item_docs() {
     check_hir(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
index 8a042fa7..e3c006a2 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
@@ -6,7 +6,8 @@ use super::tests_common::{
     CALL_TO_CYCLIC_FUNCTION_WITH_CLASSICAL_ARGUMENT, CALL_TO_CYCLIC_FUNCTION_WITH_DYNAMIC_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_CLASSICAL_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_DYNAMIC_ARGUMENT, CALL_UNRESOLVED_FUNCTION, CUSTOM_MEASUREMENT,
-    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
+    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, CUSTOM_RESET,
+    CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
     MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL, RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION,
     USE_DYNAMICALLY_SIZED_ARRAY, USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE,
     USE_DYNAMIC_FUNCTION, USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP,
@@ -24,6 +25,14 @@ fn check_profile(source: &str, expect: &Expect) {
     check(source, expect, TargetCapabilityFlags::Adaptive);
 }
 
+fn check_profile_extended(source: &str, capabilities: TargetCapabilityFlags, expect: &Expect) {
+    check(
+        source,
+        expect,
+        TargetCapabilityFlags::Adaptive | capabilities,
+    );
+}
+
 fn check_profile_for_exe(source: &str, expect: &Expect) {
     check_for_exe(source, expect, TargetCapabilityFlags::Adaptive);
 }
@@ -466,6 +475,28 @@ fn custom_measurement_with_simulatable_intrinsic_yields_no_errors() {
     );
 }
 
+#[test]
+fn custom_reset_yields_no_errors() {
+    check_profile_extended(
+        CUSTOM_RESET,
+        TargetCapabilityFlags::QubitReset,
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
+fn custom_reset_with_simulatable_intrinsic_yields_no_errors() {
+    check_profile_extended(
+        CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR,
+        TargetCapabilityFlags::QubitReset,
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
 #[test]
 fn use_of_dynamic_index_yields_errors() {
     check_profile(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
index 710fbdac..d582f843 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
@@ -6,7 +6,8 @@ use super::tests_common::{
     CALL_TO_CYCLIC_FUNCTION_WITH_CLASSICAL_ARGUMENT, CALL_TO_CYCLIC_FUNCTION_WITH_DYNAMIC_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_CLASSICAL_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_DYNAMIC_ARGUMENT, CALL_UNRESOLVED_FUNCTION, CUSTOM_MEASUREMENT,
-    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
+    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, CUSTOM_RESET,
+    CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
     MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL, RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION,
     USE_DYNAMICALLY_SIZED_ARRAY, USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE,
     USE_DYNAMIC_FUNCTION, USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP,
@@ -581,6 +582,40 @@ fn custom_measurement_with_simulatable_intrinsic_yields_errors() {
     );
 }
 
+#[test]
+fn custom_reset_yields_errors() {
+    check_profile(
+        CUSTOM_RESET,
+        &expect![[r#"
+            [
+                CallToCustomReset(
+                    Span {
+                        lo: 145,
+                        hi: 151,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn custom_reset_with_simulatable_intrinsic_yields_errors() {
+    check_profile(
+        CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR,
+        &expect![[r#"
+            [
+                CallToCustomReset(
+                    Span {
+                        lo: 145,
+                        hi: 151,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
 #[test]
 fn use_of_dynamic_index_yields_errors() {
     check_profile(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
index d6d50caf..6b742770 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
@@ -353,6 +353,39 @@ pub const CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR: &str = r#"
         }
     }"#;
 
+pub const CUSTOM_RESET: &str = r#"
+    namespace Test {
+        operation Main() : Result {
+            use q = Qubit();
+            H(q);
+            let res = M(q);
+            Foo(q);
+            res
+        }
+
+        @Reset()
+        operation Foo(q: Qubit) : Unit {
+            body intrinsic;
+        }
+    }"#;
+
+pub const CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR: &str = r#"
+    namespace Test {
+        operation Main() : Result {
+            use q = Qubit();
+            H(q);
+            let res = M(q);
+            Foo(q);
+            res
+        }
+
+        @Reset()
+        @SimulatableIntrinsic()
+        operation Foo(q: Qubit) : Unit {
+            Reset(q);
+        }
+    }"#;
+
 pub const USE_DYNAMIC_INDEX: &str = r#"
     namespace Test {
         import Std.Convert.*;
diff --git a/compiler/qsc_passes/src/reset/tests.rs b/compiler/qsc_passes/src/reset/tests.rs
new file mode 100644
index 00000000..a818d729
--- /dev/null
+++ b/compiler/qsc_passes/src/reset/tests.rs
@@ -0,0 +1,145 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#![allow(clippy::needless_raw_string_hashes)]
+
+use crate::reset::validate_reset_declarations;
+use expect_test::{expect, Expect};
+use indoc::indoc;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
+
+fn check(file: &str, expr: &str, expect: &Expect) {
+    let sources = SourceMap::new([("test".into(), file.into())], Some(expr.into()));
+    let unit = compile(
+        &PackageStore::new(compile::core()),
+        &[],
+        sources,
+        TargetCapabilityFlags::all(),
+        LanguageFeatures::default(),
+    );
+
+    let errors = validate_reset_declarations(&unit.package);
+    expect.assert_debug_eq(&errors);
+}
+
+#[test]
+fn test_custom_reset_declaration_with_simulatable_intrinsic_attr() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                @SimulatableIntrinsic()
+                operation CustomReset(target: Qubit) : Unit {
+                    Reset(target);
+                }
+            }"},
+        "",
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
+fn test_custom_reset_declaration() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit) : Unit { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
+fn test_reset_with_no_arguments_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset() : Unit { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NoArguments(
+                    Span {
+                        lo: 56,
+                        hi: 58,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_non_intrinsic_reset_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit) : Unit { Reset(target); }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NotIntrinsic(
+                    Span {
+                        lo: 45,
+                        hi: 56,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_non_qubit_arguments_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit, n: Int) : Unit { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NonQubitArgument(
+                    Span {
+                        lo: 56,
+                        hi: 79,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_non_unit_output_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit) : Reset { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NonUnitOutput(
+                    Span {
+                        lo: 35,
+                        hi: 99,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs
index f661f661..9965803d 100644
--- a/compiler/qsc_qasm3/src/tests/statement/reset.rs
+++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs
@@ -139,7 +139,7 @@ fn reset_with_adaptive_ri_profile_generates_reset_qir() -> miette::Result<(), Ve
           ret void
         }
 
-        declare void @__quantum__qis__reset__body(%Qubit*)
+        declare void @__quantum__qis__reset__body(%Qubit*) #1
 
         declare void @__quantum__qis__h__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll b/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll
index 33a10374..cf12385b 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll
@@ -93,7 +93,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll b/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll
index e2c6cea4..5090b319 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll
@@ -41,7 +41,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll b/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll
index 879ab5d4..b0f16a39 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll
@@ -47,7 +47,7 @@ declare void @__quantum__qis__rx__body(double, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll b/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll
index c4274122..93f74a3f 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll
@@ -65,7 +65,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll b/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll
index 8e3bfb43..b30fe0ae 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll
@@ -95,7 +95,7 @@ declare void @__quantum__qis__s__adj(%Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll
index 6bf77e60..600b675c 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll
@@ -119,7 +119,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll
index a0bc857a..54f88077 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll
@@ -47,7 +47,7 @@ declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll
index a6f23124..1d86c703 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll
@@ -28,7 +28,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll
index 8a86cd7c..1d479f1e 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll
@@ -18,7 +18,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll b/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll
index 195bdee7..725e3ae6 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll
@@ -38,7 +38,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll b/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll
index 2543d69c..7c07f5a2 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll
@@ -305,7 +305,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__y__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll b/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll
index ac85c5fc..f1c5ecce 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll
@@ -14,7 +14,7 @@ declare void @__quantum__qis__h__body(%Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll b/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll
index 4d4ec739..65f847d5 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll
@@ -46,7 +46,7 @@ declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
 
 declare void @__quantum__qis__z__body(%Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll b/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll
index a9c1a11e..d946deb2 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll
@@ -38,7 +38,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll b/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll
index eaa3c824..038810da 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll
@@ -36,7 +36,7 @@ declare void @__quantum__qis__x__body(%Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll b/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll
index 0d5f9f3c..03607bf2 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll
@@ -59,7 +59,7 @@ declare void @__quantum__qis__x__body(%Qubit*)
 
 declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll b/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll
index 80437b89..325d88bb 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll
@@ -58,7 +58,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__ry__body(double, %Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll b/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll
index 944aff01..2d136271 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll
@@ -283,7 +283,7 @@ declare i1 @__quantum__qis__read_result__body(%Result*)
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll b/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll
index 493b644b..544d8f4f 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll
@@ -27,7 +27,7 @@ declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests/test_interpreter.py b/pip/tests/test_interpreter.py
index f2b90a97..816cce04 100644
--- a/pip/tests/test_interpreter.py
+++ b/pip/tests/test_interpreter.py
@@ -489,3 +489,11 @@ def test_unsupported_operation_circuit() -> None:
         )
         != -1
     )
+
+
+def test_results_are_comparable() -> None:
+    e = Interpreter(TargetProfile.Unrestricted)
+    r = e.interpret("[One, Zero]")
+    assert r == [Result.One, Result.Zero]
+    r.sort()
+    assert r == [Result.Zero, Result.One]