Repository: microsoft/qsharp
Pull request #1976 (2024-10-25 22:43:28)

Add .qs file icons


Patch:
diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs
index cdbb9371..e1560bac 100644
--- a/compiler/qsc/src/interpret.rs
+++ b/compiler/qsc/src/interpret.rs
@@ -13,6 +13,7 @@ mod tests;
 
 pub use qsc_eval::{
     debug::Frame,
+    noise::PauliNoise,
     output::{self, GenericReceiver},
     val::Closure,
     val::Range as ValueRange,
@@ -468,8 +469,17 @@ impl Interpreter {
 
     /// Runs the given entry expression on a new instance of the environment and simulator,
     /// but using the current compilation.
-    pub fn run(&mut self, receiver: &mut impl Receiver, expr: Option<&str>) -> InterpretResult {
-        self.run_with_sim(&mut SparseSim::new(), receiver, expr)
+    pub fn run(
+        &mut self,
+        receiver: &mut impl Receiver,
+        expr: Option<&str>,
+        noise: Option<PauliNoise>,
+    ) -> InterpretResult {
+        let mut sim = match noise {
+            Some(noise) => SparseSim::new_with_noise(&noise),
+            None => SparseSim::new(),
+        };
+        self.run_with_sim(&mut sim, receiver, expr)
     }
 
     /// Gets the current quantum state of the simulator.
diff --git a/pip/qsharp/__init__.py b/pip/qsharp/__init__.py
index 50f3224b..1d068b86 100644
--- a/pip/qsharp/__init__.py
+++ b/pip/qsharp/__init__.py
@@ -14,6 +14,10 @@ from ._qsharp import (
     dump_circuit,
     StateDump,
     ShotResult,
+    PauliNoise,
+    DepolarizingNoise,
+    BitFlipNoise,
+    PhaseFlipNoise,
 )
 
 from ._native import Result, Pauli, QSharpError, TargetProfile
@@ -46,4 +50,8 @@ __all__ = [
     "TargetProfile",
     "StateDump",
     "ShotResult",
+    "PauliNoise",
+    "DepolarizingNoise",
+    "BitFlipNoise",
+    "PhaseFlipNoise"
 ]
diff --git a/pip/qsharp/_native.pyi b/pip/qsharp/_native.pyi
index 8f8b8119..0bf47a2d 100644
--- a/pip/qsharp/_native.pyi
+++ b/pip/qsharp/_native.pyi
@@ -139,12 +139,19 @@ class Interpreter:
         """
         ...
 
-    def run(self, entry_expr: str, output_fn: Callable[[Output], None]) -> Any:
+    def run(
+        self,
+        entry_expr: str,
+        output_fn: Callable[[Output], None],
+        noise: Optional[Tuple[float, float, float]],
+    ) -> Any:
         """
         Runs the given Q# expression with an independent instance of the simulator.
 
         :param entry_expr: The entry expression.
         :param output_fn: A callback function that will be called with each output.
+        :param noise: A tuple with probabilities of Pauli-X, Pauli-Y, and Pauli-Z errors
+            to use in simulation as a parametric Pauli noise.
 
         :returns values: A result or runtime errors.
 
diff --git a/pip/qsharp/_qsharp.py b/pip/qsharp/_qsharp.py
index f617384f..539e99f2 100644
--- a/pip/qsharp/_qsharp.py
+++ b/pip/qsharp/_qsharp.py
@@ -14,6 +14,7 @@ from typing import (
     Callable,
     Dict,
     Optional,
+    Tuple,
     TypedDict,
     Union,
     List,
@@ -94,6 +95,38 @@ class Config:
     ) -> Dict[str, Dict[str, str]]:
         return {"application/x.qsharp-config": self._config}
 
+class PauliNoise(Tuple[float, float, float]):
+    """
+    The Pauli noise to use in simulation represented
+    as probabilities of Pauli-X, Pauli-Y, and Pauli-Z errors
+    """
+    def __new__(cls, x: float, y: float, z: float):
+        if x < 0 or y < 0 or z < 0:
+            raise ValueError("Pauli noise probabilities must be non-negative.")
+        if x + y + z > 1:
+            raise ValueError("The sum of Pauli noise probabilities must be at most 1.")
+        return super().__new__(cls, (x, y, z))
+
+class DepolarizingNoise(PauliNoise):
+    """
+    The depolarizing noise to use in simulation.
+    """
+    def __new__(cls, p: float):
+        return super().__new__(cls, p / 3, p / 3, p / 3)
+
+class BitFlipNoise(PauliNoise):
+    """
+    The bit flip noise to use in simulation.
+    """
+    def __new__(cls, p: float):
+        return super().__new__(cls, p, 0, 0)
+
+class PhaseFlipNoise(PauliNoise):
+    """
+    The phase flip noise to use in simulation.
+    """
+    def __new__(cls, p: float):
+        return super().__new__(cls, 0, 0, p)
 
 def init(
     *,
@@ -214,6 +247,9 @@ def run(
     *,
     on_result: Optional[Callable[[ShotResult], None]] = None,
     save_events: bool = False,
+    noise: Optional [
+        Tuple[float, float, float] | PauliNoise | BitFlipNoise | PhaseFlipNoise | DepolarizingNoise
+    ] = None
 ) -> List[Any]:
     """
     Runs the given Q# expression for the given number of shots.
@@ -223,6 +259,7 @@ def run(
     :param shots: The number of shots to run.
     :param on_result: A callback function that will be called with each result.
     :param save_events: If true, the output of each shot will be saved. If false, they will be printed.
+    :param noise: The noise to use in simulation.
 
     :returns values: A list of results or runtime errors. If `save_events` is true,
     a List of ShotResults is returned.
@@ -253,7 +290,9 @@ def run(
     for shot in range(shots):
         results.append({"result": None, "events": []})
         run_results = get_interpreter().run(
-            entry_expr, on_save_events if save_events else print_output
+            entry_expr,
+            on_save_events if save_events else print_output,
+            noise,
         )
         results[-1]["result"] = run_results
         if on_result:
diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs
index bc7c087c..8e34d096 100644
--- a/pip/src/interpreter.rs
+++ b/pip/src/interpreter.rs
@@ -24,7 +24,7 @@ use qsc::{
     interpret::{
         self,
         output::{Error, Receiver},
-        CircuitEntryPoint, Value,
+        CircuitEntryPoint, PauliNoise, Value,
     },
     packages::BuildableProgram,
     project::{FileSystem, PackageCache, PackageGraphSources},
@@ -333,15 +333,25 @@ impl Interpreter {
         Circuit(self.interpreter.get_circuit()).into_py(py)
     }
 
-    #[pyo3(signature=(entry_expr=None, callback=None))]
+    #[pyo3(signature=(entry_expr=None, callback=None, noise=None))]
     fn run(
         &mut self,
         py: Python,
         entry_expr: Option<&str>,
         callback: Option<PyObject>,
+        noise: Option<(f64, f64, f64)>,
     ) -> PyResult<PyObject> {
         let mut receiver = OptionalCallbackReceiver { callback, py };
-        match self.interpreter.run(&mut receiver, entry_expr) {
+
+        let noise = match noise {
+            None => None,
+            Some((px, py, pz)) => match PauliNoise::from_probabilities(px, py, pz) {
+                Ok(noise_struct) => Some(noise_struct),
+                Err(error_message) => return Err(PyException::new_err(error_message)),
+            },
+        };
+
+        match self.interpreter.run(&mut receiver, entry_expr, noise) {
             Ok(value) => Ok(ValueWrapper(value).into_py(py)),
             Err(errors) => Err(QSharpError::new_err(format_errors(errors))),
         }
diff --git a/vscode/package.json b/vscode/package.json
index 852772a4..a96876a7 100644
--- a/vscode/package.json
+++ b/vscode/package.json
@@ -436,6 +436,10 @@
         "extensions": [
           ".qs"
         ],
+        "icon": {
+          "light": "./resources/file-icon-light.svg",
+          "dark": "./resources/file-icon-dark.svg"
+        },
         "configuration": "./language-configuration.json"
       }
     ],
diff --git a/vscode/resources/file-icon-dark.svg b/vscode/resources/file-icon-dark.svg
new file mode 100644
index 00000000..6b8d88d5
--- /dev/null
+++ b/vscode/resources/file-icon-dark.svg
@@ -0,0 +1,27 @@
+<svg viewBox="-100 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
+  <g fill="none" stroke="black" stroke-width="8">
+    <path fill="#0078d4" d="M 205 580 L 742 890 L 819 845 L 818 756 L 205 402"/>
+    <path fill="#0078d4" d="M 512 135 L 819 313 L 819 667 L 512 845 L 205 667 L 205 313 L 512 135 Z"/>
+    <path fill="none" d="M 205 580 L 742 890 L 819 845 L 818 756 L 205 402"/>
+    <path d="M 204 579 L 743 268"/>
+    <path d="M 664 224 L 207 489"/>
+    <path d="M 206 400 L 588 180"/>
+    <path d="M 205 667 L 818 313"/>
+    <path d="M 205 490 L 820 845"/>
+    <path d="M 207 314 L 818 667"/>
+    <path d="M 282 269 L 820 580"/>
+    <path d="M 820 490 L 357 223"/>
+    <path d="M 435 180 L 818 400"/>
+    <path d="M 281 710 L 281 271"/>
+    <path d="M 357 755 L 357 223"/>
+    <path d="M 283 711 L 820 400"/>
+    <path d="M 434 797 L 434 181"/>
+    <path d="M 511 136 L 511 844"/>
+    <path d="M 588 799 L 588 182"/>
+    <path d="M 665 223 L 665 845"/>
+    <path d="M 742 887 L 742 267"/>
+    <path d="M 665 845 L 816 758"/>
+    <path d="M 433 801 L 820 577"/>
+    <path d="M 820 489 L 360 755"/>
+  </g>
+</svg>
diff --git a/vscode/resources/file-icon-light.svg b/vscode/resources/file-icon-light.svg
new file mode 100644
index 00000000..c7a61191
--- /dev/null
+++ b/vscode/resources/file-icon-light.svg
@@ -0,0 +1,27 @@
+<svg viewBox="-100 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
+  <g fill="none" stroke="black" stroke-width="8">
+    <path fill="#8bc0e8" d="M 205 580 L 742 890 L 819 845 L 818 756 L 205 402"/>
+    <path fill="#8bc0e8" d="M 512 135 L 819 313 L 819 667 L 512 845 L 205 667 L 205 313 L 512 135 Z"/>
+    <path fill="none" d="M 205 580 L 742 890 L 819 845 L 818 756 L 205 402"/>
+    <path d="M 204 579 L 743 268"/>
+    <path d="M 664 224 L 207 489"/>
+    <path d="M 206 400 L 588 180"/>
+    <path d="M 205 667 L 818 313"/>
+    <path d="M 205 490 L 820 845"/>
+    <path d="M 207 314 L 818 667"/>
+    <path d="M 282 269 L 820 580"/>
+    <path d="M 820 490 L 357 223"/>
+    <path d="M 435 180 L 818 400"/>
+    <path d="M 281 710 L 281 271"/>
+    <path d="M 357 755 L 357 223"/>
+    <path d="M 283 711 L 820 400"/>
+    <path d="M 434 797 L 434 181"/>
+    <path d="M 511 136 L 511 844"/>
+    <path d="M 588 799 L 588 182"/>
+    <path d="M 665 223 L 665 845"/>
+    <path d="M 742 887 L 742 267"/>
+    <path d="M 665 845 L 816 758"/>
+    <path d="M 433 801 L 820 577"/>
+    <path d="M 820 489 L 360 755"/>
+  </g>
+</svg>


Test patch:
diff --git a/compiler/qsc/src/interpret/tests.rs b/compiler/qsc/src/interpret/tests.rs
index ebcbb204..1de1aea3 100644
--- a/compiler/qsc/src/interpret/tests.rs
+++ b/compiler/qsc/src/interpret/tests.rs
@@ -23,7 +23,10 @@ mod given_interpreter {
     fn run(interpreter: &mut Interpreter, expr: &str) -> (InterpretResult, String) {
         let mut cursor = Cursor::new(Vec::<u8>::new());
         let mut receiver = CursorReceiver::new(&mut cursor);
-        (interpreter.run(&mut receiver, Some(expr)), receiver.dump())
+        (
+            interpreter.run(&mut receiver, Some(expr), None),
+            receiver.dump(),
+        )
     }
 
     fn entry(interpreter: &mut Interpreter) -> (InterpretResult, String) {
diff --git a/pip/tests/test_qsharp.py b/pip/tests/test_qsharp.py
index 322ffed9..0779972f 100644
--- a/pip/tests/test_qsharp.py
+++ b/pip/tests/test_qsharp.py
@@ -257,6 +257,21 @@ def test_dump_operation() -> None:
             else:
                 assert res[i][j] == complex(0.0, 0.0)
 
+def test_run_with_noise_produces_noisy_results() -> None:
+    qsharp.init()
+    qsharp.set_quantum_seed(0)
+    result = qsharp.run(
+        "{ mutable errors=0; for _ in 0..100 { use q1=Qubit(); use q2=Qubit(); H(q1); CNOT(q1, q2); if MResetZ(q1) != MResetZ(q2) { set errors+=1; } } errors }",
+        shots=1,
+        noise=qsharp.BitFlipNoise(0.1),
+    )
+    assert result[0] > 5
+    result = qsharp.run(
+        "{ mutable errors=0; for _ in 0..100 { use q=Qubit(); if MResetZ(q) != Zero { set errors+=1; } } errors }",
+        shots=1,
+        noise=qsharp.BitFlipNoise(0.1),
+    )
+    assert result[0] > 5
 
 def test_compile_qir_input_data() -> None:
     qsharp.init(target_profile=qsharp.TargetProfile.Base)