Repository: microsoft/qsharp
Pull request #2319 (2025-04-21 18:32:49)

Removes |1> Gate from Circuit Editor
Removes |1> gate from toolbox and qsharp gen.

Also fixes lint about making the circuit a function when empty.


Patch:
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index ef5fb741..061115eb 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -67,6 +67,22 @@ jobs:
           find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.qs" | tr "\n" "," > \
             $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME
 
+      - name: Gather the Seed Inputs (qasm)
+        if: matrix.target_name == 'qasm'
+        run: |
+          cd $OWNER_RDPATH                    # Enter the dir containing the fuzzing infra.
+
+          # Clone openqasm repo for samples:
+          git clone --depth 1 --single-branch --no-tags --recurse-submodules --shallow-submodules --jobs 4 \
+            https://github.com/openqasm/openqasm.git $SEEDS_RDPATH/${{ matrix.target_name }}/openqasm
+
+
+          # Build a comma-separated list of all the .qasm and .inc files in $SEEDS_FNAME file:
+          find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.qasm" | tr "\n" "," > \
+            $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME
+          find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.inc" | tr "\n" "," > \
+            $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME
+
       - name: Build and Run the Fuzz Target
         run: |
           cd $OWNER_RDPATH               # Enter the dir containing the fuzzing infra.
@@ -188,6 +204,7 @@ jobs:
         if: failure()
         uses: actions/upload-artifact@v4
         with:
+          name: ${{ matrix.target_name }}-fuzz-failure-artifacts
           path: |
             ${{ env.OWNER_RDPATH }}/${{ env.STDERR_LOG_FNAME }}
             ${{ env.OWNER_RDPATH }}/${{ env.TMIN_LOG_FNAME }}
diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs
index 6eb4af95..dac43a35 100644
--- a/compiler/qsc/src/interpret.rs
+++ b/compiler/qsc/src/interpret.rs
@@ -408,10 +408,12 @@ impl Interpreter {
                 compile_unit,
                 // see https://github.com/microsoft/qsharp/pull/1627 for context
                 // on why we override this config
-                Some(&[qsc_linter::LintConfig {
-                    kind: LintKind::Hir(HirLint::NeedlessOperation),
-                    level: LintLevel::Warn,
-                }]),
+                Some(&[qsc_linter::LintOrGroupConfig::Lint(
+                    qsc_linter::LintConfig {
+                        kind: LintKind::Hir(HirLint::NeedlessOperation),
+                        level: LintLevel::Warn,
+                    },
+                )]),
             )
         } else {
             Vec::new()
diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs
index abfce34b..67d6b6bc 100644
--- a/compiler/qsc/src/lib.rs
+++ b/compiler/qsc/src/lib.rs
@@ -58,7 +58,9 @@ pub use qsc_eval::{
 };
 
 pub mod linter {
-    pub use qsc_linter::{run_lints, LintConfig, LintKind, LintLevel};
+    pub use qsc_linter::{
+        run_lints, GroupConfig, LintConfig, LintKind, LintLevel, LintOrGroupConfig,
+    };
 }
 
 pub use qsc_doc_gen::{display, generate_docs};
diff --git a/compiler/qsc_circuit/src/builder.rs b/compiler/qsc_circuit/src/builder.rs
index cbd28737..af0685d5 100644
--- a/compiler/qsc_circuit/src/builder.rs
+++ b/compiler/qsc_circuit/src/builder.rs
@@ -88,35 +88,35 @@ impl Backend for Builder {
 
     fn rx(&mut self, theta: f64, q: usize) {
         let q = self.map(q);
-        self.push_gate(rotation_gate("rx", theta, [q]));
+        self.push_gate(rotation_gate("Rx", theta, [q]));
     }
 
     fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
         let q0 = self.map(q0);
         let q1 = self.map(q1);
-        self.push_gate(rotation_gate("rxx", theta, [q0, q1]));
+        self.push_gate(rotation_gate("Rxx", theta, [q0, q1]));
     }
 
     fn ry(&mut self, theta: f64, q: usize) {
         let q = self.map(q);
-        self.push_gate(rotation_gate("ry", theta, [q]));
+        self.push_gate(rotation_gate("Ry", theta, [q]));
     }
 
     fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
         let q0 = self.map(q0);
         let q1 = self.map(q1);
-        self.push_gate(rotation_gate("ryy", theta, [q0, q1]));
+        self.push_gate(rotation_gate("Ryy", theta, [q0, q1]));
     }
 
     fn rz(&mut self, theta: f64, q: usize) {
         let q = self.map(q);
-        self.push_gate(rotation_gate("rz", theta, [q]));
+        self.push_gate(rotation_gate("Rz", theta, [q]));
     }
 
     fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
         let q0 = self.map(q0);
         let q1 = self.map(q1);
-        self.push_gate(rotation_gate("rzz", theta, [q0, q1]));
+        self.push_gate(rotation_gate("Rzz", theta, [q0, q1]));
     }
 
     fn sadj(&mut self, q: usize) {
diff --git a/compiler/qsc_circuit/src/circuit_to_qsharp.rs b/compiler/qsc_circuit/src/circuit_to_qsharp.rs
index 18461ece..c5619897 100644
--- a/compiler/qsc_circuit/src/circuit_to_qsharp.rs
+++ b/compiler/qsc_circuit/src/circuit_to_qsharp.rs
@@ -80,12 +80,19 @@ fn build_operation_def(circuit_name: &str, circuit: &Circuit) -> String {
     let mut measure_results = vec![];
     let indent = "    ".repeat(indentation_level);
 
-    // Add an assert for the number of qubits
-    qsharp_str.push_str(&generate_qubit_validation(
-        circuit_name,
-        &qubits,
-        indentation_level,
-    ));
+    // If there are operation, add an assert for the number of qubits
+    if !circuit.component_grid.is_empty()
+        && circuit
+            .component_grid
+            .iter()
+            .any(|col| !col.components.is_empty())
+    {
+        qsharp_str.push_str(&generate_qubit_validation(
+            circuit_name,
+            &qubits,
+            indentation_level,
+        ));
+    }
 
     let mut body_str = String::new();
     let mut should_add_pi = false;
@@ -230,24 +237,15 @@ fn generate_unitary_call(
 }
 
 fn generate_ket_call(ket: &Ket, qubits: &FxHashMap<usize, String>, indent: &str) -> String {
-    if ket.gate == "1" {
-        // Note "|1〉" will generate two operations: Reset and X
-        let ket_str = ket_call(ket, qubits);
-        let mut call_str = format!("{indent}{ket_str};\n");
-        let op_x = Unitary {
-            gate: "X".to_string(),
-            is_adjoint: false,
-            controls: vec![],
-            targets: ket.targets.clone(),
-            args: vec![],
-            children: vec![],
-        };
-        let operation_str = operation_call(&op_x, qubits);
-        writeln!(call_str, "{indent}{operation_str};").expect("could not write to call_str");
-        call_str
-    } else {
+    // Note: The only supported ket operation is "0"
+    if ket.gate == "0" {
         let ket_str = ket_call(ket, qubits);
         format!("{indent}{ket_str};\n")
+    } else {
+        format!(
+            "{indent}fail \"Unsupported ket operation: |{}〉\";\n",
+            ket.gate
+        )
     }
 }
 
@@ -300,6 +298,7 @@ fn measurement_call(measurement: &Measurement, qubits: &FxHashMap<usize, String>
 }
 
 fn ket_call(ket: &Ket, qubits: &FxHashMap<usize, String>) -> String {
+    // Note: The only supported ket operation is "0" which is a reset operation
     let targets = ket
         .targets
         .iter()
diff --git a/compiler/qsc_linter/src/lib.rs b/compiler/qsc_linter/src/lib.rs
index efe0d908..856e6e76 100644
--- a/compiler/qsc_linter/src/lib.rs
+++ b/compiler/qsc_linter/src/lib.rs
@@ -61,10 +61,12 @@
 
 #![deny(missing_docs)]
 
+mod lint_groups;
 mod linter;
 mod lints;
 #[cfg(test)]
 mod tests;
 
-pub use linter::{run_lints, Lint, LintConfig, LintKind, LintLevel};
+pub use lint_groups::GroupConfig;
+pub use linter::{run_lints, Lint, LintConfig, LintKind, LintLevel, LintOrGroupConfig};
 pub use lints::{ast::AstLint, hir::HirLint};
diff --git a/compiler/qsc_linter/src/lint_groups.rs b/compiler/qsc_linter/src/lint_groups.rs
new file mode 100644
index 00000000..104bc37b
--- /dev/null
+++ b/compiler/qsc_linter/src/lint_groups.rs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+use crate::{LintKind, LintLevel};
+use serde::{Deserialize, Serialize};
+
+/// End-user configuration for a lint group.
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+pub struct GroupConfig {
+    #[serde(rename = "group")]
+    /// The lint group.
+    pub lint_group: LintGroup,
+    /// The group level.
+    pub level: LintLevel,
+}
+
+#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub enum LintGroup {
+    Deprecations,
+}
+
+impl LintGroup {
+    pub fn unfold(self) -> Vec<LintKind> {
+        use crate::AstLint::*;
+        use crate::HirLint::*;
+        match self {
+            LintGroup::Deprecations => {
+                vec![
+                    LintKind::Ast(DeprecatedNewtype),
+                    LintKind::Ast(DeprecatedSet),
+                    LintKind::Hir(DeprecatedFunctionConstructor),
+                    LintKind::Hir(DeprecatedWithOperator),
+                    LintKind::Hir(DeprecatedDoubleColonOperator),
+                ]
+            }
+        }
+    }
+}
diff --git a/compiler/qsc_linter/src/linter.rs b/compiler/qsc_linter/src/linter.rs
index c7ba2120..8f0de2b0 100644
--- a/compiler/qsc_linter/src/linter.rs
+++ b/compiler/qsc_linter/src/linter.rs
@@ -5,11 +5,15 @@ pub(crate) mod ast;
 pub(crate) mod hir;
 
 use self::{ast::run_ast_lints, hir::run_hir_lints};
-use crate::lints::{ast::AstLint, hir::HirLint};
+use crate::{
+    lints::{ast::AstLint, hir::HirLint},
+    GroupConfig,
+};
 use miette::{Diagnostic, LabeledSpan};
 use qsc_data_structures::span::Span;
 use qsc_frontend::compile::{CompileUnit, PackageStore};
 use qsc_hir::hir::{Item, ItemId};
+use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 use std::fmt::Display;
 
@@ -19,7 +23,7 @@ use std::fmt::Display;
 pub fn run_lints(
     package_store: &PackageStore,
     compile_unit: &CompileUnit,
-    config: Option<&[LintConfig]>,
+    config: Option<&[LintOrGroupConfig]>,
 ) -> Vec<Lint> {
     let mut lints = run_lints_without_deduplication(package_store, compile_unit, config);
     remove_duplicates(&mut lints);
@@ -33,15 +37,25 @@ pub fn run_lints(
 pub(crate) fn run_lints_without_deduplication(
     package_store: &PackageStore,
     compile_unit: &CompileUnit,
-    config: Option<&[LintConfig]>,
+    config: Option<&[LintOrGroupConfig]>,
 ) -> Vec<Lint> {
     let compilation = Compilation {
         package_store,
         compile_unit,
     };
 
-    let mut ast_lints = run_ast_lints(&compile_unit.ast.package, config, compilation);
-    let mut hir_lints = run_hir_lints(&compile_unit.package, config, compilation);
+    let unfolded_config = config.map(unfold_groups);
+
+    let mut ast_lints = run_ast_lints(
+        &compile_unit.ast.package,
+        unfolded_config.as_deref(),
+        compilation,
+    );
+    let mut hir_lints = run_hir_lints(
+        &compile_unit.package,
+        unfolded_config.as_deref(),
+        compilation,
+    );
 
     let mut lints = Vec::new();
     lints.append(&mut ast_lints);
@@ -49,6 +63,35 @@ pub(crate) fn run_lints_without_deduplication(
     lints
 }
 
+/// Unfolds groups into lists of lints. Specific lints override group configs.
+pub(crate) fn unfold_groups(config: &[LintOrGroupConfig]) -> Vec<LintConfig> {
+    let mut config_map: FxHashMap<LintKind, LintLevel> = FxHashMap::default();
+
+    // Unfold groups in the order they appear.
+    for elt in config {
+        if let LintOrGroupConfig::Group(group) = elt {
+            for lint in group.lint_group.unfold() {
+                config_map.insert(lint, group.level);
+            }
+        }
+    }
+
+    // Specific lints override group configs.
+    for elt in config {
+        if let LintOrGroupConfig::Lint(lint) = elt {
+            config_map.insert(lint.kind, lint.level);
+        }
+    }
+
+    config_map
+        .iter()
+        .map(|(kind, level)| LintConfig {
+            kind: *kind,
+            level: *level,
+        })
+        .collect()
+}
+
 pub(crate) fn remove_duplicates<T: Eq + std::hash::Hash + Clone>(vec: &mut Vec<T>) {
     let mut seen = rustc_hash::FxHashSet::default();
     vec.retain(|x| seen.insert(x.clone()));
@@ -211,11 +254,21 @@ impl Display for LintLevel {
     }
 }
 
-/// End-user configuration for each lint level.
+/// End-user configuration for a specific lint or a lint group.
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(untagged)]
+pub enum LintOrGroupConfig {
+    /// An specific lint configuration.
+    Lint(LintConfig),
+    /// A lint group configuration.
+    Group(GroupConfig),
+}
+
+/// End-user configuration for a specific lint.
 #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
 pub struct LintConfig {
     #[serde(rename = "lint")]
-    /// Represents the lint name.
+    /// The lint name.
     pub kind: LintKind,
     /// The lint level.
     pub level: LintLevel,
diff --git a/compiler/qsc_linter/src/linter/ast.rs b/compiler/qsc_linter/src/linter/ast.rs
index 9befd4a0..7bca591d 100644
--- a/compiler/qsc_linter/src/linter/ast.rs
+++ b/compiler/qsc_linter/src/linter/ast.rs
@@ -3,7 +3,7 @@
 
 use crate::{
     lints::ast::{AstLint, CombinedAstLints},
-    Lint, LintConfig, LintLevel,
+    Lint, LintLevel,
 };
 use qsc_ast::{
     ast::{
@@ -24,9 +24,9 @@ pub fn run_ast_lints(
     let config: Vec<(AstLint, LintLevel)> = config
         .unwrap_or(&[])
         .iter()
-        .filter_map(|lint_config| {
-            if let LintKind::Ast(kind) = lint_config.kind {
-                Some((kind, lint_config.level))
+        .filter_map(|lint| {
+            if let LintKind::Ast(kind) = lint.kind {
+                Some((kind, lint.level))
             } else {
                 None
             }
@@ -342,4 +342,4 @@ macro_rules! declare_ast_lints {
 
 pub(crate) use declare_ast_lints;
 
-use super::{Compilation, LintKind};
+use super::{Compilation, LintConfig, LintKind};
diff --git a/compiler/qsc_linter/src/linter/hir.rs b/compiler/qsc_linter/src/linter/hir.rs
index a8e0b6b7..dfa1c323 100644
--- a/compiler/qsc_linter/src/linter/hir.rs
+++ b/compiler/qsc_linter/src/linter/hir.rs
@@ -3,7 +3,7 @@
 
 use crate::{
     lints::hir::{CombinedHirLints, HirLint},
-    Lint, LintConfig, LintLevel,
+    Lint, LintLevel,
 };
 use qsc_hir::{
     hir::{Block, CallableDecl, Expr, Ident, Item, Package, Pat, QubitInit, SpecDecl, Stmt},
@@ -21,9 +21,9 @@ pub fn run_hir_lints(
     let config: Vec<(HirLint, LintLevel)> = config
         .unwrap_or(&[])
         .iter()
-        .filter_map(|lint_config| {
-            if let LintKind::Hir(kind) = lint_config.kind {
-                Some((kind, lint_config.level))
+        .filter_map(|lint| {
+            if let LintKind::Hir(kind) = lint.kind {
+                Some((kind, lint.level))
             } else {
                 None
             }
@@ -264,4 +264,4 @@ macro_rules! declare_hir_lints {
 
 pub(crate) use declare_hir_lints;
 
-use super::{Compilation, LintKind};
+use super::{Compilation, LintConfig, LintKind};
diff --git a/compiler/qsc_project/src/manifest.rs b/compiler/qsc_project/src/manifest.rs
index 8f3a20cf..8200d0b7 100644
--- a/compiler/qsc_project/src/manifest.rs
+++ b/compiler/qsc_project/src/manifest.rs
@@ -9,7 +9,7 @@ use std::{
     fs::{self, DirEntry, FileType},
 };
 
-pub use qsc_linter::LintConfig;
+pub use qsc_linter::LintOrGroupConfig;
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 use std::path::PathBuf;
@@ -25,7 +25,7 @@ pub struct Manifest {
     #[serde(default)]
     pub language_features: Vec<String>,
     #[serde(default)]
-    pub lints: Vec<LintConfig>,
+    pub lints: Vec<LintOrGroupConfig>,
     #[serde(default)]
     pub dependencies: FxHashMap<String, PackageRef>,
     #[serde(default)]
diff --git a/compiler/qsc_project/src/project.rs b/compiler/qsc_project/src/project.rs
index b8a750c7..bfb8d1ac 100644
--- a/compiler/qsc_project/src/project.rs
+++ b/compiler/qsc_project/src/project.rs
@@ -9,7 +9,7 @@ use async_trait::async_trait;
 use futures::FutureExt;
 use miette::Diagnostic;
 use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_linter::LintConfig;
+use qsc_linter::LintOrGroupConfig;
 use rustc_hash::FxHashMap;
 use std::{
     cell::RefCell,
@@ -33,7 +33,7 @@ pub struct Project {
     /// configuration settings.
     pub package_graph_sources: PackageGraphSources,
     /// Lint configuration for the project, typically comes from the root `qsharp.json`.
-    pub lints: Vec<LintConfig>,
+    pub lints: Vec<LintOrGroupConfig>,
     /// Any errors encountered while loading the project.
     pub errors: Vec<Error>,
 }
diff --git a/compiler/qsc_qasm/src/compiler.rs b/compiler/qsc_qasm/src/compiler.rs
index 02201406..0cb47632 100644
--- a/compiler/qsc_qasm/src/compiler.rs
+++ b/compiler/qsc_qasm/src/compiler.rs
@@ -128,7 +128,7 @@ impl QasmCompiler {
             ProgramType::Fragments => (self.build_fragments(), None),
         };
 
-        QasmCompileUnit::new(self.source_map, self.errors, Some(package), signature)
+        QasmCompileUnit::new(self.source_map, self.errors, package, signature)
     }
 
     /// Build a package with namespace and an operation
diff --git a/compiler/qsc_qasm/src/lib.rs b/compiler/qsc_qasm/src/lib.rs
index 65ecefce..4daa515b 100644
--- a/compiler/qsc_qasm/src/lib.rs
+++ b/compiler/qsc_qasm/src/lib.rs
@@ -231,10 +231,10 @@ pub struct QasmCompileUnit {
     /// Semantic errors encountered during compilation.
     /// These are always fatal errors that prevent compilation.
     errors: Vec<WithSource<crate::Error>>,
-    /// The compiled AST package, if compilation was successful.
+    /// The compiled AST package
     /// There is no guarantee that this package is valid unless
     /// there are no errors.
-    package: Option<Package>,
+    package: Package,
     /// The signature of the operation created from the QASM source code.
     /// None if the program type is `ProgramType::Fragments`.
     signature: Option<OperationSignature>,
@@ -249,7 +249,7 @@ impl QasmCompileUnit {
     pub fn new(
         source_map: SourceMap,
         errors: Vec<WithSource<crate::Error>>,
-        package: Option<Package>,
+        package: Package,
         signature: Option<OperationSignature>,
     ) -> Self {
         Self {
@@ -279,7 +279,7 @@ impl QasmCompileUnit {
     ) -> (
         SourceMap,
         Vec<WithSource<crate::Error>>,
-        Option<Package>,
+        Package,
         Option<OperationSignature>,
     ) {
         (self.source_map, self.errors, self.package, self.signature)
diff --git a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs
index d3926410..d7ccc58e 100644
--- a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs
+++ b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs
@@ -8,6 +8,7 @@ import Std.Convert.IntAsBigInt;
 import Std.Convert.IntAsBoolArray;
 import Std.Convert.IntAsDouble;
 import Std.Diagnostics.Fact;
+import Std.Math.RoundHalfAwayFromZero;
 import Convert.__IntAsResultArrayBE__;
 import Convert.__ResultAsInt__;
 
@@ -231,7 +232,7 @@ function __MultiplyAngleByBigInt__(angle : __Angle__, factor : BigInt) : __Angle
     let (value, size) = angle!;
     let value : BigInt = Std.Convert.IntAsBigInt(value);
     let value = (value * factor) % Std.Convert.IntAsBigInt(1 <<< size);
-    let value = Std.Convert.BoolArrayAsInt(Std.Convert.BigIntAsBoolArray(value, size));
+    let value = Std.Convert.BigIntAsInt(value);
     new __Angle__ { Value = value, Size = size }
 }
 
@@ -254,20 +255,3 @@ function __NegAngle__(angle : __Angle__) : __Angle__ {
     let value = (1 <<< size) - value;
     new __Angle__ { Value = value, Size = size }
 }
-
-// not exported
-function RoundHalfAwayFromZero(value : Double) : Int {
-    let roundedValue = Microsoft.Quantum.Math.Round(value);
-    let EPSILON = 2.2204460492503131e-16;
-    let diff = Std.Math.AbsD(value - Std.Convert.IntAsDouble(roundedValue));
-    if (Std.Math.AbsD(diff - 0.5) < EPSILON) {
-        if (value > 0.0) {
-            return roundedValue + 1;
-        } else {
-            return roundedValue - 1;
-        }
-    } else {
-        return roundedValue;
-    }
-}
-
diff --git a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs
index 22dfb0c0..eb5eee6a 100644
--- a/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs
+++ b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs
@@ -5,10 +5,7 @@ import Std.Math.AbsI;
 
 /// The POW function is used to implement the `pow` modifier in QASM for integers.
 operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj + Ctl), target : 'T) : Unit {
-    let op = if N > 0 { () => op(target) } else { () => Adjoint op(target) };
-    for _ in 1..AbsI(N) {
-        op()
-    }
+    ApplyOperationPowerA(N, op, target);
 }
 
 /// The ``BARRIER`` function is used to implement the `barrier` statement in QASM.
diff --git a/compiler/qsc_qasm/src/stdlib/compile.rs b/compiler/qsc_qasm/src/stdlib/compile.rs
index f0de74ec..2cc2ed63 100644
--- a/compiler/qsc_qasm/src/stdlib/compile.rs
+++ b/compiler/qsc_qasm/src/stdlib/compile.rs
@@ -16,6 +16,8 @@ fn compiles_with_base_profile() {
     let _ = package_store_with_qasm(TargetCapabilityFlags::empty());
 }
 
+/// return a tuple with the stdlib package id, the qasm library package id, and the package store
+/// that contains the core, std, and qasmstd packages.
 #[must_use]
 pub fn package_store_with_qasm(
     capabilities: TargetCapabilityFlags,
diff --git a/fuzz/fuzz_targets/qasm.rs b/fuzz/fuzz_targets/qasm.rs
index b4c38dfb..e7c9f299 100644
--- a/fuzz/fuzz_targets/qasm.rs
+++ b/fuzz/fuzz_targets/qasm.rs
@@ -8,10 +8,57 @@ allocator::assign_global!();
 #[cfg(feature = "do_fuzz")]
 use libfuzzer_sys::fuzz_target;
 
+use qsc::{
+    compile::compile_ast,
+    hir::PackageId,
+    qasm::{
+        compile_to_qsharp_ast_with_config, io::InMemorySourceResolver, package_store_with_qasm,
+        CompilerConfig, OutputSemantics, ProgramType, QubitSemantics,
+    },
+    target::Profile,
+    PackageStore, PackageType,
+};
+
 fn compile(data: &[u8]) {
     if let Ok(fuzzed_code) = std::str::from_utf8(data) {
-        let mut resolver = qsc::qasm::io::InMemorySourceResolver::from_iter([]);
-        let _ = qsc::qasm::parser::parse_source(fuzzed_code, "fuzz.qasm", &mut resolver);
+        thread_local! {
+            static STORE_STD: (PackageId, PackageId, PackageStore) = {
+                package_store_with_qasm(Profile::Unrestricted.into())
+            };
+        }
+        STORE_STD.with(|(stdid, qasmid, store)| {
+            let mut resolver = InMemorySourceResolver::from_iter([]);
+            let config = CompilerConfig::new(
+                QubitSemantics::Qiskit,
+                OutputSemantics::OpenQasm,
+                ProgramType::File,
+                Some("Fuzz".into()),
+                None,
+            );
+
+            let unit = compile_to_qsharp_ast_with_config(
+                fuzzed_code,
+                "fuzz.qasm",
+                Some(&mut resolver),
+                config,
+            );
+            let (sources, _, package, _) = unit.into_tuple();
+
+            let dependencies = vec![
+                (PackageId::CORE, None),
+                (*stdid, None),
+                (*qasmid, Some("QasmStd".into())),
+            ];
+
+            let (mut _unit, _errors) = compile_ast(
+                store,
+                &dependencies,
+                package,
+                sources,
+                PackageType::Lib,
+                Profile::Unrestricted.into(),
+            );
+        });
     }
 }
 
diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs
index 3521fad2..aaf335fd 100644
--- a/language_service/src/compilation.rs
+++ b/language_service/src/compilation.rs
@@ -14,7 +14,7 @@ use qsc::{
     target::Profile,
     CompileUnit, LanguageFeatures, PackageStore, PackageType, PassContext, SourceMap, Span,
 };
-use qsc_linter::{LintConfig, LintLevel};
+use qsc_linter::{LintLevel, LintOrGroupConfig};
 use qsc_project::{PackageGraphSources, Project};
 use rustc_hash::FxHashMap;
 use std::sync::Arc;
@@ -62,7 +62,7 @@ impl Compilation {
         package_type: PackageType,
         target_profile: Profile,
         language_features: LanguageFeatures,
-        lints_config: &[LintConfig],
+        lints_config: &[LintOrGroupConfig],
         package_graph_sources: PackageGraphSources,
         project_errors: Vec<project::Error>,
         friendly_name: &Arc<str>,
@@ -127,7 +127,7 @@ impl Compilation {
         cells: I,
         target_profile: Profile,
         language_features: LanguageFeatures,
-        lints_config: &[LintConfig],
+        lints_config: &[LintOrGroupConfig],
         project: Option<Project>,
     ) -> Self
     where
@@ -330,7 +330,7 @@ impl Compilation {
         package_type: PackageType,
         target_profile: Profile,
         language_features: LanguageFeatures,
-        lints_config: &[LintConfig],
+        lints_config: &[LintOrGroupConfig],
     ) {
         let sources = self
             .user_unit()
@@ -408,7 +408,7 @@ fn run_linter_passes(
     errors: &mut Vec<WithSource<compile::ErrorKind>>,
     package_store: &PackageStore,
     unit: &CompileUnit,
-    config: &[LintConfig],
+    config: &[LintOrGroupConfig],
 ) {
     if errors.is_empty() {
         let lints = qsc::linter::run_lints(package_store, unit, Some(config));
diff --git a/language_service/src/protocol.rs b/language_service/src/protocol.rs
index 978da9d4..17e02a2b 100644
--- a/language_service/src/protocol.rs
+++ b/language_service/src/protocol.rs
@@ -5,7 +5,8 @@ use miette::Diagnostic;
 use qsc::line_column::Range;
 use qsc::location::Location;
 use qsc::{compile, project};
-use qsc::{linter::LintConfig, project::Manifest, target::Profile, LanguageFeatures, PackageType};
+use qsc::{project::Manifest, target::Profile, LanguageFeatures, PackageType};
+use qsc_linter::LintOrGroupConfig;
 use thiserror::Error;
 
 /// A change to the workspace configuration
@@ -14,7 +15,7 @@ pub struct WorkspaceConfigurationUpdate {
     pub target_profile: Option<Profile>,
     pub package_type: Option<PackageType>,
     pub language_features: Option<LanguageFeatures>,
-    pub lints_config: Option<Vec<LintConfig>>,
+    pub lints_config: Option<Vec<LintOrGroupConfig>>,
 }
 
 #[derive(Clone, Debug, Diagnostic, Error)]
diff --git a/language_service/src/state.rs b/language_service/src/state.rs
index 89fbc6c3..804b07e3 100644
--- a/language_service/src/state.rs
+++ b/language_service/src/state.rs
@@ -14,7 +14,7 @@ use log::{debug, trace};
 use miette::Diagnostic;
 use qsc::line_column::Encoding;
 use qsc::{compile, project, target::Profile, LanguageFeatures, PackageType};
-use qsc_linter::LintConfig;
+use qsc_linter::LintOrGroupConfig;
 use qsc_project::{FileSystemAsync, JSProjectHost, PackageCache, Project};
 use rustc_hash::{FxHashMap, FxHashSet};
 use std::path::PathBuf;
@@ -65,7 +65,7 @@ struct Configuration {
     pub target_profile: Profile,
     pub package_type: PackageType,
     pub language_features: LanguageFeatures,
-    pub lints_config: Vec<LintConfig>,
+    pub lints_config: Vec<LintOrGroupConfig>,
 }
 
 impl Default for Configuration {
@@ -84,7 +84,7 @@ pub struct PartialConfiguration {
     pub target_profile: Option<Profile>,
     pub package_type: Option<PackageType>,
     pub language_features: Option<LanguageFeatures>,
-    pub lints_config: Vec<LintConfig>,
+    pub lints_config: Vec<LintOrGroupConfig>,
 }
 
 pub(super) struct CompilationStateUpdater<'a> {
@@ -644,9 +644,26 @@ fn merge_configurations(
     let mut override_lints = compilation_overrides.lints_config.clone();
     override_lints.retain(|override_lint| {
         for merged_lint in &mut merged_lints {
-            if merged_lint.kind == override_lint.kind {
-                merged_lint.level = override_lint.level;
-                return false;
+            match (merged_lint, override_lint) {
+                (
+                    LintOrGroupConfig::Lint(lint_config),
+                    LintOrGroupConfig::Lint(lint_config_override),
+                ) => {
+                    if lint_config.kind == lint_config_override.kind {
+                        lint_config.level = lint_config_override.level;
+                        return false;
+                    }
+                }
+                (
+                    LintOrGroupConfig::Group(group_config),
+                    LintOrGroupConfig::Group(group_config_override),
+                ) => {
+                    if group_config.lint_group == group_config_override.lint_group {
+                        group_config.level = group_config_override.level;
+                        return false;
+                    }
+                }
+                _ => (),
             }
         }
         true
diff --git a/library/std/src/Std/Canon.qs b/library/std/src/Std/Canon.qs
index 98751cc6..1eccc6fb 100644
--- a/library/std/src/Std/Canon.qs
+++ b/library/std/src/Std/Canon.qs
@@ -583,6 +583,22 @@ operation ApplyXorInPlaceL(value : BigInt, target : Qubit[]) : Unit is Adj + Ctl
     }
     adjoint self;
 }
+
+/// # Summary
+/// Applies operation `op` to the `target` `power` times.
+/// If `power` is negative, the adjoint of `op` is used.
+/// If `power` is 0, the operation `op` is not applied.
+operation ApplyOperationPowerA<'T>(
+    power : Int,
+    op : 'T => Unit is Adj,
+    target : 'T
+) : Unit is Adj {
+    let u = if power >= 0 { op } else { Adjoint op };
+    for _ in 1..AbsI(power) {
+        u(target);
+    }
+}
+
 /// # Summary
 /// Relabels the qubits in the `current` array with the qubits in the `updated` array. The `updated` array
 /// must be a valid permutation of the `current` array.
@@ -647,4 +663,5 @@ export
     SwapReverseRegister,
     ApplyXorInPlace,
     ApplyXorInPlaceL,
+    ApplyOperationPowerA,
     Relabel;
diff --git a/library/std/src/Std/Convert.qs b/library/std/src/Std/Convert.qs
index 58d8953f..7af5d066 100644
--- a/library/std/src/Std/Convert.qs
+++ b/library/std/src/Std/Convert.qs
@@ -162,6 +162,34 @@ function BigIntAsBoolArray(number : BigInt, bits : Int) : Bool[] {
     result
 }
 
+/// # Summary
+/// Converts a BigInt number into Int. Raises an error if the number is too large to fit.
+///
+/// # Input
+/// ## number
+/// A BigInt number to be converted.
+///
+/// # Output
+/// Int representation of a number.
+function BigIntAsInt(number : BigInt) : Int {
+    let max = 9_223_372_036_854_775_807L;
+    let min = -9_223_372_036_854_775_808L;
+    Fact(number >= min and number <= max, $"BigIntAsInt: {number} is too big to fit into Int.");
+
+    mutable result = 0;
+    mutable powL = 1L;
+    mutable pow = 1;
+    for _ in 0..63 {
+        if number &&& powL != 0L {
+            result |||= pow;
+        }
+        powL <<<= 1;
+        pow <<<= 1;
+    }
+
+    result
+}
+
 /// # Summary
 /// Produces a non-negative integer from a string of Results in little-endian format.
 ///
@@ -289,9 +317,10 @@ export
     IntAsBoolArray,
     BoolArrayAsBigInt,
     BigIntAsBoolArray,
+    BigIntAsInt,
     ResultArrayAsInt,
     ResultArrayAsBoolArray,
     BoolArrayAsResultArray,
     ComplexAsComplexPolar,
     ComplexPolarAsComplex,
-    DoubleAsStringWithPrecision;
\ No newline at end of file
+    DoubleAsStringWithPrecision;
diff --git a/library/std/src/Std/Math.qs b/library/std/src/Std/Math.qs
index 7e9cdf11..943473f7 100644
--- a/library/std/src/Std/Math.qs
+++ b/library/std/src/Std/Math.qs
@@ -396,16 +396,27 @@ function Floor(value : Double) : Int {
 }
 
 /// # Summary
-/// Returns the nearest integer to the specified number.
-/// For example: Round(3.7) = 4; Round(-3.7) = -4
+/// Returns the nearest integer to the specified number. Half is rounded towards zero.
+/// For example: Round(3.7) = 4; Round(-3.7) = -4; Round(3.5) = 3;
+///
+/// # References
+/// [Wikipedia article - Rounding](https://en.wikipedia.org/wiki/Rounding#Rounding_half_toward_zero)
 function Round(value : Double) : Int {
     let (truncated, remainder, isPositive) = ExtendedTruncation(value);
-    if AbsD(remainder) <= 1e-15 {
-        truncated
-    } else {
-        let abs = AbsD(remainder);
-        truncated + (abs <= 0.5 ? 0 | (isPositive ? 1 | -1))
-    }
+    let abs = AbsD(remainder);
+    truncated + (abs <= 0.5 ? 0 | (isPositive ? 1 | -1))
+}
+
+/// # Summary
+/// Returns the nearest integer to the specified number. Half is rounded away from zero.
+/// For example: RoundHalfAwayFromZero(-3.7) = -4, RoundHalfAwayFromZero(3.5) = 4;
+///
+/// # References
+/// [Wikipedia article - Rounding](https://en.wikipedia.org/wiki/Rounding#Rounding_half_away_from_zero)
+function RoundHalfAwayFromZero(value : Double) : Int {
+    let (truncated, remainder, isPositive) = ExtendedTruncation(value);
+    let abs = AbsD(remainder);
+    truncated + (abs < 0.5 ? 0 | (isPositive ? 1 | -1))
 }
 
 //
@@ -1493,6 +1504,7 @@ export
     Ceiling,
     Floor,
     Round,
+    RoundHalfAwayFromZero,
     DivRemI,
     DivRemL,
     ModulusI,
diff --git a/npm/qsharp/ux/circuit-vis/circuitManipulation.ts b/npm/qsharp/ux/circuit-vis/circuitManipulation.ts
index 06f7f168..870c5d40 100644
--- a/npm/qsharp/ux/circuit-vis/circuitManipulation.ts
+++ b/npm/qsharp/ux/circuit-vis/circuitManipulation.ts
@@ -129,27 +129,49 @@ const _moveY = (
   movingControl: boolean,
 ): void => {
   // Check if the source operation already has a target or control on the target wire
-  let registers: Register[];
+  let targets: Register[];
   switch (sourceOperation.kind) {
     case "unitary":
-      registers = [
-        ...sourceOperation.targets,
-        ...(sourceOperation.controls || []),
-      ];
+    case "ket":
+      targets = sourceOperation.targets;
       break;
     case "measurement":
-      registers = [...sourceOperation.qubits, ...sourceOperation.results];
+      targets = sourceOperation.qubits;
+      break;
+  }
+
+  let controls: Register[];
+  switch (sourceOperation.kind) {
+    case "unitary":
+      controls = sourceOperation.controls || [];
       break;
+    case "measurement":
     case "ket":
-      registers = sourceOperation.targets;
+      controls = [];
       break;
   }
 
-  if (registers.find((target) => target.qubit === targetWire)) {
-    // If the target or control already exists, don't move the target/control
+  let likeRegisters: Register[];
+  let unlikeRegisters: Register[];
+  if (movingControl) {
+    likeRegisters = controls;
+    unlikeRegisters = targets;
+  } else {
+    likeRegisters = targets;
+    unlikeRegisters = controls;
+  }
+
+  // If a similar register already exists, don't move the gate
+  if (likeRegisters.find((reg) => reg.qubit === targetWire)) {
     return;
   }
 
+  // If a different kind of register already exists, swap the control and target
+  if (unlikeRegisters.find((reg) => reg.qubit === targetWire)) {
+    const index = unlikeRegisters.findIndex((reg) => reg.qubit === targetWire);
+    unlikeRegisters[index].qubit = sourceWire;
+  }
+
   switch (sourceOperation.kind) {
     case "unitary":
       if (movingControl) {
diff --git a/npm/qsharp/ux/circuit-vis/events.ts b/npm/qsharp/ux/circuit-vis/events.ts
index 76bba9e9..e9f83534 100644
--- a/npm/qsharp/ux/circuit-vis/events.ts
+++ b/npm/qsharp/ux/circuit-vis/events.ts
@@ -483,7 +483,7 @@ class CircuitEvents {
                 targetLoc,
                 this.selectedWire,
                 targetWire,
-                false,
+                this.movingControl,
                 insertNewColumn,
               );
             } else {
diff --git a/npm/qsharp/ux/circuit-vis/panel.ts b/npm/qsharp/ux/circuit-vis/panel.ts
index 65961ddc..f7001f9b 100644
--- a/npm/qsharp/ux/circuit-vis/panel.ts
+++ b/npm/qsharp/ux/circuit-vis/panel.ts
@@ -322,7 +322,6 @@ const toolboxGateDictionary: GateDictionary = {
   H: _makeUnitary("H"),
   SX: _makeUnitary("SX"),
   Reset: _makeKet("0"),
-  ResetOne: _makeKet("1"),
   Measure: _makeMeasurement("Measure"),
 };
 
diff --git a/npm/qsharp/ux/circuit.tsx b/npm/qsharp/ux/circuit.tsx
index 47ca3f09..21d6f2b3 100644
--- a/npm/qsharp/ux/circuit.tsx
+++ b/npm/qsharp/ux/circuit.tsx
@@ -270,7 +270,13 @@ export function CircuitPanel(props: CircuitProps) {
       )}
       <p>
         Learn more at{" "}
-        <a href="https://aka.ms/qdk.circuits">https://aka.ms/qdk.circuits</a>
+        {props.isEditable ? (
+          <a href="https://aka.ms/qdk.circuit-editor">
+            https://aka.ms/qdk.circuit-editor
+          </a>
+        ) : (
+          <a href="https://aka.ms/qdk.circuits">https://aka.ms/qdk.circuits</a>
+        )}
       </p>
       {props.calculating ? (
         <div>
diff --git a/pip/src/interop.rs b/pip/src/interop.rs
index 368cba06..b65c4a0c 100644
--- a/pip/src/interop.rs
+++ b/pip/src/interop.rs
@@ -287,9 +287,6 @@ pub(crate) fn compile_qasm_enriching_errors<S: AsRef<str>, R: SourceResolver>(
     if !errors.is_empty() {
         return Err(QasmError::new_err(format_qasm_errors(errors)));
     }
-    let Some(package) = package else {
-        return Err(QasmError::new_err("package should have had value"));
-    };
 
     let Some(signature) = sig else {
         return Err(QasmError::new_err(
diff --git a/playground/src/editor.tsx b/playground/src/editor.tsx
index ba9f377a..6b9d22eb 100644
--- a/playground/src/editor.tsx
+++ b/playground/src/editor.tsx
@@ -299,6 +299,7 @@ export function Editor(props: {
     return () => {
       log.info("Disposing a monaco editor");
       window.removeEventListener("resize", onResize);
+      props.languageService.closeDocument(srcModel.uri.toString());
       newEditor.dispose();
     };
   }, []);
diff --git a/vscode/qsharp.schema.json b/vscode/qsharp.schema.json
index ffb22cfe..274b7489 100644
--- a/vscode/qsharp.schema.json
+++ b/vscode/qsharp.schema.json
@@ -26,28 +26,50 @@
       "title": "Lints",
       "type": "array",
       "items": {
+        "title": "lint",
         "type": "object",
-        "properties": {
-          "lint": {
-            "type": "string",
-            "enum": [
-              "divisionByZero",
-              "needlessParens",
-              "redundantSemicolons",
-              "deprecatedNewtype",
-              "deprecatedSet",
-              "discourageChainAssignment",
-              "needlessOperation",
-              "deprecatedFunctionConstructor",
-              "deprecatedWithOperator",
-              "deprecatedDoubleColonOperator"
-            ]
+        "oneOf": [
+          {
+            "properties": {
+              "group": {
+                "type": "string",
+                "enum": ["deprecations"]
+              },
+              "level": {
+                "type": "string",
+                "enum": ["allow", "warn", "error"]
+              }
+            },
+            "required": ["group", "level"],
+            "additionalProperties": false
           },
-          "level": {
-            "type": "string",
-            "enum": ["allow", "warn", "error"]
+          {
+            "properties": {
+              "lint": {
+                "type": "string",
+                "enum": [
+                  "divisionByZero",
+                  "needlessParens",
+                  "redundantSemicolons",
+                  "deprecatedNewtype",
+                  "deprecatedSet",
+                  "discourageChainAssignment",
+                  "doubleEquality",
+                  "needlessOperation",
+                  "deprecatedFunctionConstructor",
+                  "deprecatedWithOperator",
+                  "deprecatedDoubleColonOperator"
+                ]
+              },
+              "level": {
+                "type": "string",
+                "enum": ["allow", "warn", "error"]
+              }
+            },
+            "required": ["lint", "level"],
+            "additionalProperties": false
           }
-        }
+        ]
       }
     },
     "dependencies": {
diff --git a/wasm/src/language_service.rs b/wasm/src/language_service.rs
index 8a2895a0..ebe22996 100644
--- a/wasm/src/language_service.rs
+++ b/wasm/src/language_service.rs
@@ -9,7 +9,8 @@ use crate::{
     test_discovery::TestDescriptor,
 };
 use qsc::{
-    self, line_column::Encoding, linter::LintConfig, target::Profile, LanguageFeatures, PackageType,
+    self, line_column::Encoding, linter::LintOrGroupConfig, target::Profile, LanguageFeatures,
+    PackageType,
 };
 use qsc_project::Manifest;
 use qsls::protocol::{DiagnosticUpdate, TestCallable, TestCallables};
@@ -378,13 +379,13 @@ serializable_type! {
         pub targetProfile: Option<String>,
         pub packageType: Option<String>,
         pub languageFeatures: Option<Vec<String>>,
-        pub lints: Option<Vec<LintConfig>>
+        pub lints: Option<Vec<LintOrGroupConfig>>
     },
     r#"export interface IWorkspaceConfiguration {
         targetProfile?: TargetProfile;
         packageType?: "exe" | "lib";
         languageFeatures?: LanguageFeatures[];
-        lints?: { lint: string; level: string }[];
+        lints?: ({ lint: string; level: string } | { group: string; level: string })[];
     }"#,
     IWorkspaceConfiguration
 }
diff --git a/wasm/src/project_system.rs b/wasm/src/project_system.rs
index a15699b1..a6cf7eff 100644
--- a/wasm/src/project_system.rs
+++ b/wasm/src/project_system.rs
@@ -4,7 +4,7 @@
 use crate::{diagnostic::project_errors_into_qsharp_errors, serializable_type};
 use async_trait::async_trait;
 use miette::Report;
-use qsc::{linter::LintConfig, packages::BuildableProgram, LanguageFeatures};
+use qsc::{linter::LintOrGroupConfig, packages::BuildableProgram, LanguageFeatures};
 use qsc_project::{EntryType, FileSystemAsync, JSFileEntry, JSProjectHost, PackageCache};
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
@@ -267,7 +267,7 @@ serializable_type! {
         pub package_type: Option<String>,
     },
     r#"
-    
+
     export interface IPackageInfo {
         sources: [string, string][];
         languageFeatures: string[];
@@ -302,7 +302,7 @@ serializable_type! {
         pub project_name: String,
         pub project_uri: String,
         pub package_graph_sources: PackageGraphSources,
-        pub lints: Vec<LintConfig>,
+        pub lints: Vec<LintOrGroupConfig>,
     },
     r#"export interface IProjectConfig {
         /**
@@ -314,10 +314,7 @@ serializable_type! {
          */
         projectUri: string;
         packageGraphSources: IPackageGraphSources;
-        lints: {
-          lint: string;
-          level: string;
-        }[];
+        lints: ({ lint: string; level: string } | { group: string; level: string })[];
         errors: string[];
     }"#,
     IProjectConfig


Test patch:
diff --git a/compiler/qsc/src/interpret/circuit_tests.rs b/compiler/qsc/src/interpret/circuit_tests.rs
index a2832ab7..5543bb0a 100644
--- a/compiler/qsc/src/interpret/circuit_tests.rs
+++ b/compiler/qsc/src/interpret/circuit_tests.rs
@@ -148,9 +148,9 @@ fn rotation_gate() {
         .circuit(CircuitEntryPoint::EntryPoint, false)
         .expect("circuit generation should succeed");
 
-    expect![[r"
-        q_0    ─ rx(1.5708) ──
-    "]]
+    expect![[r#"
+        q_0    ─ Rx(1.5708) ──
+    "#]]
     .assert_eq(&circ.to_string());
 }
 
@@ -883,10 +883,10 @@ fn operation_with_long_gates_properly_aligned() {
     expect![[r#"
         q_0    ── H ────────────────────────────────────── ● ──────── M ────────────────────────────────── ● ─────────
                                                            │          ╘════════════════════════════════════╪══════════
-        q_1    ── H ──────── X ─────── ry(1.0000) ──────── X ───────────────────────────── rxx(1.0000) ────┼───── M ──
+        q_1    ── H ──────── X ─────── Ry(1.0000) ──────── X ───────────────────────────── Rxx(1.0000) ────┼───── M ──
                                                                                                 ┆          │      ╘═══
-        q_2    ── H ─── rx(1.0000) ──────── H ─────── rx(1.0000) ──── H ─── rx(1.0000) ─────────┆──────────┼──────────
-        q_3    ─────────────────────────────────────────────────────────────────────────── rxx(1.0000) ─── X ──── M ──
+        q_2    ── H ─── Rx(1.0000) ──────── H ─────── Rx(1.0000) ──── H ─── Rx(1.0000) ─────────┆──────────┼──────────
+        q_3    ─────────────────────────────────────────────────────────────────────────── Rxx(1.0000) ─── X ──── M ──
                                                                                                                   ╘═══
     "#]]
     .assert_eq(&circ.to_string());
@@ -919,12 +919,12 @@ fn operation_with_subsequent_qubits_gets_horizontal_lines() {
         .expect("circuit generation should succeed");
 
     expect![[r#"
-        q_0    ─ rxx(1.0000) ─
+        q_0    ─ Rxx(1.0000) ─
                       ┆
-        q_1    ─ rxx(1.0000) ─
-        q_2    ─ rxx(1.0000) ─
+        q_1    ─ Rxx(1.0000) ─
+        q_2    ─ Rxx(1.0000) ─
                       ┆
-        q_3    ─ rxx(1.0000) ─
+        q_3    ─ Rxx(1.0000) ─
     "#]]
     .assert_eq(&circ.to_string());
 }
@@ -953,9 +953,9 @@ fn operation_with_subsequent_qubits_no_double_rows() {
         .expect("circuit generation should succeed");
 
     expect![[r#"
-        q_0    ─ rxx(1.0000) ── rxx(1.0000) ─
+        q_0    ─ Rxx(1.0000) ── Rxx(1.0000) ─
                       ┆              ┆
-        q_1    ─ rxx(1.0000) ── rxx(1.0000) ─
+        q_1    ─ Rxx(1.0000) ── Rxx(1.0000) ─
     "#]]
     .assert_eq(&circ.to_string());
 }
@@ -989,12 +989,12 @@ fn operation_with_subsequent_qubits_no_added_rows() {
         .expect("circuit generation should succeed");
 
     expect![[r#"
-        q_0    ─ rxx(1.0000) ─── M ──
+        q_0    ─ Rxx(1.0000) ─── M ──
                       ┆          ╘═══
-        q_1    ─ rxx(1.0000) ────────
-        q_2    ─ rxx(1.0000) ─── M ──
+        q_1    ─ Rxx(1.0000) ────────
+        q_2    ─ Rxx(1.0000) ─── M ──
                       ┆          ╘═══
-        q_3    ─ rxx(1.0000) ────────
+        q_3    ─ Rxx(1.0000) ────────
     "#]]
     .assert_eq(&circ.to_string());
 }
diff --git a/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs b/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs
index 40230b58..2adb6e7f 100644
--- a/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs
+++ b/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs
@@ -458,11 +458,34 @@ fn empty_circuit() {
 }
 
 #[test]
-fn circuit_with_qubit_missing_num_results() {
+fn empty_circuit_with_qubits() {
     check(
         r#"
 {
   "componentGrid": [],
+  "qubits": [{ "id": 0 }, { "id": 1 }]
+}"#,
+        &expect![[r#"
+            /// Expects a qubit register of size 2.
+            operation Test(qs : Qubit[]) : Unit is Ctl + Adj {
+            }
+
+        "#]],
+    );
+}
+
+#[test]
+fn circuit_with_qubit_missing_num_results() {
+    check(
+        r#"
+{
+  "componentGrid": [
+    {
+      "components": [
+        { "kind": "unitary", "gate": "H", "targets": [{ "qubit": 0 }] }
+      ]
+    }
+  ],
   "qubits": [
     { "id": 0 }
   ]
@@ -473,6 +496,7 @@ fn circuit_with_qubit_missing_num_results() {
                 if Length(qs) != 1 {
                     fail "Invalid number of qubits. Operation Test expects a qubit register of size 1.";
                 }
+                H(qs[0]);
             }
 
         "#]],
@@ -505,8 +529,7 @@ fn circuit_with_ket_gates() {
                     fail "Invalid number of qubits. Operation Test expects a qubit register of size 2.";
                 }
                 Reset(qs[0]);
-                Reset(qs[1]);
-                X(qs[1]);
+                fail "Unsupported ket operation: |1〉";
             }
 
         "#]],
diff --git a/compiler/qsc_linter/src/tests.rs b/compiler/qsc_linter/src/tests.rs
index efbeb273..f40b2ec6 100644
--- a/compiler/qsc_linter/src/tests.rs
+++ b/compiler/qsc_linter/src/tests.rs
@@ -2,8 +2,9 @@
 // Licensed under the MIT License.
 
 use crate::{
+    lint_groups::LintGroup,
     linter::{remove_duplicates, run_lints_without_deduplication},
-    Lint, LintLevel,
+    Lint, LintLevel, LintOrGroupConfig,
 };
 use expect_test::{expect, Expect};
 use indoc::indoc;
@@ -101,6 +102,35 @@ fn set_keyword_lint() {
     );
 }
 
+#[test]
+fn lint_group() {
+    check_with_config(
+        &wrap_in_callable("newtype Foo = ()", CallableKind::Operation),
+        Some(&[LintOrGroupConfig::Group(crate::GroupConfig {
+            lint_group: LintGroup::Deprecations,
+            level: LintLevel::Error,
+        })]),
+        &expect![[r#"
+            [
+                SrcLint {
+                    source: "newtype Foo = ()",
+                    level: Error,
+                    message: "deprecated `newtype` declarations",
+                    help: "`newtype` declarations are deprecated, use `struct` instead",
+                    code_action_edits: [],
+                },
+                SrcLint {
+                    source: "RunProgram",
+                    level: Allow,
+                    message: "operation does not contain any quantum operations",
+                    help: "this callable can be declared as a function instead",
+                    code_action_edits: [],
+                },
+            ]
+        "#]],
+    );
+}
+
 #[test]
 fn multiple_lints() {
     check(
@@ -759,7 +789,7 @@ fn check_that_hir_lints_are_deduplicated_in_operations_with_multiple_specializat
     );
 }
 
-fn compile_and_collect_lints(source: &str) -> Vec<Lint> {
+fn compile_and_collect_lints(source: &str, config: Option<&[LintOrGroupConfig]>) -> Vec<Lint> {
     let mut store = PackageStore::new(compile::core());
     let std = store.insert(compile::std(&store, TargetCapabilityFlags::all()));
     let sources = SourceMap::new([("source.qs".into(), source.into())], None);
@@ -774,13 +804,21 @@ fn compile_and_collect_lints(source: &str) -> Vec<Lint> {
 
     let id = store.insert(unit);
     let unit = store.get(id).expect("user package should exist");
-
-    run_lints_without_deduplication(&store, unit, None)
+    run_lints_without_deduplication(&store, unit, config)
 }
 
 fn check(source: &str, expected: &Expect) {
     let source = wrap_in_namespace(source);
-    let actual: Vec<_> = compile_and_collect_lints(&source)
+    let actual: Vec<_> = compile_and_collect_lints(&source, None)
+        .into_iter()
+        .map(|lint| SrcLint::from(&lint, &source))
+        .collect();
+    expected.assert_debug_eq(&actual);
+}
+
+fn check_with_config(source: &str, config: Option<&[LintOrGroupConfig]>, expected: &Expect) {
+    let source = wrap_in_namespace(source);
+    let actual: Vec<_> = compile_and_collect_lints(&source, config)
         .into_iter()
         .map(|lint| SrcLint::from(&lint, &source))
         .collect();
@@ -789,7 +827,7 @@ fn check(source: &str, expected: &Expect) {
 
 fn check_with_deduplication(source: &str, expected: &Expect) {
     let source = wrap_in_namespace(source);
-    let mut lints = compile_and_collect_lints(&source);
+    let mut lints = compile_and_collect_lints(&source, None);
     remove_duplicates(&mut lints);
     let actual: Vec<_> = lints
         .into_iter()
diff --git a/compiler/qsc_partial_eval/src/tests/qubits.rs b/compiler/qsc_partial_eval/src/tests/qubits.rs
index cb537f2c..f9985a4c 100644
--- a/compiler/qsc_partial_eval/src/tests/qubits.rs
+++ b/compiler/qsc_partial_eval/src/tests/qubits.rs
@@ -377,6 +377,6 @@ fn qubit_relabel_in_dynamic_block_triggers_capability_error() {
 
     assert_error(
         &error,
-        &expect!["CapabilityError(UseOfDynamicQubit(Span { lo: 59760, hi: 59773 }))"],
+        &expect!["CapabilityError(UseOfDynamicQubit(Span { lo: 60173, hi: 60186 }))"],
     );
 }
diff --git a/compiler/qsc_qasm/src/tests.rs b/compiler/qsc_qasm/src/tests.rs
index 04baf99f..acdd7652 100644
--- a/compiler/qsc_qasm/src/tests.rs
+++ b/compiler/qsc_qasm/src/tests.rs
@@ -194,7 +194,7 @@ where
 fn compile_qasm_to_qir(source: &str, profile: Profile) -> Result<String, Vec<Report>> {
     let unit = compile(source)?;
     fail_on_compilation_errors(&unit);
-    let package = unit.package.expect("no package found");
+    let package = unit.package;
     let qir = generate_qir_from_ast(package, unit.source_map, profile).map_err(|errors| {
         errors
             .iter()
@@ -231,7 +231,7 @@ fn compile_qasm_best_effort(source: &str, profile: Profile) {
     let (mut _unit, _errors) = compile_ast(
         &store,
         &dependencies,
-        package.expect("package must exist"),
+        package,
         sources,
         PackageType::Lib,
         profile.into(),
@@ -244,7 +244,7 @@ pub(crate) fn gen_qsharp_stmt(stmt: &Stmt) -> String {
 
 #[allow(dead_code)]
 pub(crate) fn compare_compilation_to_qsharp(unit: &QasmCompileUnit, expected: &str) {
-    let package = unit.package.as_ref().expect("package must exist");
+    let package = &unit.package;
     let despanned_ast = AstDespanner.despan(package);
     let qsharp = gen_qsharp(&despanned_ast);
     difference::assert_diff!(&qsharp, expected, "\n", 0);
@@ -306,10 +306,7 @@ pub fn compile_qasm_to_qsharp_file(source: &str) -> miette::Result<String, Vec<R
         let errors = unit.errors.into_iter().map(Report::new).collect();
         return Err(errors);
     }
-    let Some(package) = unit.package else {
-        panic!("Expected package, got None");
-    };
-    let qsharp = gen_qsharp(&package);
+    let qsharp = gen_qsharp(&unit.package);
     Ok(qsharp)
 }
 
@@ -326,10 +323,7 @@ pub fn compile_qasm_to_qsharp_operation(source: &str) -> miette::Result<String,
         let errors = unit.errors.into_iter().map(Report::new).collect();
         return Err(errors);
     }
-    let Some(package) = unit.package else {
-        panic!("Expected package, got None");
-    };
-    let qsharp = gen_qsharp(&package);
+    let qsharp = gen_qsharp(&unit.package);
     Ok(qsharp)
 }
 
@@ -357,10 +351,7 @@ pub fn qsharp_from_qasm_compilation(unit: QasmCompileUnit) -> miette::Result<Str
         let errors = unit.errors.into_iter().map(Report::new).collect();
         return Err(errors);
     }
-    let Some(package) = unit.package else {
-        panic!("Expected package, got None");
-    };
-    let qsharp = gen_qsharp(&package);
+    let qsharp = gen_qsharp(&unit.package);
     Ok(qsharp)
 }
 
@@ -384,10 +375,7 @@ pub fn compile_qasm_stmt_to_qsharp_with_semantics(
         let errors = unit.errors.into_iter().map(Report::new).collect();
         return Err(errors);
     }
-    let Some(package) = unit.package else {
-        panic!("Expected package, got None");
-    };
-    let qsharp = get_last_statement_as_qsharp(&package);
+    let qsharp = get_last_statement_as_qsharp(&unit.package);
     Ok(qsharp)
 }
 
@@ -471,8 +459,7 @@ pub(crate) fn compare_qasm_and_qasharp_asts(source: &str) {
         Some(&mut resolver),
         config,
     );
-    let qasm_package = unit.package.as_ref().expect("package must exist");
-    let despanned_qasm_ast = AstDespanner.despan(qasm_package);
+    let despanned_qasm_ast = AstDespanner.despan(&unit.package);
 
     // 2. Generate Q# source from the QASM ast.
     let qsharp_src = gen_qsharp(&despanned_qasm_ast);
diff --git a/compiler/qsc_qasm/src/tests/output.rs b/compiler/qsc_qasm/src/tests/output.rs
index 69fef17b..040fc8f8 100644
--- a/compiler/qsc_qasm/src/tests/output.rs
+++ b/compiler/qsc_qasm/src/tests/output.rs
@@ -37,7 +37,7 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec<Report>> {
     );
     let unit = compile_with_config(source, config).expect("parse failed");
     fail_on_compilation_errors(&unit);
-    let qsharp = gen_qsharp(&unit.package.expect("no package found"));
+    let qsharp = gen_qsharp(&unit.package);
     expect![[r#"
         namespace qasm_import {
             import QasmStd.Angle.*;
@@ -88,7 +88,7 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu
     );
     let unit = compile_with_config(source, config).expect("parse failed");
     fail_on_compilation_errors(&unit);
-    let qsharp = gen_qsharp(&unit.package.expect("no package found"));
+    let qsharp = gen_qsharp(&unit.package);
     expect![[r#"
         namespace qasm_import {
             import QasmStd.Angle.*;
@@ -139,7 +139,7 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed(
     );
     let unit = compile_with_config(source, config).expect("parse failed");
     fail_on_compilation_errors(&unit);
-    let qsharp = gen_qsharp(&unit.package.expect("no package found"));
+    let qsharp = gen_qsharp(&unit.package);
     expect![[r#"
         namespace qasm_import {
             import QasmStd.Angle.*;
@@ -197,7 +197,7 @@ c2[2] = measure q[4];
     );
     let unit = compile_with_config(source, config).expect("parse failed");
     fail_on_compilation_errors(&unit);
-    let package = unit.package.expect("no package found");
+    let package = unit.package;
     let qsharp = gen_qsharp(&package.clone());
     expect![[r#"
         namespace qasm_import {
diff --git a/compiler/qsc_qasm/src/tests/sample_circuits/bell_pair.rs b/compiler/qsc_qasm/src/tests/sample_circuits/bell_pair.rs
index 27262250..c7a3f16d 100644
--- a/compiler/qsc_qasm/src/tests/sample_circuits/bell_pair.rs
+++ b/compiler/qsc_qasm/src/tests/sample_circuits/bell_pair.rs
@@ -33,9 +33,6 @@ fn it_compiles() {
 
     print_compilation_errors(&unit);
     assert!(!unit.has_errors());
-    let Some(package) = &unit.package else {
-        panic!("no package found");
-    };
-    let qsharp = gen_qsharp(package);
+    let qsharp = gen_qsharp(&unit.package);
     println!("{qsharp}");
 }
diff --git a/compiler/qsc_qasm/src/tests/sample_circuits/rgqft_multiplier.rs b/compiler/qsc_qasm/src/tests/sample_circuits/rgqft_multiplier.rs
index ee0f9387..ac297ec1 100644
--- a/compiler/qsc_qasm/src/tests/sample_circuits/rgqft_multiplier.rs
+++ b/compiler/qsc_qasm/src/tests/sample_circuits/rgqft_multiplier.rs
@@ -21,10 +21,7 @@ fn it_compiles() {
 
     print_compilation_errors(&unit);
     assert!(!unit.has_errors());
-    let Some(package) = &unit.package else {
-        panic!("no package found");
-    };
-    let qsharp = gen_qsharp(package);
+    let qsharp = gen_qsharp(&unit.package);
     println!("{qsharp}");
 }
 
diff --git a/compiler/qsc_qasm/src/tests/statement/reset.rs b/compiler/qsc_qasm/src/tests/statement/reset.rs
index 69957f09..6e1abe82 100644
--- a/compiler/qsc_qasm/src/tests/statement/reset.rs
+++ b/compiler/qsc_qasm/src/tests/statement/reset.rs
@@ -32,7 +32,7 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec<Report>> {
     );
     let unit = compile_with_config(source, config)?;
     fail_on_compilation_errors(&unit);
-    let qsharp = gen_qsharp(&unit.package.expect("no package found"));
+    let qsharp = gen_qsharp(&unit.package);
     expect![[r#"
         namespace qasm_import {
             import QasmStd.Angle.*;
diff --git a/language_service/src/state/tests.rs b/language_service/src/state/tests.rs
index 13b83eeb..6e458a84 100644
--- a/language_service/src/state/tests.rs
+++ b/language_service/src/state/tests.rs
@@ -12,7 +12,7 @@ use crate::{
 use expect_test::{expect, Expect};
 use miette::Diagnostic;
 use qsc::{line_column::Encoding, target::Profile, LanguageFeatures, PackageType};
-use qsc_linter::{AstLint, LintConfig, LintKind, LintLevel};
+use qsc_linter::{AstLint, LintConfig, LintKind, LintLevel, LintOrGroupConfig};
 use std::{
     cell::RefCell,
     fmt::{Display, Write},
@@ -1337,18 +1337,22 @@ async fn loading_lints_config_from_manifest() {
         &updater,
         &expect![[r#"
             [
-                LintConfig {
-                    kind: Ast(
-                        DivisionByZero,
-                    ),
-                    level: Error,
-                },
-                LintConfig {
-                    kind: Ast(
-                        NeedlessParens,
-                    ),
-                    level: Error,
-                },
+                Lint(
+                    LintConfig {
+                        kind: Ast(
+                            DivisionByZero,
+                        ),
+                        level: Error,
+                    },
+                ),
+                Lint(
+                    LintConfig {
+                        kind: Ast(
+                            NeedlessParens,
+                        ),
+                        level: Error,
+                    },
+                ),
             ]"#]],
     )
     .await;
@@ -1440,10 +1444,10 @@ async fn lints_prefer_workspace_over_defaults() {
     let test_cases = RefCell::new(Vec::new());
     let mut updater = new_updater(&received_errors, &test_cases);
     updater.update_configuration(WorkspaceConfigurationUpdate {
-        lints_config: Some(vec![LintConfig {
+        lints_config: Some(vec![LintOrGroupConfig::Lint(LintConfig {
             kind: LintKind::Ast(AstLint::DivisionByZero),
             level: LintLevel::Warn,
-        }]),
+        })]),
         ..WorkspaceConfigurationUpdate::default()
     });
 
@@ -1489,10 +1493,10 @@ async fn lints_prefer_manifest_over_workspace() {
     let test_cases = RefCell::new(Vec::new());
     let mut updater = new_updater_with_file_system(&received_errors, &test_cases, &fs);
     updater.update_configuration(WorkspaceConfigurationUpdate {
-        lints_config: Some(vec![LintConfig {
+        lints_config: Some(vec![LintOrGroupConfig::Lint(LintConfig {
             kind: LintKind::Ast(AstLint::DivisionByZero),
             level: LintLevel::Warn,
-        }]),
+        })]),
         ..WorkspaceConfigurationUpdate::default()
     });
 
diff --git a/library/src/tests/canon.rs b/library/src/tests/canon.rs
index 7133ebf2..a7afda85 100644
--- a/library/src/tests/canon.rs
+++ b/library/src/tests/canon.rs
@@ -556,3 +556,18 @@ fn check_apply_xor_in_place_l() {
         &Value::Int(953),
     );
 }
+
+#[test]
+fn check_apply_operation_power_a() {
+    test_expression(
+        {
+            "{
+            use q = Qubit();
+            ApplyOperationPowerA(12, Rx(Std.Math.PI()/16.0, _), q);
+            ApplyOperationPowerA(-3, Rx(Std.Math.PI()/4.0, _), q);
+            M(q)
+        }"
+        },
+        &Value::RESULT_ZERO,
+    );
+}
diff --git a/library/src/tests/convert.rs b/library/src/tests/convert.rs
index 415dbe0a..b209711e 100644
--- a/library/src/tests/convert.rs
+++ b/library/src/tests/convert.rs
@@ -170,6 +170,37 @@ fn check_bigint_as_bool_array() {
     );
 }
 
+#[test]
+fn check_big_int_as_int_0() {
+    test_expression("Std.Convert.BigIntAsInt(0L)", &Value::Int(0));
+}
+
+#[test]
+fn check_big_int_as_int_1() {
+    test_expression("Std.Convert.BigIntAsInt(1L)", &Value::Int(1));
+}
+
+#[test]
+fn check_big_int_as_int_n1() {
+    test_expression("Std.Convert.BigIntAsInt(-1L)", &Value::Int(-1));
+}
+
+#[test]
+fn check_big_int_as_int_max() {
+    test_expression(
+        "Std.Convert.BigIntAsInt(9_223_372_036_854_775_807L)",
+        &Value::Int(i64::MAX),
+    );
+}
+
+#[test]
+fn check_big_int_as_int_min() {
+    test_expression(
+        "Std.Convert.BigIntAsInt(-9_223_372_036_854_775_808L)",
+        &Value::Int(i64::MIN),
+    );
+}
+
 #[test]
 fn check_bool_array_as_big_int() {
     test_expression(
diff --git a/library/src/tests/math.rs b/library/src/tests/math.rs
index b1d8c85e..dd295bae 100644
--- a/library/src/tests/math.rs
+++ b/library/src/tests/math.rs
@@ -367,8 +367,28 @@ fn check_floor() {
 
 #[test]
 fn check_round() {
-    test_expression("Microsoft.Quantum.Math.Round(3.1)", &Value::Int(3));
-    test_expression("Microsoft.Quantum.Math.Round(-3.7)", &Value::Int(-4));
+    test_expression("Std.Math.Round(0.5-1.0e-16)", &Value::Int(0));
+    test_expression("Std.Math.Round(0.5)", &Value::Int(0));
+    test_expression("Std.Math.Round(0.5+1.0e-16)", &Value::Int(1));
+    test_expression("Std.Math.Round(3.1)", &Value::Int(3));
+    test_expression("Std.Math.Round(-3.7)", &Value::Int(-4));
+    test_expression("Std.Math.Round(-3.5)", &Value::Int(-3));
+}
+
+#[test]
+fn check_round_away_from_zero() {
+    test_expression(
+        "Std.Math.RoundHalfAwayFromZero(0.5-1.0e-16)",
+        &Value::Int(0),
+    );
+    test_expression("Std.Math.RoundHalfAwayFromZero(0.5)", &Value::Int(1));
+    test_expression(
+        "Std.Math.RoundHalfAwayFromZero(0.5+1.0e-16)",
+        &Value::Int(1),
+    );
+    test_expression("Std.Math.RoundHalfAwayFromZero(3.1)", &Value::Int(3));
+    test_expression("Std.Math.RoundHalfAwayFromZero(-3.7)", &Value::Int(-4));
+    test_expression("Std.Math.RoundHalfAwayFromZero(-3.5)", &Value::Int(-4));
 }
 
 //