diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index cce8e639836..a9f6dd5fd68 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -9,15 +9,14 @@ RUN apt-get -y update && \
apt-get -y install --no-install-recommends mono-complete nuget && \
apt-get clean && rm -rf /var/lib/apt/lists/
-# Install CMake, Ninja, LLVM/Clang tools
-RUN apt-get -y update && \
- apt-get install -y cmake && \
- apt-get install -y ninja-build && \
- apt-get install -y clang-11 && \
- apt-get install -y clang-tidy-11 && \
- apt-get clean && rm -rf /var/lib/apt/lists/
+RUN apt update \
+ && apt-get install -y cmake \
+ ninja-build \
+ clang-11 \
+ clang-tidy-11 \
+ build-essential \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/
-# Install C/C++ build tools and libraries
-RUN apt-get -y update && \
- apt-get install -y build-essential && \
- apt-get clean && rm -rf /var/lib/apt/lists/
+RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
+CMD [ "pwsh" ]
diff --git a/.gitignore b/.gitignore
index 5b33b48524e..e1cb5a9ea3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -339,3 +339,9 @@ ASALocalRun/
/src/Simulation/Simulators.Tests/TestProjects/QSharpExe/built
/src/Simulation/Simulators.Tests/TestProjects/TargetedExe/built
dbw_test
+
+# Jupyter caches
+.ipynb_checkpoints
+
+# Ignore drops from building native simulators.
+xplat
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000000..c12c6fd5236
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,19 @@
+# This file configures the pre-commit tool to run an initial
+# suite of lightweight tests on each commit, reducing the
+# probability of failing in CI.
+# For more information on pre-commit, see https://pre-commit.com/.
+repos:
+ - repo: https://github.com/doublify/pre-commit-rust
+ rev: v1.0
+ hooks:
+ - id: fmt
+ args: ['--manifest-path', 'src/Simulation/qdk_sim_rs/Cargo.toml', '--']
+ - id: cargo-check
+ args: ['--manifest-path', 'src/Simulation/qdk_sim_rs/Cargo.toml', '--']
+ # This step runs cargo-clippy, a linting tool provided with the
+ # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy
+ # and https://rust-lang.github.io/rust-clippy/master/index.html
+ # for more information.
+ - id: clippy
+ args: ['--manifest-path', 'src/Simulation/qdk_sim_rs/Cargo.toml', '--']
+
diff --git a/bootstrap.ps1 b/bootstrap.ps1
index d8b102b7937..0ca15813a99 100644
--- a/bootstrap.ps1
+++ b/bootstrap.ps1
@@ -7,6 +7,18 @@ Push-Location (Join-Path $PSScriptRoot "build")
.\prerequisites.ps1
Pop-Location
+Push-Location (Join-Path $PSScriptRoot "./src/Simulation/qdk_sim_rs")
+ # We use dotnet-script to inject the version number into Cargo.toml,
+ # so we go on ahead here and restore any missing tools.
+ # Since that Cargo.toml is referenced by CMake lists in the QIR
+ # runtime, this injection has to be the first thing we do.
+ dotnet tool restore
+ dotnet script inject-version.csx -- `
+ --template Cargo.toml.template `
+ --out-path Cargo.toml `
+ --version $Env:NUGET_VERSION;
+Pop-Location
+
if (-not (Test-Path Env:AGENT_OS)) {
if ($Env:ENABLE_NATIVE -ne "false") {
Write-Host "Build release flavor of the native simulator"
@@ -14,6 +26,16 @@ if (-not (Test-Path Env:AGENT_OS)) {
Push-Location (Join-Path $PSScriptRoot "src/Simulation/Native")
.\build-native-simulator.ps1
Pop-Location
+ Push-Location (Join-Path $PSScriptRoot "src/Simulation/qdk_sim_rs")
+ # Don't run the experimental simulator build if we're local
+ # and prerequisites are missing.
+ $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true";
+ if ((Get-Command cargo -ErrorAction SilentlyContinue) -or $IsCI) {
+ .\build-qdk-sim-rs.ps1
+ } else {
+ Write-Verbose "cargo was not installed, skipping qdk_sim_rs build.";
+ }
+ Pop-Location
$Env:BUILD_CONFIGURATION = $null
}
if ($Env:ENABLE_QIRRUNTIME -ne "false") {
diff --git a/build/build.ps1 b/build/build.ps1
index dec45cec756..177b5eb6a8b 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -26,6 +26,33 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") {
Write-Host "Skipping build of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME"
}
+if ($Env:ENABLE_EXPERIMENTALSIM -ne "false") {
+ if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
+ # Cargo was missing, so cannot build experimental simulators.
+ # That's fine if running locally, we'll warn the user and then skip.
+ # On CI, though, we should fail when the experimental simulator build
+ # is turned on by ENABLE_EXPERIMENTALSIM, but we can't actually
+ # proceed.
+ if ("$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true") {
+ Write-Host "##[error]Experimental simulators enabled, but cargo was not installed in CI pipeline.";
+ } else {
+ Write-Warning `
+ "Experimental simulators enabled, but cargo missing. " + `
+ "Either install cargo, or set `$Env:ENABLE_EXPERIMENTALSIM " + `
+ "to `"false`". Skipping experimental simulators.";
+ }
+ } else {
+ # Prerequisites are met, so let's go.
+ $expSim = (Join-Path $PSScriptRoot "../src/Simulation/qdk_sim_rs")
+ & "$expSim/build-qdk-sim-rs.ps1"
+ if ($LastExitCode -ne 0) {
+ $script:all_ok = $False
+ }
+ }
+} else {
+ Write-Host "Skipping build of experimental simulators because ENABLE_OPENSIM variable is set to: $Env:ENABLE_OPENSIM."
+}
+
function Build-One {
param(
[string]$action,
diff --git a/build/pack.ps1 b/build/pack.ps1
index c786bf8bff9..80a20f1fa2c 100644
--- a/build/pack.ps1
+++ b/build/pack.ps1
@@ -6,14 +6,36 @@ $ErrorActionPreference = 'Stop'
& "$PSScriptRoot/set-env.ps1"
$all_ok = $True
+$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot "..");
+
Write-Host "##[info]Copy Native simulator xplat binaries"
-pushd ../src/Simulation/Native
-If (-not (Test-Path 'osx')) { mkdir 'osx' }
-If (-not (Test-Path 'linux')) { mkdir 'linux' }
-$DROP = "$Env:DROP_NATIVE/src/Simulation/Native/build/drop"
-If (Test-Path "$DROP/libMicrosoft.Quantum.Simulator.Runtime.dylib") { copy "$DROP/libMicrosoft.Quantum.Simulator.Runtime.dylib" "osx/Microsoft.Quantum.Simulator.Runtime.dll" }
-If (Test-Path "$DROP/libMicrosoft.Quantum.Simulator.Runtime.so") { copy "$DROP/libMicrosoft.Quantum.Simulator.Runtime.so" "linux/Microsoft.Quantum.Simulator.Runtime.dll" }
-popd
+Push-Location (Join-Path $PSScriptRoot ../src/Simulation/Native)
+ If (-not (Test-Path 'osx')) { mkdir 'osx' }
+ If (-not (Test-Path 'linux')) { mkdir 'linux' }
+ If (-not (Test-Path 'win10')) { mkdir 'win10' }
+
+ $DROP = "$Env:DROP_NATIVE/src/Simulation/Native/build/drop"
+ Write-Host "##[info]Copying Microsoft.Quantum.Simulator.Runtime files from $DROP...";
+ If (Test-Path "$DROP/libMicrosoft.Quantum.Simulator.Runtime.dylib") {
+ Copy-Item -Verbose "$DROP/libMicrosoft.Quantum.Simulator.Runtime.dylib" "osx/Microsoft.Quantum.Simulator.Runtime.dll"
+ }
+ If (Test-Path "$DROP/libMicrosoft.Quantum.Simulator.Runtime.so") {
+ Copy-Item -Verbose "$DROP/libMicrosoft.Quantum.Simulator.Runtime.so" "linux/Microsoft.Quantum.Simulator.Runtime.dll"
+ }
+
+
+ $DROP = "$Env:DROP_NATIVE/src/Simulation/qdk_sim_rs/drop";
+ Write-Host "##[info]Copying qdk_sim_rs files from $DROP...";
+ if (Test-Path "$DROP/libqdk_sim.dylib") {
+ Copy-Item -Verbose "$DROP/libqdk_sim.dylib" "osx/Microsoft.Quantum.Experimental.Simulators.Runtime.dll"
+ }
+ if (Test-Path "$DROP/libqdk_sim.so") {
+ Copy-Item -Verbose "$DROP/libqdk_sim.so" "linux/Microsoft.Quantum.Experimental.Simulators.Runtime.dll"
+ }
+ if (Test-Path "$DROP/qdk_sim.dll") {
+ Copy-Item -Verbose "$DROP/qdk_sim.dll" "win10/Microsoft.Quantum.Experimental.Simulators.Runtime.dll"
+ }
+Pop-Location
function Pack-One() {
@@ -31,7 +53,7 @@ function Pack-One() {
$version = $Env:NUGET_VERSION
}
- nuget pack $project `
+ nuget pack (Join-Path $PSScriptRoot $project) `
-OutputDirectory $Env:NUGET_OUTDIR `
-Properties Configuration=$Env:BUILD_CONFIGURATION `
-Version $version `
@@ -68,7 +90,7 @@ function Pack-Dotnet() {
$version = $Env:NUGET_VERSION
}
- dotnet pack $project `
+ dotnet pack (Join-Path $PSScriptRoot $project) `
-o $Env:NUGET_OUTDIR `
-c $Env:BUILD_CONFIGURATION `
-v detailed `
@@ -87,6 +109,49 @@ function Pack-Dotnet() {
}
+function Pack-Crate() {
+ param(
+ [string]
+ $PackageDirectory,
+
+ [string]
+ $OutPath
+ );
+
+ "##[info]Packing crate at $PackageDirectory to $OutPath..." | Write-Host
+
+ # Resolve relative to where the build script is located,
+ # not the PackageDirectory.
+ if (-not [IO.Path]::IsPathRooted($OutPath)) {
+ $OutPath = Resolve-Path (Join-Path $PSScriptRoot $OutPath);
+ }
+ Push-Location (Join-Path $PSScriptRoot $PackageDirectory)
+ cargo package;
+ Copy-Item -Force -Recurse (Join-Path . "target" "package") $OutPath;
+ Pop-Location
+}
+
+function Pack-Wheel() {
+ param(
+ [string]
+ $PackageDirectory,
+
+ [string]
+ $OutPath
+ );
+
+ "##[info]Packing wheel at $PackageDirectory to $OutPath..." | Write-Host
+
+ # Resolve relative to where the build script is located,
+ # not the PackageDirectory.
+ if (-not [IO.Path]::IsPathRooted($OutPath)) {
+ $OutPath = Resolve-Path (Join-Path $PSScriptRoot $OutPath);
+ }
+ Push-Location (Join-Path $PSScriptRoot $PackageDirectory)
+ pip wheel --wheel-dir $OutPath .;
+ Pop-Location
+}
+
Write-Host "##[info]Using nuget to create packages"
Pack-Dotnet '../src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj'
Pack-One '../src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj' '-IncludeReferencedProjects'
@@ -102,6 +167,8 @@ Pack-Dotnet '../src/Simulation/Type3Core/Microsoft.Quantum.Type3.Core.csproj'
Pack-One '../src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec'
Pack-One '../src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec'
Pack-One '../src/Xunit/Microsoft.Quantum.Xunit.csproj'
+Pack-Crate -PackageDirectory "../src/Simulation/qdk_sim_rs" -OutPath $Env:CRATE_OUTDIR;
+Pack-Wheel -PackageDirectory "../src/Simulation/qdk_sim_rs" -OutPath $Env:WHEEL_OUTDIR;
Pack-One '../src/Qir/Runtime/Microsoft.Quantum.Qir.Runtime.nuspec' -ForcePrerelease
if (-not $all_ok) {
diff --git a/build/set-env.ps1 b/build/set-env.ps1
index 39799bdf9f7..1173a7a0a07 100644
--- a/build/set-env.ps1
+++ b/build/set-env.ps1
@@ -9,7 +9,8 @@ If ($Env:BUILD_BUILDNUMBER -eq $null) { $Env:BUILD_BUILDNUMBER ="0.0.1.0" }
If ($Env:BUILD_CONFIGURATION -eq $null) { $Env:BUILD_CONFIGURATION ="Debug" }
If ($Env:BUILD_VERBOSITY -eq $null) { $Env:BUILD_VERBOSITY ="m" }
If ($Env:ASSEMBLY_VERSION -eq $null) { $Env:ASSEMBLY_VERSION ="$Env:BUILD_BUILDNUMBER" }
-If ($Env:NUGET_VERSION -eq $null) { $Env:NUGET_VERSION ="$Env:ASSEMBLY_VERSION-alpha" }
+If ($Env:PYTHON_VERSION -eq $null) { $Env:PYTHON_VERSION = "${Env:ASSEMBLY_VERSION}a1" }
+If ($Env:NUGET_VERSION -eq $null) { $Env:NUGET_VERSION ="0.0.1-alpha" }
If (($Env:ENABLE_NATIVE -ne "false") -and ($Env:NATIVE_SIMULATOR -eq $null) ) {
$Env:NATIVE_SIMULATOR = (Join-Path $PSScriptRoot "..\src\Simulation\Native\build\drop")
@@ -20,10 +21,27 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false" -and $Env:QIR_DROPS -eq $null) {
}
If ($Env:DROPS_DIR -eq $null) { $Env:DROPS_DIR = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\drops")) }
+if ($Env:DROP_NATIVE -eq $null) {
+ $Env:DROP_NATIVE = (Join-Path $PSScriptRoot "..")
+}
If ($Env:NUGET_OUTDIR -eq $null) { $Env:NUGET_OUTDIR = (Join-Path $Env:DROPS_DIR "nugets") }
If (-not (Test-Path -Path $Env:NUGET_OUTDIR)) { [IO.Directory]::CreateDirectory($Env:NUGET_OUTDIR) }
+If ($Env:CRATE_OUTDIR -eq $null) { $Env:CRATE_OUTDIR = (Join-Path $Env:DROPS_DIR "crates") }
+If (-not (Test-Path -Path $Env:CRATE_OUTDIR)) { [IO.Directory]::CreateDirectory($Env:CRATE_OUTDIR) }
+
+If ($Env:WHEEL_OUTDIR -eq $null) { $Env:WHEEL_OUTDIR = (Join-Path $Env:DROPS_DIR "wheels") }
+If (-not (Test-Path -Path $Env:WHEEL_OUTDIR)) { [IO.Directory]::CreateDirectory($Env:WHEEL_OUTDIR) }
+
If ($Env:DOCS_OUTDIR -eq $null) { $Env:DOCS_OUTDIR = (Join-Path $Env:DROPS_DIR "docs") }
If (-not (Test-Path -Path $Env:DOCS_OUTDIR)) { [IO.Directory]::CreateDirectory($Env:DOCS_OUTDIR) }
+Get-ChildItem @(
+ "Env:\DROPS_DIR",
+ "Env:\DROP_NATIVE",
+ "Env:\NUGET_OUTDIR",
+ "Env:\CRATE_OUTDIR",
+ "Env:\WHEEL_OUTDIR",
+ "Env:\DOCS_OUTDIR"
+ ) | Format-Table
\ No newline at end of file
diff --git a/build/steps-init.yml b/build/steps-init.yml
index 28b5251b15b..42d9df112aa 100644
--- a/build/steps-init.yml
+++ b/build/steps-init.yml
@@ -14,6 +14,23 @@ steps:
packageType: sdk
version: '3.1.300'
+- script: |
+ curl https://sh.rustup.rs -sSf | sh -s -- -y
+ echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
+ displayName: Install rust
+ condition: ne( variables['Agent.OS'], 'Windows_NT' )
+- script: |
+ curl -sSf -o rustup-init.exe https://win.rustup.rs
+ rustup-init.exe -y
+ echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
+ displayName: Windows install rust
+ condition: eq( variables['Agent.OS'], 'Windows_NT' )
+
+- script: |
+ rustup install nightly
+ rustup component add rustfmt clippy
+ rustup component add rustfmt clippy --toolchain nightly
+ displayName: Enable Rust formatting and nightly options.
##
# Custom pre-reqs
diff --git a/build/test.ps1 b/build/test.ps1
index 974c2a61615..76d614af61c 100644
--- a/build/test.ps1
+++ b/build/test.ps1
@@ -57,6 +57,33 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") {
Write-Host "Skipping test of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME."
}
+
+if ($Env:ENABLE_EXPERIMENTALSIM -ne "false") {
+ if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
+ # Cargo was missing, so cannot test experimental simulators.
+ # That's fine if running locally, we'll warn the user and then skip.
+ # On CI, though, we should fail when the experimental simulator build
+ # is turned on by ENABLE_EXPERIMENTALSIM, but we can't actually
+ # proceed.
+ if ("$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true") {
+ Write-Host "##[error]Experimental simulators enabled, but cargo was not installed in CI pipeline.";
+ } else {
+ Write-Warning `
+ "Experimental simulators enabled, but cargo missing. " + `
+ "Either install cargo, or set `$Env:ENABLE_EXPERIMENTALSIM " + `
+ "to `"false`". Skipping experimental simulators.";
+ }
+ } else {
+ $expSim = (Join-Path $PSScriptRoot "../src/Simulation/qdk_sim_rs")
+ & "$expSim/test-qdk-sim-rs.ps1"
+ if ($LastExitCode -ne 0) {
+ $script:all_ok = $False
+ }
+ }
+} else {
+ Write-Host "Skipping test of experimental simulators because ENABLE_OPENSIM variable is set to: $Env:ENABLE_OPENSIM."
+}
+
if (-not $all_ok) {
throw "At least one project failed during testing. Check the logs."
}
diff --git a/documentation/examples/experimental-simulators-from-python.ipynb b/documentation/examples/experimental-simulators-from-python.ipynb
new file mode 100644
index 00000000000..66585a75a4c
--- /dev/null
+++ b/documentation/examples/experimental-simulators-from-python.ipynb
@@ -0,0 +1,2527 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "a21769ec",
+ "metadata": {},
+ "source": [
+ "# Using Experimental Simulators with Q# and Python"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a7510c5c",
+ "metadata": {},
+ "source": [
+ "The experimental simulators use the [QuTiP](https://qutip.org) library for Python to help represent noise models, so we import it here."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "7430263c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import qutip as qt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "67abc680",
+ "metadata": {},
+ "source": [
+ "To use the experimental simulators, we start by importing Q# interoperability as normal."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "00c0728f",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Preparing Q# environment...\n",
+ "."
+ ]
+ }
+ ],
+ "source": [
+ "import qsharp"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a83cdd60",
+ "metadata": {},
+ "source": [
+ "We can then use `qsharp.experimental.enable_noisy_simulation()` to add support for experimental simulators."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "ad41d44b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import qsharp.experimental\n",
+ "qsharp.experimental.enable_noisy_simulation()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "03daa4e4",
+ "metadata": {},
+ "source": [
+ "Doing so adds the `.simulate_noise` method to Python representations of Q# callables:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "a97eefa7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%qsharp\n",
+ "\n",
+ "operation DumpPlus() : Unit {\n",
+ " use q = Qubit();\n",
+ " H(q);\n",
+ " Microsoft.Quantum.Diagnostics.DumpMachine();\n",
+ " X(q);\n",
+ " Reset(q);\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "a7cb1504",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Data\":[0.5000000000000001,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5000000000000001,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5000000000000001,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5000000000000001,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],\"NQubits\":3}",
+ "text/html": [
+ "\r\n",
+ "
\r\n",
+ " Mixed state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 3 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " \r\n",
+ " $$\r\n",
+ " \\left(\r\n",
+ " \\begin{matrix}\r\n",
+ " 0.5000000000000001 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0.5000000000000001 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0.5000000000000001 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0.5000000000000001 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\\\\\n",
+ "0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i & 0 + 0 i\r\n",
+ " \\end{matrix}\r\n",
+ " \\right)\r\n",
+ " $$\r\n",
+ " | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Mixed state on 3 qubits: [ [0.5000000000000001 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0.5000000000000001 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0.5000000000000001 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0.5000000000000001 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] [0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i, 0 + 0 i] ]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpPlus.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d1fdf2e3",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the above, we notice two distinct differences with the output from `.simulate()`:\n",
+ "\n",
+ "- The experimental simulators use quantum registers of a fixed size (by default, three qubits), and allocate qubits from that register.\n",
+ "- By default, the experimental simulators represent quantum states as density operators ($\\rho = \\left|\\psi\\right\\rangle\\left\\langle\\psi\\right|$) instead of as state vectors ($\\left|\\psi\\right\\rangle$).\n",
+ "\n",
+ "For example, in the output above, the experimental simulator has output the density operator $\\rho = \\left|+00\\right\\rangle\\left\\langle+00\\right|$, as we can verify by using QuTiP."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "9a5bccc8",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket\n",
+ "Qobj data =\n",
+ "[[1.]\n",
+ " [0.]]"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ket_zero = qt.basis(2, 0)\n",
+ "ket_zero"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "cd735ba6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.707\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket\n",
+ "Qobj data =\n",
+ "[[0.70710678]\n",
+ " [0.70710678]]"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ket_one = qt.basis(2, 1)\n",
+ "ket_plus = (ket_zero + ket_one).unit()\n",
+ "ket_plus"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "02f7f83c",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2, 2, 2], [2, 2, 2]], shape = (8, 8), type = oper, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}0.500 & 0.0 & 0.0 & 0.0 & 0.500 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\\0.500 & 0.0 & 0.0 & 0.0 & 0.500 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2, 2, 2], [2, 2, 2]], shape = (8, 8), type = oper, isherm = True\n",
+ "Qobj data =\n",
+ "[[0.5 0. 0. 0. 0.5 0. 0. 0. ]\n",
+ " [0. 0. 0. 0. 0. 0. 0. 0. ]\n",
+ " [0. 0. 0. 0. 0. 0. 0. 0. ]\n",
+ " [0. 0. 0. 0. 0. 0. 0. 0. ]\n",
+ " [0.5 0. 0. 0. 0.5 0. 0. 0. ]\n",
+ " [0. 0. 0. 0. 0. 0. 0. 0. ]\n",
+ " [0. 0. 0. 0. 0. 0. 0. 0. ]\n",
+ " [0. 0. 0. 0. 0. 0. 0. 0. ]]"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ket_psi = qt.tensor(ket_plus, ket_zero, ket_zero)\n",
+ "rho = ket_psi * ket_psi.dag()\n",
+ "rho"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9e1fd1cd",
+ "metadata": {},
+ "source": [
+ "## Configuring Open Systems Noise Models"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9aa83aef",
+ "metadata": {},
+ "source": [
+ "The experimental simulators can be configured by the use of the `qsharp.config` object. For example, to change the size of the register used, we can modify the `experimental.simulators.nQubits` configuration setting:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "865fa045",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.nQubits'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "ff5b88d7",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Data\":[0.5000000000000001,0.0,0.5000000000000001,0.0,0.5000000000000001,0.0,0.5000000000000001,0.0],\"NQubits\":1}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Mixed state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 1 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " \r\n",
+ " $$\r\n",
+ " \\left(\r\n",
+ " \\begin{matrix}\r\n",
+ " 0.5000000000000001 + 0 i & 0.5000000000000001 + 0 i\\\\\n",
+ "0.5000000000000001 + 0 i & 0.5000000000000001 + 0 i\r\n",
+ " \\end{matrix}\r\n",
+ " \\right)\r\n",
+ " $$\r\n",
+ " | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Mixed state on 1 qubits: [ [0.5000000000000001 + 0 i, 0.5000000000000001 + 0 i] [0.5000000000000001 + 0 i, 0.5000000000000001 + 0 i] ]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpPlus.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "436a1534",
+ "metadata": {},
+ "source": [
+ "We can modify the noise model used in simulating Q# programs by using several functions in the `qsharp.experimental` module. For instance, to initialize the noise model to an ideal model (that is, with no noise), we can use `set_noise_model_by_name` or the `%noise_model --set-by-name` magic command:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "4cfc0e13",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.experimental.set_noise_model_by_name('ideal')\n",
+ "%noise_model --set-by-name ideal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fab9fb78",
+ "metadata": {},
+ "source": [
+ "We can then access the noise model by using `get_noise_model`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "422a7ee2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "noise_model = qsharp.experimental.get_noise_model()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c3dba96a",
+ "metadata": {},
+ "source": [
+ "This noise model is represented as a Python dictionary from preparations, measurements, and gates to Python objects representing the noise in each. For example, in the ideal noise model, the `Microsoft.Quantum.Intrinsic.H` operation is simulated by a unitary matrix:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "a2774610",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707 & 0.707\\\\0.707 & -0.707\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
+ "Qobj data =\n",
+ "[[ 0.70710678 0.70710678]\n",
+ " [ 0.70710678 -0.70710678]]"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "noise_model['h']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "81c5310c",
+ "metadata": {},
+ "source": [
+ "We can modify this to add depolarizing noise using QuTiP functions to build a depolarizing noise channel:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "b0a01d12",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "I, X, Y, Z = [P.as_qobj() for P in qsharp.Pauli]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "938accec",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def depolarizing_noise(p=1.0):\n",
+ " return p * qt.to_super(I) + ((1 - p) / 4) * sum(map(qt.to_super, [I, X, Y, Z]))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "3add5aa9",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}0.500 & 0.495 & 0.495 & 0.500\\\\0.495 & -0.495 & 0.495 & -0.495\\\\0.495 & 0.495 & -0.495 & -0.495\\\\0.500 & -0.495 & -0.495 & 0.500\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = True\n",
+ "Qobj data =\n",
+ "[[ 0.5 0.495 0.495 0.5 ]\n",
+ " [ 0.495 -0.495 0.495 -0.495]\n",
+ " [ 0.495 0.495 -0.495 -0.495]\n",
+ " [ 0.5 -0.495 -0.495 0.5 ]]"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "noise_model['h'] = depolarizing_noise(0.99) * qt.to_super(qt.qip.operations.hadamard_transform())\n",
+ "noise_model['h']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "d2a4a48a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket\n",
+ "Qobj data =\n",
+ "[[1.]\n",
+ " [0.]]"
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ket_zero = qt.basis(2, 0)\n",
+ "ket_zero"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "147c70db",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0 & 0.0\\\\0.0 & 0.0\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
+ "Qobj data =\n",
+ "[[1. 0.]\n",
+ " [0. 0.]]"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "rho_zero = ket_zero * ket_zero.dag()\n",
+ "rho_zero"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "b268e69b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}0.500 & 0.495\\\\0.495 & 0.500\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
+ "Qobj data =\n",
+ "[[0.5 0.495]\n",
+ " [0.495 0.5 ]]"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "noise_model['h'](rho_zero)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b6dc2da9",
+ "metadata": {},
+ "source": [
+ "Once we have modified our noise model in this way, we can set it as the active noise model used in simulating Q# programs:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "id": "23379c8b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.experimental.set_noise_model(noise_model)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "05dbbef9",
+ "metadata": {},
+ "source": [
+ "Using this model, we no longer get the exact $|+\\rangle\\langle+|$ state, but see that our Q# program has incurred some small error due to noise in the application of `Microsoft.Quantum.Intrinsic.H`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "id": "f791e6f6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Data\":[0.5032581095356969,0.0,0.4951069263733158,0.0,0.4951069263733158,0.0,0.49667422634133085,0.0],\"NQubits\":1}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Mixed state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 1 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " \r\n",
+ " $$\r\n",
+ " \\left(\r\n",
+ " \\begin{matrix}\r\n",
+ " 0.5032581095356969 + 0 i & 0.4951069263733158 + 0 i\\\\\n",
+ "0.4951069263733158 + 0 i & 0.49667422634133085 + 0 i\r\n",
+ " \\end{matrix}\r\n",
+ " \\right)\r\n",
+ " $$\r\n",
+ " | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Mixed state on 1 qubits: [ [0.5032581095356969 + 0 i, 0.4951069263733158 + 0 i] [0.4951069263733158 + 0 i, 0.49667422634133085 + 0 i] ]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpPlus.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "c664c0c5",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
+ " Qobj data =\n",
+ " [[ 0.06123724 -0.02041241]\n",
+ " [-0.02041241 0.02041241]],\n",
+ " Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
+ " Qobj data =\n",
+ " [[ 0.70445014 0.70445014]\n",
+ " [ 0.70445014 -0.70445014]],\n",
+ " Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = False\n",
+ " Qobj data =\n",
+ " [[ 0.05707046 -0.02948997]\n",
+ " [ 0.00190948 0.02948997]],\n",
+ " Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = False\n",
+ " Qobj data =\n",
+ " [[-0.00103545 0.04950143]\n",
+ " [ 0.00197827 0.05044425]]]"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "qt.to_kraus(noise_model['h'])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "94c70d54",
+ "metadata": {},
+ "source": [
+ "## Configuring Stabilizer Noise Models"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "05d1a8f5",
+ "metadata": {},
+ "source": [
+ "We can also configure the experimental simulator to use stabilizer (_a.k.a._ CHP) simulation. This time, let's get a new noise model by using `get_noise_model_by_name`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "a02721e7",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'initial_state': {'n_qubits': 1,\n",
+ " 'data': {'Stabilizer': {'n_qubits': 1,\n",
+ " 'table': array([[ True, False, False],\n",
+ " [False, True, False]])}}},\n",
+ " 'cnot': {'n_qubits': 2, 'data': {'ChpDecomposition': [{'Cnot': [0, 1]}]}},\n",
+ " 'i': {'n_qubits': 1, 'data': {'Sequence': []}},\n",
+ " 's': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Phase': 0}]}},\n",
+ " 's_adj': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'AdjointPhase': 0}]}},\n",
+ " 't': {'n_qubits': 1, 'data': 'Unsupported'},\n",
+ " 't_adj': {'n_qubits': 1, 'data': 'Unsupported'},\n",
+ " 'h': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Hadamard': 0}]}},\n",
+ " 'x': {'n_qubits': 1,\n",
+ " 'data': {'ChpDecomposition': [{'Hadamard': 0},\n",
+ " {'Phase': 0},\n",
+ " {'Phase': 0},\n",
+ " {'Hadamard': 0}]}},\n",
+ " 'y': {'n_qubits': 1,\n",
+ " 'data': {'ChpDecomposition': [{'AdjointPhase': 0},\n",
+ " {'Hadamard': 0},\n",
+ " {'Phase': 0},\n",
+ " {'Phase': 0},\n",
+ " {'Hadamard': 0},\n",
+ " {'Phase': 0}]}},\n",
+ " 'z': {'n_qubits': 1,\n",
+ " 'data': {'ChpDecomposition': [{'Phase': 0}, {'Phase': 0}]}},\n",
+ " 'z_meas': {'ZMeasurement': {'pr_readout_error': 0}}}"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "noise_model = qsharp.experimental.get_noise_model_by_name('ideal_stabilizer')\n",
+ "noise_model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "9f7791f6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.experimental.set_noise_model(noise_model)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "df293936",
+ "metadata": {},
+ "source": [
+ "To make the best use of stabilizer noise models, we also need to configure the simulator to start off in the stabilizer representation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "id": "fe0142d8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.representation'] = 'stabilizer'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "id": "4db703f8",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[2,3],\"Data\":[false,true,false,true,false,false],\"AsArray\":[false,true,false,true,false,false]},\"Data\":[false,true,false,true,false,false],\"NQubits\":1}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Stabilizer state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 1 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " $$\\left(\\begin{array}{c|c|c}0 & 1 & 0\\\\\n",
+ "\\hline\n",
+ "1 & 0 & 0\\end{array}\\right)$$ | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Microsoft.Quantum.Experimental.StabilizerState"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpPlus.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5e340d9d",
+ "metadata": {},
+ "source": [
+ "Notably, the stabilizer representation does not support operations outside of the stabilizer formalism, such as `T` and `CCNOT`. This allows the stabilizer representation to support significantly more qubits than other representations:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "id": "5f24b886",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.nQubits'] = 10"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "id": "5f4d45b8",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[20,21],\"Data\":[false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false],\"AsArray\":[false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false]},\"Data\":[false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false],\"NQubits\":10}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Stabilizer state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 10 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " $$\\left(\\begin{array}{cccccccccc|cccccccccc|c}0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "\\hline\n",
+ "1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\end{array}\\right)$$ | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Microsoft.Quantum.Experimental.StabilizerState"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 28,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpPlus.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b6e4795c",
+ "metadata": {},
+ "source": [
+ "If we turn off visualization, we can get significantly more qubits still!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "id": "138ff19b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%qsharp\n",
+ "open Microsoft.Quantum.Arrays;\n",
+ "\n",
+ "operation SampleRandomBitstring(nQubits : Int) : Result[] {\n",
+ " use register = Qubit[nQubits];\n",
+ " ApplyToEachCA(H, register);\n",
+ " return ForEach(M, register);\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "2f55cd3b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.nQubits'] = 1000"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "id": "92fc3ab1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Wall time: 2.85 s\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "[1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0,\n",
+ " 1,\n",
+ " 0]"
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "%time SampleRandomBitstring.simulate_noise(nQubits=1000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3534390d",
+ "metadata": {},
+ "source": [
+ "For now, though, we'll turn back down the number of qubits just to make dumps easier to read!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "id": "f3587f4c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.nQubits'] = 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f5e96ca9",
+ "metadata": {},
+ "source": [
+ "The visualization style for stabilizer states can be selected by using the `experimental.simulators.stabilizerStateStyle` configuration setting:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "id": "2204f4ae",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%qsharp\n",
+ "\n",
+ "operation DumpBellPair() : Unit {\n",
+ " use left = Qubit();\n",
+ " use right = Qubit();\n",
+ " within {\n",
+ " H(left);\n",
+ " CNOT(left, right);\n",
+ " } apply {\n",
+ " Microsoft.Quantum.Diagnostics.DumpMachine();\n",
+ " }\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "id": "70908857",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.stabilizerStateStyle'] = 'matrixWithoutDestabilizers'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "id": "89b437a8",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[8,9],\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"AsArray\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false]},\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"NQubits\":4}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Stabilizer state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 4 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " $$\\left(\\begin{array}{cccc|cccc|c}1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 1 & 1 & 0 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n",
+ "0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\end{array}\\right)$$ | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Microsoft.Quantum.Experimental.StabilizerState"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpBellPair.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "id": "4708c2d9",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"div_id\":\"dump-machine-div-2c22aa8a-df12-4fb0-a61b-91284bd88c5c\",\"qubit_ids\":[0,1],\"n_qubits\":2,\"amplitudes\":[{\"Real\":0.7071067811865476,\"Imaginary\":0.0,\"Magnitude\":0.7071067811865476,\"Phase\":0.0},{\"Real\":0.0,\"Imaginary\":0.0,\"Magnitude\":0.0,\"Phase\":0.0},{\"Real\":0.0,\"Imaginary\":0.0,\"Magnitude\":0.0,\"Phase\":0.0},{\"Real\":0.7071067811865476,\"Imaginary\":0.0,\"Magnitude\":0.7071067811865476,\"Phase\":0.0}]}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " | Qubit IDs | \r\n",
+ " 0, 1 | \r\n",
+ "
\r\n",
+ " \r\n",
+ " \r\n",
+ " | Basis state (little endian) | \r\n",
+ " Amplitude | Meas. Pr. | Phase | \r\n",
+ "
\r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " | $\\left|0\\right\\rangle$ | \r\n",
+ " $0.7071 + 0.0000 i$ | \r\n",
+ " \r\n",
+ " \r\n",
+ " | \r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " ↑\r\n",
+ " | \r\n",
+ " \r\n",
+ "
\r\n",
+ " \n",
+ "\r\n",
+ " \r\n",
+ " | $\\left|1\\right\\rangle$ | \r\n",
+ " $0.0000 + 0.0000 i$ | \r\n",
+ " \r\n",
+ " \r\n",
+ " | \r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " ↑\r\n",
+ " | \r\n",
+ " \r\n",
+ "
\r\n",
+ " \n",
+ "\r\n",
+ " \r\n",
+ " | $\\left|2\\right\\rangle$ | \r\n",
+ " $0.0000 + 0.0000 i$ | \r\n",
+ " \r\n",
+ " \r\n",
+ " | \r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " ↑\r\n",
+ " | \r\n",
+ " \r\n",
+ "
\r\n",
+ " \n",
+ "\r\n",
+ " \r\n",
+ " | $\\left|3\\right\\rangle$ | \r\n",
+ " $0.7071 + 0.0000 i$ | \r\n",
+ " \r\n",
+ " \r\n",
+ " | \r\n",
+ " \r\n",
+ " \r\n",
+ " \r\n",
+ " ↑\r\n",
+ " | \r\n",
+ " \r\n",
+ "
\r\n",
+ " \r\n",
+ " \r\n",
+ "
"
+ ],
+ "text/plain": [
+ "|0⟩\t0.7071067811865476 + 0𝑖\n",
+ "|1⟩\t0 + 0𝑖\n",
+ "|2⟩\t0 + 0𝑖\n",
+ "|3⟩\t0.7071067811865476 + 0𝑖"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpBellPair.simulate()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "id": "40431ed1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.stabilizerStateStyle'] = 'denseGroupPresentation'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "id": "1cc366e4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[8,9],\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"AsArray\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false]},\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"NQubits\":4}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Stabilizer state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 4 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " $$\\left\\langle XX𝟙𝟙, ZZ𝟙𝟙, 𝟙𝟙Z𝟙, 𝟙𝟙𝟙Z \\right\\rangle$$ | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Microsoft.Quantum.Experimental.StabilizerState"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpBellPair.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "id": "07439931",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.config['experimental.simulators.stabilizerStateStyle'] = 'sparseGroupPresentation'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "id": "7f4560f8",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[8,9],\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"AsArray\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false]},\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"NQubits\":4}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Stabilizer state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 4 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " $$\\left\\langle X_{0}X_{1}, Z_{0}Z_{1}, Z_{2}, Z_{3} \\right\\rangle$$ | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Microsoft.Quantum.Experimental.StabilizerState"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpBellPair.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "58b9d547",
+ "metadata": {},
+ "source": [
+ "So far, we've only used ideal stabilizer simulation, but what happens if one of our operations is followed by a mixed Pauli channel?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "5b80e500",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "SequenceProcess(n_qubits=1, processes=[ChpDecompositionProcess(n_qubits=1, operations=[Hadamard(idx_target=0)]), MixedPauliProcess(n_qubits=1, operators=[(0.9, 'I'), (0.1, 'Z')])])"
+ ]
+ },
+ "execution_count": 42,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "noise_model['h'] = qsharp.experimental.SequenceProcess(1, \n",
+ " [\n",
+ " qsharp.experimental.ChpDecompositionProcess(1, [\n",
+ " qsharp.experimental.Hadamard(0)\n",
+ " ]),\n",
+ " qsharp.experimental.MixedPauliProcess(1, [\n",
+ " (0.9, 'I'),\n",
+ " (0.1, 'Z')\n",
+ " ])\n",
+ " ]\n",
+ ")\n",
+ "noise_model['h']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "94c8b146",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qsharp.experimental.set_noise_model(noise_model)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "id": "c517bb74",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[8,9],\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,true,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"AsArray\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,true,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false]},\"Data\":[false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,true,false,false,false,false,false,false,true,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false],\"NQubits\":4}",
+ "text/html": [
+ "\r\n",
+ " \r\n",
+ " Stabilizer state\r\n",
+ " \r\n",
+ " | # of qubits | \r\n",
+ " 4 | \r\n",
+ "
\r\n",
+ "\r\n",
+ " \r\n",
+ " | State data | \r\n",
+ " $$\\left\\langle -X_{0}X_{1}, Z_{0}Z_{1}, Z_{2}, Z_{3} \\right\\rangle$$ | \r\n",
+ "
\r\n",
+ "
\r\n",
+ " "
+ ],
+ "text/plain": [
+ "Microsoft.Quantum.Experimental.StabilizerState"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DumpBellPair.simulate_noise()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a64c1a27",
+ "metadata": {},
+ "source": [
+ "## Epilogue"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "id": "56168b7e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'iqsharp': LooseVersion ('1.0.0'),\n",
+ " 'Jupyter Core': LooseVersion ('1.5.0.0'),\n",
+ " '.NET Runtime': LooseVersion ('.NETCoreApp,Version=v3.1'),\n",
+ " 'qsharp': LooseVersion ('0.0.1.0a1'),\n",
+ " 'experimental': {'simulators': {'features': ['DEFAULT'],\n",
+ " 'name': 'Microsoft.Quantum.Experimental.Simulators',\n",
+ " 'opt_level': '3',\n",
+ " 'target': 'x86_64-pc-windows-msvc',\n",
+ " 'version': '0.17.210627752-alpha'}}}"
+ ]
+ },
+ "execution_count": 57,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "qsharp.component_versions()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "85f8f2e8",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "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.8.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/documentation/experimental-simulators.md b/documentation/experimental-simulators.md
new file mode 100644
index 00000000000..944cbcb9a8b
--- /dev/null
+++ b/documentation/experimental-simulators.md
@@ -0,0 +1,51 @@
+# Using the Experimental Simulators for the Quantum Development Kit
+
+As an experimental feature, the Quantum Development Kit provides capabilities for noisy and stabilizer simulation. This feature allows for simulating the behavior of Q# programs under the influence of noise, and for using the stabilizer representation (a.k.a. CHP simulation) with programs that only call Clifford operations.
+
+> For more information about the development of this feature, please see the GitHub issue at .
+
+Currently, the experimental simulators are supported for use with:
+
+- C# host programs
+- Python host programs
+- Q# standalone notebooks
+
+The experimental simulators are not yet supported by:
+
+- Q# standalone command-line programs
+- QIR-based executables
+
+## Known issues and limitations
+
+As this feature is currently under development, there are still a number of limitations and missing capabilities.
+
+- Continuous-time rotations (e.g.: `Rx`, `Ry`, `Rz`, and `Exp`) are not yet supported.
+- Fractional rotations (e.g.: `R1Frac`, `ExpFrac`) are not yet supported.
+- The `Controlled Y` operation with more than one control qubit is not yet supported.
+- The `Controlled T` operation is not yet supported.
+- Joint measurement is not yet supported.
+- In some cases, qubits may need to be manually `Reset` before releasing, even if they have been measured.
+
+Some limitations are inherent to open systems simulation, and may not ever be supported:
+
+- Assertions (e.g.: `AssertMeasurement` and `AssertMeasurementProbability`) are not supported, as these assertions may fail for correct code in the presence of noise. These assertions are no-ops on the experimental simulators.
+
+## Using Experimental Simulators from Python
+
+> ### **ⓘ** TIP
+>
+> See the [example on using the experimental simulators from Python](./examples/experimental-simulators-from-python.ipynb) for more details.
+
+Once you have the right version of the `qsharp-core` Python package installed, you can enable the use of the experimental simulators by using the `qsharp.experimental` module:
+
+```python
+import qsharp
+import qsharp.experimental
+qsharp.experimental.enable_noisy_simulation()
+```
+
+After calling `enable_noisy_simulation()`, Q# operations imported into Python will expose a `.simulate_noise()` method that can be used to run Q# programs against the experimental simulators.
+
+By default, `.simulate_noise()` will assume an ideal error model (that is, no noise). To configure a particular error model, use the `qsharp.experimental.get_noise_model` and `qsharp.experimental.set_noise_model` functions to get and set the current noise model for the experimental simulators. Each error model is represented as a dictionary from intrinsic operation names to objects representing the errors in those intrinsic operations.
+
+For open systems simulation, error channels can be represented by [QuTiP](https://qutip.org/) `Qobj` objects encoding superoperators.
diff --git a/omnisharp.json b/omnisharp.json
new file mode 100644
index 00000000000..d705c6415c8
--- /dev/null
+++ b/omnisharp.json
@@ -0,0 +1,5 @@
+{
+ "script": {
+ "enableScriptNuGetReferences": true
+ }
+}
\ No newline at end of file
diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props
index 62a9ed4319b..3efc2a5f4ef 100644
--- a/src/Simulation/Common/Simulators.Dev.props
+++ b/src/Simulation/Common/Simulators.Dev.props
@@ -5,8 +5,10 @@
bin\$(BuildConfiguration)\$(TargetFramework)\$(AssemblyName).xml
$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory)..\..\..\))
$([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/Native/build/drop))
+ $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/qdk_sim_rs/drop))
+
$([MSBuild]::NormalizePath($(NativeBuildPath)/libMicrosoft.Quantum.Simulator.Runtime.dylib))
$([MSBuild]::NormalizePath($(NativeBuildPath)/libMicrosoft.Quantum.Simulator.Runtime.so))
@@ -28,7 +30,25 @@
false
-
+
+
+
+ $([MSBuild]::NormalizePath($(ExperimentalSimBuildPath)/libqdk_sim.dylib))
+ $([MSBuild]::NormalizePath($(ExperimentalSimBuildPath)/libqdk_sim.so))
+ $([MSBuild]::NormalizePath($(ExperimentalSimBuildPath)/qdk_sim.dll))
+ $(ExperimentalSimDllMac)
+ $(ExperimentalSimDllLinux)
+ $(ExperimentalSimDllWindows)
+
+
+
+
+ Microsoft.Quantum.Experimental.Simulators.Runtime.dll
+ PreserveNewest
+ false
+
+
+
diff --git a/src/Simulation/Native/.gitignore b/src/Simulation/Native/.gitignore
index 8f1fc6e9a01..aa51796222b 100644
--- a/src/Simulation/Native/.gitignore
+++ b/src/Simulation/Native/.gitignore
@@ -10,3 +10,14 @@ foo*
CMakeFiles/
CMakeCache.txt
*.so
+
+# Ignore build artifacts...
+win10
+# ...except for vcomp140, which isn't actually a build
+# artifact, but provides the Visual C/C++ OpenMP runtime
+# needed by the full-state simulator.
+#
+# Making sure that this file is in the repo will make
+# sure it ends up in our final NuGet packages, as needed
+# for the full-state simulator to work correctly.
+!win10/vcomp140.dll
diff --git a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs
index 64d4808065e..829ee22d483 100644
--- a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs
+++ b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs
@@ -1,42 +1,45 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits {
-
open Microsoft.Quantum.Intrinsic;
-
-
- operation TeleportTest () : Unit {
-
-
- using (qubits = Qubit[3]) {
- let q1 = qubits[0];
- let q2 = qubits[1];
- let q3 = qubits[2];
-
- // create a Bell pair
- H(q1);
- CNOT(q1, q2);
-
- // create quantum state
- H(q3);
- Rz(1.1, q3);
-
- // teleport
- CNOT(q3, q2);
- H(q3);
- Controlled X([q2], q1);
- Controlled Z([q3], q1);
-
- // check teleportation success
- Rz(-1.1, q1);
- H(q1);
-
- // Make sure all allocated qubits are retrurned to zero before release
- ResetAll(qubits);
+ open Microsoft.Quantum.Diagnostics;
+
+ @Test("QuantumSimulator")
+ // TODO: Disabled until we have a noise model for Rz.
+ // @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
+ operation TestTeleport() : Unit {
+ use q1 = Qubit();
+ use q2 = Qubit();
+ use q3 = Qubit();
+
+ // create a Bell pair
+ H(q1);
+ CNOT(q1, q2);
+
+ // create quantum state
+ H(q3);
+ Rz(1.1, q3);
+
+ // teleport
+ CNOT(q3, q2);
+ H(q3);
+ Controlled X([q2], q1);
+ Controlled Z([q3], q1);
+
+ // check teleportation success
+ Rz(-1.1, q1);
+ H(q1);
+
+ // Measure and make sure we get Zero.
+ // We do so without use of diagnostics functions and operations, since
+ // many don't exist yet at this point in the build.
+ if M(q1) != Zero {
+ fail "Expected Zero after teleportation, but got One.";
}
- }
-
-}
+ // Make sure all allocated qubits are retrurned to zero before release
+ ResetAll([q1, q2, q3]);
+ }
+}
diff --git a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs
new file mode 100644
index 00000000000..cb95eaba9af
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Xunit;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public partial class NativeInterfaceTests
+ {
+ [Fact]
+ public void GetIdealNoiseModelByNameWorks()
+ {
+ var ideal = NativeInterface.GetNoiseModelByName("ideal");
+ // TODO: Add assertions here to check properties of the above noise model.
+ }
+
+ [Fact]
+ public void GetIdealStabilizerNoiseModelByNameWorks()
+ {
+ var idealStabilizer = NativeInterface.GetNoiseModelByName("ideal_stabilizer");
+ // TODO: Add assertions here to check properties of each noise model.
+ }
+
+ [Fact]
+ public void GetNoiseModelByNameThrowsExceptionForInvalidNames()
+ {
+ Assert.Throws(() => {
+ NativeInterface.GetNoiseModelByName("invalid");
+ });
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/TestOperations.qs b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/TestOperations.qs
new file mode 100644
index 00000000000..db149453199
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/TestOperations.qs
@@ -0,0 +1,199 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// # Design notes
+//
+// In testing the open systems simulator, we can't use the same approach used
+// in testing other simulators. In particular, AssertMeasurement and
+// AssertMeasurementProbability are no-ops on experimental simulators. Thus,
+// we need to be a bit more indirect.
+//
+// Moreover, not all decompositions are supported yet
+// (see documentation/experimental-simulators.md), such that we need to avoid
+// unsupported cases.
+//
+// In this file, we list a bunch of operations that are unlikely to work
+// correctly in the presence of decomposition bugs. This is not a guarantee,
+// as we may have in the case of testing Choi–Jamilkowski states with
+// assertions, but it should help build confidence in experimental simulator
+// decompositions.
+//
+// In the future, consolidating these decompositions with those used in other
+// targeting packages will allow using assertions on the full-state simulator
+// to help build confidence in shared decompositions, further improving test
+// coverage.
+
+namespace Microsoft.Quantum.Experimental.Tests {
+ open Microsoft.Quantum.Intrinsic;
+ open Microsoft.Quantum.Diagnostics;
+
+ internal function Fact(expected : Bool, message : String) : Unit {
+ if not expected {
+ fail $"Fact was false: {message}";
+ }
+ }
+
+ internal operation MX(target : Qubit) : Result {
+ return Measure([PauliX], [target]);
+ }
+
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
+ operation CheckBellBasisParitiesWithSingleQubitMeasurements() : Unit {
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ Fact(M(left) == M(right), "Z parity in 00 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ Fact(MX(left) == MX(right), "X parity in 00 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ X(left);
+
+ Fact(M(left) != M(right), "Z parity in 10 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ X(left);
+
+ Fact(MX(left) == MX(right), "X parity in 10 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ Z(left);
+
+ Fact(M(left) == M(right), "Z parity in 01 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ Z(left);
+
+ Fact(MX(left) != MX(right), "X parity in 01 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ X(left);
+ Z(left);
+
+ Fact(M(left) != M(right), "Z parity in 11 case was wrong.");
+ ResetAll([left, right]);
+ }
+
+ use (left, right) = (Qubit(), Qubit()) {
+ H(left);
+ CNOT(left, right);
+
+ X(left);
+ Z(left);
+
+ Fact(MX(left) != MX(right), "X parity in 11 case was wrong.");
+ ResetAll([left, right]);
+ }
+ }
+
+ internal function Xor(a : Bool, b : Bool) : Bool {
+ return (a or b) and ((not a) or (not b));
+ }
+
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
+ @Test("QuantumSimulator") // validate against full-state simulator.
+ operation CheckToffoliOnComputationalBasisStates() : Unit {
+ for in0 in [false, true] {
+ for in1 in [false, true] {
+ for output in [false, true] {
+ for useCcz in [false, true] {
+ use qs = Qubit[3];
+ if in0 { X(qs[0]); }
+ if in1 { X(qs[1]); }
+ if output { X(qs[2]); }
+
+ let expectedOut = Xor(output, in0 and in1);
+
+ if useCcz {
+ within {
+ H(qs[2]);
+ } apply {
+ Controlled Z([qs[0], qs[1]], qs[2]);
+ }
+ } else {
+ Controlled X([qs[0], qs[1]], qs[2]);
+ }
+
+ let results = [M(qs[0]), M(qs[1]), M(qs[2])];
+ let expected = [in0 ? One | Zero, in1 ? One | Zero, expectedOut ? One | Zero];
+
+ Fact(results[0] == expected[0], $"in0 was incorrect in case: {in0} {in1} {output}. Got {results[0]}, expected {expected[0]}.");
+ Fact(results[1] == expected[1], $"in1 was incorrect in case: {in0} {in1} {output}. Got {results[1]}, expected {expected[1]}.");
+ Fact(results[2] == expected[2], $"expected was incorrect in case: {in0} {in1} {output}. Got {results[2]}, expected {expected[2]}.");
+
+ ResetAll(qs);
+ }
+ }
+ }
+ }
+ }
+
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
+ @Test("QuantumSimulator") // validate against full-state simulator.
+ operation CheckXHSZSHIsNoOp() : Unit {
+ use q = Qubit();
+
+ X(q);
+ within {
+ H(q);
+ S(q);
+ } apply {
+ Z(q);
+ }
+
+ Fact(M(q) == Zero, "XHSZSH was not a no-op.");
+ }
+
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
+ @Test("QuantumSimulator") // validate against full-state simulator.
+ operation CheckControlledHWorks() : Unit {
+ use control = Qubit();
+ use target = Qubit();
+
+
+ Controlled H([control], target);
+ within {
+ X(control);
+ } apply {
+ Controlled H([control], target);
+ }
+ H(target);
+
+ Fact(M(control) == Zero, "Controlled H did not work correctly.");
+ Fact(M(target) == Zero, "Controlled H did not work correctly.");
+
+ }
+
+}
diff --git a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs
new file mode 100644
index 00000000000..57af996e521
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Quantum.Experimental;
+using Microsoft.Quantum.Simulation.Core;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace Microsoft.Quantum.Simulation.Simulators.Tests
+{
+ public class NoiseModelSerializationTests
+ {
+ private const string idealJson = @"{""initial_state"":{""n_qubits"":1,""data"":{""Mixed"":{""v"":1,""dim"":[2,2],""data"":[[1,0],[0,0],[0,0],[0,0]]}}},""i"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[1,0],[0,0],[0,0],[1,0]]}}},""x"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[0,0],[1,0],[1,0],[0,0]]}}},""y"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[0,0],[0,1],[0,-1],[0,0]]}}},""z"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[1,0],[0,0],[0,0],[-1,0]]}}},""h"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[0.7071067811865476,0],[0.7071067811865476,0],[0.7071067811865476,0],[-0.7071067811865476,0]]}}},""s"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,1.0]]}}},""s_adj"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[1.0,-0.0],[0.0,-0.0],[0.0,-0.0],[0.0,-1.0]]}}},""t"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.7071067811865476,0.7071067811865476]]}}},""t_adj"":{""n_qubits"":1,""data"":{""Unitary"":{""v"":1,""dim"":[2,2],""data"":[[1.0,-0.0],[0.0,-0.0],[0.0,-0.0],[0.7071067811865476,-0.7071067811865476]]}}},""cnot"":{""n_qubits"":2,""data"":{""Unitary"":{""v"":1,""dim"":[4,4],""data"":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0]]}}},""z_meas"":{""Effects"":[{""n_qubits"":1,""data"":{""KrausDecomposition"":{""v"":1,""dim"":[1,2,2],""data"":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}},{""n_qubits"":1,""data"":{""KrausDecomposition"":{""v"":1,""dim"":[1,2,2],""data"":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0]]}}}]}}";
+
+ [Fact]
+ public void IdealNoiseModelDeserializes()
+ {
+ var idealNoiseModel = JsonSerializer.Deserialize(idealJson);
+
+ // Assert some stuff about the ideal noise model to make sure we got it right.
+ idealNoiseModel.ZMeas.AssertTypeAnd(instrument =>
+ {
+ Assert.Equal(2, instrument.Effects.Count);
+ });
+ idealNoiseModel.Z.AssertTypeAnd(process =>
+ {
+ Assert.Equal(1, process.NQubits);
+ });
+ }
+
+ [Fact]
+ public void IdealNoiseModelRoundTrips()
+ {
+ var idealNoiseModel = (
+ NoiseModel.TryGetByName("ideal", out var model)
+ ? model
+ : throw new Exception("Failed to get noise model by name.")
+ );
+ idealNoiseModel.AssertSerializationRoundTrips();
+ }
+
+ [Fact]
+ public void IdealStabilizerNoiseModelRoundTrips()
+ {
+ var idealStabilizerModel = (
+ NoiseModel.TryGetByName("ideal_stabilizer", out var model)
+ ? model
+ : throw new Exception("Could not get noise model by name.")
+ );
+ idealStabilizerModel.AssertSerializationRoundTrips();
+ }
+ }
+}
diff --git a/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs
new file mode 100644
index 00000000000..1e518bff509
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs
@@ -0,0 +1,118 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Quantum.Experimental;
+using Microsoft.Quantum.Simulation.Core;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace Microsoft.Quantum.Simulation.Simulators.Tests
+{
+ public class ProcessSerializationTests
+ {
+ [Fact]
+ public void MixedPauliSerializesCorrectly()
+ {
+ var mixedPauli = new MixedPauliProcess(
+ 1,
+ new List<(double, IList)>
+ {
+ (0.9, new List { Pauli.PauliI }),
+ (0.1, new List { Pauli.PauliX }),
+ }
+ );
+ var actualJson = JsonSerializer.Serialize(mixedPauli);
+ @"{
+ ""n_qubits"": 1,
+ ""data"": {
+ ""MixedPauli"": [
+ [0.9, [""I""]],
+ [0.1, [""X""]]
+ ]
+ }
+ }".AssertJsonIsEqualTo(actualJson);
+ }
+
+ [Fact]
+ public void MixedPauliRoundTripsCorrectly()
+ {
+ var mixedPauli = new MixedPauliProcess(
+ 1,
+ new List<(double, IList)>
+ {
+ (0.9, new List { Pauli.PauliI }),
+ (0.1, new List { Pauli.PauliX }),
+ }
+ );
+ var expectedJson = JsonSerializer.Serialize(mixedPauli);
+ var actualJson = JsonSerializer.Serialize(JsonSerializer.Deserialize(expectedJson));
+ expectedJson.AssertJsonIsEqualTo(actualJson);
+ }
+
+ [Fact]
+ public void CnotSerializesCorrectly()
+ {
+ var cnot = new ChpOperation.Cnot
+ {
+ IdxControl = 0,
+ IdxTarget = 1
+ };
+ var json = JsonSerializer.Serialize(cnot);
+ var expectedJson = @"{
+ ""Cnot"": [0, 1]
+ }";
+
+ expectedJson.AssertJsonIsEqualTo(json);
+ }
+
+ [Fact]
+ public void HadamardSerializesCorrectly()
+ {
+ var h = new ChpOperation.Hadamard
+ {
+ IdxTarget = 1
+ };
+ var json = JsonSerializer.Serialize(h);
+ var expectedJson = @"{
+ ""Hadamard"": 1
+ }";
+
+ expectedJson.AssertJsonIsEqualTo(json);
+ }
+
+ [Fact]
+ public void PhaseSerializesCorrectly()
+ {
+ var s = new ChpOperation.Phase
+ {
+ IdxTarget = 1
+ };
+ var json = JsonSerializer.Serialize(s);
+ var expectedJson = @"{
+ ""Phase"": 1
+ }";
+
+ expectedJson.AssertJsonIsEqualTo(json);
+ }
+
+ [Fact]
+ public void AdjointPhaseSerializesCorrectly()
+ {
+ var sAdj = new ChpOperation.AdjointPhase
+ {
+ IdxTarget = 1
+ };
+ var json = JsonSerializer.Serialize(sAdj);
+ var expectedJson = @"{
+ ""AdjointPhase"": 1
+ }";
+
+ expectedJson.AssertJsonIsEqualTo(json);
+ }
+ }
+}
diff --git a/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs b/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs
new file mode 100644
index 00000000000..8194aa7b0b8
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Quantum.Experimental;
+using Microsoft.Quantum.Simulation.Core;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace Microsoft.Quantum.Simulation.Simulators.Tests
+{
+ internal static class SerializationExtensions
+ {
+ internal static void AssertTypeAnd(this object obj, Action then)
+ {
+ if (!(obj is T t))
+ {
+ Assert.IsType(obj);
+ }
+ else
+ {
+ then(t);
+ }
+ }
+
+ internal static string Serialize(this JsonDocument document)
+ {
+ var stream = new MemoryStream();
+ var writer = new Utf8JsonWriter(stream);
+ document.WriteTo(writer);
+ writer.Flush();
+ return Encoding.UTF8.GetString(stream.ToArray());
+ }
+
+ internal static void AssertSerializationRoundTrips(this T obj)
+ {
+ var expected = JsonSerializer.Serialize(obj);
+ var actual = JsonSerializer.Serialize(JsonSerializer.Deserialize(expected));
+ expected.AssertJsonIsEqualTo(actual);
+ }
+
+ internal static void AssertJsonIsEqualTo(this string expectedJson, string actualJson)
+ {
+ // To get a stable text representation, we first parse both strings to JsonDocument
+ // objects, then re-serialize them. This avoids numerical precision issues in
+ // JToken.DeepEquals, and allows for highlighting diffs more easily.
+ var expectedNormalized = JsonDocument.Parse(expectedJson).Serialize();
+ var actualNormalized = JsonDocument.Parse(actualJson).Serialize();
+ Assert.Equal(expectedNormalized, actualNormalized);
+ }
+ }
+}
diff --git a/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs
new file mode 100644
index 00000000000..e90fce0465b
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Quantum.Experimental;
+using Microsoft.Quantum.Simulation.Core;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace Microsoft.Quantum.Simulation.Simulators.Tests
+{
+ public class StateSerializationTests
+ {
+ [Fact]
+ public void StabilizerStateSerializesCorrectly()
+ {
+ var state = new StabilizerState
+ {
+ NQubits = 2,
+ Table = new StabilizerState.TableArray
+ {
+ Data = new List
+ {
+ true, false, false, false, true, false
+ },
+ Dimensions = new List { 2, 3 }
+ }
+ };
+ var json = JsonSerializer.Serialize(state);
+ var expectedJson = @"{
+ ""n_qubits"": 2,
+ ""data"": {
+ ""Stabilizer"": {
+ ""n_qubits"": 2,
+ ""table"": {
+ ""v"": 1,
+ ""dim"": [2, 3],
+ ""data"": [true,false,false,false,true,false]
+ }
+ }
+ }
+ }";
+
+ expectedJson.AssertJsonIsEqualTo(json);
+ }
+
+ [Fact]
+ public void StabilizerArrayDeserializesCorrectly()
+ {
+ var expectedJson = @"
+ {
+ ""v"": 1,
+ ""dim"": [2, 3],
+ ""data"": [true,false,false,false,true,false]
+ }
+ ";
+ var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(expectedJson));
+ var array = JsonSerializer.Deserialize(ref reader);
+ var state = new StabilizerState
+ {
+ NQubits = 2,
+ Table = array
+ };
+ }
+ }
+}
diff --git a/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Arrays/Tests.qs b/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Arrays/Tests.qs
index 3bbc428984a..518bb5128ea 100644
--- a/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Arrays/Tests.qs
+++ b/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Arrays/Tests.qs
@@ -8,6 +8,7 @@ namespace Microsoft.Quantum.Arrays {
/// Checks that empty arrays are indeed empty.
@Test("QuantumSimulator")
@Test("ToffoliSimulator")
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
function EmptyArraysAreEmpty() : Unit {
Fact(
Length(EmptyArray()) == 0,
diff --git a/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Random/Tests.qs b/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Random/Tests.qs
index c646960b606..3dfe4f48f75 100644
--- a/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Random/Tests.qs
+++ b/src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Random/Tests.qs
@@ -14,7 +14,7 @@ namespace Microsoft.Quantum.Tests {
internal function SampleMeanAndVariance(samples : Double[]) : (Double, Double) {
mutable meanAcc = 0.0;
mutable varAcc = 0.0;
- for (idx in 0..Length(samples) - 1) {
+ for idx in 0..Length(samples) - 1 {
let sample = samples[idx];
let oldMeanAcc = meanAcc;
let delta = (sample - meanAcc);
@@ -27,7 +27,7 @@ namespace Microsoft.Quantum.Tests {
internal operation EstimateMeanAndVariance(dist : ContinuousDistribution, nSamples : Int) : (Double, Double) {
mutable samples = new Double[nSamples];
- for (idx in 0..nSamples - 1) {
+ for idx in 0..nSamples - 1 {
set samples w/= idx <- dist::Sample();
}
return SampleMeanAndVariance(samples);
@@ -60,8 +60,9 @@ namespace Microsoft.Quantum.Tests {
/// Checks that @"microsoft.quantum.random.drawrandomdouble" obeys ranges.
@Test("QuantumSimulator")
@Test("ToffoliSimulator")
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
operation CheckDrawRandomDoubleObeysRanges() : Unit {
- for (j in 0..10000) {
+ for j in 0..10000 {
let random = DrawRandomDouble(0.0, 1.0);
if (random < 0.0 or random > 1.0) {
fail $"DrawRandomDouble(0.0, 1.0) returned {random}, outside the allowed interval.";
@@ -73,6 +74,7 @@ namespace Microsoft.Quantum.Tests {
/// Checks that @"microsoft.quantum.random.drawrandomdint" obeys ranges.
@Test("QuantumSimulator")
@Test("ToffoliSimulator")
+ @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")
operation CheckDrawRandomIntObeysRanges() : Unit {
mutable randomInt = DrawRandomInt(0, 45);
if (randomInt > 45 or randomInt < 0) {
diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj
index 1cb8ed1a068..9baf8e3e8b6 100644
--- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj
+++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj
@@ -24,6 +24,12 @@
+
+
+
+
+
+
runtimes\win-x64\native\%(RecursiveDir)%(FileName)%(Extension)
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/ContinuousTimeOperations.cs b/src/Simulation/Simulators/OpenSystemsSimulator/ContinuousTimeOperations.cs
new file mode 100644
index 00000000000..fb604f16fdf
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/ContinuousTimeOperations.cs
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Common;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Microsoft.Quantum.Intrinsic.Interfaces;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public partial class OpenSystemsSimulator
+ {
+ // These gates are not yet supported, pending a design for how to extend
+ // noise models to continuous-time gates (that is, those parameterized
+ // by real numbers, such as angles).
+
+ void IIntrinsicExp.Body(IQArray paulis, double angle, IQArray targets)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IIntrinsicExp.AdjointBody(IQArray paulis, double angle, IQArray targets)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IIntrinsicExp.ControlledBody(IQArray controls, IQArray paulis, double angle, IQArray targets)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IIntrinsicExp.ControlledAdjointBody(IQArray controls, IQArray paulis, double angle, IQArray targets)
+ {
+ throw new NotImplementedException();
+ }
+ void IIntrinsicR.Body(Pauli pauli, double angle, Qubit target)
+ {
+ if (pauli == Pauli.PauliI)
+ {
+ // Don't apply global phases on uncontrolled operations.
+ return;
+ }
+ throw new NotImplementedException("Arbitrary rotation with noise is not yet supported.");
+ }
+
+ void IIntrinsicR.AdjointBody(Pauli pauli, double angle, Qubit target)
+ {
+ (this as IIntrinsicR).Body(pauli, -angle, target);
+ }
+
+ void IIntrinsicR.ControlledBody(IQArray controls, Pauli pauli, double angle, Qubit target)
+ {
+ if (controls is { Count: 0 })
+ {
+ (this as IIntrinsicR).Body(pauli, angle, target);
+ }
+ else
+ {
+ throw new NotImplementedException("Arbitrary controlled rotation with noise is not yet supported.");
+ }
+ }
+
+ void IIntrinsicR.ControlledAdjointBody(IQArray controls, Pauli pauli, double angle, Qubit target)
+ {
+ (this as IIntrinsicR).ControlledBody(controls, pauli, -angle, target);
+ }
+
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs
new file mode 100644
index 00000000000..67a23acd2af
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs
@@ -0,0 +1,251 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NumSharp;
+
+namespace Microsoft.Quantum.Experimental
+{
+ ///
+ /// Converts instances of dtype double
+ /// and with a trailing index of length 2 to and from a JSON
+ /// serialization.
+ ///
+ ///
+ ///
+ /// The JSON representation consumed and produced by this converter is
+ /// compatible with the representation used by Rust's ndarray
+ /// and serde crates.
+ ///
+ ///
+ /// In particular, each JSON object produced by this converter has
+ /// three properties:
+ ///
+ ///
+ /// - "v": Indicates the ndarray format version being
+ /// being used. Always 1 for this implementation.
+ /// - "dim": Lists the dimensions of the array being
+ /// serialized. Will always contain one less dimension than the
+ /// original array, as one dimension represents the real and imaginary
+ /// components of each element.
+ ///
- "data": Lists the elements of the array, with the
+ /// right-most dimension varying the fastest (defined by ndarray
+ /// as "logical order", and by NumPy as "C-ordered").
+ ///
+ ///
+ public class ComplexArrayConverter : JsonConverter
+ {
+ public override NDArray Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // By the end, we need to have read "v", "dim", and "data".
+ int? version = null;
+ List? dims = null;
+ List? data = null;
+
+ // We require that the reader be in the right state to read an
+ // object.
+ if (reader.TokenType != JsonTokenType.StartObject)
+ {
+ throw new JsonException();
+ }
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndObject)
+ {
+ // We're at the end of the array, and can break out of the
+ // read loop.
+ break;
+ }
+
+ // If it's not the end of the object, the current token needs
+ // to be a property name.
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ throw new JsonException();
+ }
+
+ var propertyName = reader.GetString();
+
+ switch (propertyName)
+ {
+ case "v":
+ reader.Read();
+ version = reader.GetInt32();
+ break;
+
+ case "dim":
+ dims = new List();
+ reader.Require(JsonTokenType.StartArray);
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+ {
+ dims.Add(reader.GetInt32());
+ }
+ break;
+
+ case "data":
+ data = new List();
+ reader.Require(JsonTokenType.StartArray);
+
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+ {
+ // We expect an inner array at this point, so
+ // go on and read the begin array token.
+ reader.Require(JsonTokenType.StartArray, read: false);
+
+ reader.Read();
+ var real = reader.GetDouble();
+
+ reader.Read();
+ var imag = reader.GetDouble();
+
+ reader.Require(JsonTokenType.EndArray);
+
+ data.Add(new Complex(real, imag));
+ }
+ break;
+
+ default:
+ throw new JsonException();
+ }
+ }
+
+ // At this point, none of version, dims, or data should be null.
+ if (version == null) throw new JsonException(nameof(version));
+ if (dims == null) throw new JsonException(nameof(dims));
+ if (data == null) throw new JsonException(nameof(data));
+
+ // We now know we have everything we need to make the new array.
+ // In doing so, we allocate an ndarray with of shape (nElements, 2)
+ // where the last index represents real-vs-imag. We'll reshape
+ // it to the correct shape at the end.
+ var array = np.zeros((data.Count, 2));
+ foreach (var idx in Enumerable.Range(0, data.Count))
+ {
+ var element = data[idx];
+ array[idx, 0] = element.Real;
+ array[idx, 1] = element.Imaginary;
+ }
+ return np.reshape(array, dims.Concat(new [] { 2 }).ToArray());
+ }
+
+ public override void Write(Utf8JsonWriter writer, NDArray value, JsonSerializerOptions options)
+ {
+ // Before proceeding, check that `value` is complex-like. That is,
+ // that `value` is of dtype double, and has a trailing dimension
+ // of length 2.
+ if (!value.IsComplexLike())
+ {
+ throw new ArgumentException($"Cannot serialize ndarray, as it is not complex-like: {value}");
+ }
+
+ writer.WriteStartObject();
+ writer.WriteNumber("v", 1);
+
+ writer.WritePropertyName("dim");
+ writer.WriteStartArray();
+ foreach (var dim in value.shape[0..^1])
+ {
+ writer.WriteNumberValue(dim);
+ }
+ writer.WriteEndArray();
+
+ writer.WritePropertyName("data");
+ writer.WriteStartArray();
+ // By default, NumSharp reshapes in C-order, matching
+ // ndarray's logical ordering. Thus, we reshape down to
+ // a two-axis array, and loop over the left most axis
+ // (corresponding to elements of the serialized array),
+ // leaving the second axis (corresponding to
+ // real-vs-imag).
+ var nElements = value.shape[0..^1].Aggregate((acc, dim) => acc * dim);
+ var flattened = value.reshape((nElements, 2));
+ foreach (var idx in Enumerable.Range(0, flattened.shape[0]))
+ {
+ var element = flattened[idx];
+ // Each element is a JSON array `[real, imag]`.
+ writer.WriteStartArray();
+ writer.WriteNumberValue((double) element[0]);
+ writer.WriteNumberValue((double) element[1]);
+ writer.WriteEndArray();
+ }
+ writer.WriteEndArray();
+ writer.WriteEndObject();
+ }
+
+ public static (int nQubits, string kind, NDArray data) ReadQubitSizedArray(ref Utf8JsonReader reader, JsonSerializerOptions options)
+ {
+ var arrayConverter = new ComplexArrayConverter();
+ int? nQubits = null;
+ NDArray? data = null;
+ string? kind = null;
+
+ // We require that the reader be in the right state to read an
+ // object.
+ if (reader.TokenType != JsonTokenType.StartObject)
+ {
+ throw new JsonException();
+ }
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndObject)
+ {
+ // We're at the end of the array, and can break out of the
+ // read loop.
+ break;
+ }
+
+ // If it's not the end of the object, the current token needs
+ // to be a property name.
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ throw new JsonException();
+ }
+
+ var propertyName = reader.GetString();
+
+ switch (propertyName)
+ {
+ case "n_qubits":
+ reader.Read();
+ nQubits = reader.GetInt32();
+ break;
+
+ case "data":
+ // Here, we expect an object with one property indicating
+ // the kind of state. The value of that property can
+ // be read using the complexarrayconverter from above.
+ reader.Require(JsonTokenType.StartObject);
+ reader.Require(JsonTokenType.PropertyName);
+ kind = reader.GetString();
+
+ // Advance the reader onto the array itself and use
+ // the converter.
+ reader.Read();
+ data = arrayConverter.Read(ref reader, typeof(NDArray), options);
+
+ // Finally, require an end to the object.
+ reader.Require(JsonTokenType.EndObject);
+ break;
+
+ default:
+ throw new JsonException($"Unexpected property name {propertyName}.");
+ }
+ }
+
+ if (nQubits == null) throw new JsonException(nameof(nQubits));
+ if (data == null) throw new JsonException(nameof(data));
+ if (kind == null) throw new JsonException(nameof(kind));
+
+ return (nQubits.Value, kind!, data!);
+ }
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs
new file mode 100644
index 00000000000..85900a3e4a7
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs
@@ -0,0 +1,132 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.Quantum.Experimental
+{
+
+ public class ChpOperationConverter : JsonConverter
+ {
+ public override ChpOperation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ reader.Require(JsonTokenType.StartObject, read: false);
+ reader.Require(JsonTokenType.PropertyName);
+ ChpOperation? operation = null;
+ ulong idx;
+ switch (reader.GetString())
+ {
+ case "Cnot":
+ var idxs = JsonSerializer.Deserialize>(ref reader);
+ operation = new ChpOperation.Cnot
+ {
+ IdxControl = idxs[0],
+ IdxTarget = idxs[1]
+ };
+ break;
+
+ case "Hadamard":
+ reader.Read();
+ idx = reader.GetUInt64();
+ operation = new ChpOperation.Hadamard
+ {
+ IdxTarget = idx
+ };
+ break;
+
+ case "Phase":
+ reader.Read();
+ idx = reader.GetUInt64();
+ operation = new ChpOperation.Phase
+ {
+ IdxTarget = idx
+ };
+ break;
+
+ case "AdjointPhase":
+ reader.Read();
+ idx = reader.GetUInt64();
+ operation = new ChpOperation.AdjointPhase
+ {
+ IdxTarget = idx
+ };
+ break;
+ }
+ if (operation == null)
+ {
+ throw new JsonException("Did not read an operation.");
+ }
+ reader.Require(JsonTokenType.EndObject);
+ return operation;
+ }
+
+ public override void Write(Utf8JsonWriter writer, ChpOperation value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WritePropertyName(
+ value switch
+ {
+ ChpOperation.Cnot _ => "Cnot",
+ ChpOperation.Hadamard _ => "Hadamard",
+ ChpOperation.Phase _ => "Phase",
+ ChpOperation.AdjointPhase _ => "AdjointPhase",
+ _ => throw new JsonException()
+ }
+ );
+ if (value is ChpOperation.Cnot cnot)
+ {
+ writer.WriteStartArray();
+ writer.WriteNumberValue(cnot.IdxControl);
+ writer.WriteNumberValue(cnot.IdxTarget);
+ writer.WriteEndArray();
+ }
+ else
+ {
+ writer.WriteNumberValue(
+ value switch
+ {
+ ChpOperation.AdjointPhase { IdxTarget: var target } => target,
+ ChpOperation.Phase { IdxTarget: var target } => target,
+ ChpOperation.Hadamard { IdxTarget: var target } => target,
+ _ => throw new JsonException()
+ }
+ );
+ }
+ writer.WriteEndObject();
+ }
+ }
+
+ [JsonConverter(typeof(ChpOperationConverter))]
+ public abstract class ChpOperation
+ {
+ private ChpOperation()
+ { }
+
+ public class Cnot : ChpOperation
+ {
+ public ulong IdxControl { get; set; }
+ public ulong IdxTarget { get; set; }
+ }
+
+ public class Hadamard : ChpOperation
+ {
+ public ulong IdxTarget { get; set; }
+ }
+
+ public class Phase : ChpOperation
+ {
+ public ulong IdxTarget { get; set; }
+ }
+
+ public class AdjointPhase : ChpOperation
+ {
+ public ulong IdxTarget { get; set; }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/DelegatedConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/DelegatedConverter.cs
new file mode 100644
index 00000000000..7ec7db03fa9
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/DelegatedConverter.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NumSharp;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public class DelegatedConverter : Newtonsoft.Json.JsonConverter
+ {
+ public override T ReadJson(JsonReader reader, Type objectType, [AllowNull] T existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
+ {
+ var serialized = JToken.ReadFrom(reader).ToString();
+ return System.Text.Json.JsonSerializer.Deserialize(serialized);
+ }
+
+ public override void WriteJson(JsonWriter writer, [AllowNull] T value, Newtonsoft.Json.JsonSerializer serializer)
+ {
+ var serialized = System.Text.Json.JsonSerializer.Serialize(value);
+ var deserialized = Newtonsoft.Json.Linq.JToken.Parse(serialized);
+ deserialized.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs
new file mode 100644
index 00000000000..670234325ab
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs
@@ -0,0 +1,164 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Text.Json;
+using System.Collections.Generic;
+using System.Linq;
+using NumSharp;
+using System;
+
+namespace Microsoft.Quantum.Experimental
+{
+ internal delegate Func ReaderContinuation(ref Utf8JsonReader reader, string variant);
+ internal static class Extensions
+ {
+ internal static bool HasProperty(this JsonElement element, string propertyName) =>
+ element.TryGetProperty(propertyName, out var _);
+
+ internal static string AsLaTeXMatrixOfComplex(this NDArray array) =>
+ // NB: Assumes 𝑛 × 𝑛 × 2 array, where the trailing index is
+ // [real, imag].
+ // TODO: Consolidate with logic at:
+ // https://github.com/microsoft/QuantumLibraries/blob/505fc27383c9914c3e1f60fb63d0acfe60b11956/Visualization/src/DisplayableUnitaryEncoders.cs#L43
+ string.Join(
+ "\\\\\n",
+ Enumerable
+ .Range(0, array.Shape[0])
+ .Select(
+ idxRow => string.Join(" & ",
+ Enumerable
+ .Range(0, array.Shape[1])
+ .Select(
+ idxCol => $"{array[idxRow, idxCol, 0]} + {array[idxRow, idxCol, 1]} i"
+ )
+ )
+ )
+ );
+
+ internal static IEnumerable IterateOverLeftmostIndex(this NDArray array)
+ {
+ foreach (var idx in Enumerable.Range(0, array.shape[0]))
+ {
+ yield return array[idx, Slice.Ellipsis];
+ }
+ }
+
+ internal static string AsTextMatrixOfComplex(this NDArray array, string rowSep = "\n") =>
+ // NB: Assumes 𝑛 × 𝑛 × 2 array, where the trailing index is
+ // [real, imag].
+ // TODO: Consolidate with logic at:
+ // https://github.com/microsoft/QuantumLibraries/blob/505fc27383c9914c3e1f60fb63d0acfe60b11956/Visualization/src/DisplayableUnitaryEncoders.cs#L43
+ "[" + rowSep + string.Join(
+ rowSep,
+ Enumerable
+ .Range(0, array.Shape[0])
+ .Select(
+ idxRow => "[" + string.Join(", ",
+ Enumerable
+ .Range(0, array.Shape[1])
+ .Select(
+ idxCol => $"{array[idxRow, idxCol, 0]} + {array[idxRow, idxCol, 1]} i"
+ )
+ ) + "]"
+ )
+ ) + rowSep + "]";
+
+ public static void Require(this ref Utf8JsonReader reader, JsonTokenType type, bool read = true)
+ {
+ if (read) reader.Read();
+ if (reader.TokenType != type)
+ {
+ // Try to read what it actually was.
+ string? value = reader.TokenType switch
+ {
+ JsonTokenType.String => reader.GetString(),
+ JsonTokenType.Number => reader.GetDecimal().ToString(),
+ JsonTokenType.True => "true",
+ JsonTokenType.False => "false",
+ JsonTokenType.Null => "null",
+ JsonTokenType.PropertyName => reader.GetString(),
+ _ => null
+ };
+ throw new JsonException($"Expected a JSON token of type {type}, got {reader.TokenType} instead.{(value == null ? "" : $"\nValue: {value}")}");
+ }
+ }
+ public static bool IsComplexLike(this NDArray array) =>
+ array.dtype == typeof(double) &&
+ array.shape[^1] == 2;
+
+ public static Func Bind(this TInput input, Func action) =>
+ (completion) => action(completion, input);
+
+ internal static TResult ReadQubitSizedData(this ref Utf8JsonReader reader, ReaderContinuation readData)
+ {
+
+ reader.Require(JsonTokenType.StartObject, read: false);
+
+ int? nQubits = null;
+ Func? completion = null;
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndObject)
+ {
+ // We're at the end of the object, and can break out of the
+ // read loop.
+ break;
+ }
+
+ // If it's not the end of the object, the current token needs
+ // to be a property name.
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ throw new JsonException();
+ }
+
+ var propertyName = reader.GetString();
+
+ switch (propertyName)
+ {
+ case "n_qubits":
+ reader.Read();
+ nQubits = reader.GetInt32();
+ break;
+
+ case "data":
+ // In most cases, we expect an object with one property
+ // indicating
+ // the kind of data for the object.
+ // For variants of type unit, it's just the string.
+ reader.Read();
+ if (reader.TokenType == JsonTokenType.StartObject)
+ {
+ reader.Require(JsonTokenType.PropertyName);
+ var kind = reader.GetString();
+
+ reader.Read();
+ completion = readData(ref reader, kind);
+
+ // Finally, require an end to the object.
+ reader.Require(JsonTokenType.EndObject);
+ }
+ else if (reader.TokenType == JsonTokenType.String)
+ {
+ var kind = reader.GetString();
+ completion = readData(ref reader, kind);
+ }
+ else
+ {
+ throw new JsonException($"Expected either the start of an object or a string.");
+ }
+ break;
+
+ default:
+ throw new JsonException($"Unexpected property name {propertyName}.");
+ }
+ }
+
+ if (nQubits == null) throw new JsonException(nameof(nQubits));
+ if (completion == null) throw new JsonException();
+
+ return completion(nQubits.Value);
+ }
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs
new file mode 100644
index 00000000000..f99737d0a54
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs
@@ -0,0 +1,92 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NumSharp;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public class InstrumentConverter : JsonConverter
+ {
+ public override Instrument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // We use the technique at
+ // https://stackoverflow.com/questions/58074304/is-polymorphic-deserialization-possible-in-system-text-json/59744873#59744873
+ // to implement polymorphic deserialization, based on the property name.
+ reader.Require(JsonTokenType.StartObject, read: false);
+ reader.Require(JsonTokenType.PropertyName);
+
+ var variant = reader.GetString();
+
+ Instrument result = variant switch
+ {
+ "Effects" => new EffectsInstrument
+ {
+ Effects = JsonSerializer.Deserialize>(ref reader)
+ },
+ "ZMeasurement" => JsonSerializer.Deserialize(ref reader),
+ _ => throw new JsonException($"Enum variant {variant} not yet supported.")
+ };
+
+ reader.Require(JsonTokenType.EndObject);
+
+ return result;
+ }
+
+ public override void Write(Utf8JsonWriter writer, Instrument value, JsonSerializerOptions options)
+ {
+
+ switch (value)
+ {
+ case EffectsInstrument effectsInstrument:
+ writer.WriteStartObject();
+ writer.WritePropertyName("Effects");
+ JsonSerializer.Serialize(writer, effectsInstrument.Effects);
+ writer.WriteEndObject();
+ break;
+
+ case ZMeasurementInstrument zInstrument:
+ writer.WriteStartObject();
+ writer.WritePropertyName("ZMeasurement");
+ JsonSerializer.Serialize(writer, zInstrument);
+ writer.WriteEndObject();
+ break;
+
+ default:
+ throw new JsonException($"Enum variant {value.GetType()} not yet supported.");
+ }
+
+ }
+ }
+
+ [JsonConverter(typeof(InstrumentConverter))]
+ public abstract class Instrument
+ {
+
+ }
+
+ public class EffectsInstrument : Instrument
+ {
+ [JsonPropertyName("effects")]
+ public IList Effects { get; set; } = new List();
+
+ public override string ToString() =>
+ $"Instrument {{ Effects = {String.Join(", ", Effects.Select(effect => effect.ToString()))} }}";
+ }
+
+ public class ZMeasurementInstrument : Instrument
+ {
+ [JsonPropertyName("pr_readout_error")]
+ public double PrReadoutError { get; set; } = 0.0;
+
+ public override string ToString() =>
+ $"Instrument {{ Z measurement with readout error = {PrReadoutError} }}";
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs
new file mode 100644
index 00000000000..79c122a102d
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NumSharp;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Quantum.Experimental
+{
+ // NB: To make this compatible with Newtonsoft.Json as well as
+ // System.Text.Json, we use a Newtonsoft converter that delegates to
+ // S.T.Json.
+ [Newtonsoft.Json.JsonConverter(typeof(DelegatedConverter))]
+ public class NoiseModel
+ {
+ [JsonPropertyName("initial_state")]
+ public State? InitialState { get; set; }
+
+ [JsonPropertyName("cnot")]
+ public Process? Cnot { get; set; }
+
+ [JsonPropertyName("i")]
+ public Process? I { get; set; }
+
+ [JsonPropertyName("s")]
+ public Process? S { get; set; }
+
+ [JsonPropertyName("s_adj")]
+ public Process? SAdj { get; set; }
+
+ [JsonPropertyName("t")]
+ public Process? T { get; set; }
+
+ [JsonPropertyName("t_adj")]
+ public Process? TAdj { get; set; }
+
+ [JsonPropertyName("h")]
+ public Process? H { get; set; }
+
+ [JsonPropertyName("x")]
+ public Process? X { get; set; }
+
+ [JsonPropertyName("y")]
+ public Process? Y { get; set; }
+
+ [JsonPropertyName("z")]
+ public Process? Z { get; set; }
+
+ [JsonPropertyName("z_meas")]
+ public Instrument? ZMeas { get; set; }
+
+ public static bool TryGetByName(string name, [NotNullWhen(true)] out NoiseModel? model)
+ {
+ try
+ {
+ model = NativeInterface.GetNoiseModelByName(name);
+ return true;
+ }
+ catch (SimulationException)
+ {
+ model = null;
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs
new file mode 100644
index 00000000000..cde27687137
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs
@@ -0,0 +1,263 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.Quantum.Simulation.Core;
+using NumSharp;
+using static System.Math;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public class ProcessConverter : JsonConverter
+ {
+ private static IList<(double, IList)> ReadMixedPauliData(ref Utf8JsonReader reader)
+ {
+ var results = new List<(double, IList)>();
+ reader.Require(JsonTokenType.StartArray, read: false);
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray)
+ {
+ break;
+ }
+
+ reader.Require(JsonTokenType.StartArray, read: false);
+ reader.Read();
+ var p = reader.GetDouble();
+ var ops = new List();
+ reader.Require(JsonTokenType.StartArray);
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray)
+ {
+ break;
+ }
+
+ ops.Add(reader.GetString() switch
+ {
+ "I" => Pauli.PauliI,
+ "X" => Pauli.PauliX,
+ "Y" => Pauli.PauliY,
+ "Z" => Pauli.PauliZ,
+ var unknown => throw new JsonException($"Expected I, X, Y, or Z for a Pauli value, but got {unknown}.")
+ });
+ }
+ reader.Require(JsonTokenType.EndArray);
+
+ results.Add((p, ops));
+ }
+ return results;
+ }
+
+ public override Process Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ reader.Require(JsonTokenType.StartObject, read: false);
+
+ var arrayConverter = new ComplexArrayConverter();
+ return reader.ReadQubitSizedData((ref Utf8JsonReader reader, string kind) =>
+ kind switch
+ {
+ "Unitary" => arrayConverter.Read(ref reader, typeof(NDArray), options).Bind(
+ (int nQubits, NDArray data) => new UnitaryProcess(nQubits, data)
+ ),
+ "KrausDecomposition" => arrayConverter.Read(ref reader, typeof(NDArray), options).Bind(
+ (int nQubits, NDArray data) => new KrausDecompositionProcess(nQubits, data)
+ ),
+ "MixedPauli" => ReadMixedPauliData(ref reader).Bind(
+ (int nQubits, IList<(double, IList)> data) => new MixedPauliProcess(nQubits, data)
+ ),
+ "Sequence" => JsonSerializer.Deserialize>(ref reader).Bind(
+ (int nQubits, IList processes) => new SequenceProcess(nQubits, processes)
+ ),
+ "ChpDecomposition" => JsonSerializer.Deserialize>(ref reader).Bind(
+ (int nQubits, IList operations) => new ChpDecompositionProcess(nQubits, operations)
+ ),
+ "Unsupported" => (null as object).Bind(
+ (int nQubits, object? _) => new UnsupportedProcess(nQubits)
+ ),
+ _ => throw new JsonException($"Process kind {kind} not supported for deserialization.")
+ }
+ );
+ }
+
+ public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerOptions options)
+ {
+ var arrayConverter = new ComplexArrayConverter();
+
+ writer.WriteStartObject();
+ writer.WriteNumber("n_qubits", value.NQubits);
+
+ writer.WritePropertyName("data");
+ if (value is UnsupportedProcess)
+ {
+ writer.WriteStringValue("Unsupported");
+ }
+ else
+ {
+ writer.WriteStartObject();
+ writer.WritePropertyName(
+ value switch
+ {
+ UnitaryProcess _ => "Unitary",
+ KrausDecompositionProcess _ => "KrausDecomposition",
+ MixedPauliProcess _ => "MixedPauli",
+ SequenceProcess _ => "Sequence",
+ ChpDecompositionProcess _ => "ChpDecomposition",
+ _ => throw new JsonException()
+ }
+ );
+
+ if (value is ArrayProcess { Data: var data })
+ {
+ arrayConverter.Write(writer, data, options);
+ }
+ else if (value is MixedPauliProcess mixedPauliProcess)
+ {
+ writer.WriteStartArray();
+ foreach (var op in mixedPauliProcess.Operators)
+ {
+ writer.WriteStartArray();
+ writer.WriteNumberValue(op.Item1);
+ writer.WriteStartArray();
+ foreach (var p in op.Item2)
+ {
+ writer.WriteStringValue(p switch
+ {
+ Pauli.PauliI => "I",
+ Pauli.PauliX => "X",
+ Pauli.PauliY => "Y",
+ Pauli.PauliZ => "Z",
+ var unknown => throw new JsonException($"Unexpected Pauli value {unknown}.")
+ });
+ }
+ writer.WriteEndArray();
+ writer.WriteEndArray();
+ }
+ writer.WriteEndArray();
+ }
+ else if (value is ChpDecompositionProcess chpDecomposition)
+ {
+ JsonSerializer.Serialize(writer, chpDecomposition.Operations);
+ }
+ else if (value is SequenceProcess sequence)
+ {
+ JsonSerializer.Serialize(writer, sequence.Processes);
+ }
+ writer.WriteEndObject();
+ }
+ writer.WriteEndObject();
+ }
+ }
+
+ [JsonConverter(typeof(ProcessConverter))]
+ public abstract class Process
+ {
+ [JsonPropertyName("n_qubits")]
+ public int NQubits { get; }
+
+ internal Process(int nQubits)
+ {
+ NQubits = nQubits;
+ }
+ }
+
+ public abstract class ArrayProcess : Process
+ {
+ [JsonPropertyName("data")]
+ public NDArray Data { get; }
+
+ internal ArrayProcess(int nQubits, NDArray data) : base(nQubits)
+ {
+ this.Data = data;
+ }
+ }
+
+ public class MixedPauliProcess : Process
+ {
+ public IList<(double, IList)> Operators;
+
+ internal MixedPauliProcess(int nQubits, IList<(double, IList)> operators) : base(nQubits)
+ {
+ this.Operators = operators;
+ }
+ }
+
+ public class ChpDecompositionProcess : Process
+ {
+ public IList Operations;
+
+ internal ChpDecompositionProcess(int nQubits, IList operations) : base(nQubits)
+ {
+ this.Operations = operations;
+ }
+ }
+
+ public class SequenceProcess : Process
+ {
+ public IList Processes;
+
+ internal SequenceProcess(int nQubits, IList processes) : base(nQubits)
+ {
+ this.Processes = processes;
+ }
+ }
+
+ public class UnsupportedProcess : Process
+ {
+ internal UnsupportedProcess(int nQubits) : base(nQubits)
+ { }
+ }
+
+ public class UnitaryProcess : ArrayProcess
+ {
+ public UnitaryProcess(int nQubits, NDArray data) : base(nQubits, data)
+ {
+ // Unitary matrices should be of dimension (2^n, 2^n, 2), with the last
+ // index indicating real-vs-imag.
+ var dim = (int) Pow(2, nQubits);
+ if (data.shape.Length != 3 || data.shape[0] != dim || data.shape[1] != dim || data.shape[2] != 2)
+ {
+ throw new ArgumentException("Expected (2^nQubits, 2) array.", nameof(data));
+ }
+ }
+
+ public override string ToString() =>
+ $@"Unitary process on {NQubits} qubits: {Data.AsTextMatrixOfComplex(rowSep: " ")}";
+ }
+ public class KrausDecompositionProcess : ArrayProcess
+ {
+ public KrausDecompositionProcess(int nQubits, NDArray data) : base(nQubits, data)
+ {
+ // Kraus decompositions should have between 1 and 4^n operators,
+ // each of which should be 2^n by 2^n, for a final dims of
+ // [k, 2^n, 2^n, 2] where 1 ≤ k ≤ 4^n.
+ var dim = (int) Pow(2, nQubits);
+ var superDim = dim * dim;
+
+ if (data.shape.Length != 4 || data.shape[0] > superDim || data.shape[0] < 1
+ || data.shape[1] != dim
+ || data.shape[2] != dim
+ || data.shape[3] != 2)
+ {
+ throw new ArgumentException("Expected (k, 2^nQubits, 2^nQubits, 2) array.", nameof(data));
+ }
+ }
+
+ public override string ToString()
+ {
+ var ops = String.Join(",",
+ Data.IterateOverLeftmostIndex()
+ .Select(op => op.AsTextMatrixOfComplex(rowSep: " "))
+ );
+ return $@"Kraus decomposition of process on {NQubits} qubits: {ops}";
+ }
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/README.md b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/README.md
new file mode 100644
index 00000000000..8fa99c08df3
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/README.md
@@ -0,0 +1,9 @@
+# Open systems simulation data model
+
+The C# classes in this folder define a data model and JSON serialization logic that can be used to exchange data with the native runtime for the open systems simulator.
+
+## Newtonsoft.Json versus System.Text.Json
+
+The JSON converters in this folder target System.Text.Json as this is the recommended path for .NET Core 3.1 and forward, both in terms of where new functionality will be developed, and in terms of performance improvements due to `Span` and other recent additions to the .NET platform.
+
+Since IQ# and many other Quantum Development Kit components use Newtonsoft.Json, however, the `DelegatedConverter` class allows for exposing a System.Text.Json converter as a Newtonsoft.Json converter; this has a significant performance implication, but provides a path forward for using System.Text.Json in the future.
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs
new file mode 100644
index 00000000000..439e3570b81
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NumSharp;
+using static System.Math;
+
+namespace Microsoft.Quantum.Experimental
+{
+ [JsonConverter(typeof(StateConverter))]
+ public abstract class State
+ {
+ [JsonPropertyName("n_qubits")]
+ public int NQubits { get; set; } = 1;
+
+ internal State()
+ { }
+
+ internal State(int nQubits)
+ {
+ NQubits = nQubits;
+ }
+ }
+
+ public class StabilizerState : State
+ {
+ // Design note:
+ // We could use the same array converter as used of complex-like arrays
+ // of floats, but it's a bit easier in this case to directly
+ // deserialize into a type that represents the underlying data that
+ // we need.
+ public class TableArray
+ {
+ // When serializing multidimensional arrays with serde, the
+ // `ndarray` crate for Rust uses the "v" property to denote
+ // serialization schema versions. This property name is hardcoded
+ // at https://github.com/rust-ndarray/ndarray/blob/master/src/array_serde.rs#L96,
+ // such that we follow that property name here to make it easier
+ // to interoperate with `ndarray`.
+ [JsonPropertyName("v")]
+ public int SchemaVersion { get; set; } = 1;
+
+ [JsonPropertyName("dim")]
+ public List? Dimensions { get; set; }
+
+ [JsonPropertyName("data")]
+ public List? Data { get; set; }
+
+ [JsonIgnore]
+ public NDArray? AsArray =>
+ Dimensions == null || Data == null
+ ? null
+ : np.ndarray(
+ new Shape(Dimensions.ToArray()),
+ typeof(bool),
+ Data.ToArray()
+ );
+ }
+
+ [JsonPropertyName("table")]
+ public TableArray? Table { get; set; }
+
+ [JsonIgnore]
+ public NDArray? Data => Table?.AsArray;
+ }
+
+ public abstract class ArrayState : State
+ {
+ public NDArray Data { get; }
+
+ internal ArrayState(int nQubits, NDArray data) : base(nQubits)
+ {
+ this.Data = data;
+ }
+ }
+
+ public class PureState : ArrayState
+ {
+ public PureState(int nQubits, NDArray data) : base(nQubits, data)
+ {
+ // Pure states should be of dimension (2^n, 2), with the last
+ // index indicating real-vs-imag.
+ if (data.shape.Length != 2 || data.shape[0] != (int) (Pow(2, nQubits)) || data.shape[1] != 2)
+ {
+ throw new ArgumentException("Expected (2^nQubits, 2) array.", nameof(data));
+ }
+ }
+
+ // TODO: Override ToString here!
+ }
+
+ public class MixedState : ArrayState
+ {
+ public MixedState(int nQubits, NDArray data) : base(nQubits, data)
+ {
+ // Pure states should be of dimension (2^n, 2^n, 2), with the last
+ // index indicating real-vs-imag.
+ var dim = (int) Pow(2, nQubits);
+ if (data.shape.Length != 3 || data.shape[0] != dim || data.shape[1] != dim || data.shape[2] != 2)
+ {
+ throw new ArgumentException("Expected (2^nQubits, 2) array.", nameof(data));
+ }
+ }
+
+ public override string ToString() =>
+ $@"Mixed state on {NQubits} qubits: {Data.AsTextMatrixOfComplex(rowSep: " ")}";
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs
new file mode 100644
index 00000000000..22c1fa3867c
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NumSharp;
+using static System.Math;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public class StateConverter : JsonConverter
+ {
+
+ /*
+ We expect JSON of the form:
+
+ {
+ "n_qubits": 1,
+ "data": {
+ "Mixed": {
+ "v": 1,
+ "dim": [2, 2],
+ "data": [
+ [1, 0],
+ [0, 0],
+ [0, 0],
+ [0, 0]
+ ]
+ }
+ }
+ }
+
+ Here, the presence of either $.data.Mixed or $.data.Pure tells us how
+ to interpret data.
+ */
+
+ public override State Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ reader.Require(JsonTokenType.StartObject, read: false);
+
+ var arrayConverter = new ComplexArrayConverter();
+ return reader.ReadQubitSizedData((ref Utf8JsonReader reader, string kind) =>
+ kind switch
+ {
+ "Pure" => arrayConverter.Read(ref reader, typeof(NDArray), options).Bind(
+ (int nQubits, NDArray data) => new PureState(nQubits, data)
+ ),
+ "Mixed" => arrayConverter.Read(ref reader, typeof(NDArray), options).Bind(
+ (int nQubits, NDArray data) => new MixedState(nQubits, data)
+ ),
+ "Stabilizer" => JsonSerializer.Deserialize(ref reader).Bind(
+ (int nQubits, StabilizerState state) =>
+ {
+ System.Diagnostics.Debug.Assert((state?.Data as object) != null);
+ System.Diagnostics.Debug.Assert(nQubits == state.NQubits);
+ return state;
+ }
+ ),
+ _ => throw new JsonException($"Unknown state kind {kind}.")
+ }
+ );
+ }
+
+ public override void Write(Utf8JsonWriter writer, State value, JsonSerializerOptions options)
+ {
+ var arrayConverter = new ComplexArrayConverter();
+
+ writer.WriteStartObject();
+ writer.WriteNumber("n_qubits", value.NQubits);
+
+ writer.WritePropertyName("data");
+ writer.WriteStartObject();
+ writer.WritePropertyName(
+ value switch
+ {
+ PureState _ => "Pure",
+ MixedState _ => "Mixed",
+ StabilizerState _ => "Stabilizer",
+ _ => throw new JsonException($"Unknown state type {value.GetType()}.")
+ }
+ );
+
+ if (value is ArrayState { Data: var data })
+ {
+ arrayConverter.Write(writer, data, options);
+ }
+ else if (value is StabilizerState stabilizerState)
+ {
+ var array = new StabilizerState.TableArray
+ {
+ Data = stabilizerState.Data.flat.ToArray().ToList(),
+ Dimensions = stabilizerState.Data.Shape.Dimensions.ToList()
+ };
+ writer.WriteStartObject();
+ writer.WritePropertyName("n_qubits");
+ writer.WriteNumberValue(stabilizerState.NQubits);
+ writer.WritePropertyName("table");
+ JsonSerializer.Serialize(writer, array);
+ writer.WriteEndObject();
+ }
+ writer.WriteEndObject();
+ writer.WriteEndObject();
+ }
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/ApplyWithLessControls.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/ApplyWithLessControls.qs
new file mode 100644
index 00000000000..115ea5744f4
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/ApplyWithLessControls.qs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// NB: Copied from Utils.qs.
+namespace Microsoft.Quantum.Experimental.Decompositions {
+ open Microsoft.Quantum.Experimental.Native as Native;
+
+ /// Given a multiply-controlled operation that requires k controls
+ /// applies it using ceiling(k/2) controls and using floor(k/2) temporary qubits
+ operation ApplyWithLessControlsA<'T> (op : ((Qubit[],'T) => Unit is Adj), (controls : Qubit[], arg : 'T)) : Unit is Adj {
+ let numControls = Length(controls);
+ let numControlPairs = numControls / 2;
+ use temps = Qubit[numControlPairs] {
+ within {
+ for numPair in 0 .. numControlPairs - 1 { // constant depth
+ PhaseCCX(controls[2 * numPair], controls[2 * numPair + 1], temps[numPair]);
+ }
+ }
+ apply {
+ let newControls = numControls % 2 == 0 ? temps | temps + [controls[numControls - 1]];
+ op(newControls, arg);
+ }
+ }
+ }
+
+ operation PhaseCCX (control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
+ // https://arxiv.org/pdf/1210.0974.pdf#page=2
+ Native.H(target);
+ Native.CNOT(target,control1);
+ Native.CNOT(control1,control2);
+ Native.T(control2);
+ Adjoint Native.T(control1);
+ Native.T(target);
+ Native.CNOT(target,control1);
+ Native.CNOT(control1,control2);
+ Adjoint Native.T(control2);
+ Native.CNOT(target,control2);
+ Native.H(target);
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/ControlledH.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/ControlledH.qs
new file mode 100644
index 00000000000..147e496c0e8
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/ControlledH.qs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.Quantum.Experimental.Decompositions {
+ open Microsoft.Quantum.Experimental.Native as Native;
+ open Microsoft.Quantum.Experimental.Intrinsic as Intrinsic;
+
+ operation PauliZFlip(basis : Pauli, target : Qubit) : Unit
+ is Adj {
+ if basis == PauliI {
+ fail $"PauliX cannot be mapped to PauliI using conjugation by Clifford";
+ } elif basis == PauliX {
+ Native.H(target);
+ } elif basis == PauliY {
+ Adjoint Native.S(target);
+ Native.H(target);
+ } elif basis != PauliZ {
+ fail $"PauliZ must be the only remaining case";
+ }
+ }
+
+ operation Ty(target : Qubit) : Unit
+ is Adj {
+ within {
+ PauliZFlip(PauliY, target);
+ } apply {
+ Native.T(target);
+ }
+ }
+
+ operation ApplyControlledH(controls : Qubit[], target : Qubit) : Unit
+ is Adj {
+ if Length(controls) == 1 {
+ within {
+ Adjoint Ty(target);
+ } apply {
+ Controlled Intrinsic.Z([controls[0]], target);
+ }
+ } else {
+ ApplyWithLessControlsA(Controlled Intrinsic.H, (controls, target));
+ }
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/IntrinsicInterface.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/IntrinsicInterface.qs
new file mode 100644
index 00000000000..e21ec2a5f46
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/IntrinsicInterface.qs
@@ -0,0 +1,212 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// This namespace contains definitions that parallel each operation in
+// M.Q.Intrinsic, making it easier to forward intrinsic definitions from the
+// simulator.
+
+namespace Microsoft.Quantum.Experimental.Intrinsic {
+ open Microsoft.Quantum.Experimental.Native as Native;
+ open Microsoft.Quantum.Experimental.Decompositions;
+
+ operation H(target : Qubit) : Unit is Adj + Ctl {
+ body (...) {
+ Native.H(target);
+ }
+
+ controlled (controls, ...) {
+ if Length(controls) == 0 {
+ H(target);
+ } else {
+ ApplyControlledH(controls, target);
+ }
+ }
+ }
+
+ operation X(target : Qubit) : Unit is Adj + Ctl {
+ body (...) {
+ Native.X(target);
+ }
+
+ controlled (controls, ...) {
+ if Length(controls) == 0 {
+ X(target);
+ } elif Length(controls) == 1 {
+ Native.CNOT(controls[0], target);
+ } elif Length(controls) == 2 {
+ // Forward to decomposition of CCZ.
+ within {
+ H(target);
+ } apply {
+ Controlled Z(controls, target);
+ }
+ } else {
+ ApplyWithLessControlsA(Controlled X, (controls, target));
+ }
+ }
+ }
+
+ operation Y(target : Qubit) : Unit is Adj + Ctl {
+ body (...) {
+ Native.Y(target);
+ }
+
+ controlled (controls, ...) {
+ if Length(controls) == 0 {
+ Y(target);
+ } elif Length(controls) == 1 {
+ within {
+ Native.H(target);
+ Native.S(target);
+ } apply {
+ Native.CNOT(controls[0], target);
+ }
+ } elif Length(controls) == 2 {
+ within {
+ within {
+ Native.H(target);
+ } apply {
+ Native.S(target);
+ }
+ } apply {
+ Controlled Z(controls, target);
+ }
+ } else {
+ ApplyWithLessControlsA(Controlled Y, (controls, target));
+ }
+ }
+ }
+
+ operation Z(target : Qubit) : Unit is Adj + Ctl {
+ body (...) {
+ Native.Z(target);
+ }
+
+ controlled (controls, ...) {
+ if Length(controls) == 0 {
+ Native.Z(target);
+ } elif Length(controls) == 1 {
+ within {
+ H(target);
+ } apply {
+ Native.CNOT(controls[0], target);
+ }
+ } elif Length(controls) == 2 {
+ // [Page 15 of arXiv:1206.0758v3](https://arxiv.org/pdf/1206.0758v3.pdf#page=15)
+ Adjoint Native.T(controls[0]);
+ Adjoint Native.T(controls[1]);
+ Native.CNOT(target, controls[0]);
+ Native.T(controls[0]);
+ Native.CNOT(controls[1], target);
+ Native.CNOT(controls[1], controls[0]);
+ Native.T(target);
+ Adjoint Native.T(controls[0]);
+ Native.CNOT(controls[1], target);
+ Native.CNOT(target, controls[0]);
+ Adjoint Native.T(target);
+ Native.T(controls[0]);
+ Native.CNOT(controls[1], controls[0]);
+ } else {
+ ApplyWithLessControlsA(Controlled Z, (controls, target));
+ }
+ }
+ }
+
+ operation S(target : Qubit) : Unit is Adj + Ctl {
+ body (...) {
+ Native.S(target);
+ }
+
+ controlled (controls, ...) {
+ if Length(controls) == 0 {
+ Native.S(target);
+ } elif (Length(controls) == 1) {
+ Native.T(controls[0]);
+ Native.T(target);
+ within {
+ Native.CNOT(controls[0], target);
+ } apply {
+ Adjoint Native.T(target);
+ }
+ } else {
+ ApplyWithLessControlsA(Controlled S, (controls, target));
+ }
+ }
+ }
+
+ operation T(target : Qubit) : Unit is Adj + Ctl {
+ body (...) {
+ Native.T(target);
+ }
+
+ controlled (controls, ...) {
+ if Length(controls) == 0 {
+ Native.T(target);
+ } else {
+ // TODO: Decompositions of `Controlled T` currently used in Q#
+ // target packages rely on R1Frac, which is not yet
+ // implemented in experimental simulation.
+ fail "Controlled T operations are not yet supported.";
+ }
+ }
+ }
+
+ // NB: We separate out this operation to avoid hardware targeting rewrite
+ // steps from trying to lift this operation and modifying the C#
+ // code gen in the process.
+ // See https://github.com/microsoft/qsharp-compiler/issues/768.
+ internal operation MeasureWithoutPauliI(bases : Pauli[], register : Qubit[]) : Result {
+ mutable newBases = [];
+ mutable newQubits = [];
+ // NB: using Zipped would be nice here, but this is built before
+ // M.Q.Standard...
+ for idxBasis in 0..Length(bases) - 1 {
+ if bases[idxBasis] != PauliI {
+ set newBases += [bases[idxBasis]];
+ set newQubits += [register[idxBasis]];
+ }
+ }
+
+ if Length(newBases) == 0 {
+ return Zero;
+ } else {
+ return Measure(newBases, newQubits);
+ }
+ }
+
+ operation Measure(bases : Pauli[], register : Qubit[]) : Result {
+ // If anything is PauliI, strip out and recurse.
+ if IsAnyPauliI(bases) {
+ return MeasureWithoutPauliI(bases, register);
+ }
+
+ // At this point, we're guaranteed that bases contains no PauliI, and
+ // has length at least one. The simulator implementation likewise
+ // guarantees that in the single-qubit case, the basis is not Z.
+ // Let's check the easy single-qubit case first, then, and handle the
+ // X and Y cases.
+ if Length(bases) == 1 {
+ let basis = bases[0];
+ let target = register[0];
+ mutable result = Zero;
+ within {
+ if basis == PauliX {
+ Native.H(target);
+ } elif basis == PauliY {
+ Native.H(target);
+ Native.S(target);
+ } else {
+ // In the PauliZ case, we don't need to do anything here
+ // since Native.M is already in the Z basis by definition.
+ }
+ } apply {
+ set result = Native.M(target);
+ }
+ return result;
+ } else {
+ // TODO
+ fail "Multi-qubit measurement is not yet implemented.";
+ }
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/NativeInterface.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/NativeInterface.qs
new file mode 100644
index 00000000000..7fb4695bf16
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/NativeInterface.qs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// This namespace contains those intrinsics supported by the experimental
+// simulators native interface (NativeInterface.cs).
+
+namespace Microsoft.Quantum.Experimental.Native {
+
+ operation H(target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint self;
+ }
+
+ operation X(target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint self;
+ }
+
+ operation Y(target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint self;
+ }
+
+ operation Z(target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint self;
+ }
+
+ operation S(target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint intrinsic;
+ }
+
+ operation T(target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint intrinsic;
+ }
+
+ operation CNOT(control : Qubit, target : Qubit) : Unit is Adj {
+ body intrinsic;
+ adjoint self;
+ }
+
+ operation M(target : Qubit) : Result {
+ body intrinsic;
+ }
+
+}
\ No newline at end of file
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md
new file mode 100644
index 00000000000..f687ce8d0d6
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md
@@ -0,0 +1,4 @@
+# TODO
+
+The decompositions in this folder should eventually be converted into a new "type 4" targeting package.
+We use the legacy approach to making intrinsics available to simulators, however, on a temporary basis for compatibility with Python and IQ#.
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Utils.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Utils.qs
new file mode 100644
index 00000000000..b06e781dba2
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Utils.qs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.Quantum.Experimental.Decompositions {
+ open Microsoft.Quantum.Experimental.Native as Native;
+
+ function IsAnyPauliI(bases : Pauli[]) : Bool {
+ for basis in bases {
+ if basis == PauliI {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Diagnostics.cs b/src/Simulation/Simulators/OpenSystemsSimulator/Diagnostics.cs
new file mode 100644
index 00000000000..422f2e88395
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/Diagnostics.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Common;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Microsoft.Quantum.Intrinsic.Interfaces;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public partial class OpenSystemsSimulator
+ {
+
+ public class OpenSystemsDumpMachine : Quantum.Diagnostics.DumpMachine
+ {
+ private OpenSystemsSimulator Simulator { get; }
+
+ public OpenSystemsDumpMachine(OpenSystemsSimulator m) : base(m)
+ {
+ this.Simulator = m;
+ }
+
+ public override Func __Body__ => (location) =>
+ {
+ if (location == null) { throw new ArgumentNullException(nameof(location)); }
+ Simulator.MaybeDisplayDiagnostic(Simulator.CurrentState);
+ return QVoid.Instance;
+ };
+ }
+
+ // TODO: implement this by adding a new PartialTrace trait to the
+ // Rust side, and then exposing it through the C API.
+ // Until we have that, there's not a sensible way to interpret
+ // states on subregisters in general.
+ public class OpenSystemsDumpRegister : Quantum.Diagnostics.DumpRegister
+ {
+ private OpenSystemsSimulator Simulator { get; }
+
+ public OpenSystemsDumpRegister(OpenSystemsSimulator m) : base(m)
+ {
+ this.Simulator = m;
+ }
+
+ public override Func<(T, IQArray), QVoid> __Body__ => (args) =>
+ {
+ var (location, register) = args;
+ if (location == null) { throw new ArgumentNullException(nameof(location)); }
+ this.Simulator.Get().__Body__?.Invoke("DumpRegister not yet supported on OpenSystemsSimulator; skipping.");
+ return QVoid.Instance;
+ };
+ }
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeImplementations.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeImplementations.cs
new file mode 100644
index 00000000000..1334559dfa6
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeImplementations.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Common;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Microsoft.Quantum.Intrinsic.Interfaces;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Newtonsoft.Json.Linq;
+
+using Native = Microsoft.Quantum.Experimental.Native;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public partial class OpenSystemsSimulator : SimulatorBase
+ {
+
+ internal class H : Native.H
+ {
+ public H(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ {
+ NativeInterface.H((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class X : Native.X
+ {
+ public X(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ {
+ NativeInterface.X((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class Y : Native.Y
+ {
+ public Y(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ {
+ NativeInterface.Y((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class Z : Native.Z
+ {
+ public Z(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ {
+ NativeInterface.Z((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class S : Native.S
+ {
+ public S(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ {
+ NativeInterface.S((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+
+ public override Func __AdjointBody__ => (target) =>
+ {
+ NativeInterface.SAdj((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class T : Native.T
+ {
+ public T(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ {
+ NativeInterface.T((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+
+ public override Func __AdjointBody__ => (target) =>
+ {
+ NativeInterface.TAdj((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class CNOT : Native.CNOT
+ {
+ public CNOT(IOperationFactory m) : base(m) { }
+
+ public override Func<(Qubit, Qubit), QVoid> __Body__ => (args) =>
+ {
+ NativeInterface.CNOT((this.__Factory__ as OpenSystemsSimulator).Id, args.Item1, args.Item2);
+ return QVoid.Instance;
+ };
+ }
+
+ internal class M : Native.M
+ {
+ public M(IOperationFactory m) : base(m) { }
+
+ public override Func __Body__ => (target) =>
+ NativeInterface.M((this.__Factory__ as OpenSystemsSimulator).Id, target);
+ }
+
+ }
+
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs
new file mode 100644
index 00000000000..84db6154488
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs
@@ -0,0 +1,265 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Text.Json;
+using System.Runtime.InteropServices;
+using Microsoft.Quantum.Simulation.Core;
+using Newtonsoft.Json.Linq;
+
+#nullable enable
+
+namespace Microsoft.Quantum.Experimental
+{
+ ///
+ /// Represents an exception that is raised by native simulator code.
+ ///
+ [Serializable()]
+ public class SimulationException : Exception
+ {
+ private readonly string? source;
+
+ ///
+ /// The name of the native shared library which raised this
+ /// exception if known, null otherwise.
+ ///
+ public string? SourceLibrary => source;
+
+ internal SimulationException(string message, string? source) : base(message)
+ {
+ this.source = source;
+ }
+ }
+
+ ///
+ /// Abstracts away calls to and from the experimental simulators DLL.
+ ///
+ internal static class NativeInterface
+ {
+ public static event Action? OnVerbose = null;
+ private static void LogCall(string callName) =>
+ OnVerbose?.Invoke($"[VERBOSE] NativeInterface: calling {callName}.");
+
+ private static void CheckCall(Int64 errorCode)
+ {
+ if (errorCode != 0)
+ {
+ var error = _LastError();
+ throw new SimulationException($"Exception in native open systems simulator runtime: {error}", DLL_NAME);
+ }
+ }
+
+
+ public const string DLL_NAME = "Microsoft.Quantum.Experimental.Simulators.Runtime.dll";
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="lasterr")]
+ private static extern string _LastError();
+
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_simulator_info")]
+ private static extern string _GetSimulatorInfo();
+
+ internal static readonly JToken SimulatorInfo;
+
+ static NativeInterface()
+ {
+ SimulatorInfo = JToken.Parse(_GetSimulatorInfo());
+ }
+
+ public static string Name
+ {
+ get
+ {
+ return SimulatorInfo.Value("name");
+ }
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="init")]
+ private static extern Int64 _Init(uint initialCapacity, string representation, out uint simulatorId);
+
+ public static ulong Init(uint initialCapacity, string representation = "mixed")
+ {
+ LogCall("init");
+ CheckCall(_Init(initialCapacity, representation, out var simulatorId));
+ return simulatorId;
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="destroy")]
+ private static extern Int64 _Destroy(ulong simId);
+
+ public static void Destroy(ulong simId)
+ {
+ LogCall("destroy");
+ CheckCall(_Destroy(simId));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="dump_to_console")]
+ private static extern void _DumpToConsole(ulong simId);
+
+ public static void DumpToConsole(ulong simId)
+ {
+ LogCall("dump_to_console");
+ _DumpToConsole(simId);
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_current_state")]
+ private static extern string _GetCurrentState(ulong simId);
+
+ public static State GetCurrentState(ulong simId)
+ {
+ LogCall("get_current_state");
+ return JsonSerializer.Deserialize(_GetCurrentState(simId));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling =true, CallingConvention =CallingConvention.Cdecl, EntryPoint="get_noise_model_by_name")]
+ private static extern Int64 _GetNoiseModelByName(string name, out string noiseModel);
+
+ public static NoiseModel GetNoiseModelByName(string name)
+ {
+ LogCall("get_noise_model_by_name");
+ CheckCall(_GetNoiseModelByName(name, out var noiseModelJson));
+ return JsonSerializer.Deserialize(noiseModelJson);
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_noise_model")]
+ private static extern Int64 _GetNoiseModel(ulong simId, out string noiseModel);
+
+ public static NoiseModel GetNoiseModel(ulong simId)
+ {
+ LogCall("get_noise_model");
+ CheckCall(_GetNoiseModel(simId, out var noiseModelJson));
+ return JsonSerializer.Deserialize(noiseModelJson);
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="set_noise_model")]
+ private static extern Int64 _SetNoiseModel(ulong simId, string noiseModel);
+
+ public static void SetNoiseModel(ulong simId, NoiseModel noiseModel)
+ {
+ LogCall("set_noise_model");
+ string? jsonData = null;
+ try
+ {
+ jsonData = JsonSerializer.Serialize(noiseModel);
+ CheckCall(_SetNoiseModel(simId, jsonData));
+ }
+ catch (NotSupportedException ex)
+ {
+ throw new ArgumentException("Could not serialize noise model to JSON, as no suitable serializer was found.", ex);
+ }
+ catch (JsonException ex)
+ {
+ throw new Exception($"Could not serialize noise model: {ex.Message}", ex);
+ }
+ catch (SimulationException ex)
+ {
+ throw new Exception($"Could not set noise model from JSON: {ex.Message}\nJSON data:\n{jsonData}", ex);
+ }
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="set_noise_model_by_name")]
+ private static extern Int64 _SetNoiseModelByName(ulong simId, string name);
+
+ public static void SetNoiseModelByName(ulong simId, string name)
+ {
+ LogCall("set_noise_model_by_name");
+ CheckCall(_SetNoiseModelByName(simId, name));
+ }
+
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="h")]
+ private static extern Int64 _H(ulong simId, uint idx);
+
+ public static void H(ulong simId, Qubit q)
+ {
+ LogCall("h");
+ CheckCall(_H(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="x")]
+ private static extern Int64 _X(ulong simId, uint idx);
+
+ public static void X(ulong simId, Qubit q)
+ {
+ LogCall("x");
+ CheckCall(_X(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="y")]
+ private static extern Int64 _Y(ulong simId, uint idx);
+
+ public static void Y(ulong simId, Qubit q)
+ {
+ LogCall("y");
+ CheckCall(_Y(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="z")]
+ private static extern Int64 _Z(ulong simId, uint idx);
+
+ public static void Z(ulong simId, Qubit q)
+ {
+ LogCall("z");
+ CheckCall(_Z(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="s")]
+ private static extern Int64 _S(ulong simId, uint idx);
+
+ public static void S(ulong simId, Qubit q)
+ {
+ LogCall("s");
+ CheckCall(_S(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="s_adj")]
+ private static extern Int64 _SAdj(ulong simId, uint idx);
+
+ public static void SAdj(ulong simId, Qubit q)
+ {
+ LogCall("s");
+ CheckCall(_SAdj(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="t")]
+ private static extern Int64 _T(ulong simId, uint idx);
+
+ public static void T(ulong simId, Qubit q)
+ {
+ LogCall("t");
+ CheckCall(_T(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="t_adj")]
+ private static extern Int64 _TAdj(ulong simId, uint idx);
+
+ public static void TAdj(ulong simId, Qubit q)
+ {
+ LogCall("t_adj");
+ CheckCall(_TAdj(simId, (uint)q.Id));
+ }
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="cnot")]
+ private static extern Int64 _CNOT(ulong simId, uint idxControl, uint idxTarget);
+
+ public static void CNOT(ulong simId, Qubit control, Qubit target)
+ {
+ LogCall("cnot");
+ CheckCall(_CNOT(simId, (uint)control.Id, (uint)target.Id));
+ }
+
+
+ [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="m")]
+ private static extern Int64 _M(ulong simId, uint idx, out uint result);
+
+ public static Result M(ulong simId, Qubit q)
+ {
+ LogCall("m");
+ CheckCall(_M(simId, (uint)q.Id, out var result));
+ return result == 1 ? Result.One : Result.Zero;
+ }
+
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs
new file mode 100644
index 00000000000..9c957cc4c97
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs
@@ -0,0 +1,140 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Common;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Microsoft.Quantum.Intrinsic.Interfaces;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Newtonsoft.Json.Linq;
+
+using ExpIntrin = Microsoft.Quantum.Experimental.Intrinsic;
+
+namespace Microsoft.Quantum.Experimental
+{
+ // NB: This class should not implement IQSharpCore, but does so temporarily
+ // to make the simulator available to IQ# (note that the I in IQSharpCore
+ // refers to interfaces, and not to IQ# itself...)
+ public partial class OpenSystemsSimulator : SimulatorBase, IQSharpCore
+ {
+ public static JToken BuildInfo => NativeInterface.SimulatorInfo;
+
+ private readonly ulong Id;
+
+ public override string Name => NativeInterface.Name;
+
+ public NoiseModel NoiseModel
+ {
+ get
+ {
+ return NativeInterface.GetNoiseModel(Id);
+ }
+
+ set
+ {
+ NativeInterface.SetNoiseModel(Id, value);
+ }
+ }
+
+ public State CurrentState => NativeInterface.GetCurrentState(this.Id);
+
+ public OpenSystemsSimulator(uint capacity = 4, string representation = "mixed") : base(new QubitManager((long)capacity))
+ {
+ this.Id = NativeInterface.Init(capacity, representation);
+ }
+
+ Result IIntrinsicMeasure.Body(IQArray paulis, IQArray targets) =>
+ Get().__Body__((paulis, targets));
+
+ void IIntrinsicH.Body(Qubit target)
+ {
+ Get().__Body__(target);
+ }
+
+ void IIntrinsicH.ControlledBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledBody__((controls, target));
+ }
+
+ void IIntrinsicS.Body(Qubit target)
+ {
+ Get().__Body__(target);
+ }
+
+ void IIntrinsicS.AdjointBody(Qubit target)
+ {
+ Get().__AdjointBody__(target);
+ }
+
+ void IIntrinsicS.ControlledBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledBody__((controls, target));
+ }
+
+ void IIntrinsicS.ControlledAdjointBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledAdjointBody__((controls, target));
+ }
+
+ void IIntrinsicT.Body(Qubit target)
+ {
+ Get().__Body__(target);
+ }
+
+ void IIntrinsicT.AdjointBody(Qubit target)
+ {
+ Get().__AdjointBody__(target);
+ }
+
+ void IIntrinsicT.ControlledBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledBody__((controls, target));
+ }
+
+ void IIntrinsicT.ControlledAdjointBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledAdjointBody__((controls, target));
+ }
+
+ void IIntrinsicX.Body(Qubit target)
+ {
+ Get().__Body__(target);
+ }
+
+ void IIntrinsicX.ControlledBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledBody__((controls, target));
+ }
+
+ void IIntrinsicY.Body(Qubit target)
+ {
+ Get().__Body__(target);
+ }
+
+ void IIntrinsicY.ControlledBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledBody__((controls, target));
+ }
+
+ void IIntrinsicZ.Body(Qubit target)
+ {
+ Get().__Body__(target);
+ }
+
+ void IIntrinsicZ.ControlledBody(IQArray controls, Qubit target)
+ {
+ Get().__ControlledBody__((controls, target));
+ }
+
+ public void Dispose()
+ {
+ NativeInterface.Destroy(this.Id);
+ }
+
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/QubitManager.cs b/src/Simulation/Simulators/OpenSystemsSimulator/QubitManager.cs
new file mode 100644
index 00000000000..80c0b3062d2
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/QubitManager.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Common;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Microsoft.Quantum.Intrinsic.Interfaces;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.Quantum.Experimental
+{
+ public partial class OpenSystemsSimulator
+ {
+
+ public class OpenSystemsQubitManager : QubitManager
+ {
+ private readonly OpenSystemsSimulator Parent;
+ public OpenSystemsQubitManager(OpenSystemsSimulator parent, uint capacity)
+ : base(capacity)
+ {
+ this.Parent = parent;
+ }
+
+ protected override void Release(Qubit qubit, bool wasUsedOnlyForBorrowing)
+ {
+ // When a qubit is released, the developer has promised that
+ // either of the following two conditions holds:
+ //
+ // 1. The qubit has been measured immediately before
+ // releasing, such that its state is known
+ // determinstically at this point in the program.
+ // 2. The qubit has been coherently unprepared, such that
+ // the the qubit is known determistically to be in the
+ // |0⟩ state at this point in the program.
+ //
+ // In either case, noise can cause a correct Q# program to fail
+ // to meet the conditions for releasing a qubit, such that we
+ // want to track the effects of that noise without failing.
+ //
+ // Thus, our strategy will be to always allow the release to
+ // proceed, doing any resets needed to deal with case (1)
+ // above.
+ if (qubit != null && qubit.IsMeasured)
+ {
+ // Try to reset measured qubits.
+ // TODO: There are better ways to do this; increment on the
+ // design and make it customizable.
+ // FIXME: In particular, this implementation uses a lot of
+ // extraneous measurements.
+ if ((this.Parent as IIntrinsicMeasure).Body(new QArray(Pauli.PauliZ), new QArray(qubit)) == Result.One)
+ {
+ (this.Parent as IIntrinsicX).Body(qubit);
+ }
+ }
+ base.Release(qubit, wasUsedOnlyForBorrowing);
+ }
+ }
+
+ }
+}
diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/README.md b/src/Simulation/Simulators/OpenSystemsSimulator/README.md
new file mode 100644
index 00000000000..c14d3fe1b79
--- /dev/null
+++ b/src/Simulation/Simulators/OpenSystemsSimulator/README.md
@@ -0,0 +1,65 @@
+# .NET Bindings for Quantum Development Kit Experimental Simulators
+
+The .NET classes defined in this folder utilize the C library built from the `qdk_sim` Rust crate to provide bindings to the experimental simulators that can be used from Q# programs.
+
+
+For more details on the `qdk_sim` crate, and its APIs for Rust, C, and Python, please see the documentation provided with that crate. From the root of this repository:
+
+```bash
+pwsh ./bootstrap.ps1
+cd src/Simulation/qdk_sim_rs
+cargo +nightly doc --features python --open
+```
+
+For more details on using these simulators from Q# programs called from Python hosts, please see .
+
+## Native interface
+
+The core interoperability boundary between the C# runtime for the Quantum Development Kit and the `qdk_sim` native library is defined in the [`NativeInterface` static class](./NativeInterface.cs). This class provides P/Invoke declarations for each function exposed by the C API for the `qdk_sim` crate, as well as methods that call into these C API methods. The managed methods that correspond to each C API function check error codes returned by C API functions and convert them into .NET exceptions, allowing for C API errors to be easily caught by managed code.
+
+For example, to use the native API to create a new simulator with a mixed-state representation:
+
+```csharp
+using static Microsoft.Quantum.Experimental.NativeInterface;
+
+var simId = Init(initialCapacity: 3, representation: "mixed");
+try
+{
+ H(simId, 0);
+ var result = M(simId, 0);
+ System.Console.Out.WriteLine($"Got {result}!");
+}
+finally
+{
+ Destroy(simId);
+}
+```
+
+## Q# simulator interface
+
+For most applications, the native interface is not useful on its own, but as called as a Q# simulator. The [`OpenSystemsSimulator` class](./OpenSystemsSimulator.cs) provides bindings between the native interface described above and the `SimulatorBase` class in the C# runtime for Q#.
+
+This class implements each of the intrinsic Q# operations either directly as a call into the `qdk_sim` library, or by using [decompositions](./Decompositions) to reduce Q# intrinsics into those intrinsics supported by the experimental simulators.
+
+To create a new simulator, use the constructor for the `OpenSystemsSimulator` class:
+
+```csharp
+var qsim = new OpenSystemsSimulator(3, "mixed");
+```
+
+The noise model for the simulator can be accessed or set using the `OpenSystemsSimulator.NoiseModel` property (see [noise modeling API](#noise-modeling-api), below)):
+
+```csharp
+qsim.NoiseModel =
+ NoiseModel.TryGetByName("ideal_stabilizer", out var noiseModel)
+ ? noiseModel
+ : throw new Exception("Noise model with name 'ideal_stabilizer' not found.");
+```
+
+This simulator can then be used as any other Q# simulator.
+
+## Noise modeling API
+
+To get and set noise models, each of the data structures in the `qdk_sim` crate has an analogous C# class that can be used to serialize and deserialize `qdk_sim` data structures as JSON objects.
+
+In particular, the [`NoiseModel` class](./DataModel/NoiseModel.cs) class parallels the `NoiseModel` struct in the `qdk_sim` crate, and allows accessing or modifying the noise model used by experimental simulators to run each Q# program.
diff --git a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs
index 2797a2b13ab..8bb52e118e4 100644
--- a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs
+++ b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs
@@ -248,15 +248,15 @@ public void CheckQubit(Qubit q, string qubitName)
{
if (q == null) throw new ArgumentNullException(qubitName, "Trying to perform an intrinsic operation on a null Qubit");
- if (!QubitManager.IsValid(q))
+ if (!(QubitManager?.IsValid(q) ?? true))
{
throw new ArgumentException($"Cannot use qubit {q.Id}. Qubit is invalid.", qubitName);
}
- if (QubitManager.IsFree(q))
+ if (QubitManager?.IsFree(q) ?? false)
{
throw new ArgumentException($"Cannot use qubit {q.Id}. Qubit has already been released.", qubitName);
}
- if (QubitManager.IsDisabled(q))
+ if (QubitManager?.IsDisabled(q) ?? false)
{
throw new ArgumentException($"Cannot use qubit {q.Id}. Qubit is disabled.", qubitName);
}
@@ -291,6 +291,10 @@ public class Allocate : Intrinsic.Allocate
public Allocate(SimulatorBase m) : base(m)
{
this.sim = m;
+ if (m.QubitManager == null)
+ {
+ throw new NullReferenceException($"SimulatorBase {m} has a null-valued qubit manager, but a qubit manager is required to implement the Allocate operation.");
+ }
this.manager = m.QubitManager;
}
@@ -323,6 +327,10 @@ public class Release : Intrinsic.Release
public Release(SimulatorBase m) : base(m)
{
this.sim = m;
+ if (m.QubitManager == null)
+ {
+ throw new NullReferenceException($"SimulatorBase {m} has a null-valued qubit manager, but a qubit manager is required to implement the Release operation.");
+ }
this.manager = m.QubitManager;
}
@@ -353,6 +361,10 @@ public class Borrow : Intrinsic.Borrow
public Borrow(SimulatorBase m) : base(m)
{
this.sim = m;
+ if (m.QubitManager == null)
+ {
+ throw new NullReferenceException($"SimulatorBase {m} has a null-valued qubit manager, but a qubit manager is required to implement the Borrow operation.");
+ }
this.manager = m.QubitManager;
}
@@ -385,6 +397,10 @@ public class Return : Intrinsic.Return
public Return(SimulatorBase m) : base(m)
{
this.sim = m;
+ if (m.QubitManager == null)
+ {
+ throw new NullReferenceException($"SimulatorBase {m} has a null-valued qubit manager, but a qubit manager is required to implement the Return operation.");
+ }
this.manager = m.QubitManager;
}
@@ -414,6 +430,10 @@ public class GetQubitsAvailableToUse : Environment.GetQubitsAvailableToUse
public GetQubitsAvailableToUse(SimulatorBase m) : base(m)
{
this.sim = m;
+ if (m.QubitManager == null)
+ {
+ throw new NullReferenceException($"SimulatorBase {m} has a null-valued qubit manager, but a qubit manager is required to implement the GetQubitsAvailableToUse operation.");
+ }
this.manager = m.QubitManager;
}
@@ -432,6 +452,11 @@ public class GetQubitsAvailableToBorrow : Environment.GetQubitsAvailableToBorrow
public GetQubitsAvailableToBorrow(SimulatorBase m) : base(m)
{
this.sim = m;
+
+ if (m.QubitManager == null)
+ {
+ throw new NullReferenceException($"SimulatorBase {m} has a null-valued qubit manager, but a qubit manager is required to implement the GetQubitsAvailableToBorrow operation.");
+ }
this.manager = m.QubitManager;
}
diff --git a/src/Simulation/qdk_sim_rs/.config/dotnet-tools.json b/src/Simulation/qdk_sim_rs/.config/dotnet-tools.json
new file mode 100644
index 00000000000..8e180ea3016
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-script": {
+ "version": "1.1.0",
+ "commands": [
+ "dotnet-script"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Simulation/qdk_sim_rs/.gitignore b/src/Simulation/qdk_sim_rs/.gitignore
new file mode 100644
index 00000000000..a740f3ce698
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/.gitignore
@@ -0,0 +1,22 @@
+linux
+osx
+win10
+target
+drop
+
+# We inject version numbers into Cargo.toml, so don't want them stored in repo.
+Cargo.toml
+# In the future, it would be good to enable reproducible builds by committing
+# the lockfile and using --locked in calls to cargo.
+Cargo.lock
+
+# Ignore Python temporaries and build artifacts.
+*.pyd
+*.whl
+*.egg-info
+
+# Ignore generated C/C++ headers.
+include/
+
+# Don't ignore documentation here.
+!docs
diff --git a/src/Simulation/qdk_sim_rs/CONTRIBUTING.md b/src/Simulation/qdk_sim_rs/CONTRIBUTING.md
new file mode 100644
index 00000000000..894a188cb9b
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# Contributing to the QDK Experimental Simulators
+
+## Build prerequisites
+
+The build for the experimental simulators requires the nightly Rust toolchain to be installed, along with support for clippy and rustfmt. These prerequisites can be installed by using the `prerequisites.ps1` script in this folder:
+
+```pwsh
+PS> ./prerequistes.ps1
+```
+
+## Code quality checks
+
+The build for this crate enforces the following mandatory code quality checks:
+
+- `rustfmt`: Check code style and formatting rules.
+- `clippy`: Check for common programming errors and linter violations.
+- `#[deny(missing_docs)]`: Require API documentation for all public crates, modules, traits, functions, and types.
+
+## Testing strategy
+
+Tests for the open systems simulator consist of five distinct parts:
+
+- Rust-language unit tests for the Rust library.
+ These tests are defined in `#[cfg(test)]` submodules of each module in `./src/`.
+- Rust-language integration tests for the Rust library.
+ These tests are defined in modules under the `./tests/` folder.
+- Q#-language unit tests in the C#-based simulation runtime.
+ These tests ensure that the binding of the Rust library works as expected when included into the C#-based runtime, and are defined in operations marked with `@Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator")` under the `qsharp-runtime/src/Simulation/Simulators.Tests/QuantumTestSuite` folder.
+- C#-language unit tests in the IQ# kernel.
+ These tests ensure that noise models and noisy simulation can be correctly exposed to Python and Q# notebook users; please see the `microsoft/iqsharp` repo for more details.
diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template
new file mode 100644
index 00000000000..f589d6d411b
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template
@@ -0,0 +1,92 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+[package]
+name = "qdk_sim_experimental"
+version = "0.1.0"
+authors = ["Microsoft"]
+edition = "2018"
+license = "MIT"
+description = "Experimental simulators for use with the Quantum Development Kit."
+homepage = "https://github.com/microsoft/qsharp-runtime"
+repository = "https://github.com/microsoft/qsharp-runtime"
+readme = "README.md"
+
+exclude = [
+ # Exclude files specific to QDK build pipelines.
+ "*.template", "*.csx", "*.ps1", "NuGet.Config", "drop",
+ # Don't include cbindgen configs and outputs.
+ "include", "cbindgen.toml",
+ # Don't include Python sources or build artifacts.
+ "*.egg-info", "qdk_sim_experimental", "setup.py", "*.whl", "pyproject.toml"
+]
+
+[lib]
+name = "qdk_sim"
+path = "src/lib.rs"
+crate-type = ["rlib", "staticlib", "cdylib"]
+
+# Optional build-time features: we use this to create Python and WASM bindings.
+[features]
+default = []
+wasm = ["web-sys"]
+# When Python bindings are enabled, we also need the pyo3 dependency.
+python = ["pyo3", "numpy"]
+
+# Enable LaTeX on docs.rs.
+# See https://stackoverflow.com/a/54573800/267841 and
+# https://github.com/rust-num/num/pull/226/files for why this works.
+[package.metadata.docs.rs]
+rustdoc-args = [ "--html-in-header", "docs-includes/header.html", "--html-after-content", "docs-includes/after.html" ]
+
+
+[profile.release]
+opt-level = 3
+codegen-units = 1 # Reduce number of codegen units to increase optimizations.
+panic = 'unwind'
+
+[dependencies]
+ndarray = { version = "0.15.2", features = ["serde"] }
+num-complex = { version = "0.4", features = ["serde"] }
+num-traits = "0.2"
+derive_more = "0.99.10"
+itertools = "0.9.0"
+rand = { version = "0.7.3", features = ["alloc"] }
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+lazy_static = "1.4.0"
+cfg-if = "1.0.0"
+num_enum = "0.5.1"
+# See https://github.com/rust-random/rand/issues/990
+# and https://docs.rs/getrandom/0.1.15/getrandom/index.html#support-for-webassembly-and-asmjs
+# for why this is needed.
+# NB: We depend on 0.1.15, since that's what gets brought in transitively
+# by rand and rand_core.
+getrandom = { version = "0.1.15", features = ["wasm-bindgen"] }
+
+# We only need web-sys when compiling with the wasm feature.
+web-sys = { version = "0.3.4", features = ['console'], optional = true }
+
+# We only need PyO3 when generating Python bindings.
+pyo3 = { version = "0.13.2", features = ["extension-module"], optional = true }
+numpy = {version = "0.13.1", optional = true}
+
+# We use built to expose compile-time metadata about how this crate
+# was built to C and Rust callers.
+built = "0.5.0"
+
+[build-dependencies]
+built = "0.5.0"
+
+
+[dev-dependencies]
+assert-json-diff = "2.0.1"
+criterion = { version = "0.3", features = ['html_reports', 'csv_output'] }
+
+[[bench]]
+name = "c_api_benchmark"
+harness = false
+
+[[bench]]
+name = "microbenchmark"
+harness = false
diff --git a/src/Simulation/qdk_sim_rs/NuGet.Config b/src/Simulation/qdk_sim_rs/NuGet.Config
new file mode 100644
index 00000000000..8b638f09a31
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/NuGet.Config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md
new file mode 100644
index 00000000000..f48c36c0bd5
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/README.md
@@ -0,0 +1,150 @@
+
+
+# Quantum Development Kit Experimental Simulators
+
+> ## **⚠** WARNING **⚠**
+>
+> This crate is **experimental**, and may undergo breaking API changes with no notice, and may not be maintained in future versions of the Quantum Development Kit.
+>
+> As an experimental feature of the Quantum Development Kit, this crate may be buggy or incomplete. Please check the tracking issue at [microsoft/qsharp-runtime#714](https://github.com/microsoft/qsharp-runtime/issues/714) for more information.
+
+> ## **ⓘ** TIP
+>
+> This crate provides low-level APIs for interacting with experimental simulators. If you're interested in using the experimental simulators to run your Q# programs, please see the installation instructions at .
+
+This **experimental** crate implements simulation functionality for the Quantum Development Kit, including:
+
+- Open systems simulation
+- Stabilizer simulation
+
+The [`c_api`] module allows for using the simulation functionality in this crate from C, or from other languages with a C FFI (e.g.: C++ or C#), while Rust callers can take advantage of the structs and methods in this crate directly.
+
+Similarly, the [`python`] module allows exposing data structures in this crate to Python programs.
+
+## Cargo Features
+
+- **`python`**: Enables Python bindings for this crate.
+- **`wasm`**: Ensures that the crate is compatible with usage from WebAssembly.
+
+## Representing quantum systems
+
+This crate provides several different data structures for representing quantum systems in a variety of different conventions:
+
+- [`State`]\: Represents stabilizer, pure, or mixed states of a register of qubits.
+- [`Process`]\: Represents processes that map states to states.
+- [`Instrument`]\: Represents quantum instruments, the most general form of measurement.
+
+## Noise model serialization
+
+Noise models can be serialized to JSON for interoperability across languages. In particular, each noise model is represented by a JSON object with properties for each operation, for the initial state, and for the instrument used to implement $Z$-basis measurement.
+
+For example:
+
+```json
+{
+ "initial_state": {
+ "n_qubits": 1,
+ "data": {
+ "Mixed": {
+ "v": 1, "dim":[2 ,2],
+ "data": [[1.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
+ }
+ }
+ },
+ "i": {
+ "n_qubits": 1,
+ "data": {
+ "Unitary": {
+ "v": 1,"dim": [2, 2],
+ "data": [[1.0, 0.0], [0.0, 0.0], [0.0, 0.0], [1.0, 0.0]]
+ }
+ }
+ },
+ ...
+ "z_meas": {
+ "Effects": [
+ {
+ "n_qubits": 1,
+ "data": {
+ "KrausDecomposition": {
+ "v":1, "dim": [1, 2, 2],
+ "data": [[1.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
+ }
+ }
+ },
+ {
+ "n_qubits": 1,
+ "data": {
+ "KrausDecomposition": {
+ "v": 1,"dim": [1, 2, 2],
+ "data":[[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [1.0, 0.0]]
+ }
+ }
+ }
+ ]
+ }
+}
+```
+
+The value of the `initial_state` property is a serialized [`State`], the value of each operation property (i.e.: `i`, `x`, `y`, `z`, `h`, `s`, `s_adj`, `t`, `t_adj`, and `cnot`) is a serialized [`Process`], and the value of `z_meas` is a serialized [`Instrument`].
+
+### Representing arrays of complex numbers
+
+Throughout noise model serialization, JSON objects representing $n$-dimensional arrays of complex numbers are used to store various vectors, matrices, and tensors. Such arrays are serialized as JSON objects with three properties:
+
+- `v`: The version number of the JSON schema; must be `"1"`.
+- `dims`: A list of the dimensions of the array being represented.
+- `data`: A list of the elements of the flattened array, each of which is represented as a list with two entries representing the real and complex parts of each element.
+
+For example, consider the serialization of the ideal `y` operation:
+
+```json
+"y": {
+ "n_qubits": 1,
+ "data": {
+ "Unitary": {
+ "v": 1, "dim": [2, 2],
+ "data": [[0.0, 0.0], [0.0, 1.0], [0.0, -1.0], [0.0, 0.0]]
+ }
+ }
+}
+```
+
+### Representing states and processes
+
+Each state and process is represented in JSON by an object with two properties, `n_qubits` and `data`. The value of `data` is itself a JSON object with one property indicating which variant of the [`StateData`] or [`ProcessData`] enum is used to represent that state or process, respectively.
+
+For example, the following JSON object represents the mixed state $\ket{0}\bra{0}$:
+
+```json
+{
+ "n_qubits": 1,
+ "data": {
+ "Mixed": {
+ "v": 1, "dim":[2 ,2],
+ "data": [[1.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
+ }
+ }
+}
+```
+
+### Representing instruments
+
+TODO
+
+## Known issues
+
+- Performance of open systems simulation still needs additional work for larger registers.
+- Some gaps in different conversion functions and methods.
+- Stabilizer states cannot yet be measured through [`Instrument`] struct, only through underlying [`Tableau`].
+- Many parts of the crate do not yet have Python bindings.
+- Stabilizer simulation not yet exposed via C API.
+- Test and microbenchmark coverage still incomplete.
+- Too many APIs `panic!` or `unwrap`, and need replaced with `Result` returns instead.
diff --git a/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs
new file mode 100644
index 00000000000..d4ab3fbdc19
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+//! This set of benchmarks exercises the open systems simulator exclusively via
+//! its C API, so as to gauge any potential issues for C-based consumers of the
+//! simulator.
+
+use criterion::{criterion_group, criterion_main, Criterion};
+use qdk_sim::c_api;
+use std::ffi::CString;
+
+// Use include_str! to store test case JSON as a string into the compiled
+// test executable.
+static BENCHMARK_NOISE_MODEL_JSON: &str = include_str!("data/benchmark-noise-model.json");
+
+fn with_test_suite(
+ sim_id: usize,
+ group: &mut criterion::BenchmarkGroup,
+) {
+ group.bench_function("apply x", |b| {
+ b.iter(|| {
+ c_api::x(sim_id, 0);
+ })
+ });
+ group.bench_function("apply z", |b| {
+ b.iter(|| {
+ c_api::z(sim_id, 0);
+ })
+ });
+ group.bench_function("apply cnot", |b| {
+ b.iter(|| {
+ c_api::cnot(sim_id, 0, 1);
+ })
+ });
+ group.bench_function("measure", |b| {
+ b.iter(|| {
+ let mut result: usize = 0;
+ // NB: The C API is not in general safe.
+ unsafe {
+ c_api::m(sim_id, 0, &mut result);
+ }
+ })
+ });
+}
+
+fn ideal(c: &mut Criterion) {
+ let mut sim_id: usize = 0;
+ unsafe {
+ let _err = c_api::init(3, CString::new("mixed").unwrap().as_ptr(), &mut sim_id);
+ }
+ let mut group = c.benchmark_group("ideal");
+ with_test_suite(sim_id, &mut group);
+ group.finish();
+ c_api::destroy(sim_id);
+}
+
+fn noisy(c: &mut Criterion) {
+ let mut sim_id: usize = 0;
+ unsafe {
+ let _err = c_api::init(3, CString::new("mixed").unwrap().as_ptr(), &mut sim_id);
+ }
+ // NB: The C API is not in general safe.
+ unsafe {
+ c_api::set_noise_model(
+ sim_id,
+ CString::new(BENCHMARK_NOISE_MODEL_JSON).unwrap().as_ptr(),
+ );
+ }
+ let mut group = c.benchmark_group("noisy");
+ with_test_suite(sim_id, &mut group);
+ group.finish();
+ c_api::destroy(sim_id);
+}
+
+criterion_group!(benches, ideal, noisy);
+criterion_main!(benches);
diff --git a/src/Simulation/qdk_sim_rs/benches/data/benchmark-noise-model.json b/src/Simulation/qdk_sim_rs/benches/data/benchmark-noise-model.json
new file mode 100644
index 00000000000..e9d7f107f7c
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/benches/data/benchmark-noise-model.json
@@ -0,0 +1 @@
+{"initial_state":{"n_qubits":1,"data":{"Mixed":{"v":1,"dim":[2,2],"data":[[1,0],[0,0],[0,0],[0,0]]}}},"cnot":{"n_qubits":2,"data":{"Unitary":{"v":1,"dim":[4,4],"data":[[1,0],[0,0],[0,0],[0,0],[0,0],[1,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1,0],[0,0],[0,0],[1,0],[0,0]]}}},"i":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1,0],[0,0],[0,0],[1,0]]}}},"s":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1,0],[0,0],[0,0],[0,1]]}}},"s_adj":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1,0],[0,0],[0,0],[0,-1]]}}},"t":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1,0],[0,0],[0,0],[0.7071067811865476,0.7071067811865476]]}}},"t_adj":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1,0],[0,0],[0,0],[0.7071067811865476,-0.7071067811865476]]}}},"h":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.7071067811865476,0],[0.7071067811865476,0],[0.7071067811865476,0],[-0.7071067811865476,0]]}}},"x":{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[4,2,2],"data":[[0,0],[0.961769203083567,0],[0.9617692030835675,0],[0,0],[0,0],[0.15811388300841897,0],[-0.1581138830084189,0],[0,0],[0.22360679774997896,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.22360679774997896,0]]}}},"y":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0,0],[0,1],[0,-1],[0,0]]}}},"z":{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[2,2,2],"data":[[0.9746794344808962,0],[0,0],[0,0],[-0.9746794344808962,0],[0.2236067977499789,0],[0,0],[0,0],[0.22360679774997896,0]]}}},"z_meas":{"effects":[{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[0.975,0],[0,0],[0,0],[0.025000000000000022,0]]}}},{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[0.025000000000000022,0],[0,0],[0,0],[0.9749999999999999,0]]}}}]}}
\ No newline at end of file
diff --git a/src/Simulation/qdk_sim_rs/benches/data/noise-model-export.ipynb b/src/Simulation/qdk_sim_rs/benches/data/noise-model-export.ipynb
new file mode 100644
index 00000000000..804cd28ccd4
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/benches/data/noise-model-export.ipynb
@@ -0,0 +1,212 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "logical-diving",
+ "metadata": {},
+ "source": [
+ "This notebook is used to define a noise model for benchmarking purposes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "understanding-business",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import qutip as qt"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "fallen-office",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Preparing Q# environment...\n"
+ ]
+ }
+ ],
+ "source": [
+ "import qsharp\n",
+ "from qsharp.experimental import enable_noisy_simulation, get_noise_model, set_noise_model\n",
+ "enable_noisy_simulation()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "pursuant-plenty",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "I, X, Y, Z = qt.qeye(2), qt.sigmax(), qt.sigmay(), qt.sigmaz()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "labeled-strike",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sI, sX, sY, sZ = map(qt.to_super, [I, X, Y, Z])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "answering-europe",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def total_dephasing_channel():\n",
+ " return (1 / 2) * sI + (1 / 2) * sZ"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "attached-juice",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def dephasing_channel(p):\n",
+ " return (1 - p) * sI + p * total_dephasing_channel()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "serious-warner",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.900 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.900 & 0.0\\\\0.0 & 0.0 & 0.0 & 1.0\\\\\\end{array}\\right)\\end{equation*}"
+ ],
+ "text/plain": [
+ "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = True\n",
+ "Qobj data =\n",
+ "[[1. 0. 0. 0. ]\n",
+ " [0. 0.9 0. 0. ]\n",
+ " [0. 0. 0.9 0. ]\n",
+ " [0. 0. 0. 1. ]]"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dephasing_channel(0.1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "requested-instruction",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def total_depolarizing_channel():\n",
+ " return (sI + sX + sY + sZ) / 4"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "exact-argument",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def depolarizing_channel(p):\n",
+ " return (1 - p) * sI + p * total_depolarizing_channel()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "academic-focus",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def finite_visibility_meas(visibility):\n",
+ " return [\n",
+ " qt.to_super(visibility * (I + sign * Z) / 2 + (1 - visibility) * I / 2)\n",
+ " for sign in (+1, -1)\n",
+ " ]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "inclusive-active",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "noise_model = get_noise_model()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "instructional-mortality",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "noise_model['x'] = depolarizing_channel(0.1) * sX\n",
+ "noise_model['z'] = dephasing_channel(0.1) * sZ\n",
+ "noise_model['z_meas']['effects'] = finite_visibility_meas(0.95)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "voluntary-parallel",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "set_noise_model(noise_model)\n",
+ "qsharp.client._execute('%experimental.noise_model --save benchmark-noise-model.json')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "functional-concentrate",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "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.9.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs
new file mode 100644
index 00000000000..89ccd07b809
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs
@@ -0,0 +1,86 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+//! The benchmarks in this module exercise the internals of the simulator at
+//! a low level, and thus are not indicative of user-facing performance.
+//! Rather, these microbenchmarks are intended to help diagnose what the root
+//! cause may be when user-facing performance is degraded.
+//! In particular, optimizing these benchmarks may not translate into improved
+//! performance in user code.
+
+use criterion::{criterion_group, criterion_main, Criterion};
+use qdk_sim::{
+ common_matrices,
+ common_matrices::nq_eye,
+ linalg::{extend_one_to_n, extend_two_to_n, Tensor},
+};
+
+fn linalg(c: &mut Criterion) {
+ let mut group = c.benchmark_group("linalg");
+ for n_qubits in [1usize, 2, 3, 4].iter() {
+ group.bench_with_input(format!("nq_eye({})", n_qubits), n_qubits, |b, nq| {
+ b.iter(|| {
+ let _eye = nq_eye(*nq);
+ })
+ });
+ }
+ for idx_qubit in [0usize, 1, 2].iter() {
+ group.bench_with_input(
+ format!(
+ "extend_one_to_n(n_left: {}, n_right: {})",
+ idx_qubit,
+ 2 - idx_qubit
+ ),
+ idx_qubit,
+ |b, i| {
+ // Create some test data.
+ let data = nq_eye(1);
+ b.iter(|| {
+ let _extended = extend_one_to_n(data.view(), *i, 3);
+ })
+ },
+ );
+ }
+ for idx_qubit in [0usize, 1, 2].iter() {
+ group.bench_with_input(
+ format!(
+ "extend_two_to_n(n_left: {}, n_right: {})",
+ idx_qubit,
+ 2 - idx_qubit
+ ),
+ idx_qubit,
+ |b, i| {
+ // Create some test data.
+ let data = common_matrices::cnot();
+ b.iter(|| {
+ let _extended = extend_two_to_n(data.view(), *i, 3, 4);
+ })
+ },
+ );
+ }
+ group.bench_function("tensor 2x2 with 2x2", |b| {
+ let x = common_matrices::x();
+ let y = common_matrices::y();
+ b.iter(|| {
+ let _result = x.tensor(&y);
+ })
+ });
+ group.bench_function("tensor 2x2 with 4x4", |b| {
+ let x = common_matrices::x();
+ let cnot = common_matrices::cnot();
+ b.iter(|| {
+ let _result = x.tensor(&cnot);
+ })
+ });
+ group.bench_function("tensor 4x4 with 2x2", |b| {
+ let x = common_matrices::x();
+ let cnot = common_matrices::cnot();
+ b.iter(|| {
+ let _result = cnot.tensor(&x);
+ })
+ });
+ group.finish();
+}
+
+criterion_group!(benches, linalg);
+criterion_main!(benches);
diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1
new file mode 100644
index 00000000000..4ab192620d6
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1
@@ -0,0 +1,41 @@
+& (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1");
+$IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true";
+
+Push-Location $PSScriptRoot
+ # Start with the quick check first and make sure that Rust sources
+ # meet formatting and style guide rules.
+ cargo fmt -- --check
+ $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0;
+
+ # Check linting rules defined by clippy, a linting tool provided with the
+ # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy
+ # and https://rust-lang.github.io/rust-clippy/master/index.html
+ # for more information.
+ # If there's a false positive, that check should be explicitly disabled
+ # at the point where the false positive occurs with an explanation as to
+ # why it's OK.
+ cargo clippy -- -D warnings
+ $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0;
+
+ $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @();
+
+ # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647)
+ # for interoperating Rust and C.
+ # NB: CFG is only supported on Windows, but the Rust flag is supported on
+ # all platforms; it's ignored on platforms without CFG functionality.
+ $Env:RUSTFLAGS = "-C control-flow-guard";
+
+ # Actually run the build.
+ cargo +nightly build -Z unstable-options @releaseFlag --out-dir "drop";
+
+ # Make sure docs are complete.
+ $Env:RUSTDOCFLAGS = "--html-in-header $(Resolve-Path docs-includes/header.html) " + `
+ "--html-after-content $(Resolve-Path docs-includes/after.html)"
+ cargo +nightly doc;
+
+ # When building in CI, free disk space by cleaning up.
+ # Note that this takes longer, but saves ~1 GB of space.
+ if ($IsCI) {
+ cargo clean;
+ }
+Pop-Location
diff --git a/src/Simulation/qdk_sim_rs/build.rs b/src/Simulation/qdk_sim_rs/build.rs
new file mode 100644
index 00000000000..52562de4dd0
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/build.rs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+use std::env;
+use std::path::Path;
+
+fn main() -> Result<(), String> {
+ built::write_built_file().expect("Failed to acquire build-time information");
+
+ Ok(())
+}
diff --git a/src/Simulation/qdk_sim_rs/cbindgen.toml b/src/Simulation/qdk_sim_rs/cbindgen.toml
new file mode 100644
index 00000000000..08094f28fc2
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/cbindgen.toml
@@ -0,0 +1 @@
+language = "C"
diff --git a/src/Simulation/qdk_sim_rs/docs-includes/after.html b/src/Simulation/qdk_sim_rs/docs-includes/after.html
new file mode 100644
index 00000000000..d855ca7e178
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/docs-includes/after.html
@@ -0,0 +1,12 @@
+
diff --git a/src/Simulation/qdk_sim_rs/docs-includes/header.html b/src/Simulation/qdk_sim_rs/docs-includes/header.html
new file mode 100644
index 00000000000..affe693fa82
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/docs-includes/header.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/Simulation/qdk_sim_rs/docs/c-api.md b/src/Simulation/qdk_sim_rs/docs/c-api.md
new file mode 100644
index 00000000000..6ebc45bd426
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/docs/c-api.md
@@ -0,0 +1,149 @@
+# Using Experimental Simulators from C
+
+This module exposes a C API for this crate, useful for embedding into simulation
+runtimes.
+
+## Safety
+
+As this is a foreign-function interface, many of the functions exposed here
+are **unsafe**, representing that the caller is required to ensure that safety
+conditions in the host language are upheld.
+
+Please pay attention to any listed safety notes when calling into this C
+API.
+
+## Generating and Using C API Headers
+
+The [`qdk_sim`](..) crate has enabled the use of [`cbindgen`](https://crates.io/crates/cbindgen), such that C-language header files are generated automatically as part of the build for this crate.
+
+```bash
+cargo install --force cbindgen
+cbindgen --language C --output include/qdk_sim.h
+cbindgen --language C++ --output include/qdk_sim.hpp
+```
+
+This will generate `include/qdk_sim.h` and `include/qdk_sim.hpp`, which can then be used from C and C++ callers, respectively. For example, to call from C:
+
+```c
+#include
+#include "qdk_sim.h"
+
+int main() {
+ uintptr_t sim_id;
+ uintptr_t result0, result1;
+
+ init(2, "mixed", &sim_id);
+ h(sim_id, 0);
+ h(sim_id, 1);
+
+ m(sim_id, 0, &result0);
+ m(sim_id, 1, &result1);
+
+ printf("got %llu %llu", result0, result1);
+ destroy(sim_id);
+}
+```
+
+To build and run the above example using Clang on Windows:
+
+```bash
+$ clang example.c -Iinclude -Ltarget/debug -lqdk_sim -lws2_32 -lAdvapi32 -lUserenv
+$ ./a.exe
+got 1 1
+```
+
+## Error Handling and Return Values
+
+Most C API functions for this crate return an integer, with `0` indicating success and any other value indicating failure. In the case that a non-zero value is returned, API functions will also set the last error message, accessible by calling [`lasterr`]:
+
+```c
+#include
+#include "qdk_sim.h"
+
+int main() {
+ uintptr_t sim_id;
+ uintptr_t result0, result1;
+
+ if (init(2, "invalid", &sim_id) != 0) {
+ printf("Got an error message: %s", lasterr());
+ } else {
+ destroy(sim_id);
+ }
+}
+```
+
+C API functions that need to return data to the caller, such as [`m`], do so by accepting pointers to memory where results should be stored.
+
+> **⚠ WARNING**: It is the caller's responsibility to ensure that pointers used to hold results are valid (that is, point to memory that can be safely written into).
+
+For example:
+
+```c
+uintptr_t result;
+if (m(sim_id, 0, &result) != 0) {
+ printf("Got an error message: %s", lasterr());
+} else {
+ printf("Got a measurement result: %llu", result);
+}
+```
+
+## Initializing, Using, and Destroying Simulators
+
+To create a new simulator from C, use the [`init`] function. This function accepts a pointer to an unsigned integer that will be set to an ID for the new simulator:
+
+```c
+uintptr_t sim_id;
+// Initialize a new simulator with two qubits and using a mixed-state
+// representation.
+if (init(2, "mixed", &sim_id) != 0) {
+ printf("Error initializing simulator: %s", lasterr());
+}
+```
+
+The ID for the newly created simulator can then be used to call into functions that apply different quantum operations, such as [`x`], [`h`], or [`cnot`]:
+
+```c
+// Apply an 𝑋 operation to qubit #0.
+if (x(sim_id, 0) != 0) {
+ printf("Error applying X: %s", lasterr());
+}
+```
+
+To free the memory associated with a given simulator, use the [`destroy`] function:
+
+```c
+if (destroy(sim_id) != 0) {
+ printf("Error destroying simulator: %s", lasterr());
+}
+```
+
+## Getting and Setting Noise Models
+
+Noise models for each simulator can be accessed or set by using [`get_noise_model`], [`get_noise_model_by_name`], [`set_noise_model`], and [`set_noise_model_by_name`]. Each of these functions accepts either a name of a built-in noise model (see [`crate::NoiseModel::get_by_name`] for details).
+
+Noise models in the C API are represented by strings containing JSON serializations of the [`crate::NoiseModel`] data model. For example:
+
+```c
+#include
+#include "qdk_sim.h"
+
+int main() {
+ const char *noise_model;
+
+ if (get_noise_model_by_name("ideal", &noise_model) != 0) {
+ printf("Error getting noise model: %s", lasterr());
+ }
+
+ printf("Noise model:\n%s", noise_model);
+}
+
+```
+
+Running the above results in the JSON representation of the ideal noise model being written to the console:
+
+```text
+Noise model:
+{"initial_state":{"n_qubits":1,"data":{"Mixed":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}},"i":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0]]}}},"x":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.0,0.0],[1.0,0.0],[1.0,0.0],[0.0,0.0]]}}},"y":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.0,0.0],[0.0,1.0],[-0.0,-1.0],[0.0,0.0]]}}},"z":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[-1.0,-0.0]]}}},"h":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.7071067811865476,0.0],[0.7071067811865476,0.0],[0.7071067811865476,0.0],[-0.7071067811865476,-0.0]]}}},"s":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,1.0]]}}},"s_adj":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,-0.0],[0.0,-0.0],[0.0,-0.0],[0.0,-1.0]]}}},"t":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.7071067811865476,0.7071067811865476]]}}},"t_adj":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,-0.0],[0.0,-0.0],[0.0,-0.0],[0.7071067811865476,-0.7071067811865476]]}}},"cnot":{"n_qubits":2,"data":{"Unitary":{"v":1,"dim":[4,4],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0]]}}},"z_meas":{"Effects":[{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}},{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0]]}}}]}}
+```
+
+See [noise model serialization](crate#noise-model-serialization) for more details.
diff --git a/src/Simulation/qdk_sim_rs/docs/python-api.md b/src/Simulation/qdk_sim_rs/docs/python-api.md
new file mode 100644
index 00000000000..1950730920c
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/docs/python-api.md
@@ -0,0 +1,45 @@
+# Using Experimental Simulators from Python
+
+This module exposes the various data structures from this crate as Python objects, useful for embedding in Python programs.
+
+Note that this module contains Python-specific functionality, and cannot be used directly from Rust.
+
+> **ⓘ NOTE**: The Python API for this crate allows direct and low-level access to simulation data structures. This is distinct from using Python to run Q# programs on the experimental simulators implemented by this library. For details on how to use Python and Q# together with experimental simulators, please see documentation on the repository.
+
+## Building the Python API
+
+This crate automatically builds a Python extension module when compiled using the `python` Cargo feature. This module can be built into an installable Python package by using the `pip` command:
+
+```bash
+# Install the qdk_sim crate as a Python package.
+pip install .
+
+# Build this crate as a redistributable Python wheel, targeting the current
+# host architecture.
+pip wheel .
+```
+
+In both cases, `pip` will automatically discover and use your Rust toolchain to call `cargo build` and include its output in the Python package built from this crate.
+
+## Importing and working with the Python API
+
+Once installed using the above steps, the Python interface into the experimental simulators can be accessed as `import qdk_sim`. Doing so gives access to Python representations of different data structures in this crate. For example:
+
+```python
+>>> import qdk_sim
+>>> noise_model = qdk_sim.NoiseModel.get_by_name("ideal_stabilizer")
+>>> noise_model
+
+```
+
+Many Python objects implemented by this crate offer `as_json` methods that can be used to convert experimental simulator data structures to built-in Python objects:
+
+```python
+>>> import json
+>>> json.loads(qdk_sim.NoiseModel.get_by_name("ideal_stabilizer").as_json())
+{'initial_state': {'n_qubits': 1, 'data': {'Stabilizer': {'n_qubits': 1, 'table': {'v': 1, 'dim': [2, 3], 'data': [True, False, False, False, True, False]}}}}, 'i': {'n_qubits': 1, 'data': {'Sequence': []}}, 'x': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Hadamard': 0}, {'Phase': 0}, {'Phase': 0}, {'Hadamard': 0}]}}, 'y': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'AdjointPhase': 0}, {'Hadamard': 0}, {'Phase': 0}, {'Phase': 0}, {'Hadamard': 0}, {'Phase': 0}]}}, 'z': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Phase': 0}, {'Phase': 0}]}}, 'h': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Hadamard':
+0}]}}, 's': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Phase': 0}]}}, 's_adj': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'AdjointPhase': 0}]}}, 't': {'n_qubits': 1, 'data': 'Unsupported'}, 't_adj': {'n_qubits': 1, 'data': 'Unsupported'}, 'cnot': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Cnot': [0, 1]}]}}, 'z_meas': {'ZMeasurement': {'pr_readout_error': 0.0}}}
+```
diff --git a/src/Simulation/qdk_sim_rs/inject-version.csx b/src/Simulation/qdk_sim_rs/inject-version.csx
new file mode 100644
index 00000000000..ec71338f066
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/inject-version.csx
@@ -0,0 +1,39 @@
+#r "nuget: System.CommandLine, 2.0.0-beta1.21216.1"
+#r "nuget: Tommy, 2.0.0"
+
+using System.CommandLine;
+using System.Linq;
+using System.CommandLine.Invocation;
+using Tommy;
+
+// Create a root command with some options
+var rootCommand = new RootCommand
+{
+ new Option(
+ "--template",
+ description: "A file to use as the template for cargo manifest."),
+ new Option(
+ "--out-path",
+ description: "Path to write the generated manifest to."),
+ new Option(
+ "--version",
+ description: "The version number to inject.")
+};
+
+// Note that the parameters of the handler method are matched according to the names of the options
+rootCommand.Handler = CommandHandler.Create((template, outPath, version) =>
+{
+ Console.Out.WriteLine($"Injecting version {version} into {template} and writing to {outPath}.");
+ using var reader = new StreamReader(File.OpenRead(template.FullName));
+ var table = TOML.Parse(reader);
+
+ // Set the version number in the table.
+ table["package"]["version"] = version;
+
+ using var writer = new StreamWriter(File.OpenWrite(outPath));
+ table.WriteTo(writer);
+ // Remember to flush the data if needed!
+ writer.Flush();
+});
+
+await rootCommand.InvokeAsync(Args.ToArray());
diff --git a/src/Simulation/qdk_sim_rs/prerequisites.ps1 b/src/Simulation/qdk_sim_rs/prerequisites.ps1
new file mode 100644
index 00000000000..494635b723e
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/prerequisites.ps1
@@ -0,0 +1,25 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+if (-not (Get-Command rustup -ErrorAction SilentlyContinue)) {
+ if ($IsWindows -or $PSVersionTable.PSEdition -eq "Desktop") {
+ Invoke-WebRequest "https://win.rustup.rs" -OutFile rustup-init.exe
+ Unblock-File rustup-init.exe;
+ ./rustup-init.exe -y
+ } elseif ($IsLinux -or $IsMacOS) {
+ Invoke-WebRequest "https://sh.rustup.rs" | Select-Object -ExpandProperty Content | sh -s -- -y;
+ } else {
+ Write-Error "Host operating system not recognized as being Windows, Linux, or macOS; please download Rust manually from https://rustup.rs/."
+ }
+
+ if (-not (Get-Command rustup -ErrorAction SilentlyContinue)) {
+ Write-Error "After running rustup-init, rustup was not available. Please check logs above to see if something went wrong.";
+ exit -1;
+ }
+}
+
+# Now that rustup is available, go on and make sure that nightly support for
+# rustfmt and clippy is available.
+rustup install nightly
+rustup component add rustfmt clippy
+rustup component add rustfmt clippy --toolchain nightly
diff --git a/src/Simulation/qdk_sim_rs/pyproject.toml b/src/Simulation/qdk_sim_rs/pyproject.toml
new file mode 100644
index 00000000000..8619d8784c5
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools", "wheel", "setuptools-rust"]
+build-backend = "setuptools.build_meta"
diff --git a/src/Simulation/qdk_sim_rs/qdk_sim_experimental/__init__.py b/src/Simulation/qdk_sim_rs/qdk_sim_experimental/__init__.py
new file mode 100644
index 00000000000..53262d7bb3a
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/qdk_sim_experimental/__init__.py
@@ -0,0 +1,53 @@
+#!/bin/env python
+# -*- coding: utf-8 -*-
+##
+# __init__.py: Root for the qdk_sim_experimental package.
+##
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+##
+
+## IMPORTS ##
+
+from typing import Dict, List, Union
+from enum import Enum
+import enum
+import json
+
+import qdk_sim_experimental._qdk_sim_rs as _native
+try:
+ import qdk_sim_experimental.version as _version
+except ImportError:
+ # This could happen if setup.py was not run.
+ _version = None
+
+## EXPORTS ##
+
+__all__ = [
+ "Tableau", "NoiseModel", "Instrument", "State", "Process",
+ "Pauli"
+]
+
+# Re-export native classes.
+from qdk_sim_experimental._qdk_sim_rs import (
+ Tableau, NoiseModel, Instrument, State, Process
+)
+
+class Pauli(enum.Enum):
+ I = 0
+ X = 1
+ Y = 3
+ Z = 2
+
+## BUILD METADATA ##
+
+# Re-export the autogenerated version.
+__version__ = getattr(_version, "__version__", "")
+_is_conda = getattr(_version, "_is_conda", False)
+
+def build_info() -> Dict[str, Union[List[str], str]]:
+ """
+ Returns information about the environment in which this
+ module was built.
+ """
+ return json.loads(_native.build_info_json())
diff --git a/src/Simulation/qdk_sim_rs/qdk_sim_experimental/version.py b/src/Simulation/qdk_sim_rs/qdk_sim_experimental/version.py
new file mode 100644
index 00000000000..d15c19d5933
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/qdk_sim_experimental/version.py
@@ -0,0 +1,9 @@
+# Auto-generated file, do not edit.
+##
+# version.py: Specifies the version of the qsharp package.
+##
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+##
+__version__ = "0.0.0.1"
+_is_conda = False
diff --git a/src/Simulation/qdk_sim_rs/setup.py b/src/Simulation/qdk_sim_rs/setup.py
new file mode 100644
index 00000000000..6d3fad30592
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/setup.py
@@ -0,0 +1,47 @@
+#!/bin/env python
+# -*- coding: utf-8 -*-
+##
+# setup.py: Installs Python integration for qdk_sim_experimental.
+##
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+##
+
+## IMPORTS ##
+
+import setuptools
+from setuptools_rust import Binding, RustExtension
+import os
+
+## VERSION INFORMATION ##
+# Our build process sets the PYTHON_VERSION environment variable to a version
+# string that is compatible with PEP 440, and so we inherit that version number
+# here and propagate that to qsharp/version.py.
+#
+# To make sure that local builds still work without the same environment
+# variables, we'll default to 0.0.0.1 as a development version.
+
+version = os.environ.get('PYTHON_VERSION', '0.0.0.1')
+is_conda = bool(os.environ.get('CONDA_BUILD', False))
+
+with open('./qdk_sim_experimental/version.py', 'w') as f:
+ f.write(f'''# Auto-generated file, do not edit.
+##
+# version.py: Specifies the version of the qsharp package.
+##
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+##
+__version__ = "{version}"
+_is_conda = {is_conda}
+''')
+
+setuptools.setup(
+ name="qdk_sim_experimental",
+ version=version,
+ rust_extensions=[RustExtension("qdk_sim_experimental._qdk_sim_rs", binding=Binding.PyO3, features=["python"])],
+ packages=["qdk_sim_experimental"],
+ # rust extensions are not zip safe, just like C-extensions.
+ zip_safe=False,
+ include_package_data=True,
+)
diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs
new file mode 100644
index 00000000000..bf88c09065e
--- /dev/null
+++ b/src/Simulation/qdk_sim_rs/src/c_api.rs
@@ -0,0 +1,443 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// The following two attributes include the README.md for this module when
+// building docs (requires +nightly).
+// See https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643
+// for discussion.
+#![cfg_attr(doc, feature(extended_key_value_attributes))]
+#![cfg_attr(doc, cfg_attr(doc, doc = include_str!("../docs/c-api.md")))]
+
+use crate::{built_info, NoiseModel, Process, State};
+use lazy_static::lazy_static;
+use serde_json::json;
+use std::collections::HashMap;
+use std::ffi::CStr;
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::ptr;
+use std::sync::Mutex;
+
+struct CApiState {
+ register_state: State,
+ noise_model: NoiseModel,
+}
+
+lazy_static! {
+ static ref STATE: Mutex> = Mutex::new(HashMap::new());
+ static ref LAST_ERROR: Mutex