From b5aeb8846c50330fdced5f9922db18e735ee6dfc Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 14:29:17 -0800 Subject: [PATCH 001/158] Experiment to hook up Rust crate to cmake. --- src/QirRuntime/build-qir-runtime.ps1 | 2 +- src/QirRuntime/lib/Simulators/CMakeLists.txt | 17 +- .../lib/Simulators/OpenSystemsSimulator.cpp | 264 ++++++++++++++++++ .../lib/Simulators/opensim/Cargo.lock | 5 + .../lib/Simulators/opensim/Cargo.toml | 7 + .../lib/Simulators/opensim/src/c_api.rs | 44 +++ .../lib/Simulators/opensim/src/lib.rs | 2 + .../opensim/target/rls/.rustc_info.json | 1 + .../opensim/target/rls/CACHEDIR.TAG | 3 + 9 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp create mode 100644 src/QirRuntime/lib/Simulators/opensim/Cargo.lock create mode 100644 src/QirRuntime/lib/Simulators/opensim/Cargo.toml create mode 100644 src/QirRuntime/lib/Simulators/opensim/src/c_api.rs create mode 100644 src/QirRuntime/lib/Simulators/opensim/src/lib.rs create mode 100644 src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json create mode 100644 src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG diff --git a/src/QirRuntime/build-qir-runtime.ps1 b/src/QirRuntime/build-qir-runtime.ps1 index ea0654513e8..5c4a93b33b3 100644 --- a/src/QirRuntime/build-qir-runtime.ps1 +++ b/src/QirRuntime/build-qir-runtime.ps1 @@ -37,7 +37,7 @@ if ($Env:ENABLE_QIRRUNTIME -eq "true") { Push-Location $qirRuntimeBuildFolder - cmake -G Ninja -D CMAKE_BUILD_TYPE="$Env:BUILD_CONFIGURATION" ../.. + cmake -G Ninja -D CMAKE_BUILD_TYPE="$Env:BUILD_CONFIGURATION" $PSScriptRoot cmake --build . --target install Pop-Location diff --git a/src/QirRuntime/lib/Simulators/CMakeLists.txt b/src/QirRuntime/lib/Simulators/CMakeLists.txt index 190117a6304..ac1f10cb1e4 100644 --- a/src/QirRuntime/lib/Simulators/CMakeLists.txt +++ b/src/QirRuntime/lib/Simulators/CMakeLists.txt @@ -3,19 +3,34 @@ set(component_name "simulators") set(source_files "FullstateSimulator.cpp" "ToffoliSimulator.cpp" + "OpenSystemsSimulator.cpp" ) set(includes "${public_includes}" ) +#=============================================================================== +# Import Rust crates into CMake. + +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/AndrewGaspar/corrosion.git + GIT_TAG origin/master # Optionally specify a version tag or branch here +) + +FetchContent_MakeAvailable(Corrosion) + +corrosion_import_crate(MANIFEST_PATH opensim/Cargo.toml) #=============================================================================== # Produce static lib for users to link directly to add_library(${component_name} STATIC ${source_files}) target_include_directories(${component_name} PUBLIC ${includes}) -target_link_libraries(${component_name} ${CMAKE_DL_LIBS}) +target_link_libraries(${component_name} ${CMAKE_DL_LIBS} PUBLIC rust_example) #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp new file mode 100644 index 00000000000..27ff5d771b9 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +#include "SimFactory.hpp" +#include "QuantumApi_I.hpp" + +using namespace std; +typedef long long int i64; + +// Define the API with the open systems simulator. +extern "C" +{ + size_t init(size_t initialcapacity); + i64 destroy(size_t sim_id); + void dump_to_console(size_t sim_id); + i64 x(size_t sim_id, size_t idx); + i64 y(size_t sim_id, size_t idx); + i64 z(size_t sim_id, size_t idx); + i64 h(size_t sim_id, size_t idx); + i64 s(size_t sim_id, size_t idx); + i64 s_adj(size_t sim_id, size_t idx); + i64 t(size_t sim_id, size_t idx); + i64 t_adj(size_t sim_id, size_t idx); + i64 cnot(size_t sim_id, size_t idx_control, size_t idx_target); + i64 m(size_t sim_id, size_t idx, size_t* result_out); +} + +namespace Microsoft +{ +namespace Quantum +{ + // FIXME: support public IDiagnostics + class OpenSystemSimulator : public ISimulator, public IQuantumGateSet + { + typedef void (*TSingleQubitGate)(size_t /*simulator id*/, size_t /*qubit id*/); + typedef void (*TSingleQubitControlledGate)( + unsigned /*simulator id*/, + unsigned /*number of controls*/, + unsigned* /*controls*/, + unsigned /*qubit id*/); + + // QuantumSimulator defines paulis as: + // enum Basis + // { + // PauliI = 0, + // PauliX = 1, + // PauliY = 3, + // PauliZ = 2 + // }; + // which (surprise!) matches our definition of PauliId enum + static inline unsigned GetBasis(PauliId pauli) + { + return static_cast(pauli); + } + + unsigned simulatorId = -1; + unsigned nextQubitId = 0; // the QuantumSimulator expects contiguous ids, starting from 0 + + unsigned GetQubitId(Qubit qubit) const + { + return static_cast(reinterpret_cast(qubit)); + } + + vector GetQubitIds(long num, Qubit* qubits) const + { + vector ids; + ids.reserve(num); + for (long i = 0; i < num; i++) + { + ids.push_back(static_cast(reinterpret_cast(qubits[i]))); + } + return ids; + } + + // use for debugging the simulator + void DumpState() + { + dump_to_console(simulatorId); + } + + public: + OpenSystemSimulator() + { + // FIXME: allow setting number of qubits. + this->simulatorId = init(3); + } + ~OpenSystemSimulator() + { + destroy(this->simulatorId); + } + + IQuantumGateSet* AsQuantumGateSet() override + { + return this; + } + + virtual std::string QubitToString(Qubit q) override + { + return std::to_string(GetQubitId(q)); + } + + Qubit AllocateQubit() override + { + const unsigned id = this->nextQubitId; + this->nextQubitId++; + return reinterpret_cast(id); + } + + void ReleaseQubit(Qubit q) override + { + // TODO + } + + Result M(Qubit q) override + { + size_t result; + m(this->simulatorId, GetQubitId(q), &result); + return reinterpret_cast(result); + } + + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + throw std::logic_error("not yet implemented"); + } + + void ReleaseResult(Result r) override {} + + ResultValue GetResultValue(Result r) override + { + const unsigned val = static_cast(reinterpret_cast(r)); + assert(val == 0 || val == 1); + return (val == 0) ? Result_Zero : Result_One; + } + + Result UseZero() override + { + return reinterpret_cast(0); + } + + Result UseOne() override + { + return reinterpret_cast(1); + } + + bool AreEqualResults(Result r1, Result r2) override + { + return (r1 == r2); + } + + void X(Qubit q) override + { + x(this->simulatorId, GetQubitId(q)); + } + + void ControlledX(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void Y(Qubit q) override + { + y(this->simulatorId, GetQubitId(q)); + } + + void ControlledY(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void Z(Qubit q) override + { + z(this->simulatorId, GetQubitId(q)); + } + + void ControlledZ(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void H(Qubit q) override + { + h(this->simulatorId, GetQubitId(q)); + } + + void ControlledH(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void S(Qubit q) override + { + s(this->simulatorId, GetQubitId(q)); + } + + void ControlledS(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void AdjointS(Qubit q) override + { + s_adj(this->simulatorId, GetQubitId(q)); + } + + void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void T(Qubit q) override + { + t(this->simulatorId, GetQubitId(q)); + } + + void ControlledT(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void AdjointT(Qubit q) override + { + t_adj(this->simulatorId, GetQubitId(q)); + } + + void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not yet implemented"); + } + + void R(PauliId axis, Qubit target, double theta) override + { + throw std::logic_error("not yet implemented"); + } + + void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override + { + throw std::logic_error("not yet implemented"); + } + + void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override + { + throw std::logic_error("not yet implemented"); + } + + void ControlledExp( + long numControls, + Qubit controls[], + long numTargets, + PauliId paulis[], + Qubit targets[], + double theta) override + { + throw std::logic_error("not yet implemented"); + } + }; +} // namespace Quantum +} // namespace Microsoft \ No newline at end of file diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock new file mode 100644 index 00000000000..5246b8d61b4 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rust_example" +version = "0.1.0" diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml new file mode 100644 index 00000000000..00227b22278 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust_example" +version = "0.1.0" + +[lib] +name = "rust_example" +crate-type = ["staticlib"] diff --git a/src/QirRuntime/lib/Simulators/opensim/src/c_api.rs b/src/QirRuntime/lib/Simulators/opensim/src/c_api.rs new file mode 100644 index 00000000000..484529be3f5 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/src/c_api.rs @@ -0,0 +1,44 @@ + +#[no_mangle] +pub extern fn init(initial_capacity: usize) -> usize { + 0 +} + +#[no_mangle] +pub extern fn destroy(sim_id: usize) -> i64 { 0 } + +#[no_mangle] +pub extern fn dump_to_console() -> () {} + +#[no_mangle] +pub extern fn x(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub extern fn y(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub extern fn z(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub extern fn h(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub fn s(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub fn s_adj(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub fn t(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub fn t_adj(sim_id: usize, idx: usize) -> i64 { 0 } + +#[no_mangle] +pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { 0 } + +#[no_mangle] +pub extern fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { + unsafe { *result_out = 0; } + 0 +} diff --git a/src/QirRuntime/lib/Simulators/opensim/src/lib.rs b/src/QirRuntime/lib/Simulators/opensim/src/lib.rs new file mode 100644 index 00000000000..be1b30483a5 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/src/lib.rs @@ -0,0 +1,2 @@ + +mod c_api; diff --git a/src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json b/src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json new file mode 100644 index 00000000000..e2dc831c7c9 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":14273413640433523151,"outputs":{"4476964694761187371":["___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\cgran\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n",""],"1164083562126845933":["rustc 1.47.0 (18bf6b4f0 2020-10-07)\nbinary: rustc\ncommit-hash: 18bf6b4f01a6feaf7259ba7cdae58031af1b7b39\ncommit-date: 2020-10-07\nhost: x86_64-pc-windows-msvc\nrelease: 1.47.0\nLLVM version: 11.0\n",""]},"successes":{}} \ No newline at end of file diff --git a/src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG b/src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG new file mode 100644 index 00000000000..ec615bc7c4d --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/ \ No newline at end of file From ecc9539cd101f560cdd7078f049285e70d6874a1 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 15:57:15 -0800 Subject: [PATCH 002/158] Add experimental simulator as rlib crate. --- src/QirRuntime/.gitignore | 1 + src/QirRuntime/lib/Simulators/CMakeLists.txt | 2 +- .../lib/Simulators/opensim/.gitignore | 1 + .../lib/Simulators/opensim/Cargo.lock | 351 ++++++++++++++++- .../lib/Simulators/opensim/Cargo.toml | 7 +- .../opensim/target/rls/.rustc_info.json | 1 - .../opensim/target/rls/CACHEDIR.TAG | 3 - src/Simulation/OpenSystems/.gitignore | 1 + src/Simulation/OpenSystems/runtime/Cargo.lock | 363 ++++++++++++++++++ src/Simulation/OpenSystems/runtime/Cargo.toml | 46 +++ .../OpenSystems/runtime/src/channels.rs | 311 +++++++++++++++ .../runtime/src/common_matrices.rs | 61 +++ .../OpenSystems/runtime/src/instrument.rs | 63 +++ src/Simulation/OpenSystems/runtime/src/lib.rs | 68 ++++ .../OpenSystems/runtime/src/linalg.rs | 155 ++++++++ .../OpenSystems/runtime/src/noise_model.rs | 103 +++++ .../OpenSystems/runtime/src/states.rs | 119 ++++++ .../OpenSystems/runtime/src/utils.rs | 28 ++ 18 files changed, 1676 insertions(+), 8 deletions(-) create mode 100644 src/QirRuntime/.gitignore create mode 100644 src/QirRuntime/lib/Simulators/opensim/.gitignore delete mode 100644 src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json delete mode 100644 src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG create mode 100644 src/Simulation/OpenSystems/.gitignore create mode 100644 src/Simulation/OpenSystems/runtime/Cargo.lock create mode 100644 src/Simulation/OpenSystems/runtime/Cargo.toml create mode 100644 src/Simulation/OpenSystems/runtime/src/channels.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/common_matrices.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/instrument.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/lib.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/linalg.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/noise_model.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/states.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/utils.rs diff --git a/src/QirRuntime/.gitignore b/src/QirRuntime/.gitignore new file mode 100644 index 00000000000..378eac25d31 --- /dev/null +++ b/src/QirRuntime/.gitignore @@ -0,0 +1 @@ +build diff --git a/src/QirRuntime/lib/Simulators/CMakeLists.txt b/src/QirRuntime/lib/Simulators/CMakeLists.txt index ac1f10cb1e4..c2d2ec7761d 100644 --- a/src/QirRuntime/lib/Simulators/CMakeLists.txt +++ b/src/QirRuntime/lib/Simulators/CMakeLists.txt @@ -30,7 +30,7 @@ corrosion_import_crate(MANIFEST_PATH opensim/Cargo.toml) add_library(${component_name} STATIC ${source_files}) target_include_directories(${component_name} PUBLIC ${includes}) -target_link_libraries(${component_name} ${CMAKE_DL_LIBS} PUBLIC rust_example) +target_link_libraries(${component_name} ${CMAKE_DL_LIBS} PUBLIC opensim_static) #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/QirRuntime/lib/Simulators/opensim/.gitignore b/src/QirRuntime/lib/Simulators/opensim/.gitignore new file mode 100644 index 00000000000..eb5a316cbd1 --- /dev/null +++ b/src/QirRuntime/lib/Simulators/opensim/.gitignore @@ -0,0 +1 @@ +target diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock index 5246b8d61b4..c08dff891d5 100644 --- a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock +++ b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock @@ -1,5 +1,354 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "rust_example" +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bumpalo" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matrixmultiply" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "ndarray" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "rawpointer", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "opensim_runtime" version = "0.1.0" +dependencies = [ + "derive_more", + "getrandom", + "itertools", + "ndarray", + "num-complex", + "num-traits", + "rand", + "serde", + "serde_json", +] + +[[package]] +name = "opensim_static" +version = "0.1.0" +dependencies = [ + "opensim_runtime", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml index 00227b22278..711bfc6b617 100644 --- a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml +++ b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml @@ -1,7 +1,10 @@ [package] -name = "rust_example" +name = "opensim_static" version = "0.1.0" [lib] -name = "rust_example" +name = "opensim_static" crate-type = ["staticlib"] + +[dependencies] +opensim_runtime = { path = "../../../../Simulation/OpenSystems/runtime" } diff --git a/src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json b/src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json deleted file mode 100644 index e2dc831c7c9..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/target/rls/.rustc_info.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc_fingerprint":14273413640433523151,"outputs":{"4476964694761187371":["___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\cgran\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n",""],"1164083562126845933":["rustc 1.47.0 (18bf6b4f0 2020-10-07)\nbinary: rustc\ncommit-hash: 18bf6b4f01a6feaf7259ba7cdae58031af1b7b39\ncommit-date: 2020-10-07\nhost: x86_64-pc-windows-msvc\nrelease: 1.47.0\nLLVM version: 11.0\n",""]},"successes":{}} \ No newline at end of file diff --git a/src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG b/src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG deleted file mode 100644 index ec615bc7c4d..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/target/rls/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by cargo. -# For information about cache directory tags see https://bford.info/cachedir/ \ No newline at end of file diff --git a/src/Simulation/OpenSystems/.gitignore b/src/Simulation/OpenSystems/.gitignore new file mode 100644 index 00000000000..eb5a316cbd1 --- /dev/null +++ b/src/Simulation/OpenSystems/.gitignore @@ -0,0 +1 @@ +target diff --git a/src/Simulation/OpenSystems/runtime/Cargo.lock b/src/Simulation/OpenSystems/runtime/Cargo.lock new file mode 100644 index 00000000000..22b3a1b2db6 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/Cargo.lock @@ -0,0 +1,363 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "js-sys" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "matrixmultiply" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "ndarray" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "rawpointer", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "opensim_runtime" +version = "0.1.0" +dependencies = [ + "derive_more", + "getrandom", + "itertools", + "ndarray", + "num-complex", + "num-traits", + "rand", + "serde", + "serde_json", + "web-sys", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" + +[[package]] +name = "web-sys" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml new file mode 100644 index 00000000000..3315d49137f --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "opensim_runtime" +version = "0.1.0" +authors = ["Christopher Granade "] +edition = "2018" + +[features] +default = [] + +# When enabled, web-sys-log allows for logging runtime error messages and +# warnings via console.log. +web-sys-log = ["web-sys"] + +[profile.release] +opt-level = 'z' # Optimize for size. +codegen-units = 1 # Reduce number of codegen units to increase optimizations. +panic = 'unwind' +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ndarray = { version = "0.13.1", features = ["serde"] } +num-complex = { version = "0.2", features = ["serde"] } +num-traits = "0.2" +derive_more = "0.99.10" +itertools = "0.9.0" +rand = "0.7.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# 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 web-sys-log feature. +web-sys = { version = "0.3.4", features = ['console'], optional = true } + +[lib] +name = "opensim_runtime" +path = "src/lib.rs" +# Build a Rust library from this crate. That can't be used directly except +# by other Rust crates, but allows us to retarget the runtime here as different +# kinds of libraries with thin wrappers later. +crate-type = ["rlib"] diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs new file mode 100644 index 00000000000..1ed8fc8b04d --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -0,0 +1,311 @@ + +use crate::{ log_as_err, log_message }; +use crate::NoiseModel; +use crate::linalg::{ extend_one_to_n, extend_two_to_n }; +use itertools::zip; +use std::ops::Add; +use std::convert::TryInto; +use ndarray::Axis; +use crate::zeros_like; +use std::ops::Mul; +use crate::channels::ChannelData::{ Unitary, KrausDecomposition }; +use crate::states::StateData::{ Pure, Mixed }; +use crate::State; +use crate::C64; +use ndarray::{ Array, Array2, Array3 }; +use crate::QubitSized; +use crate::linalg::ConjBy; +use itertools::Itertools; +use num_traits::{ Zero, One }; +use serde::{ Serialize, Deserialize }; + +pub type Channel = QubitSized; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum ChannelData { + Unitary(Array2), + KrausDecomposition(Array3) + // TODO: Superoperator and Choi reps. +} + +impl Channel { + // TODO: methods to forcibly convert representations. + pub fn apply(self: &Self, state: &State) -> Result { + if state.n_qubits == self.n_qubits { + Ok(State { + n_qubits: self.n_qubits, + data: match &state.data { + Pure(psi) => todo!(), + Mixed(rho) => match &self.data { + Unitary(u) => Mixed(rho.conjugate_by(u)), + KrausDecomposition(ks) => Mixed({ + let mut sum: Array2 = Array::zeros((rho.shape()[0], rho.shape()[1])); + for k in ks.axis_iter(Axis(0)) { + // FIXME: performance issue, this to_owned shouldn't be needed. + sum = sum + rho.conjugate_by(&k.to_owned()); + } + sum + }) + } + } + }) + } else { + Err(format!( + "Channel acts on {} qubits, but was applied to {}-qubit state.", + self.n_qubits, state.n_qubits + )) + } + } + + pub fn apply_to(self: &Self, idx_qubits: &[usize], state: &State) -> Result { + // Fail if there's not enough qubits. + if state.n_qubits < self.n_qubits { + return log_as_err(format!( + "Channel acts on {} qubits, but a state on only {} qubits was given.", + self.n_qubits, state.n_qubits + )); + } + + // Fail if any indices are repeated. + if idx_qubits.into_iter().unique().count() < idx_qubits.len() { + return log_as_err(format!( + "List of qubit indices {:?} contained repeated elements.", + idx_qubits + )); + } + + // Make sure that there are only as many indices as qubits that this + // channel acts upon. + if idx_qubits.len() != self.n_qubits { + return log_as_err(format!( + "Qubit indices were specified as {:?}, but this channel only acts on {} qubits.", + idx_qubits, self.n_qubits + )) + } + + // At this point we know that idx_qubits has self.n_qubits many unique + // indices in ascending order, so we can proceed to make a new channel + // that expands this channel to act on the full register and then use + // the ordinary apply method. + let expanded = match self.n_qubits { + 1 => self.extend_one_to_n(idx_qubits[0], state.n_qubits), + 2 => { + self.extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits) + }, + _ => { + log_message(&format!("Expanding {}-qubit channels is not yet implemented.", self.n_qubits)); + todo!(""); + } + }; + expanded.apply(state) + } + + pub fn extend_one_to_n(self: &Self, idx_qubit: usize, n_qubits: usize) -> Channel { + assert_eq!(self.n_qubits, 1); + Channel { + n_qubits: n_qubits, + data: match &self.data { + Unitary(u) => Unitary(extend_one_to_n(u, idx_qubit, n_qubits)), + KrausDecomposition(ks) => { + let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); + let n_kraus = ks.shape()[0]; + let mut extended: Array3 = Array::zeros((n_kraus, new_dim, new_dim)); + for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { + let mut target = extended.index_axis_mut(Axis(0), idx_kraus); + let big_kraus = extend_one_to_n( + &kraus.to_owned(), + idx_qubit, n_qubits + ); + target.assign(&big_kraus); + }; + KrausDecomposition(extended) + } + } + } + } + + pub fn extend_two_to_n(self: &Self, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize) -> Channel { + assert_eq!(self.n_qubits, 2); + Channel { + n_qubits: n_qubits, + data: match &self.data { + Unitary(u) => { + Unitary(extend_two_to_n(u, idx_qubit1, idx_qubit2, n_qubits)) + }, + KrausDecomposition(ks) => { + todo!("extend_two_to_n for Kraus decompositions") + } + } + } + } +} + +impl Mul<&Channel> for C64 { + type Output = Channel; + + fn mul(self, channel: &Channel) -> Self::Output { + Channel { + n_qubits: channel.n_qubits, + data: match &channel.data { + // Note that we need to multiply by the square root in + // both cases, since these representations are both in terms + // of linear operators, but the multiplication is on + // superoperators (two copies of the original vectorspace). + Unitary(u) => KrausDecomposition({ + let mut ks = Array3::::zeros((1, u.shape()[0], u.shape()[1])); + ks.index_axis_mut(Axis(0), 0).assign(&(self.sqrt() * u)); + ks + }), + KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks) + } + } + } +} + +impl Mul for C64 { + type Output = Channel; + fn mul(self, channel: Channel) -> Self::Output { + self * (&channel) + } +} + +impl Mul<&Channel> for f64 { + type Output = Channel; + fn mul(self, chanel: &Channel) -> Self::Output { + C64::new(self, 0f64) * chanel + } +} + +impl Mul for f64 { + type Output = Channel; + fn mul(self, channel: Channel) -> Self::Output { + self * (&channel) + } +} + +// Base case: both channels that we're composing are borrowed. +impl Mul<&Channel> for &Channel { + type Output = Channel; + + fn mul(self, rhs: &Channel) -> Self::Output { + assert_eq!(self.n_qubits, rhs.n_qubits); + Channel { + n_qubits: self.n_qubits, + data: match (&self.data, &rhs.data) { + (Unitary(u), Unitary(v)) => Unitary(u.dot(v)), + (Unitary(u), KrausDecomposition(ks)) => { + // post-multiply each kraus operator by u. + let mut post = zeros_like(ks); + for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { + post.index_axis_mut(Axis(0), idx_kraus).assign( + &u.dot(&kraus) + ); + } + KrausDecomposition(post) + }, + // TODO: product of two kraus decompositions would be... not + // fun. + _ => todo!() + } + } + } +} + +impl Add<&Channel> for &Channel { + type Output = Channel; + + fn add(self, rhs: &Channel) -> Self::Output { + assert_eq!(self.n_qubits, rhs.n_qubits); + Channel { + n_qubits: self.n_qubits, + data: match (&self.data, &rhs.data) { + (KrausDecomposition(ks1), KrausDecomposition(ks2)) => { + let mut sum = Array::zeros([ + ks1.shape()[0] + ks2.shape()[0], + ks1.shape()[1], + ks1.shape()[2] + ]); + for (idx_kraus, kraus) in ks1.axis_iter(Axis(0)).enumerate() { + sum.index_axis_mut(Axis(0), idx_kraus).assign(&kraus); + } + for (idx_kraus, kraus) in ks2.axis_iter(Axis(0)).enumerate() { + sum.index_axis_mut(Axis(0), ks1.shape()[0] + idx_kraus).assign(&kraus); + } + KrausDecomposition(sum) + }, + _ => todo!() + } + } + } +} + + +impl Mul for &Channel { + type Output = Channel; + + fn mul(self, rhs: Channel) -> Self::Output { + self * &rhs + } +} + +impl Mul<&Channel> for Channel { + type Output = Channel; + + fn mul(self, rhs: &Channel) -> Self::Output { + &self * rhs + } +} + +impl Mul for Channel { + type Output = Channel; + + fn mul(self, rhs: Channel) -> Self::Output { + &self * &rhs + } +} + +impl Add for &Channel { + type Output = Channel; + + fn add(self, rhs: Channel) -> Self::Output { + self + &rhs + } +} + +impl Add<&Channel> for Channel { + type Output = Channel; + + fn add(self, rhs: &Channel) -> Self::Output { + &self + rhs + } +} + +impl Add for Channel { + type Output = Channel; + + fn add(self, rhs: Channel) -> Self::Output { + &self + &rhs + } +} + +pub fn depolarizing_channel(p: f64) -> Channel { + let ideal = NoiseModel::ideal(); + (1.0 - p) * ideal.i + p / 3.0 * ideal.x + p / 3.0 * ideal.y + p / 3.0 * ideal.z +} + +pub fn amplitude_damping_channel(gamma: f64) -> Channel { + Channel { + n_qubits: 1, + data: KrausDecomposition(array![ + [ + [ C64::one(), C64::zero() ], + [ C64::zero(), C64::one() * (1.0 - gamma).sqrt()] + ], + + [ + [ C64::zero(), C64::one() * gamma.sqrt() ], + [ C64::zero(), C64::zero() ] + ] + ]) + } +} diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs new file mode 100644 index 00000000000..5bc1cce386e --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs @@ -0,0 +1,61 @@ +use core::f64::consts::FRAC_1_SQRT_2; +use crate::utils::*; +use ndarray::Array2; + +pub fn I() -> Array2 { + array![ + [ONE_C, ZERO_C], + [ZERO_C, ONE_C] + ] +} + +pub fn X() -> Array2 { + array![ + [ZERO_C, ONE_C], + [ONE_C, ZERO_C] + ] +} + +pub fn Y() -> Array2 { + array![ + [ZERO_C, I_C], + [-I_C, ZERO_C] + ] +} + +pub fn Z() -> Array2 { + array![ + [ONE_C, ZERO_C], + [ZERO_C, -ONE_C] + ] +} + +pub fn H() -> Array2 { + array![ + [ONE_C, ONE_C], + [ONE_C, -ONE_C] + ] * FRAC_1_SQRT_2 +} + +pub fn T() -> Array2 { + array![ + [ONE_C, ZERO_C], + [ZERO_C, C64::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2)] + ] +} + +pub fn S() -> Array2 { + array![ + [ONE_C, ZERO_C], + [ZERO_C, C64::new(0.0_f64, 1.0_f64)] + ] +} + +pub fn CNOT() -> Array2 { + array![ + [ONE_C, ZERO_C, ZERO_C, ZERO_C], + [ZERO_C, ONE_C, ZERO_C, ZERO_C], + [ZERO_C, ZERO_C, ZERO_C, ONE_C], + [ZERO_C, ZERO_C, ONE_C, ZERO_C] + ] +} diff --git a/src/Simulation/OpenSystems/runtime/src/instrument.rs b/src/Simulation/OpenSystems/runtime/src/instrument.rs new file mode 100644 index 00000000000..b82af0b1bfd --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/instrument.rs @@ -0,0 +1,63 @@ +use crate::states::StateData::Mixed; +use crate::Channel; +use std::iter::Iterator; +use rand::Rng; + +use crate::State; +use crate::channels::ChannelData; +use crate::QubitSized; +use crate::linalg::Trace; + +use serde::{ Serialize, Deserialize }; +use crate::utils::log_message; + + +#[derive(Serialize, Deserialize)] +pub struct Instrument { + pub effects: Vec +} + +impl Instrument { + pub fn sample(&self, idx_qubits: &[usize], state: &State) -> (usize, State) { + let mut possible_outcomes = self.effects.iter().enumerate().map(|(idx, effect)| { + let output_state = effect.apply_to(idx_qubits, state).unwrap(); + let tr = (&output_state).trace(); + ( + idx, + output_state, + tr.re + ) + }).collect::>(); + let mut rng = rand::thread_rng(); + let random_sample: f64 = rng.gen(); + for (idx, cum_pr) in possible_outcomes.iter().scan( + 0.0f64, + |acc, (_idx, _, pr)| { + *acc = *acc + *pr; + Some(*acc) + } + ).enumerate() { + if random_sample < cum_pr { + // In order to not have to copy the output state, we need + // to be able to move it out from the vector. To do so, + // we retain only the element of the vector whose index + // is the one we want and then pop it, leaving an empty + // vector (that is, a vector that owns no data). + possible_outcomes.retain(|(i, _, _)| idx == *i); + let (_, mut output_state, tr) = possible_outcomes.pop().unwrap(); + if tr.abs() >= 1e-10 { + if let Mixed(ref rho) = output_state.data { + output_state.data = Mixed(rho * (1.0f64 / tr)); + } else { + panic!("Couldn't renormalize, expected mixed output from instrument."); + } + } + return (idx, output_state); + } + }; + let idx_last = &possible_outcomes.len() - 1; + let (idx, output_state, _) = possible_outcomes.pop().unwrap(); + drop(possible_outcomes); + (idx, output_state) + } +} diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs new file mode 100644 index 00000000000..7983eb3a70f --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -0,0 +1,68 @@ +#[macro_use(array)] + +extern crate ndarray; +extern crate derive_more; +extern crate serde; + +mod utils; +mod common_matrices; +mod states; +mod linalg; +mod channels; +mod noise_model; +mod instrument; +use num_complex::Complex; +use ndarray::{ Array1 }; + +pub use crate::channels::depolarizing_channel; +pub use crate::noise_model::NoiseModel; +pub use crate::utils::*; +pub use crate::common_matrices::*; +pub use crate::linalg::zeros_like; +pub use crate::states::State; +pub use crate::channels::Channel; +pub use crate::channels::ChannelData::KrausDecomposition; +pub use crate::channels::ChannelData::Unitary; + +use serde::{ Serialize, Deserialize }; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct QubitSized { + n_qubits: usize, + data: T +} + +pub fn test() { + println!("Test!"); + let mut noise = NoiseModel::ideal(); + noise.h = noise.h * depolarizing_channel(0.025f64); + match &noise.h.data { + Unitary(u) => println!("{}", u), + KrausDecomposition(ks) => println!("{}", ks) + }; + let noise = noise; + + let mut rho = noise.initial_state.clone(); + // rho = rho.extend(1); + // rho = noise.h.apply_to(&[0], &rho).unwrap(); + // rho = noise.h.apply_to(&[0], &rho).unwrap(); + // rho = noise.h.apply_to(&[0], &rho).unwrap(); + // rho = noise.h.apply_to(&[0], &rho).unwrap(); + // rho = noise.h.apply_to(&[0], &rho).unwrap(); + + println!("{}", rho); + + let pi_0 = &noise.z_meas.effects[0]; + let rho_0 = pi_0.apply_to(&[0], &rho).unwrap(); + + println!("{}", rho_0); + + println!(""); + println!("{}", rho.ideal_z_meas_pr(0)); + println!("{}", rho.ideal_z_meas_pr(1)); + + println!(""); + println!("{:?}", depolarizing_channel(0.05f64)); + + // println!("{}", X()) +} diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs new file mode 100644 index 00000000000..690be2d4360 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -0,0 +1,155 @@ +use core::ops::Range; +use num_traits::Zero; +use ndarray::{ Array, Array1, Array2 }; +use std::ops::Mul; +use std::convert::TryInto; +use std::cmp; +use crate::{ C64, log_message }; + +pub trait HasDagger { + fn dag(self: &Self) -> Self; +} + +impl HasDagger for Array2 { + fn dag(self: &Self) -> Self { + self.t().map(|element| element.conj()) + } +} + +pub trait ConjBy { + fn conjugate_by(self: &Self, op: &Array2) -> Self; +} + +impl ConjBy for Array2 { + fn conjugate_by(self: &Self, op: &Array2) -> Self { + op.dot(self).dot(&op.dag()) + } +} + +pub trait Tensor { + fn tensor(self: &Self, other: &Self) -> Self; +} + +impl > Tensor for Array1 { + fn tensor(&self, other: &Self) -> Self { + let unflat = Array::from_shape_fn( + (self.shape()[0], other.shape()[0]), + |(i, j)| { + self[(i)] * other[(j)] + } + ); + unflat.into_shape(self.shape()[0] * other.shape()[0]).unwrap() + } +} + +impl > Tensor for Array2 { + fn tensor(&self, other: &Self) -> Self { + let unflat = Array::from_shape_fn( + (self.shape()[0], other.shape()[0], self.shape()[1], other.shape()[1]), + |(i, j, k, l)| { + self[(i, k)] * other[(j, l)] + } + ); + unflat.into_shape((self.shape()[0] * other.shape()[0], self.shape()[1] * other.shape()[1])).unwrap() + } +} + +pub trait Trace { + type Output; + + fn trace(self) -> Self::Output; +} + +impl Trace for Array2 { + type Output = T; + + fn trace(self) -> Self::Output { + self.diag().sum() + } +} + +impl Trace for &Array2 { + type Output = T; + + fn trace(self) -> Self::Output { + self.diag().sum() + } +} + +fn nq_eye(nq: usize) -> Array2 { + Array2::eye(2usize.pow(nq.try_into().unwrap())) +} + +// FIXME: weaken data to be a view so that to_owned isn't needed. +// FIXME: modify to Result<..., String> so that errors can propagate to the C API. +pub fn extend_one_to_n(data: &Array2, idx_qubit: usize, n_qubits: usize) -> Array2 { + let n_left = idx_qubit; + let n_right = n_qubits - idx_qubit - 1; + match (n_left, n_right) { + (0, _) => { + let right_eye = nq_eye(n_right); + data.tensor(&right_eye) + }, + (_, 0) => { + let left_eye = Array2::eye(2usize.pow(n_left.try_into().unwrap())); + left_eye.tensor(data) + }, + (_, _) => nq_eye(n_left).tensor(&data.tensor(&nq_eye(n_right))) + } +} + +pub fn extend_two_to_n(data: &Array2, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize) -> Array2 { + // TODO: double check that data is 4x4. + let mut permutation = Array::from((0..n_qubits).collect::>()); + match (idx_qubit1, idx_qubit2) { + (1, 0) => permutation.swap(0, 1), + (_, 0) => { + permutation.swap(1, idx_qubit2); + permutation.swap(1, idx_qubit1); + }, + _ => { + permutation.swap(1, idx_qubit2); + permutation.swap(0, idx_qubit1); + } + }; + + // TODO: there is almost definitely a more elegant way to write this. + if n_qubits == 2 { + permute_mtx(data, &permutation.to_vec()[..]) + } else { + permute_mtx(&data.tensor(&nq_eye(n_qubits - 2)), &permutation.to_vec()[..]) + } +} + +/// Given a two-index array (i.e.: a matrix) of dimensions 2^n × 2^n for some +/// n, permutes the left and right indices of the matrix. +/// Used to represent, for example, swapping qubits in a register. +pub fn permute_mtx(data: &Array2, new_order: &[usize]) -> Array2 { + // Check that data is square. + let (n_rows, n_cols) = (data.shape()[0], data.shape()[1]); + assert_eq!(n_rows, n_cols); + + // Check that dims are 2^n and find n. + let n_qubits = (n_rows as f64).log2().floor() as usize; + assert_eq!(n_rows, 2usize.pow(n_qubits.try_into().unwrap())); + + // Check that the length of new_order is the same as the number of qubits. + assert_eq!(n_qubits, new_order.len()); + + // FIXME: there has to be a better way to make a vector that consists of + // 2n copies of 2usize. + let new_dims: Vec = vec![2usize].iter().cycle().take(2 * n_qubits).map(|x| x.clone()).collect(); + // FIXME: make this a result and propagate the result out to the return. + let tensor = data.clone().into_shared().reshape(new_dims); + let mut permutation = new_order.to_vec(); + permutation.extend(new_order.to_vec().iter().map(|idx| idx + n_qubits)); + let permuted = tensor.permuted_axes(permutation); + + // Finish by collapsing back down. + permuted.reshape([n_rows, n_rows]).into_owned() +} + + +pub fn zeros_like(data: &Array) -> Array { + Array::zeros(data.dim()) +} diff --git a/src/Simulation/OpenSystems/runtime/src/noise_model.rs b/src/Simulation/OpenSystems/runtime/src/noise_model.rs new file mode 100644 index 00000000000..cbc6f35430a --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/noise_model.rs @@ -0,0 +1,103 @@ +use num_traits::{ Zero, One }; +use crate::C64; +use crate::common_matrices::{ I, X, Y, Z, H, T, CNOT, S }; +use crate::states::StateData::Mixed; +use crate::states::State; +use crate::channels::Channel; +use crate::channels::ChannelData::{ Unitary, KrausDecomposition }; +use crate::instrument::Instrument; +use crate::linalg::HasDagger; + +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize)] +pub struct NoiseModel { + pub initial_state: State, + pub i: Channel, + pub x: Channel, + pub y: Channel, + pub z: Channel, + pub h: Channel, + pub s: Channel, + pub s_adj: Channel, + pub t: Channel, + pub t_adj: Channel, + pub cnot: Channel, + pub z_meas: Instrument +} + +impl NoiseModel { + pub fn ideal() -> NoiseModel { + let i = Channel { + n_qubits: 1, + data: Unitary(I()) + }; + let z = Channel { + n_qubits: 1, + data: Unitary(Z()) + }; + let z_meas = Instrument { + effects: vec![ + Channel { + n_qubits: 1, + data: KrausDecomposition(array![ + [ + [C64::one(), C64::zero()], + [C64::zero(), C64::zero()] + ] + ]) + }, + Channel { + n_qubits: 1, + data: KrausDecomposition(array![ + [ + [C64::zero(), C64::zero()], + [C64::zero(), C64::one()] + ] + ]) + }, + ] + }; + NoiseModel { + initial_state: State { + n_qubits: 1, + data: Mixed((I() + Z()) / 2.0) + }, + i: i, + x: Channel { + n_qubits: 1, + data: Unitary(X()) + }, + y: Channel { + n_qubits: 1, + data: Unitary(Y()) + }, + z: z, + h: Channel { + n_qubits: 1, + data: Unitary(H()) + }, + t: Channel { + n_qubits: 1, + data: Unitary(T()) + }, + t_adj: Channel { + n_qubits: 1, + data: Unitary(T().dag()) + }, + s: Channel { + n_qubits: 1, + data: Unitary(S()) + }, + s_adj: Channel { + n_qubits: 1, + data: Unitary(S().dag()) + }, + cnot: Channel { + n_qubits: 2, + data: Unitary(CNOT()) + }, + z_meas: z_meas + } + } +} diff --git a/src/Simulation/OpenSystems/runtime/src/states.rs b/src/Simulation/OpenSystems/runtime/src/states.rs new file mode 100644 index 00000000000..b6035f9fc5c --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/states.rs @@ -0,0 +1,119 @@ + +use crate::Z; +use crate::linalg::extend_one_to_n; +use core::fmt::Display; +use num_traits::One; +use num_traits::Zero; +use crate::states::StateData::Mixed; +use crate::states::StateData::Pure; +use crate::QubitSized; +use crate::ONE_C; +use crate::linalg::Trace; +use ndarray::Array; +use crate::C64; +use ndarray::{ Array1, Array2 }; +use derive_more::{Display}; +use std::convert::TryInto; +use crate::linalg::{ HasDagger, Tensor }; +use serde::{ Serialize, Deserialize }; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum StateData { + Pure(Array1), + Mixed(Array2) +} + +pub type State = QubitSized; + +impl Display for State { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + write!( + f, + "Register state on {} qubits ({} representation)\nData:\n{}", + self.n_qubits, + match self.data { + Pure(_) => "state vector", + Mixed(_) => "density operator" + }, + self.data + ) + } +} + +impl Display for StateData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + match self { + Pure(psi) => write!(f, "{}", psi), + Mixed(rho) => write!(f, "{}", rho) + } + } +} + +fn elementary_vec(idx: usize, n: usize) -> Array1 { + Array::from_shape_fn(n, |i| if i == idx {T::one()} else {T::zero()}) +} + +fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (usize, usize)) -> Array2 { + Array::from_shape_fn((n, m), |(i, j)| if i == idx0 && j == idx1 { + T::one() + } else { + T::zero() + }) +} + +impl State { + pub fn extend(self: &Self, n_qubits: usize) -> State { + let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); + State { + n_qubits: self.n_qubits + n_qubits, + data: match &self.data { + Pure(psi) => Pure(psi.tensor(&elementary_vec(0, new_dim))), + Mixed(rho) => Mixed(rho.tensor(&elementary_matrix((0, 0), (new_dim, new_dim)))) + } + } + } + + pub fn new_mixed(n_qubits: usize) -> State { + let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); + State { + n_qubits: n_qubits, + data: Mixed(elementary_matrix((0, 0), (new_dim, new_dim))) + } + } + + // TODO: deprecate this in favor of relying on the measurement probabilities + // returned by an instrument. + pub fn ideal_z_meas_pr(&self, idx_qubit: usize) -> f64 { + match &self.data { + Pure(psi) => todo!(), + Mixed(rho) => { + let meas_op = extend_one_to_n(&Z(), idx_qubit, self.n_qubits); + let expectation: C64 = rho.dot(&meas_op).trace(); + (1.0 + expectation.re) / 2.0 + } + } + } +} + +impl Trace for &State { + type Output = C64; + + fn trace(self) -> Self::Output { + match &self.data { + Pure(_) => C64::one(), + Mixed(ref rho) => (&rho).trace() + } + } +} + +// impl DensityOperator { +// fn new(n_qubits: usize) -> Self { +// let dim = 2usize.pow(n_qubits.try_into().unwrap()); +// let mut data = Array::zeros((dim, dim)); +// data[(0, 0)] = ONE_C; +// DensityOperator { +// n_qubits: n_qubits, +// data: data +// } +// } +// } diff --git a/src/Simulation/OpenSystems/runtime/src/utils.rs b/src/Simulation/OpenSystems/runtime/src/utils.rs new file mode 100644 index 00000000000..af29baccad4 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/utils.rs @@ -0,0 +1,28 @@ +use num_traits::Num; +use num_complex::Complex; +use std::fmt::Write; + +pub type C64 = Complex; + +pub const ONE_C: C64 = Complex::new(1f64, 0f64); +pub const ZERO_C: C64 = Complex::new(0f64, 0f64); +pub const I_C: C64 = Complex::new(0f64, 1f64); + +pub fn c(re: T, im: T) -> Complex { + Complex::new(re, im) +} + +#[cfg(feature = "web-sys-log")] +pub fn log_message(msg: &String) { + web_sys::console::log_1(&msg.into()); +} + +#[cfg(not(feature = "web-sys-log"))] +pub fn log_message(msg: &String) { + println!("{}", msg); +} + +pub fn log_as_err(msg: String) -> Result { + log_message(&msg); + Err(msg) +} From 6505213ecc9d181df9b1b1cb00a1d7f169a6d0e5 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 18:21:37 -0800 Subject: [PATCH 003/158] Expanded C API, started consolidating Cargo.toml. --- .../lib/Simulators/OpenSystemsSimulator.cpp | 4 + .../lib/Simulators/opensim/Cargo.lock | 5 +- .../lib/Simulators/opensim/Cargo.toml | 2 +- .../lib/Simulators/opensim/src/c_api.rs | 44 ---- .../lib/Simulators/opensim/src/lib.rs | 4 +- src/Simulation/OpenSystems/runtime/Cargo.lock | 3 +- src/Simulation/OpenSystems/runtime/Cargo.toml | 10 +- .../OpenSystems/runtime/src/c_api.rs | 235 ++++++++++++++++++ src/Simulation/OpenSystems/runtime/src/lib.rs | 1 + 9 files changed, 253 insertions(+), 55 deletions(-) delete mode 100644 src/QirRuntime/lib/Simulators/opensim/src/c_api.rs create mode 100644 src/Simulation/OpenSystems/runtime/src/c_api.rs diff --git a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp index 27ff5d771b9..0015704f64a 100644 --- a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp +++ b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp @@ -30,6 +30,10 @@ extern "C" i64 t_adj(size_t sim_id, size_t idx); i64 cnot(size_t sim_id, size_t idx_control, size_t idx_target); i64 m(size_t sim_id, size_t idx, size_t* result_out); + const char* lasterr(); + const char* get_noise_model(size_t sim_id); + i64 set_noise_model(size_t sim_id, const char* new_model); + const char* get_current_state(size_t sim_id); } namespace Microsoft diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock index c08dff891d5..cdfaf9bfba1 100644 --- a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock +++ b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock @@ -147,12 +147,13 @@ dependencies = [ ] [[package]] -name = "opensim_runtime" +name = "opensim" version = "0.1.0" dependencies = [ "derive_more", "getrandom", "itertools", + "lazy_static", "ndarray", "num-complex", "num-traits", @@ -165,7 +166,7 @@ dependencies = [ name = "opensim_static" version = "0.1.0" dependencies = [ - "opensim_runtime", + "opensim", ] [[package]] diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml index 711bfc6b617..bc93d86acf4 100644 --- a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml +++ b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml @@ -7,4 +7,4 @@ name = "opensim_static" crate-type = ["staticlib"] [dependencies] -opensim_runtime = { path = "../../../../Simulation/OpenSystems/runtime" } +opensim = { path = "../../../../Simulation/OpenSystems/runtime" } diff --git a/src/QirRuntime/lib/Simulators/opensim/src/c_api.rs b/src/QirRuntime/lib/Simulators/opensim/src/c_api.rs deleted file mode 100644 index 484529be3f5..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/src/c_api.rs +++ /dev/null @@ -1,44 +0,0 @@ - -#[no_mangle] -pub extern fn init(initial_capacity: usize) -> usize { - 0 -} - -#[no_mangle] -pub extern fn destroy(sim_id: usize) -> i64 { 0 } - -#[no_mangle] -pub extern fn dump_to_console() -> () {} - -#[no_mangle] -pub extern fn x(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub extern fn y(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub extern fn z(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub extern fn h(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub fn s(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub fn s_adj(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub fn t(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub fn t_adj(sim_id: usize, idx: usize) -> i64 { 0 } - -#[no_mangle] -pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { 0 } - -#[no_mangle] -pub extern fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { - unsafe { *result_out = 0; } - 0 -} diff --git a/src/QirRuntime/lib/Simulators/opensim/src/lib.rs b/src/QirRuntime/lib/Simulators/opensim/src/lib.rs index be1b30483a5..ffaaf996144 100644 --- a/src/QirRuntime/lib/Simulators/opensim/src/lib.rs +++ b/src/QirRuntime/lib/Simulators/opensim/src/lib.rs @@ -1,2 +1,4 @@ +extern crate opensim; +// extern crate lazy_static; -mod c_api; +use opensim::c_api; diff --git a/src/Simulation/OpenSystems/runtime/Cargo.lock b/src/Simulation/OpenSystems/runtime/Cargo.lock index 22b3a1b2db6..c2c1b62026a 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.lock +++ b/src/Simulation/OpenSystems/runtime/Cargo.lock @@ -152,12 +152,13 @@ dependencies = [ ] [[package]] -name = "opensim_runtime" +name = "opensim" version = "0.1.0" dependencies = [ "derive_more", "getrandom", "itertools", + "lazy_static", "ndarray", "num-complex", "num-traits", diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml index 3315d49137f..a3478b893c5 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "opensim_runtime" +name = "opensim" version = "0.1.0" authors = ["Christopher Granade "] edition = "2018" @@ -26,6 +26,7 @@ itertools = "0.9.0" rand = "0.7.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +lazy_static = "1.4.0" # 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 @@ -38,9 +39,6 @@ getrandom = { version = "0.1.15", features = ["wasm-bindgen"] } web-sys = { version = "0.3.4", features = ['console'], optional = true } [lib] -name = "opensim_runtime" +name = "opensim" path = "src/lib.rs" -# Build a Rust library from this crate. That can't be used directly except -# by other Rust crates, but allows us to retarget the runtime here as different -# kinds of libraries with thin wrappers later. -crate-type = ["rlib"] +crate-type = ["rlib", "staticlib", "cdylib"] diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs new file mode 100644 index 00000000000..54145558db9 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -0,0 +1,235 @@ +use std::ffi::CStr; +use std::ffi::CString; +use std::os::raw::c_char; +use std::ptr; +use crate::{ Channel, NoiseModel, State }; +use lazy_static::lazy_static; +use std::sync::Mutex; +use std::collections::HashMap; + +const DEFAULT_CAPACITY: usize = 1; + +struct CApiState { + register_state: State, + noise_model: NoiseModel +} + +lazy_static! { + static ref STATE: Mutex> = Mutex::new(HashMap::new()); + static ref LAST_ERROR: Mutex> = Mutex::new(None); +} + +// UTILITY FUNCTIONS // + +/// Exposes a result to C callers by setting LAST_ERROR in the Error +/// case, and generating an appropriate error code. +fn as_capi_err(result: Result<(), String>) -> i64 { + match result { + Ok(_) => 0, + Err(msg) => { + *LAST_ERROR.lock().unwrap() = Some(msg); + -1 + } + } +} + +fn apply &Channel>(sim_id: usize, idxs: &[usize], channel_fn: F) -> Result<(), String> { + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + let channel = channel_fn(&sim_state.noise_model); + match channel.apply_to(idxs, &sim_state.register_state) { + Ok(new_state) => { + sim_state.register_state = new_state; + Ok(()) + }, + Err(err) => Err(err) + } + } else { + return Err(format!("No simulator with id {}.", sim_id).to_string()); + } +} + +// C API FUNCTIONS // + +#[no_mangle] +pub extern fn init(initial_capacity: usize) -> usize { + let state = &mut *STATE.lock().unwrap(); + let id = 1 + state.keys().fold( + std::usize::MIN, |a, b| a.max(*b) + ); + state.insert( + id, + CApiState { + register_state: State::new_mixed(initial_capacity), + noise_model: NoiseModel::ideal() + } + ); + id +} + +#[no_mangle] +pub extern fn destroy(sim_id: usize) -> i64 { + as_capi_err({ + let state = &mut *STATE.lock().unwrap(); + if state.contains_key(&sim_id) { + state.remove(&sim_id); + Ok(()) + } else { + Err(format!("No simulator with id {} exists.", sim_id).to_string()) + } + }) +} + +#[no_mangle] +pub extern fn dump_to_console() -> () {} + +#[no_mangle] +pub extern fn x(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.x)) +} + +#[no_mangle] +pub extern fn y(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.y))} + +#[no_mangle] +pub extern fn z(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.z)) +} + +#[no_mangle] +pub extern fn h(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.h)) +} + +#[no_mangle] +pub fn s(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.s)) +} + +#[no_mangle] +pub fn s_adj(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.s_adj)) +} + +#[no_mangle] +pub fn t(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.t)) +} + +#[no_mangle] +pub fn t_adj(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.t_adj)) +} + +#[no_mangle] +pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx_control, idx_target], |model| &model.cnot)) +} + +#[no_mangle] +pub extern fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { + as_capi_err({ + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + let instrument = &sim_state.noise_model.z_meas; + let (result, new_state) = instrument.sample(&[idx], &sim_state.register_state); + sim_state.register_state = new_state; + unsafe { + *result_out = result; + }; + Ok(()) + } else { + Err(format!("No simulator with id {} exists.", sim_id).to_string()) + } + }) +} + + +#[no_mangle] +pub extern fn lasterr() -> *const c_char { + match &*LAST_ERROR.lock().unwrap() { + None => ptr::null(), + Some(msg) => { + let wrapped_msg = CString::new(msg.as_str()).unwrap().into_raw(); + std::mem::forget(wrapped_msg); + wrapped_msg + } + } +} + +#[no_mangle] +pub extern fn get_noise_model(sim_id: usize) -> *const c_char { + let state = &*STATE.lock().unwrap(); + if let Some(sim_state) = state.get(&sim_id) { + let serialized = CString::new( + serde_json::to_string(&sim_state.noise_model).unwrap().as_str() + ).unwrap().into_raw(); + // Need to forget that we hold this reference so that it doesn't + // get released after returning to C. + std::mem::forget(serialized); + serialized + } else { + ptr::null() + } +} + +#[no_mangle] +pub extern fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { + if new_model.is_null() { + return as_capi_err(Err("set_noise_model called with null pointer".to_string())); + } + + let c_str = unsafe { + CStr::from_ptr(new_model) + }; + + as_capi_err( + match c_str.to_str() { + Ok(serialized_noise_model) => { + match serde_json::from_str(serialized_noise_model) { + Ok(noise_model) => { + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + sim_state.noise_model = noise_model; + Ok(()) + } else { + Err(format!("No simulator with id {} exists.", sim_id).to_string()) + } + }, + Err(serialization_error) => Err(format!( + "{} error deserializing noise model at line {}, column {}.", + match serialization_error.classify() { + serde_json::error::Category::Data => "Data / schema", + serde_json::error::Category::Eof => "End-of-file", + serde_json::error::Category::Io => "I/O", + serde_json::error::Category::Syntax => "Syntax", + }, + serialization_error.line(), + serialization_error.column() + )) + } + }, + Err(msg) => Err( + format!( + "UTF-8 error decoding serialized noise model; was valid until byte {}.", + msg.valid_up_to() + ) + ) + } + ) +} + +#[no_mangle] +pub extern fn get_current_state(sim_id: usize) -> *const c_char { + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + let serialized = CString::new( + serde_json::to_string(&sim_state.register_state).unwrap().as_str() + ).unwrap().into_raw(); + std::mem::forget(serialized); + serialized + } else { + ptr::null() + } +} \ No newline at end of file diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index 7983eb3a70f..9c10a6c8687 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -4,6 +4,7 @@ extern crate ndarray; extern crate derive_more; extern crate serde; +pub mod c_api; mod utils; mod common_matrices; mod states; From 21bd4b183bd2d0146fe8137a18c9ac7c481277b4 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 18:26:20 -0800 Subject: [PATCH 004/158] Just the one crate will do. --- src/QirRuntime/lib/Simulators/CMakeLists.txt | 4 +- .../lib/Simulators/opensim/.gitignore | 1 - .../lib/Simulators/opensim/Cargo.lock | 355 ------------------ .../lib/Simulators/opensim/Cargo.toml | 10 - .../lib/Simulators/opensim/src/lib.rs | 4 - 5 files changed, 2 insertions(+), 372 deletions(-) delete mode 100644 src/QirRuntime/lib/Simulators/opensim/.gitignore delete mode 100644 src/QirRuntime/lib/Simulators/opensim/Cargo.lock delete mode 100644 src/QirRuntime/lib/Simulators/opensim/Cargo.toml delete mode 100644 src/QirRuntime/lib/Simulators/opensim/src/lib.rs diff --git a/src/QirRuntime/lib/Simulators/CMakeLists.txt b/src/QirRuntime/lib/Simulators/CMakeLists.txt index c2d2ec7761d..2207cfa113b 100644 --- a/src/QirRuntime/lib/Simulators/CMakeLists.txt +++ b/src/QirRuntime/lib/Simulators/CMakeLists.txt @@ -23,14 +23,14 @@ FetchContent_Declare( FetchContent_MakeAvailable(Corrosion) -corrosion_import_crate(MANIFEST_PATH opensim/Cargo.toml) +corrosion_import_crate(MANIFEST_PATH ../../../Simulation/OpenSystems/runtime/Cargo.toml) #=============================================================================== # Produce static lib for users to link directly to add_library(${component_name} STATIC ${source_files}) target_include_directories(${component_name} PUBLIC ${includes}) -target_link_libraries(${component_name} ${CMAKE_DL_LIBS} PUBLIC opensim_static) +target_link_libraries(${component_name} ${CMAKE_DL_LIBS} PUBLIC opensim) #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/QirRuntime/lib/Simulators/opensim/.gitignore b/src/QirRuntime/lib/Simulators/opensim/.gitignore deleted file mode 100644 index eb5a316cbd1..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock b/src/QirRuntime/lib/Simulators/opensim/Cargo.lock deleted file mode 100644 index cdfaf9bfba1..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/Cargo.lock +++ /dev/null @@ -1,355 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "bumpalo" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "derive_more" -version = "0.99.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "js-sys" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matrixmultiply" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" -dependencies = [ - "rawpointer", -] - -[[package]] -name = "ndarray" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" -dependencies = [ - "matrixmultiply", - "num-complex", - "num-integer", - "num-traits", - "rawpointer", - "serde", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg", - "num-traits", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "opensim" -version = "0.1.0" -dependencies = [ - "derive_more", - "getrandom", - "itertools", - "lazy_static", - "ndarray", - "num-complex", - "num-traits", - "rand", - "serde", - "serde_json", -] - -[[package]] -name = "opensim_static" -version = "0.1.0" -dependencies = [ - "opensim", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "proc-macro2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "serde" -version = "1.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "syn" -version = "1.0.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasm-bindgen" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" diff --git a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml b/src/QirRuntime/lib/Simulators/opensim/Cargo.toml deleted file mode 100644 index bc93d86acf4..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "opensim_static" -version = "0.1.0" - -[lib] -name = "opensim_static" -crate-type = ["staticlib"] - -[dependencies] -opensim = { path = "../../../../Simulation/OpenSystems/runtime" } diff --git a/src/QirRuntime/lib/Simulators/opensim/src/lib.rs b/src/QirRuntime/lib/Simulators/opensim/src/lib.rs deleted file mode 100644 index ffaaf996144..00000000000 --- a/src/QirRuntime/lib/Simulators/opensim/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -extern crate opensim; -// extern crate lazy_static; - -use opensim::c_api; From d496b03a25428e91c10e14450c34208823bd3fc6 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 19:06:23 -0800 Subject: [PATCH 005/158] =?UTF-8?q?First=20unit=20test=20of=20QIR=20?= =?UTF-8?q?=E2=86=94=20opensim.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/QirRuntime/lib/CMakeLists.txt | 5 +- .../lib/Simulators/OpenSystemsSimulator.cpp | 34 +- src/QirRuntime/public/SimFactory.hpp | 2 + src/QirRuntime/test/CMakeLists.txt | 1 + .../test/OpenSystemsSimulator/CMakeLists.txt | 25 + .../OpenSystemsSimulatorTests.cpp | 36 + .../qir-test-opensimulator.ll | 1398 +++++++++++++++++ .../qir-test-opensimulator.qs | 82 + 8 files changed, 1580 insertions(+), 3 deletions(-) create mode 100644 src/QirRuntime/test/OpenSystemsSimulator/CMakeLists.txt create mode 100644 src/QirRuntime/test/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp create mode 100644 src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.ll create mode 100644 src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.qs diff --git a/src/QirRuntime/lib/CMakeLists.txt b/src/QirRuntime/lib/CMakeLists.txt index 3589d6b764e..60a50581225 100644 --- a/src/QirRuntime/lib/CMakeLists.txt +++ b/src/QirRuntime/lib/CMakeLists.txt @@ -1,13 +1,15 @@ add_subdirectory(QIR) add_subdirectory(Simulators) - #=============================================================================== # Produce the qdk dynamic library # add_library(qdk SHARED $ $ + # NB: The following line includes all obj files from simulators, but does + # not include Rust dependencies. To get that, we need the opensim lib + # as well, built from corrosion in the Simulators subdirectory, above. $ ) @@ -15,6 +17,7 @@ target_link_libraries(qdk ${QIR_BRIDGE_UTILITY_LIB} ${QIR_BRIDGE_QIS_UTILITY_LIB} ${CMAKE_DL_LIBS} + opensim ) set_property(TARGET qdk PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp index 0015704f64a..088fa723dae 100644 --- a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp +++ b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp @@ -40,8 +40,9 @@ namespace Microsoft { namespace Quantum { - // FIXME: support public IDiagnostics - class OpenSystemSimulator : public ISimulator, public IQuantumGateSet + // FIXME: support methods from public IDiagnostics; they currently + // just throw. + class OpenSystemSimulator : public ISimulator, public IQuantumGateSet, public IDiagnostics { typedef void (*TSingleQubitGate)(size_t /*simulator id*/, size_t /*qubit id*/); typedef void (*TSingleQubitControlledGate)( @@ -104,6 +105,30 @@ namespace Quantum { return this; } + IDiagnostics* AsDiagnostics() override + { + return this; + } + + void GetState(TGetStateCallback callback) override + { + throw std::logic_error("operation_not_supported"); + } + bool Assert(long numTargets, PauliId* bases, Qubit* targets, Result result, const char* failureMessage) override + { + throw std::logic_error("operation_not_supported"); + } + + bool AssertProbability( + long numTargets, + PauliId bases[], + Qubit targets[], + double probabilityOfZero, + double precision, + const char* failureMessage) override + { + throw std::logic_error("operation_not_supported"); + } virtual std::string QubitToString(Qubit q) override { @@ -264,5 +289,10 @@ namespace Quantum throw std::logic_error("not yet implemented"); } }; + + std::unique_ptr CreateOpenSystemsSimulator() + { + return std::make_unique(); + } } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/QirRuntime/public/SimFactory.hpp b/src/QirRuntime/public/SimFactory.hpp index dacedc4c423..76e2ba43ae3 100644 --- a/src/QirRuntime/public/SimFactory.hpp +++ b/src/QirRuntime/public/SimFactory.hpp @@ -15,5 +15,7 @@ namespace Quantum // Full State Simulator std::unique_ptr CreateFullstateSimulator(); + std::unique_ptr CreateOpenSystemsSimulator(); + } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/QirRuntime/test/CMakeLists.txt b/src/QirRuntime/test/CMakeLists.txt index 8d2fac8c254..3a5a3e6a726 100644 --- a/src/QirRuntime/test/CMakeLists.txt +++ b/src/QirRuntime/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(FullstateSimulator) +add_subdirectory(OpenSystemsSimulator) add_subdirectory(QIR-dynamic) add_subdirectory(QIR-static) add_subdirectory(unittests) diff --git a/src/QirRuntime/test/OpenSystemsSimulator/CMakeLists.txt b/src/QirRuntime/test/OpenSystemsSimulator/CMakeLists.txt new file mode 100644 index 00000000000..5826a7e788a --- /dev/null +++ b/src/QirRuntime/test/OpenSystemsSimulator/CMakeLists.txt @@ -0,0 +1,25 @@ + +compile_from_qir(qir-test-opensimulator qir_test_opensimulator_target) + +add_executable(open-systems-simulator-tests + OpenSystemsSimulatorTests.cpp) + +target_link_libraries(open-systems-simulator-tests PUBLIC + ${QIR_UTILITY_LIB} # created by compile_from_qir + ${QIR_BRIDGE_UTILITY_LIB} + ${QIR_BRIDGE_QIS_UTILITY_LIB} + qir-rt-support + qir-qis-support + simulators +) + +target_include_directories(open-systems-simulator-tests PUBLIC + "${test_includes}" + "${public_includes}" + "${PROJECT_SOURCE_DIR}/lib/QIR" +) +add_dependencies(open-systems-simulator-tests qir_test_opensimulator_target) + +install(TARGETS open-systems-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +add_unit_test(open-systems-simulator-tests) + diff --git a/src/QirRuntime/test/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp b/src/QirRuntime/test/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp new file mode 100644 index 00000000000..a70a7f4c288 --- /dev/null +++ b/src/QirRuntime/test/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +#include "QuantumApi_I.hpp" +#include "SimFactory.hpp" +#include "context.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +TEST_CASE("Open systems simulator: X and measure", "[open_simulator]") +{ + std::unique_ptr sim = CreateOpenSystemsSimulator(); + IQuantumGateSet* iqa = sim->AsQuantumGateSet(); + + Qubit q = sim->AllocateQubit(); + Result r1 = sim->M(q); + REQUIRE(Result_Zero == sim->GetResultValue(r1)); + REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); + + iqa->X(q); + Result r2 = sim->M(q); + REQUIRE(Result_One == sim->GetResultValue(r2)); + REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); + + sim->ReleaseQubit(q); + sim->ReleaseResult(r1); + sim->ReleaseResult(r2); +} diff --git a/src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.ll b/src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.ll new file mode 100644 index 00000000000..915936863f3 --- /dev/null +++ b/src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.ll @@ -0,0 +1,1398 @@ + +%Result = type opaque +%Range = type { i64, i64, i64 } +%Tuple = type opaque +%Qubit = type opaque +%Array = type opaque +%Callable = type opaque + +@ResultZero = external global %Result* +@ResultOne = external global %Result* +@PauliI = constant i2 0 +@PauliX = constant i2 1 +@PauliY = constant i2 -1 +@PauliZ = constant i2 -2 +@EmptyRange = internal constant %Range { i64 0, i64 1, i64 -1 } +@Microsoft__Quantum__Intrinsic__X = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__ctladj__wrapper] +@Microsoft__Quantum__Intrinsic__Y = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__ctladj__wrapper] +@Microsoft__Quantum__Intrinsic__Z = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__ctladj__wrapper] +@Microsoft__Quantum__Intrinsic__H = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__ctladj__wrapper] +@Microsoft__Quantum__Intrinsic__S = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__ctladj__wrapper] +@Microsoft__Quantum__Intrinsic__T = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__ctladj__wrapper] +@Microsoft__Quantum__Intrinsic__R = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__ctladj__wrapper] +@PartialApplication__1 = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__ctladj__wrapper] +@MemoryManagement__1 = constant [2 x void (%Tuple*, i64)*] [void (%Tuple*, i64)* @MemoryManagement__1__RefCount, void (%Tuple*, i64)* @MemoryManagement__1__AliasCount] + +@Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS = alias i64 (), i64 ()* @Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body + +define void @Microsoft__Quantum__Intrinsic__X__body(%Qubit* %qb) { +entry: + call void @__quantum__qis__x__body(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__x__body(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__X__adj(%Qubit* %qb) { +entry: + call void @__quantum__qis__x__body(%Qubit* %qb) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__X__ctl(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__x__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__rt__array_update_alias_count(%Array*, i64) + +declare void @__quantum__qis__x__ctl(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__X__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__x__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +define %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %qb) { +entry: + %bases__inline__1 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 1) + %0 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__1, i64 0) + %1 = bitcast i8* %0 to i2* + %2 = load i2, i2* @PauliZ + store i2 %2, i2* %1 + call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__1, i64 1) + %qubits__inline__1 = call %Array* @__quantum__rt__array_create_1d(i32 8, i64 1) + %3 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qubits__inline__1, i64 0) + %4 = bitcast i8* %3 to %Qubit** + store %Qubit* %qb, %Qubit** %4 + call void @__quantum__rt__array_update_alias_count(%Array* %qubits__inline__1, i64 1) + %5 = call %Result* @__quantum__qis__measure__body(%Array* %bases__inline__1, %Array* %qubits__inline__1) + call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__1, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits__inline__1, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %bases__inline__1, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %qubits__inline__1, i64 -1) + ret %Result* %5 +} + +declare %Array* @__quantum__rt__array_create_1d(i32, i64) + +declare i8* @__quantum__rt__array_get_element_ptr_1d(%Array*, i64) + +declare %Result* @__quantum__qis__measure__body(%Array*, %Array*) + +declare void @__quantum__rt__array_update_reference_count(%Array*, i64) + +define void @Microsoft__Quantum__Intrinsic__S__body(%Qubit* %qb) { +entry: + call void @__quantum__qis__s__body(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__s__body(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__S__adj(%Qubit* %qb) { +entry: + call void @__quantum__qis__s__adj(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__s__adj(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__S__ctl(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__s__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__s__ctl(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__S__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__s__ctladj(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__s__ctladj(%Array*, %Qubit*) + +define %Result* @Microsoft__Quantum__Intrinsic__Measure__body(%Array* %bases, %Array* %qubits) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %bases, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) + %0 = call %Result* @__quantum__qis__measure__body(%Array* %bases, %Array* %qubits) + call void @__quantum__rt__array_update_alias_count(%Array* %bases, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) + ret %Result* %0 +} + +define void @Microsoft__Quantum__Intrinsic__T__body(%Qubit* %qb) { +entry: + call void @__quantum__qis__t__body(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__t__body(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__T__adj(%Qubit* %qb) { +entry: + call void @__quantum__qis__t__adj(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__t__adj(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__T__ctl(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__t__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__t__ctl(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__T__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__t__ctladj(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__t__ctladj(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %qb) { +entry: + call void @__quantum__qis__h__body(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__h__body(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__H__adj(%Qubit* %qb) { +entry: + call void @__quantum__qis__h__body(%Qubit* %qb) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__H__ctl(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__h__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__h__ctl(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__H__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__h__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Y__body(%Qubit* %qb) { +entry: + call void @__quantum__qis__y__body(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__y__body(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__Y__adj(%Qubit* %qb) { +entry: + call void @__quantum__qis__y__body(%Qubit* %qb) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Y__ctl(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__y__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__y__ctl(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__Y__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__y__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__R__body(i2 %pauli, double %theta, %Qubit* %qubit) { +entry: + call void @__quantum__qis__r__body(i2 %pauli, double %theta, %Qubit* %qubit) + ret void +} + +declare void @__quantum__qis__r__body(i2, double, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__R__adj(i2 %pauli, double %theta, %Qubit* %qubit) { +entry: + call void @__quantum__qis__r__adj(i2 %pauli, double %theta, %Qubit* %qubit) + ret void +} + +declare void @__quantum__qis__r__adj(i2, double, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__R__ctl(%Array* %__controlQubits__, { i2, double, %Qubit* }* %0) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 + %pauli = load i2, i2* %1 + %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 + %theta = load double, double* %2 + %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 + %qubit = load %Qubit*, %Qubit** %3 + %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) + %5 = bitcast %Tuple* %4 to { i2, double, %Qubit* }* + %6 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 0 + %7 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 1 + %8 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 2 + store i2 %pauli, i2* %6 + store double %theta, double* %7 + store %Qubit* %qubit, %Qubit** %8 + call void @__quantum__qis__r__ctl(%Array* %__controlQubits__, { i2, double, %Qubit* }* %5) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) + ret void +} + +declare void @__quantum__qis__r__ctl(%Array*, { i2, double, %Qubit* }*) + +declare %Tuple* @__quantum__rt__tuple_create(i64) + +declare void @__quantum__rt__tuple_update_reference_count(%Tuple*, i64) + +define void @Microsoft__Quantum__Intrinsic__R__ctladj(%Array* %__controlQubits__, { i2, double, %Qubit* }* %0) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 + %pauli = load i2, i2* %1 + %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 + %theta = load double, double* %2 + %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 + %qubit = load %Qubit*, %Qubit** %3 + %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) + %5 = bitcast %Tuple* %4 to { i2, double, %Qubit* }* + %6 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 0 + %7 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 1 + %8 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 2 + store i2 %pauli, i2* %6 + store double %theta, double* %7 + store %Qubit* %qubit, %Qubit** %8 + call void @__quantum__qis__r__ctladj(%Array* %__controlQubits__, { i2, double, %Qubit* }* %5) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) + ret void +} + +declare void @__quantum__qis__r__ctladj(%Array*, { i2, double, %Qubit* }*) + +define void @Microsoft__Quantum__Intrinsic__Exp__body(%Array* %paulis, double %theta, %Array* %qubits) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) + call void @__quantum__qis__exp__body(%Array* %paulis, double %theta, %Array* %qubits) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) + ret void +} + +declare void @__quantum__qis__exp__body(%Array*, double, %Array*) + +define void @Microsoft__Quantum__Intrinsic__Exp__adj(%Array* %paulis, double %theta, %Array* %qubits) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) + call void @__quantum__qis__exp__adj(%Array* %paulis, double %theta, %Array* %qubits) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) + ret void +} + +declare void @__quantum__qis__exp__adj(%Array*, double, %Array*) + +define void @Microsoft__Quantum__Intrinsic__Exp__ctl(%Array* %__controlQubits__, { %Array*, double, %Array* }* %0) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + %1 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 0 + %paulis = load %Array*, %Array** %1 + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) + %2 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 1 + %theta = load double, double* %2 + %3 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 2 + %qubits = load %Array*, %Array** %3 + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) + %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) + %5 = bitcast %Tuple* %4 to { %Array*, double, %Array* }* + %6 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 0 + %7 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 1 + %8 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 2 + store %Array* %paulis, %Array** %6 + store double %theta, double* %7 + store %Array* %qubits, %Array** %8 + call void @__quantum__qis__exp__ctl(%Array* %__controlQubits__, { %Array*, double, %Array* }* %5) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) + ret void +} + +declare void @__quantum__qis__exp__ctl(%Array*, { %Array*, double, %Array* }*) + +define void @Microsoft__Quantum__Intrinsic__Exp__ctladj(%Array* %__controlQubits__, { %Array*, double, %Array* }* %0) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + %1 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 0 + %paulis = load %Array*, %Array** %1 + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) + %2 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 1 + %theta = load double, double* %2 + %3 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 2 + %qubits = load %Array*, %Array** %3 + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) + %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) + %5 = bitcast %Tuple* %4 to { %Array*, double, %Array* }* + %6 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 0 + %7 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 1 + %8 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 2 + store %Array* %paulis, %Array** %6 + store double %theta, double* %7 + store %Array* %qubits, %Array** %8 + call void @__quantum__qis__exp__ctladj(%Array* %__controlQubits__, { %Array*, double, %Array* }* %5) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) + ret void +} + +declare void @__quantum__qis__exp__ctladj(%Array*, { %Array*, double, %Array* }*) + +define void @Microsoft__Quantum__Intrinsic__Z__body(%Qubit* %qb) { +entry: + call void @__quantum__qis__z__body(%Qubit* %qb) + ret void +} + +declare void @__quantum__qis__z__body(%Qubit*) + +define void @Microsoft__Quantum__Intrinsic__Z__adj(%Qubit* %qb) { +entry: + call void @__quantum__qis__z__body(%Qubit* %qb) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Z__ctl(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__z__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +declare void @__quantum__qis__z__ctl(%Array*, %Qubit*) + +define void @Microsoft__Quantum__Intrinsic__Z__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { +entry: + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) + call void @__quantum__qis__z__ctl(%Array* %__controlQubits__, %Qubit* %qb) + call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) + ret void +} + +define i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %op) { +entry: + call void @__quantum__rt__callable_memory_management(i32 1, %Callable* %op, i64 1) + call void @__quantum__rt__callable_update_alias_count(%Callable* %op, i64 1) + %res = alloca i64 + store i64 0, i64* %res + %target = call %Qubit* @__quantum__rt__qubit_allocate() + %ctls = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) + %0 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64)) + %1 = bitcast %Tuple* %0 to { %Qubit* }* + %2 = getelementptr { %Qubit* }, { %Qubit* }* %1, i64 0, i32 0 + store %Qubit* %target, %Qubit** %2 + call void @__quantum__rt__callable_invoke(%Callable* %op, %Tuple* %0, %Tuple* null) + %3 = call %Callable* @__quantum__rt__callable_copy(%Callable* %op, i1 false) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 1) + call void @__quantum__rt__callable_make_adjoint(%Callable* %3) + %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64)) + %5 = bitcast %Tuple* %4 to { %Qubit* }* + %6 = getelementptr { %Qubit* }, { %Qubit* }* %5, i64 0, i32 0 + store %Qubit* %target, %Qubit** %6 + call void @__quantum__rt__callable_invoke(%Callable* %3, %Tuple* %4, %Tuple* null) + %7 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %target) + %8 = load %Result*, %Result** @ResultZero + %9 = call i1 @__quantum__rt__result_equal(%Result* %7, %Result* %8) + %10 = xor i1 %9, true + br i1 %10, label %then0__1, label %else__1 + +then0__1: ; preds = %entry + store i64 1, i64* %res + br label %continue__1 + +else__1: ; preds = %entry + %11 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) + %12 = bitcast i8* %11 to %Qubit** + %qb__inline__1 = load %Qubit*, %Qubit** %12 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__1) + %13 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) + %14 = bitcast i8* %13 to %Qubit** + %qb__inline__2 = load %Qubit*, %Qubit** %14 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__2) + %15 = call %Callable* @__quantum__rt__callable_copy(%Callable* %op, i1 false) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 1) + call void @__quantum__rt__callable_make_controlled(%Callable* %15) + %16 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) + %17 = bitcast %Tuple* %16 to { %Array*, %Qubit* }* + %18 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %17, i64 0, i32 0 + %19 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %17, i64 0, i32 1 + store %Array* %ctls, %Array** %18 + store %Qubit* %target, %Qubit** %19 + call void @__quantum__rt__callable_invoke(%Callable* %15, %Tuple* %16, %Tuple* null) + %20 = call %Callable* @__quantum__rt__callable_copy(%Callable* %op, i1 false) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %20, i64 1) + call void @__quantum__rt__callable_make_controlled(%Callable* %20) + call void @__quantum__rt__callable_make_adjoint(%Callable* %20) + %21 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) + %22 = bitcast %Tuple* %21 to { %Array*, %Qubit* }* + %23 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %22, i64 0, i32 0 + %24 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %22, i64 0, i32 1 + store %Array* %ctls, %Array** %23 + store %Qubit* %target, %Qubit** %24 + call void @__quantum__rt__callable_invoke(%Callable* %20, %Tuple* %21, %Tuple* null) + %25 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %target) + %26 = load %Result*, %Result** @ResultZero + %27 = call i1 @__quantum__rt__result_equal(%Result* %25, %Result* %26) + %28 = xor i1 %27, true + br i1 %28, label %then0__2, label %continue__2 + +then0__2: ; preds = %else__1 + store i64 2, i64* %res + br label %continue__2 + +continue__2: ; preds = %then0__2, %else__1 + %29 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) + %30 = bitcast i8* %29 to %Qubit** + %qb__inline__3 = load %Qubit*, %Qubit** %30 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__3) + %31 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) + %32 = bitcast i8* %31 to %Qubit** + %qb__inline__4 = load %Qubit*, %Qubit** %32 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__4) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %16, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %20, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %20, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %21, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %25, i64 -1) + br label %continue__1 + +continue__1: ; preds = %continue__2, %then0__1 + call void @__quantum__rt__qubit_release(%Qubit* %target) + call void @__quantum__rt__qubit_release_array(%Array* %ctls) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %ctls, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %7, i64 -1) + %33 = load i64, i64* %res + call void @__quantum__rt__callable_memory_management(i32 1, %Callable* %op, i64 -1) + call void @__quantum__rt__callable_update_alias_count(%Callable* %op, i64 -1) + ret i64 %33 +} + +declare void @__quantum__rt__callable_memory_management(i32, %Callable*, i64) + +declare void @__quantum__rt__callable_update_alias_count(%Callable*, i64) + +declare %Qubit* @__quantum__rt__qubit_allocate() + +declare %Array* @__quantum__rt__qubit_allocate_array(i64) + +declare void @__quantum__rt__callable_invoke(%Callable*, %Tuple*, %Tuple*) + +declare %Callable* @__quantum__rt__callable_copy(%Callable*, i1) + +declare void @__quantum__rt__callable_make_adjoint(%Callable*) + +declare i1 @__quantum__rt__result_equal(%Result*, %Result*) + +declare void @__quantum__rt__callable_make_controlled(%Callable*) + +declare void @__quantum__rt__callable_update_reference_count(%Callable*, i64) + +declare void @__quantum__rt__result_update_reference_count(%Result*, i64) + +declare void @__quantum__rt__qubit_release(%Qubit*) + +declare void @__quantum__rt__qubit_release_array(%Array*) + +define i64 @Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body() #0 { +entry: + %res = alloca i64 + store i64 0, i64* %res + %0 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__X, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %1 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %0) + store i64 %1, i64* %res + %2 = icmp ne i64 %1, 0 + br i1 %2, label %then0__1, label %continue__1 + +then0__1: ; preds = %entry + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + ret i64 %1 + +continue__1: ; preds = %entry + %3 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__Y, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %4 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %3) + store i64 %4, i64* %res + %5 = icmp ne i64 %4, 0 + br i1 %5, label %then0__2, label %continue__2 + +then0__2: ; preds = %continue__1 + %6 = add i64 10, %4 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + ret i64 %6 + +continue__2: ; preds = %continue__1 + %7 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__Z, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %8 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %7) + store i64 %8, i64* %res + %9 = icmp ne i64 %8, 0 + br i1 %9, label %then0__3, label %continue__3 + +then0__3: ; preds = %continue__2 + %10 = add i64 20, %8 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + ret i64 %10 + +continue__3: ; preds = %continue__2 + %11 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__H, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %12 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %11) + store i64 %12, i64* %res + %13 = icmp ne i64 %12, 0 + br i1 %13, label %then0__4, label %continue__4 + +then0__4: ; preds = %continue__3 + %14 = add i64 30, %12 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) + ret i64 %14 + +continue__4: ; preds = %continue__3 + %15 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__S, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %16 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %15) + store i64 %16, i64* %res + %17 = icmp ne i64 %16, 0 + br i1 %17, label %then0__5, label %continue__5 + +then0__5: ; preds = %continue__4 + %18 = add i64 40, %16 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + ret i64 %18 + +continue__5: ; preds = %continue__4 + %19 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__T, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %20 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %19) + store i64 %20, i64* %res + %21 = icmp ne i64 %20, 0 + br i1 %21, label %then0__6, label %continue__6 + +then0__6: ; preds = %continue__5 + %22 = add i64 50, %20 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) + ret i64 %22 + +continue__6: ; preds = %continue__5 + %23 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Callable*, i2, double }* getelementptr ({ %Callable*, i2, double }, { %Callable*, i2, double }* null, i32 1) to i64)) + %24 = bitcast %Tuple* %23 to { %Callable*, i2, double }* + %25 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %24, i64 0, i32 0 + %26 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %24, i64 0, i32 1 + %27 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %24, i64 0, i32 2 + %28 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__R, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) + %29 = load i2, i2* @PauliX + store %Callable* %28, %Callable** %25 + store i2 %29, i2* %26 + store double 4.200000e-01, double* %27 + %30 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @PartialApplication__1, [2 x void (%Tuple*, i64)*]* @MemoryManagement__1, %Tuple* %23) + %31 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %30) + store i64 %31, i64* %res + %32 = icmp ne i64 %31, 0 + br i1 %32, label %then0__7, label %continue__7 + +then0__7: ; preds = %continue__6 + %33 = add i64 60, %31 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %30, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %30, i64 -1) + ret i64 %33 + +continue__7: ; preds = %continue__6 + %targets = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) + %ctls = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) + %paulis__inline__1 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) + %34 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__1, i64 0) + %35 = bitcast i8* %34 to i2* + %36 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__1, i64 1) + %37 = bitcast i8* %36 to i2* + %38 = load i2, i2* @PauliX + %39 = load i2, i2* @PauliY + store i2 %38, i2* %35 + store i2 %39, i2* %37 + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__1, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) + call void @__quantum__qis__exp__body(%Array* %paulis__inline__1, double 4.200000e-01, %Array* %targets) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__1, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__1, i64 -1) + %paulis__inline__2 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) + %40 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__2, i64 0) + %41 = bitcast i8* %40 to i2* + %42 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__2, i64 1) + %43 = bitcast i8* %42 to i2* + %44 = load i2, i2* @PauliX + %45 = load i2, i2* @PauliY + store i2 %44, i2* %41 + store i2 %45, i2* %43 + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__2, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) + call void @__quantum__qis__exp__adj(%Array* %paulis__inline__2, double 4.200000e-01, %Array* %targets) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__2, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__2, i64 -1) + %46 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 0) + %47 = bitcast i8* %46 to %Qubit** + %48 = load %Qubit*, %Qubit** %47 + %49 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %48) + %50 = load %Result*, %Result** @ResultZero + %51 = call i1 @__quantum__rt__result_equal(%Result* %49, %Result* %50) + %52 = xor i1 %51, true + %53 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 1) + %54 = bitcast i8* %53 to %Qubit** + %55 = load %Qubit*, %Qubit** %54 + %56 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %55) + %57 = load %Result*, %Result** @ResultZero + %58 = call i1 @__quantum__rt__result_equal(%Result* %56, %Result* %57) + %59 = xor i1 %58, true + %60 = or i1 %52, %59 + br i1 %60, label %then0__8, label %else__1 + +then0__8: ; preds = %continue__7 + store i64 1, i64* %res + br label %continue__8 + +else__1: ; preds = %continue__7 + %61 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) + %62 = bitcast i8* %61 to %Qubit** + %qb__inline__3 = load %Qubit*, %Qubit** %62 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__3) + %63 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) + %64 = bitcast i8* %63 to %Qubit** + %qb__inline__4 = load %Qubit*, %Qubit** %64 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__4) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) + %paulis__inline__5 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) + %65 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__5, i64 0) + %66 = bitcast i8* %65 to i2* + %67 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__5, i64 1) + %68 = bitcast i8* %67 to i2* + %69 = load i2, i2* @PauliX + %70 = load i2, i2* @PauliY + store i2 %69, i2* %66 + store i2 %70, i2* %68 + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__5, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) + %71 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) + %72 = bitcast %Tuple* %71 to { %Array*, double, %Array* }* + %73 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %72, i64 0, i32 0 + %74 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %72, i64 0, i32 1 + %75 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %72, i64 0, i32 2 + store %Array* %paulis__inline__5, %Array** %73 + store double 4.200000e-01, double* %74 + store %Array* %targets, %Array** %75 + call void @__quantum__qis__exp__ctl(%Array* %ctls, { %Array*, double, %Array* }* %72) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__5, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__5, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %71, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) + %paulis__inline__6 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) + %76 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__6, i64 0) + %77 = bitcast i8* %76 to i2* + %78 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__6, i64 1) + %79 = bitcast i8* %78 to i2* + %80 = load i2, i2* @PauliX + %81 = load i2, i2* @PauliY + store i2 %80, i2* %77 + store i2 %81, i2* %79 + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__6, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) + %82 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) + %83 = bitcast %Tuple* %82 to { %Array*, double, %Array* }* + %84 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %83, i64 0, i32 0 + %85 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %83, i64 0, i32 1 + %86 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %83, i64 0, i32 2 + store %Array* %paulis__inline__6, %Array** %84 + store double 4.200000e-01, double* %85 + store %Array* %targets, %Array** %86 + call void @__quantum__qis__exp__ctladj(%Array* %ctls, { %Array*, double, %Array* }* %83) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__6, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__6, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %82, i64 -1) + %87 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) + %88 = bitcast i8* %87 to %Qubit** + %qb__inline__7 = load %Qubit*, %Qubit** %88 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__7) + %89 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) + %90 = bitcast i8* %89 to %Qubit** + %qb__inline__8 = load %Qubit*, %Qubit** %90 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__8) + %91 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 0) + %92 = bitcast i8* %91 to %Qubit** + %93 = load %Qubit*, %Qubit** %92 + %94 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %93) + %95 = load %Result*, %Result** @ResultZero + %96 = call i1 @__quantum__rt__result_equal(%Result* %94, %Result* %95) + %97 = xor i1 %96, true + %98 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 1) + %99 = bitcast i8* %98 to %Qubit** + %100 = load %Qubit*, %Qubit** %99 + %101 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %100) + %102 = load %Result*, %Result** @ResultZero + %103 = call i1 @__quantum__rt__result_equal(%Result* %101, %Result* %102) + %104 = xor i1 %103, true + %105 = or i1 %97, %104 + br i1 %105, label %then0__9, label %continue__9 + +then0__9: ; preds = %else__1 + store i64 72, i64* %res + br label %continue__9 + +continue__9: ; preds = %then0__9, %else__1 + call void @__quantum__rt__result_update_reference_count(%Result* %94, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %101, i64 -1) + br label %continue__8 + +continue__8: ; preds = %continue__9, %then0__8 + call void @__quantum__rt__qubit_release_array(%Array* %targets) + call void @__quantum__rt__qubit_release_array(%Array* %ctls) + call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %targets, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %ctls, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %49, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %56, i64 -1) + %106 = load i64, i64* %res + %107 = icmp ne i64 %106, 0 + br i1 %107, label %then0__10, label %continue__10 + +then0__10: ; preds = %continue__8 + %108 = add i64 70, %106 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %30, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %30, i64 -1) + ret i64 %108 + +continue__10: ; preds = %continue__8 + %qs = call %Array* @__quantum__rt__qubit_allocate_array(i64 3) + call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 1) + %109 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 0) + %110 = bitcast i8* %109 to %Qubit** + %qb__inline__9 = load %Qubit*, %Qubit** %110 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__9) + %111 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 2) + %112 = bitcast i8* %111 to %Qubit** + %qb__inline__10 = load %Qubit*, %Qubit** %112 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__10) + %bases__inline__11 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 3) + %113 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__11, i64 0) + %114 = bitcast i8* %113 to i2* + %115 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__11, i64 1) + %116 = bitcast i8* %115 to i2* + %117 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__11, i64 2) + %118 = bitcast i8* %117 to i2* + %119 = load i2, i2* @PauliX + %120 = load i2, i2* @PauliZ + %121 = load i2, i2* @PauliX + store i2 %119, i2* %114 + store i2 %120, i2* %116 + store i2 %121, i2* %118 + call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__11, i64 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 1) + %122 = call %Result* @__quantum__qis__measure__body(%Array* %bases__inline__11, %Array* %qs) + call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__11, i64 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %bases__inline__11, i64 -1) + %123 = load %Result*, %Result** @ResultZero + %124 = call i1 @__quantum__rt__result_equal(%Result* %122, %Result* %123) + %125 = xor i1 %124, true + br i1 %125, label %then0__11, label %continue__11 + +then0__11: ; preds = %continue__10 + store i64 80, i64* %res + br label %continue__11 + +continue__11: ; preds = %then0__11, %continue__10 + %126 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 0) + %127 = bitcast i8* %126 to %Qubit** + %qb__inline__12 = load %Qubit*, %Qubit** %127 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__12) + %128 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 2) + %129 = bitcast i8* %128 to %Qubit** + %qb__inline__13 = load %Qubit*, %Qubit** %129 + call void @__quantum__qis__h__body(%Qubit* %qb__inline__13) + call void @__quantum__rt__qubit_release_array(%Array* %qs) + call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %qs, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %122, i64 -1) + %130 = load i64, i64* %res + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %30, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %30, i64 -1) + ret i64 %130 +} + +define void @Microsoft__Quantum__Intrinsic__X__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__X__body(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__X__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__X__adj(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__X__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__X__ctl(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__X__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__X__ctladj(%Array* %3, %Qubit* %4) + ret void +} + +declare %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]*, [2 x void (%Tuple*, i64)*]*, %Tuple*) + +define void @Microsoft__Quantum__Intrinsic__Y__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__Y__body(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Y__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__Y__adj(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Y__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__Y__ctl(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Y__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__Y__ctladj(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Z__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__Z__body(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Z__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__Z__adj(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Z__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__Z__ctl(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__Z__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__Z__ctladj(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__H__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__H__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__H__adj(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__H__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__H__ctl(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__H__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__H__ctladj(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__S__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__S__body(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__S__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__S__adj(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__S__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__S__ctl(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__S__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__S__ctladj(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__T__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__T__body(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__T__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 + %2 = load %Qubit*, %Qubit** %1 + call void @Microsoft__Quantum__Intrinsic__T__adj(%Qubit* %2) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__T__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__T__ctl(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__T__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + call void @Microsoft__Quantum__Intrinsic__T__ctladj(%Array* %3, %Qubit* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__R__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { i2, double, %Qubit* }* + %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 + %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 + %4 = load i2, i2* %1 + %5 = load double, double* %2 + %6 = load %Qubit*, %Qubit** %3 + call void @Microsoft__Quantum__Intrinsic__R__body(i2 %4, double %5, %Qubit* %6) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__R__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { i2, double, %Qubit* }* + %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 + %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 + %4 = load i2, i2* %1 + %5 = load double, double* %2 + %6 = load %Qubit*, %Qubit** %3 + call void @Microsoft__Quantum__Intrinsic__R__adj(i2 %4, double %5, %Qubit* %6) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__R__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, { i2, double, %Qubit* }* }* + %1 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load { i2, double, %Qubit* }*, { i2, double, %Qubit* }** %2 + call void @Microsoft__Quantum__Intrinsic__R__ctl(%Array* %3, { i2, double, %Qubit* }* %4) + ret void +} + +define void @Microsoft__Quantum__Intrinsic__R__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, { i2, double, %Qubit* }* }* + %1 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load { i2, double, %Qubit* }*, { i2, double, %Qubit* }** %2 + call void @Microsoft__Quantum__Intrinsic__R__ctladj(%Array* %3, { i2, double, %Qubit* }* %4) + ret void +} + +define void @Lifted__PartialApplication__1__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* + %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 1 + %2 = load i2, i2* %1 + %3 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 2 + %4 = load double, double* %3 + %5 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %6 = getelementptr { %Qubit* }, { %Qubit* }* %5, i64 0, i32 0 + %7 = load %Qubit*, %Qubit** %6 + %8 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) + %9 = bitcast %Tuple* %8 to { i2, double, %Qubit* }* + %10 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 0 + %11 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 1 + %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 2 + store i2 %2, i2* %10 + store double %4, double* %11 + store %Qubit* %7, %Qubit** %12 + %13 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 + %14 = load %Callable*, %Callable** %13 + call void @__quantum__rt__callable_invoke(%Callable* %14, %Tuple* %8, %Tuple* %result-tuple) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %8, i64 -1) + ret void +} + +define void @Lifted__PartialApplication__1__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* + %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 1 + %2 = load i2, i2* %1 + %3 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 2 + %4 = load double, double* %3 + %5 = bitcast %Tuple* %arg-tuple to { %Qubit* }* + %6 = getelementptr { %Qubit* }, { %Qubit* }* %5, i64 0, i32 0 + %7 = load %Qubit*, %Qubit** %6 + %8 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) + %9 = bitcast %Tuple* %8 to { i2, double, %Qubit* }* + %10 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 0 + %11 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 1 + %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 2 + store i2 %2, i2* %10 + store double %4, double* %11 + store %Qubit* %7, %Qubit** %12 + %13 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 + %14 = load %Callable*, %Callable** %13 + %15 = call %Callable* @__quantum__rt__callable_copy(%Callable* %14, i1 false) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 1) + call void @__quantum__rt__callable_make_adjoint(%Callable* %15) + call void @__quantum__rt__callable_invoke(%Callable* %15, %Tuple* %8, %Tuple* %result-tuple) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %8, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) + ret void +} + +define void @Lifted__PartialApplication__1__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + %5 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* + %6 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 1 + %7 = load i2, i2* %6 + %8 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 2 + %9 = load double, double* %8 + %10 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) + %11 = bitcast %Tuple* %10 to { i2, double, %Qubit* }* + %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 0 + %13 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 1 + %14 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 2 + store i2 %7, i2* %12 + store double %9, double* %13 + store %Qubit* %4, %Qubit** %14 + %15 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) + %16 = bitcast %Tuple* %15 to { %Array*, { i2, double, %Qubit* }* }* + %17 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 0 + %18 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 1 + store %Array* %3, %Array** %17 + store { i2, double, %Qubit* }* %11, { i2, double, %Qubit* }** %18 + %19 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 0 + %20 = load %Callable*, %Callable** %19 + %21 = call %Callable* @__quantum__rt__callable_copy(%Callable* %20, i1 false) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 1) + call void @__quantum__rt__callable_make_controlled(%Callable* %21) + call void @__quantum__rt__callable_invoke(%Callable* %21, %Tuple* %15, %Tuple* %result-tuple) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %10, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %15, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %21, i64 -1) + ret void +} + +define void @Lifted__PartialApplication__1__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* + %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 + %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 + %3 = load %Array*, %Array** %1 + %4 = load %Qubit*, %Qubit** %2 + %5 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* + %6 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 1 + %7 = load i2, i2* %6 + %8 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 2 + %9 = load double, double* %8 + %10 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) + %11 = bitcast %Tuple* %10 to { i2, double, %Qubit* }* + %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 0 + %13 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 1 + %14 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 2 + store i2 %7, i2* %12 + store double %9, double* %13 + store %Qubit* %4, %Qubit** %14 + %15 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) + %16 = bitcast %Tuple* %15 to { %Array*, { i2, double, %Qubit* }* }* + %17 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 0 + %18 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 1 + store %Array* %3, %Array** %17 + store { i2, double, %Qubit* }* %11, { i2, double, %Qubit* }** %18 + %19 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 0 + %20 = load %Callable*, %Callable** %19 + %21 = call %Callable* @__quantum__rt__callable_copy(%Callable* %20, i1 false) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 1) + call void @__quantum__rt__callable_make_adjoint(%Callable* %21) + call void @__quantum__rt__callable_make_controlled(%Callable* %21) + call void @__quantum__rt__callable_invoke(%Callable* %21, %Tuple* %15, %Tuple* %result-tuple) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %10, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %15, i64 -1) + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %21, i64 -1) + ret void +} + +define void @MemoryManagement__1__RefCount(%Tuple* %capture-tuple, i64 %count-change) { +entry: + %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* + %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 + %2 = load %Callable*, %Callable** %1 + call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %2, i64 %count-change) + call void @__quantum__rt__callable_update_reference_count(%Callable* %2, i64 %count-change) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %capture-tuple, i64 %count-change) + ret void +} + +define void @MemoryManagement__1__AliasCount(%Tuple* %capture-tuple, i64 %count-change) { +entry: + %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* + %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 + %2 = load %Callable*, %Callable** %1 + call void @__quantum__rt__callable_memory_management(i32 1, %Callable* %2, i64 %count-change) + call void @__quantum__rt__callable_update_alias_count(%Callable* %2, i64 %count-change) + call void @__quantum__rt__tuple_update_alias_count(%Tuple* %capture-tuple, i64 %count-change) + ret void +} + +declare void @__quantum__rt__tuple_update_alias_count(%Tuple*, i64) + +attributes #0 = { "EntryPoint" } diff --git a/src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.qs b/src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.qs new file mode 100644 index 00000000000..b32c6bed364 --- /dev/null +++ b/src/QirRuntime/test/OpenSystemsSimulator/qir-test-opensimulator.qs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// This test uses all operations from the quantum instruction set that targets full state simulator. +// We are not validating the result, only that the test can compile and run against the simulator. +namespace Microsoft.Quantum.Testing.QIR +{ + open Microsoft.Quantum.Intrinsic; + + operation InvokeAllVariants(op : (Qubit => Unit is Adj + Ctl)) : Int + { + mutable res = 0; + using((target, ctls) = (Qubit(), Qubit[2])) + { + op(target); + Adjoint op(target); + if (M(target) != Zero) { let res = 1; } + else + { + H(ctls[0]); + H(ctls[1]); + Controlled op(ctls, target); + Adjoint Controlled op(ctls, target); + if (M(target) != Zero) { return 2; } + H(ctls[0]); + H(ctls[1]); + } + } + return res; + } + + @EntryPoint() + operation Test_Simulator_QIS() : Int + { + mutable res = 0; + let res = InvokeAllVariants(X); + if (res != 0) { return res; } + + let res = InvokeAllVariants(Y); + if (res != 0) { return 10 + res; } + + let res = InvokeAllVariants(Z); + if (res != 0) { return 20 + res; } + + let res = InvokeAllVariants(H); + if (res != 0) { return 30 + res; } + + let res = InvokeAllVariants(S); + if (res != 0) { return 40 + res; } + + let res = InvokeAllVariants(T); + if (res != 0) { return 50 + res; } + + let res = InvokeAllVariants(R(PauliX, 0.42, _)); + if (res != 0) { return 60 + res; } + + using((targets, ctls) = (Qubit[2], Qubit[2])) + { + let theta = 0.42; + Exp([PauliX, PauliY], theta, targets); + Adjoint Exp([PauliX, PauliY], theta, targets); + if (M(target) != Zero) { let res = 1; } + + H(ctls[0]); + H(ctls[1]); + Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); + Adjoint Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); + H(ctls[0]); + H(ctls[1]); + if (M(target) != Zero) { let res = 70 + 2; } + } + if (res != 0) { return 70 + res; } + + using (qs = Qubit[3]) + { + H(qs[0]); + H(qs[2]); + if (Measure([PauliX, PauliZ, PauliX], qs) != zero) { let res = 80; } + } + return res; + } +} From a30f93a846137b0b9e198e83f3d4f7fa09283ad8 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 19:35:08 -0800 Subject: [PATCH 006/158] Add rust to builds. --- build/steps-init.yml | 11 +++++++++++ src/QirRuntime/prerequisites.ps1 | 1 + 2 files changed, 12 insertions(+) diff --git a/build/steps-init.yml b/build/steps-init.yml index 28b5251b15b..e4566e49ee8 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -14,6 +14,17 @@ steps: packageType: sdk version: '3.1.300' +- script: | + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN + 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 --default-toolchain %RUSTUP_TOOLCHAIN% + echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" + displayName: Windows install rust + condition: eq( variables['Agent.OS'], 'Windows_NT' ) ## # Custom pre-reqs diff --git a/src/QirRuntime/prerequisites.ps1 b/src/QirRuntime/prerequisites.ps1 index e10df08fa3e..a25a382cc25 100644 --- a/src/QirRuntime/prerequisites.ps1 +++ b/src/QirRuntime/prerequisites.ps1 @@ -5,6 +5,7 @@ if ($Env:ENABLE_QIRRUNTIME -eq "true") { if (($IsWindows) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Win")))) { choco install llvm choco install ninja + choco install rustup } else { #brew install llvm # this seems to mess up native simulator build, probably because of STD libs # llvm should be already available on later Linux/Darwin systems From d35a600a19d54302cacbe5c47428a4f27ac121bb Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 19:44:57 -0800 Subject: [PATCH 007/158] Start adding to CI. --- build/build.ps1 | 9 +++++++++ src/Simulation/OpenSystems/build-open-simulator.ps1 | 4 ++++ src/Simulation/OpenSystems/test-open-simulator.ps1 | 3 +++ 3 files changed, 16 insertions(+) create mode 100644 src/Simulation/OpenSystems/build-open-simulator.ps1 create mode 100644 src/Simulation/OpenSystems/test-open-simulator.ps1 diff --git a/build/build.ps1 b/build/build.ps1 index 3af9ece1dc0..320535bc73c 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -16,6 +16,15 @@ if ($Env:ENABLE_NATIVE -ne "false") { Write-Host "Skipping build of native simulator because ENABLE_NATIVE variable is set to: $Env:ENABLE_NATIVE." } +if ($Env:ENABLE_OPENSIM -ne "false") { + $openSimulator = (Join-Path $PSScriptRoot "../src/Simulation/OpenSystems") + & "$openSimulator/build-open-simulator.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } +} else { + Write-Host "Skipping build of native simulator because ENABLE_OPENSIM variable is set to: $Env:ENABLE_OPENSIM." +} if ($Env:ENABLE_QIRRUNTIME -eq "true") { $qirRuntime = (Join-Path $PSScriptRoot "../src/QirRuntime") diff --git a/src/Simulation/OpenSystems/build-open-simulator.ps1 b/src/Simulation/OpenSystems/build-open-simulator.ps1 new file mode 100644 index 00000000000..54cbf6ac643 --- /dev/null +++ b/src/Simulation/OpenSystems/build-open-simulator.ps1 @@ -0,0 +1,4 @@ +Push-Location (Join-Path $PSScriptRoot runtime) + # TODO: Set debug/release flag. + cargo build +Pop-Location diff --git a/src/Simulation/OpenSystems/test-open-simulator.ps1 b/src/Simulation/OpenSystems/test-open-simulator.ps1 new file mode 100644 index 00000000000..6fdb212fdc0 --- /dev/null +++ b/src/Simulation/OpenSystems/test-open-simulator.ps1 @@ -0,0 +1,3 @@ +Push-Location (Join-Path $PSScriptRoot runtime) + cargo test +Pop-Location From 3e5d1d9af22c962efefa95f253d1acb276b50b18 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 19:59:18 -0800 Subject: [PATCH 008/158] Don't set toolchain in CI. --- build/steps-init.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/steps-init.yml b/build/steps-init.yml index e4566e49ee8..28268bd5294 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -15,13 +15,13 @@ steps: version: '3.1.300' - script: | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN + 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 --default-toolchain %RUSTUP_TOOLCHAIN% + 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' ) From 796670877a086de3e03038f928d0d488a8885417 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 21:35:29 -0800 Subject: [PATCH 009/158] Started building managed interface. --- .../OpenSystems/runtime/src/c_api.rs | 4 +- .../Microsoft.Quantum.Simulators.csproj | 15 ++ .../OpenSystemsSimulator/NativeInterface.cs | 208 ++++++++++++++++++ 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs index 54145558db9..4be99331c08 100644 --- a/src/Simulation/OpenSystems/runtime/src/c_api.rs +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -81,7 +81,9 @@ pub extern fn destroy(sim_id: usize) -> i64 { } #[no_mangle] -pub extern fn dump_to_console() -> () {} +pub extern fn dump_to_console(sim_id: usize) -> () { + // FIXME: implement this +} #[no_mangle] pub extern fn x(sim_id: usize, idx: usize) -> i64 { diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index df891ab5eb0..cec4a17dba1 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -38,6 +38,21 @@ PreserveNewest false + + runtimes\win-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll + PreserveNewest + false + + + runtimes\osx-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll + PreserveNewest + false + + + runtimes\linux-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll + PreserveNewest + false + diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs new file mode 100644 index 00000000000..5e5a7159455 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -0,0 +1,208 @@ +using System; +using System.IO; +using System.Reflection; +using System.Text.Json; +using System.Runtime.InteropServices; +using Microsoft.Quantum.Simulation.Core; + +#nullable enable + +namespace Microsoft.Quantum.Experimental +{ + /// + /// Abstracts away calls to and from libopensim. + /// + 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 Exception($"Exception in native open systems simulator runtime: {error}"); + } + } + + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="lasterr")] + private static extern string _LastError(); + + public const string DLL_NAME = "Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll"; + + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_name")] + private static extern string _GetName(); + + public static string Name + { + get + { + // TODO: Add get_name to c_api and uncomment this. + // LogCall("get_name"); + // return _GetName(); + return "OpenSystemsSimulator"; + } + } + + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="init")] + private static extern ulong _Init(uint initialCapacity); + + public static ulong Init(uint initialCapacity) + { + LogCall("init"); + return _Init(initialCapacity); + } + + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="destroy")] + private static extern Int64 _Destroy(uint simId); + + public static void Destroy(uint simId) + { + LogCall("init"); + CheckCall(_Destroy(simId)); + } + + + + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="dump_to_console")] + private static extern void _DumpToConsole(uint simId); + + public static void DumpToConsole(uint simId) + { + LogCall("dump_to_console"); + _DumpToConsole(simId); + } + + // TODO: Copy datamodel and uncomment. + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_current_state")] + // private static extern string _GetCurrentState(); + + // public static State CurrentState + // { + // get + // { + // LogCall("get_current_state"); + // return JsonSerializer.Deserialize(_GetCurrentState()); + // } + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_noise_model")] + // private static extern string _GetNoiseModel(); + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="set_noise_model")] + // private static extern void _SetNoiseModel(string noiseModel); + + // public static NoiseModel NoiseModel + // { + // get + // { + // LogCall("get_noise_model"); + // return JsonSerializer.Deserialize(_GetNoiseModel()); + // } + // set + // { + // LogCall("set_noise_model"); + // _SetNoiseModel(JsonSerializer.Serialize(value)); + // } + // } + + // FIXME: all of the following actually return an error code that needs to be checked, not void! + // To fix, write a new method that checks the error code that results, calls last_err if + // needed, and turns that error message into an exception. + + [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 void _Y(uint idx); + + // public static void Y(Qubit q) + // { + // LogCall("y"); + // _Y((uint)q.Id); + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="z")] + // private static extern void _Z(uint idx); + + // public static void Z(Qubit q) + // { + // LogCall("z"); + // _Z((uint)q.Id); + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="s")] + // private static extern void _S(uint idx); + + // public static void S(Qubit q) + // { + // LogCall("s"); + // _S((uint)q.Id); + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="s_adj")] + // private static extern void _SAdj(uint idx); + + // public static void SAdj(Qubit q) + // { + // LogCall("s_adj"); + // _SAdj((uint)q.Id); + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="t")] + // private static extern void _T(uint idx); + + // public static void T(Qubit q) + // { + // LogCall("t"); + // _T((uint)q.Id); + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="t_adj")] + // private static extern void _TAdj(uint idx); + + // public static void TAdj(Qubit q) + // { + // LogCall("t_adj"); + // _TAdj((uint)q.Id); + // } + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="cnot")] + // private static extern void _CNOT(uint idxControl, uint idxTarget); + + // public static void CNOT(Qubit control, Qubit target) + // { + // LogCall("cnot"); + // _CNOT((uint)control.Id, (uint)target.Id); + // } + + + // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="m")] + // private static extern void _M(uint idx, out uint result); + + // public static Result M(Qubit q) + // { + // LogCall("m"); + // _M((uint)q.Id, out var result); + // return result == 1 ? Result.One : Result.Zero; + // } + + } +} From df4028662a558365799177d0b5265315d1136c62 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 9 Feb 2021 22:56:04 -0800 Subject: [PATCH 010/158] Add System.Text.Json dependency. --- src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index cec4a17dba1..b23c23d7c4c 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -22,6 +22,10 @@ + + + + runtimes\win-x64\native\%(RecursiveDir)%(FileName)%(Extension) From 890dcc95dfb9b41a390fdfb4dcec194d464b50dc Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 00:54:05 -0800 Subject: [PATCH 011/158] Add native opensim sharedlibs to nupkg. --- build/pack.ps1 | 36 ++++++++++++++----- src/Simulation/OpenSystems/runtime/.gitignore | 3 ++ .../Microsoft.Quantum.Simulators.csproj | 6 ++-- ...crosoft.Quantum.Simulators.nuspec.template | 3 ++ 4 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 src/Simulation/OpenSystems/runtime/.gitignore diff --git a/build/pack.ps1 b/build/pack.ps1 index 8e998e1fd3c..8920833f3a3 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -7,18 +7,38 @@ $ErrorActionPreference = 'Stop' $all_ok = $True 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" } +pushd (Join-Path $PSScriptRoot ../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 +Write-Host "##[info]Copy open systems simulator xplat binaries" +Push-Location (Join-Path $PSScriptRoot ../src/Simulation/OpenSystems/runtime) + If (-not (Test-Path 'osx')) { mkdir 'osx' } + If (-not (Test-Path 'linux')) { mkdir 'linux' } + If (-not (Test-Path 'win10')) { mkdir 'win10' } + $Configuration = $Env:BUILD_CONFIGURATION.ToLowerInvariant(); + $DROP = (Join-Path "$Env:DROP_NATIVE" "src" "Simulation" "OpenSystems" "runtime" "target" $Configuration); + $DROP | Write-Host + Get-ChildItem -recurse "$DROP/*.dll" + if (Test-Path "$DROP/libopensim.dylib") { + Copy-Item "$DROP/libopensim.dylib" "osx/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" + } + if (Test-Path "$DROP/libopensim.so") { + Copy-Item "$DROP/libopensim.so" "linux/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" + } + if (Test-Path "$DROP/opensim.dll") { + Copy-Item "$DROP/opensim.dll" "win10/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" + } +Pop-Location + function Pack-One() { Param($project, $option1 = "", $option2 = "", $option3 = "") - nuget pack $project ` + nuget pack (Join-Path $PSScriptRoot $project) ` -OutputDirectory $Env:NUGET_OUTDIR ` -Properties Configuration=$Env:BUILD_CONFIGURATION ` -Version $Env:NUGET_VERSION ` @@ -41,7 +61,7 @@ function Pack-Dotnet() { } else { $args = @(); } - dotnet pack $project ` + dotnet pack (Join-Path $PSScriptRoot $project) ` -o $Env:NUGET_OUTDIR ` -c $Env:BUILD_CONFIGURATION ` -v detailed ` diff --git a/src/Simulation/OpenSystems/runtime/.gitignore b/src/Simulation/OpenSystems/runtime/.gitignore new file mode 100644 index 00000000000..5ad4050fa10 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/.gitignore @@ -0,0 +1,3 @@ +linux +osx +win10 diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index b23c23d7c4c..93a01bf893b 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -42,17 +42,17 @@ PreserveNewest false - + runtimes\win-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll PreserveNewest false - + runtimes\osx-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll PreserveNewest false - + runtimes\linux-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll PreserveNewest false diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template index 446ff2a8f7d..6937d50c19f 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template @@ -20,6 +20,9 @@ + + + From e90a97124cd92b106abb625ef3269809cc6759da Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 00:56:33 -0800 Subject: [PATCH 012/158] Updated DLL name in NativeInterface. --- .../Simulators/OpenSystemsSimulator/NativeInterface.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 5e5a7159455..b446d9a7f01 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -30,7 +30,8 @@ private static void CheckCall(Int64 errorCode) [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="lasterr")] private static extern string _LastError(); - public const string DLL_NAME = "Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll"; + public const string DLL_NAME = "Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll"; + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_name")] private static extern string _GetName(); From 5ee2983d7bc44bdf4ff08bf12cb361ded70e43e0 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 00:59:45 -0800 Subject: [PATCH 013/158] downgrade s.t.json. --- src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index 93a01bf893b..25a61754e9a 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -23,7 +23,7 @@ - + From 16c8e1b535102a83c3e18cec5678f83388aae408 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 08:03:21 -0800 Subject: [PATCH 014/158] Fix pack.ps1 paths. --- build/pack.ps1 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build/pack.ps1 b/build/pack.ps1 index 8920833f3a3..7ce857256d0 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -6,11 +6,14 @@ $ErrorActionPreference = 'Stop' & "$PSScriptRoot/set-env.ps1" $all_ok = $True +$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot ".."); +$DropNative = "$Env:DROP_NATIVE" -eq "" ? $RepoRoot : $Env:DROP_NATIVE; + Write-Host "##[info]Copy Native simulator xplat binaries" pushd (Join-Path $PSScriptRoot ../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" + $DROP = "$DropNative/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 @@ -20,10 +23,10 @@ Push-Location (Join-Path $PSScriptRoot ../src/Simulation/OpenSystems/runtime) If (-not (Test-Path 'osx')) { mkdir 'osx' } If (-not (Test-Path 'linux')) { mkdir 'linux' } If (-not (Test-Path 'win10')) { mkdir 'win10' } - $Configuration = $Env:BUILD_CONFIGURATION.ToLowerInvariant(); - $DROP = (Join-Path "$Env:DROP_NATIVE" "src" "Simulation" "OpenSystems" "runtime" "target" $Configuration); - $DROP | Write-Host - Get-ChildItem -recurse "$DROP/*.dll" + $DROP = Join-Path ` + "$DropNative" "src" "Simulation" "OpenSystems" ` + "runtime" "target" ` + $Env:BUILD_CONFIGURATION.ToLowerInvariant(); if (Test-Path "$DROP/libopensim.dylib") { Copy-Item "$DROP/libopensim.dylib" "osx/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" } From 74d1f8acf9137c3b7c1e6279032f9282598cc4e1 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 09:08:03 -0800 Subject: [PATCH 015/158] Set --release flag in opensim. --- src/Simulation/OpenSystems/build-open-simulator.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Simulation/OpenSystems/build-open-simulator.ps1 b/src/Simulation/OpenSystems/build-open-simulator.ps1 index 54cbf6ac643..32c056dc38c 100644 --- a/src/Simulation/OpenSystems/build-open-simulator.ps1 +++ b/src/Simulation/OpenSystems/build-open-simulator.ps1 @@ -1,4 +1,6 @@ +& (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); + Push-Location (Join-Path $PSScriptRoot runtime) - # TODO: Set debug/release flag. - cargo build + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + cargo build @releaseFlag; Pop-Location From 4af61f99de59bb1daafe84e05a119a6d116324b8 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 09:08:15 -0800 Subject: [PATCH 016/158] Add get_name. --- .../OpenSystems/runtime/src/c_api.rs | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs index 4be99331c08..7f038209afa 100644 --- a/src/Simulation/OpenSystems/runtime/src/c_api.rs +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -7,8 +7,6 @@ use lazy_static::lazy_static; use std::sync::Mutex; use std::collections::HashMap; -const DEFAULT_CAPACITY: usize = 1; - struct CApiState { register_state: State, noise_model: NoiseModel @@ -51,6 +49,28 @@ fn apply &Channel>(sim_id: usize, idxs: &[usize], channel_ // C API FUNCTIONS // +#[no_mangle] +pub extern fn get_name() -> *const c_char { + // There's a whole dance we have to do in order to allow the memory + // allocated for a string to be deallocated on the .NET side. + + let serialized = CString::new("open_sim").unwrap().into_raw(); + std::mem::forget(serialized); + serialized +} + +#[no_mangle] +pub extern fn lasterr() -> *const c_char { + match &*LAST_ERROR.lock().unwrap() { + None => ptr::null(), + Some(msg) => { + let wrapped_msg = CString::new(msg.as_str()).unwrap().into_raw(); + std::mem::forget(wrapped_msg); + wrapped_msg + } + } +} + #[no_mangle] pub extern fn init(initial_capacity: usize) -> usize { let state = &mut *STATE.lock().unwrap(); @@ -147,19 +167,6 @@ pub extern fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { }) } - -#[no_mangle] -pub extern fn lasterr() -> *const c_char { - match &*LAST_ERROR.lock().unwrap() { - None => ptr::null(), - Some(msg) => { - let wrapped_msg = CString::new(msg.as_str()).unwrap().into_raw(); - std::mem::forget(wrapped_msg); - wrapped_msg - } - } -} - #[no_mangle] pub extern fn get_noise_model(sim_id: usize) -> *const c_char { let state = &*STATE.lock().unwrap(); From 3be6249b5805a95536fe8085a209a3b891d37217 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 09:18:30 -0800 Subject: [PATCH 017/158] Added rest of c_api to NativeInterface.cs. --- .../OpenSystemsSimulator/NativeInterface.cs | 131 +++++++++--------- 1 file changed, 63 insertions(+), 68 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index b446d9a7f01..98a19ed7c2e 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -27,11 +27,12 @@ private static void CheckCall(Int64 errorCode) } } - [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="lasterr")] - private static extern string _LastError(); public const string DLL_NAME = "Microsoft.Quantum.Experimental.OpenSystemsSimulator.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_name")] private static extern string _GetName(); @@ -41,9 +42,8 @@ public static string Name get { // TODO: Add get_name to c_api and uncomment this. - // LogCall("get_name"); - // return _GetName(); - return "OpenSystemsSimulator"; + LogCall("get_name"); + return _GetName();; } } @@ -65,8 +65,6 @@ public static void Destroy(uint simId) CheckCall(_Destroy(simId)); } - - [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="dump_to_console")] private static extern void _DumpToConsole(uint simId); @@ -109,10 +107,7 @@ public static void DumpToConsole(uint simId) // } // } - // FIXME: all of the following actually return an error code that needs to be checked, not void! - // To fix, write a new method that checks the error code that results, calls last_err if - // needed, and turns that error message into an exception. - + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="h")] private static extern Int64 _H(ulong simId, uint idx); @@ -131,79 +126,79 @@ public static void X(ulong simId, Qubit q) CheckCall(_X(simId, (uint)q.Id)); } - // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="y")] - // private static extern void _Y(uint idx); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="y")] + private static extern Int64 _Y(ulong simId, uint idx); - // public static void Y(Qubit q) - // { - // LogCall("y"); - // _Y((uint)q.Id); - // } + 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 void _Z(uint idx); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="z")] + private static extern Int64 _Z(ulong simId, uint idx); - // public static void Z(Qubit q) - // { - // LogCall("z"); - // _Z((uint)q.Id); - // } + 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 void _S(uint idx); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="s")] + private static extern Int64 _S(ulong simId, uint idx); - // public static void S(Qubit q) - // { - // LogCall("s"); - // _S((uint)q.Id); - // } + 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 void _SAdj(uint idx); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="s_adj")] + private static extern Int64 _SAdj(ulong simId, uint idx); - // public static void SAdj(Qubit q) - // { - // LogCall("s_adj"); - // _SAdj((uint)q.Id); - // } + 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 void _T(uint idx); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="t")] + private static extern Int64 _T(ulong simId, uint idx); - // public static void T(Qubit q) - // { - // LogCall("t"); - // _T((uint)q.Id); - // } + 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 void _TAdj(uint idx); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="t_adj")] + private static extern Int64 _TAdj(ulong simId, uint idx); - // public static void TAdj(Qubit q) - // { - // LogCall("t_adj"); - // _TAdj((uint)q.Id); - // } + 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 void _CNOT(uint idxControl, uint idxTarget); + [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(Qubit control, Qubit target) - // { - // LogCall("cnot"); - // _CNOT((uint)control.Id, (uint)target.Id); - // } + 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 void _M(uint idx, out uint result); + [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(Qubit q) - // { - // LogCall("m"); - // _M((uint)q.Id, out var result); - // return result == 1 ? Result.One : Result.Zero; - // } + 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; + } } } From a48ad55e2e75f9d1ff4dfe2501bce5f43c3a3cb2 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 15:02:16 -0800 Subject: [PATCH 018/158] Initial attempt at type1 implementation. --- .../OpenSystemsSimulator/NativeInterface.cs | 11 +- .../OpenSystemsSimulator.cs | 127 ++++++++++++++++++ 2 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 98a19ed7c2e..4f8b8195643 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; using System.IO; using System.Reflection; @@ -57,18 +60,18 @@ public static ulong Init(uint initialCapacity) } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="destroy")] - private static extern Int64 _Destroy(uint simId); + private static extern Int64 _Destroy(ulong simId); - public static void Destroy(uint simId) + public static void Destroy(ulong simId) { LogCall("init"); CheckCall(_Destroy(simId)); } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="dump_to_console")] - private static extern void _DumpToConsole(uint simId); + private static extern void _DumpToConsole(ulong simId); - public static void DumpToConsole(uint simId) + public static void DumpToConsole(ulong simId) { LogCall("dump_to_console"); _DumpToConsole(simId); diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs new file mode 100644 index 00000000000..801134b1edd --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// 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; + +namespace Microsoft.Quantum.Experimental +{ + public partial class OpenSystemsSimulator : SimulatorBase, IType1Core, IDisposable + { + public override string Name => NativeInterface.Name; + private readonly ulong Id; + + public OpenSystemsSimulator(uint capacity = 3) + { + this.Id = NativeInterface.Init(capacity); + } + + public void ApplyControlledX__Body(Qubit control, Qubit target) + { + NativeInterface.CNOT(this.Id, control, target); + } + + public void ApplyControlledZ__Body(Qubit control, Qubit target) + { + // FIXME: Make this a new Type4, with CZ applied by decompositions. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledH__Body(Qubit target) + { + NativeInterface.H(this.Id, target); + } + + public void ApplyUncontrolledRx__AdjointBody(double angle, Qubit target) + { + // FIXME: Rotations are not yet supported. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledRx__Body(double angle, Qubit target) + { + // FIXME: Rotations are not yet supported. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledRy__AdjointBody(double angle, Qubit target) + { + // FIXME: Rotations are not yet supported. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledRy__Body(double angle, Qubit target) + { + // FIXME: Rotations are not yet supported. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledRz__AdjointBody(double angle, Qubit target) + { + // FIXME: Rotations are not yet supported. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledRz__Body(double angle, Qubit target) + { + // FIXME: Rotations are not yet supported. + throw new NotImplementedException(); + } + + public void ApplyUncontrolledS__AdjointBody(Qubit target) + { + NativeInterface.SAdj(this.Id, target); + } + + public void ApplyUncontrolledS__Body(Qubit target) + { + NativeInterface.S(this.Id, target); + } + + public void ApplyUncontrolledT__AdjointBody(Qubit target) + { + NativeInterface.TAdj(this.Id, target); + } + + public void ApplyUncontrolledT__Body(Qubit target) + { + NativeInterface.T(this.Id, target); + } + + public void ApplyUncontrolledX__Body(Qubit target) + { + NativeInterface.X(this.Id, target); + } + + public void ApplyUncontrolledY__Body(Qubit target) + { + NativeInterface.Y(this.Id, target); + } + + public void ApplyUncontrolledZ__Body(Qubit target) + { + NativeInterface.Z(this.Id, target); + } + + public void Dispose() + { + NativeInterface.Destroy(this.Id); + } + + public Result M__Body(Qubit target) => + NativeInterface.M(this.Id, target); + + public void Reset__Body(Qubit target) + { + throw new NotImplementedException(); + } + } +} From 1e9adcd06735414725ba86a6a204db004f004399 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 15:29:03 -0800 Subject: [PATCH 019/158] Add open systems data model. --- .../Microsoft.Quantum.Simulators.csproj | 1 + .../DataModel/ArrayConverter.cs | 252 ++++++++++++++++++ .../OpenSystemsSimulator/DataModel/Channel.cs | 116 ++++++++ .../DataModel/DelegatedConverter.cs | 34 +++ .../DataModel/Extensions.cs | 76 ++++++ .../DataModel/Instrument.cs | 23 ++ .../DataModel/NoiseModel.cs | 58 ++++ .../OpenSystemsSimulator/DataModel/README.md | 9 + .../OpenSystemsSimulator/DataModel/State.cs | 63 +++++ .../DataModel/StateConverter.cs | 78 ++++++ .../OpenSystemsSimulator/NativeInterface.cs | 59 ++-- .../OpenSystemsSimulator.cs | 18 +- 12 files changed, 754 insertions(+), 33 deletions(-) create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Channel.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/DelegatedConverter.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/README.md create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index 25a61754e9a..daf7a6bf409 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -24,6 +24,7 @@ + diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs new file mode 100644 index 00000000000..1d825d42a0c --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs @@ -0,0 +1,252 @@ +// 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/Channel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Channel.cs new file mode 100644 index 00000000000..395c544566c --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Channel.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 +{ + public class ChannelConverter : JsonConverter + { + + public override Channel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var (nQubits, kind, data) = ComplexArrayConverter.ReadQubitSizedArray(ref reader, options); + return kind switch + { + "Unitary" => new UnitaryChannel(nQubits, data), + "KrausDecomposition" => new KrausDecompositionChannel(nQubits, data), + _ => throw new JsonException($"Unknown state kind {kind}.") + }; + } + + public override void Write(Utf8JsonWriter writer, Channel value, JsonSerializerOptions options) + { + var arrayConverter = new ComplexArrayConverter(); + + writer.WriteStartObject(); + writer.WriteNumber("n_qubits", value.NQubits); + + writer.WritePropertyName("data"); + writer.WriteStartObject(); + writer.WritePropertyName( + value switch + { + UnitaryChannel _ => "Unitary", + KrausDecompositionChannel _ => "KrausDecomposition", + _ => throw new JsonException() + } + ); + + arrayConverter.Write(writer, value.Data, options); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + } + + [JsonConverter(typeof(ChannelConverter))] + public abstract class Channel + { + public int NQubits { get; } + + // NB: NDArray instances are mutable, but marking this as read-only + // means that the shape is at least fixed at initialization time. + public NDArray Data { get; } + + internal Channel(int nQubits, NDArray data) + { + NQubits = nQubits; + Data = data; + } + } + + public class UnitaryChannel : Channel + { + public UnitaryChannel(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 channel on {NQubits} qubits: {Data.AsTextMatrixOfComplex(rowSep: " ")}"; + } + public class KrausDecompositionChannel : Channel + { + public KrausDecompositionChannel(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 channel on {NQubits} qubits: {ops}"; + } + } + +} 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..63ca08cac2b --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Text.Json; +using System.Collections.Generic; +using System.Linq; +using NumSharp; + +namespace Microsoft.Quantum.Experimental +{ + 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) + { + throw new JsonException($"Expected a JSON token of type {type}, got {reader.TokenType} instead."); + } + } + public static bool IsComplexLike(this NDArray array) => + array.dtype == typeof(double) && + array.shape[^1] == 2; + } +} diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs new file mode 100644 index 00000000000..96605b94012 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs @@ -0,0 +1,23 @@ +// 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 Instrument + { + public IList Effects { get; set; } = new List(); + + public override string ToString() => + $"Instrument {{ Effects = {String.Join(", ", Effects.Select(effect => effect.ToString()))} }}"; + } +} diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs new file mode 100644 index 00000000000..9b7cd680752 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs @@ -0,0 +1,58 @@ +// 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 +{ + // 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 Channel? Cnot { get; set; } + + [JsonPropertyName("i")] + public Channel? I { get; set; } + + [JsonPropertyName("s")] + public Channel? S { get; set; } + + [JsonPropertyName("s_adj")] + public Channel? SAdj { get; set; } + + [JsonPropertyName("t")] + public Channel? T { get; set; } + + [JsonPropertyName("t_adj")] + public Channel? TAdj { get; set; } + + [JsonPropertyName("h")] + public Channel? H { get; set; } + + [JsonPropertyName("x")] + public Channel? X { get; set; } + + [JsonPropertyName("y")] + public Channel? Y { get; set; } + + [JsonPropertyName("z")] + public Channel? Z { get; set; } + + [JsonPropertyName("z_meas")] + public Instrument? ZMeas { get; set; } + } +} 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..59568e31ab6 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs @@ -0,0 +1,63 @@ +// 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 + { + public int NQubits { get; } + + public NDArray Data { get; } + + internal State(int nQubits, NDArray data) + { + NQubits = nQubits; + Data = data; + } + } + + public class PureState : State + { + 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 : State + { + 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..b79254bb211 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs @@ -0,0 +1,78 @@ +// 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) + { + var (nQubits, kind, data) = ComplexArrayConverter.ReadQubitSizedArray(ref reader, options); + return kind switch + { + "Pure" => new PureState(nQubits, data), + "Mixed" => new MixedState(nQubits, data), + _ => 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", + _ => throw new JsonException() + } + ); + + arrayConverter.Write(writer, value.Data, options); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + } + +} diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 4f8b8195643..e8e5fa19caf 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -77,38 +77,33 @@ public static void DumpToConsole(ulong simId) _DumpToConsole(simId); } - // TODO: Copy datamodel and uncomment. - // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_current_state")] - // private static extern string _GetCurrentState(); - - // public static State CurrentState - // { - // get - // { - // LogCall("get_current_state"); - // return JsonSerializer.Deserialize(_GetCurrentState()); - // } - // } - - // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_noise_model")] - // private static extern string _GetNoiseModel(); - - // [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="set_noise_model")] - // private static extern void _SetNoiseModel(string noiseModel); - - // public static NoiseModel NoiseModel - // { - // get - // { - // LogCall("get_noise_model"); - // return JsonSerializer.Deserialize(_GetNoiseModel()); - // } - // set - // { - // LogCall("set_noise_model"); - // _SetNoiseModel(JsonSerializer.Serialize(value)); - // } - // } + [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")] + private static extern string _GetNoiseModel(ulong simId); + + + public static NoiseModel GetNoiseModel(ulong simId) + { + LogCall("get_noise_model"); + return JsonSerializer.Deserialize(_GetNoiseModel(simId)); + } + + [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"); + CheckCall(_SetNoiseModel(simId, JsonSerializer.Serialize(noiseModel))); + } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="h")] diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index 801134b1edd..a1e74fc56a4 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -16,9 +16,25 @@ namespace Microsoft.Quantum.Experimental { public partial class OpenSystemsSimulator : SimulatorBase, IType1Core, IDisposable { - public override string Name => NativeInterface.Name; 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 = 3) { this.Id = NativeInterface.Init(capacity); From f6593954ad25658f08e44e03e541d39439c4ba3f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 10 Feb 2021 17:54:48 -0800 Subject: [PATCH 020/158] Fix null reference exception by adding explicit qubit manager. --- .../OpenSystemsSimulator.cs | 2 +- .../QuantumSimulator/SimulatorBase.cs | 31 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index a1e74fc56a4..92f6e59b768 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -35,7 +35,7 @@ public NoiseModel NoiseModel public State CurrentState => NativeInterface.GetCurrentState(this.Id); - public OpenSystemsSimulator(uint capacity = 3) + public OpenSystemsSimulator(uint capacity = 3) : base(new QubitManager((long)capacity)) { this.Id = NativeInterface.Init(capacity); } diff --git a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs index 0d5d5b53824..1b42a293cc2 100644 --- a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs +++ b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs @@ -238,15 +238,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); } @@ -281,6 +281,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; } @@ -313,6 +317,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; } @@ -343,6 +351,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; } @@ -375,6 +387,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; } @@ -404,6 +420,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; } @@ -422,6 +442,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; } From b7f7941904b028bc71325df12406617762c6ea59 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 11 Feb 2021 19:38:28 -0800 Subject: [PATCH 021/158] Started using QSharpCore interface temporarily. --- .../Decompositions/README.md | 4 + .../OpenSystemsSimulator.cs | 155 ++++++++++++++---- 2 files changed, 124 insertions(+), 35 deletions(-) create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md new file mode 100644 index 00000000000..e3ff7b78b41 --- /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#. \ No newline at end of file diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index 92f6e59b768..aef9433e0aa 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -14,7 +14,10 @@ namespace Microsoft.Quantum.Experimental { - public partial class OpenSystemsSimulator : SimulatorBase, IType1Core, IDisposable + // 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 { private readonly ulong Id; @@ -34,110 +37,192 @@ public NoiseModel NoiseModel public State CurrentState => NativeInterface.GetCurrentState(this.Id); + 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) + { + 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.Measure__Body(new QArray(Pauli.PauliZ), new QArray(qubit)) == Result.One) + { + this.Parent.X__Body(qubit); + } + } + base.Release(qubit, wasUsedOnlyForBorrowing); + } + } public OpenSystemsSimulator(uint capacity = 3) : base(new QubitManager((long)capacity)) { this.Id = NativeInterface.Init(capacity); } - public void ApplyControlledX__Body(Qubit control, Qubit target) + public void Exp__Body(IQArray paulis, double angle, IQArray targets) { - NativeInterface.CNOT(this.Id, control, target); + throw new NotImplementedException(); } - public void ApplyControlledZ__Body(Qubit control, Qubit target) + public void Exp__AdjointBody(IQArray paulis, double angle, IQArray targets) { - // FIXME: Make this a new Type4, with CZ applied by decompositions. throw new NotImplementedException(); } - public void ApplyUncontrolledH__Body(Qubit target) + public void Exp__ControlledBody(IQArray controls, IQArray paulis, double angle, IQArray targets) { - NativeInterface.H(this.Id, target); + throw new NotImplementedException(); } - public void ApplyUncontrolledRx__AdjointBody(double angle, Qubit target) + public void Exp__ControlledAdjointBody(IQArray controls, IQArray paulis, double angle, IQArray targets) { - // FIXME: Rotations are not yet supported. throw new NotImplementedException(); } - public void ApplyUncontrolledRx__Body(double angle, Qubit target) + public void H__Body(Qubit target) + { + NativeInterface.H(this.Id, target); + } + + public void H__ControlledBody(IQArray controls, Qubit target) { - // FIXME: Rotations are not yet supported. throw new NotImplementedException(); } - public void ApplyUncontrolledRy__AdjointBody(double angle, Qubit target) + public Result Measure__Body(IQArray paulis, IQArray targets) + { + if (targets is { Count: 1 } && paulis is { Count: 1 } && paulis.Single() == Pauli.PauliZ) + { + return NativeInterface.M(this.Id, targets.Single()); + } + else + { + // FIXME: Pass multiqubit and non-Z cases to decompositions. + throw new NotImplementedException(); + } + } + + public void R__Body(Pauli pauli, double angle, Qubit target) { - // FIXME: Rotations are not yet supported. throw new NotImplementedException(); } - public void ApplyUncontrolledRy__Body(double angle, Qubit target) + public void R__AdjointBody(Pauli pauli, double angle, Qubit target) { - // FIXME: Rotations are not yet supported. throw new NotImplementedException(); } - public void ApplyUncontrolledRz__AdjointBody(double angle, Qubit target) + public void R__ControlledBody(IQArray controls, Pauli pauli, double angle, Qubit target) { - // FIXME: Rotations are not yet supported. throw new NotImplementedException(); } - public void ApplyUncontrolledRz__Body(double angle, Qubit target) + public void R__ControlledAdjointBody(IQArray controls, Pauli pauli, double angle, Qubit target) { - // FIXME: Rotations are not yet supported. throw new NotImplementedException(); } - public void ApplyUncontrolledS__AdjointBody(Qubit target) + public void S__Body(Qubit target) + { + NativeInterface.S(this.Id, target); + } + + public void S__AdjointBody(Qubit target) { NativeInterface.SAdj(this.Id, target); } - public void ApplyUncontrolledS__Body(Qubit target) + public void S__ControlledBody(IQArray controls, Qubit target) { - NativeInterface.S(this.Id, target); + throw new NotImplementedException(); } - public void ApplyUncontrolledT__AdjointBody(Qubit target) + public void S__ControlledAdjointBody(IQArray controls, Qubit target) { - NativeInterface.TAdj(this.Id, target); + throw new NotImplementedException(); } - public void ApplyUncontrolledT__Body(Qubit target) + public void T__Body(Qubit target) { NativeInterface.T(this.Id, target); } - public void ApplyUncontrolledX__Body(Qubit target) + public void T__AdjointBody(Qubit target) + { + NativeInterface.TAdj(this.Id, target); + } + + public void T__ControlledBody(IQArray controls, Qubit target) + { + throw new NotImplementedException(); + } + + public void T__ControlledAdjointBody(IQArray controls, Qubit target) + { + throw new NotImplementedException(); + } + + public void X__Body(Qubit target) { NativeInterface.X(this.Id, target); } - public void ApplyUncontrolledY__Body(Qubit target) + public void X__ControlledBody(IQArray controls, Qubit target) { - NativeInterface.Y(this.Id, target); + // TODO: pass off to decompositions for more than one control. + if (controls is { Count: 1 }) + { + NativeInterface.CNOT(this.Id, controls[0], target); + } + else + { + throw new NotImplementedException(); + } } - public void ApplyUncontrolledZ__Body(Qubit target) + public void Y__Body(Qubit target) { - NativeInterface.Z(this.Id, target); + NativeInterface.Y(this.Id, target); } - public void Dispose() + public void Y__ControlledBody(IQArray controls, Qubit target) { - NativeInterface.Destroy(this.Id); + throw new NotImplementedException(); } - public Result M__Body(Qubit target) => - NativeInterface.M(this.Id, target); + public void Z__Body(Qubit target) + { + NativeInterface.Z(this.Id, target); + } - public void Reset__Body(Qubit target) + public void Z__ControlledBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } + + public void Dispose() + { + NativeInterface.Destroy(this.Id); + } + // public void Reset__Body(Qubit target) + // { + // // The open systems simulator doesn't have a reset operation, so simulate + // // it via an M followed by a conditional X. + // var res = M__Body(target); + // if (res == Result.One) + // { + // ApplyUncontrolledX__Body(target); + // } + // } } } From d6e7b123565c067e054e2c0e817108f7b05b8766 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 11 Feb 2021 20:03:06 -0800 Subject: [PATCH 022/158] Begin adding decompositions for opensim. --- .../OpenSystemsSimulator/Decompositions/CZ.qs | 13 +++++++++++ .../Decompositions/Interface.qs | 18 +++++++++++++++ .../OpenSystemsSimulator.cs | 22 ++++++++++++++++--- 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/CZ.qs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Interface.qs diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/CZ.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/CZ.qs new file mode 100644 index 00000000000..7fc5eea3f53 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/CZ.qs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Experimental { + + internal operation ApplyCZUsingCNOT(control : Qubit, target : Qubit) : Unit { + within { + ApplyUncontrolledH(target); + } apply { + ApplySinglyControlledX(control, target); + } + } +} diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Interface.qs b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Interface.qs new file mode 100644 index 00000000000..31059665b33 --- /dev/null +++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/Interface.qs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Experimental { + open Microsoft.Quantum.Intrinsic; + + operation ApplyUncontrolledH(target : Qubit) : Unit is Adj { + body intrinsic; + adjoint self; + } + + operation ApplySinglyControlledX(control : Qubit, target : Qubit) : Unit is Adj { + body intrinsic; + adjoint self; + } + +} + diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index aef9433e0aa..d4757690767 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -180,7 +180,11 @@ public void X__Body(Qubit target) public void X__ControlledBody(IQArray controls, Qubit target) { // TODO: pass off to decompositions for more than one control. - if (controls is { Count: 1 }) + if (controls is { Count: 0 }) + { + X__Body(target); + } + else if (controls is { Count: 1 }) { NativeInterface.CNOT(this.Id, controls[0], target); } @@ -207,7 +211,19 @@ public void Z__Body(Qubit target) public void Z__ControlledBody(IQArray controls, Qubit target) { - throw new NotImplementedException(); + // TODO: pass off to decompositions for more than one control. + if (controls is { Count: 0 }) + { + Z__Body(target); + } + else if (controls is { Count: 1 }) + { + Get().__Body__((controls[0], target)); + } + else + { + throw new NotImplementedException(); + } } public void Dispose() From 5fd4e795605bb0288dce73132db6634097e9248f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 12 Feb 2021 08:29:44 -0800 Subject: [PATCH 023/158] Expose noise_model::ideal() through c_api. --- src/Simulation/OpenSystems/runtime/src/c_api.rs | 12 ++++++++++++ .../OpenSystemsSimulator/DataModel/NoiseModel.cs | 2 ++ .../OpenSystemsSimulator/NativeInterface.cs | 12 +++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs index 7f038209afa..1e4c6c5e5c6 100644 --- a/src/Simulation/OpenSystems/runtime/src/c_api.rs +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -183,6 +183,18 @@ pub extern fn get_noise_model(sim_id: usize) -> *const c_char { } } +/// Returns a JSON serialization of the ideal noise model (i.e.: a noise model +/// that agrees with closed-system simulation). +#[no_mangle] +pub extern fn ideal_noise_model() -> *const c_char { + let serialized = CString::new( + serde_json::to_string(&NoiseModel::ideal()).unwrap().as_str() + ).unwrap().into_raw(); + std::mem::forget(serialized); + serialized +} + + #[no_mangle] pub extern fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { if new_model.is_null() { diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs index 9b7cd680752..7936a4abed2 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs @@ -54,5 +54,7 @@ public class NoiseModel [JsonPropertyName("z_meas")] public Instrument? ZMeas { get; set; } + + public static NoiseModel Ideal => NativeInterface.IdealNoiseModel(); } } diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index e8e5fa19caf..f7d6358e570 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -105,7 +105,17 @@ public static void SetNoiseModel(ulong simId, NoiseModel noiseModel) CheckCall(_SetNoiseModel(simId, JsonSerializer.Serialize(noiseModel))); } - + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="ideal_noise_model")] + private static extern string _IdealNoiseModel(); + + + public static NoiseModel IdealNoiseModel() + { + LogCall("ideal_noise_model"); + return JsonSerializer.Deserialize(_IdealNoiseModel()); + } + + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="h")] private static extern Int64 _H(ulong simId, uint idx); From 79c90032a96c380ad24c954e83aeea93d140ecb2 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 12 Feb 2021 14:58:04 -0800 Subject: [PATCH 024/158] Fix json serialization of instruments. --- .../Simulators/OpenSystemsSimulator/DataModel/Instrument.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs index 96605b94012..0de9c2b3117 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs @@ -15,6 +15,7 @@ namespace Microsoft.Quantum.Experimental { public class Instrument { + [JsonPropertyName("effects")] public IList Effects { get; set; } = new List(); public override string ToString() => From ca5a533b776df7d544087d4d7d9d5709246f944c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 12 Feb 2021 15:17:35 -0800 Subject: [PATCH 025/158] More informative exceptions in SetNoiseModel --- .../OpenSystemsSimulator/NativeInterface.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index f7d6358e570..6aa4d1ca9bc 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -102,7 +102,20 @@ public static NoiseModel GetNoiseModel(ulong simId) public static void SetNoiseModel(ulong simId, NoiseModel noiseModel) { LogCall("set_noise_model"); - CheckCall(_SetNoiseModel(simId, JsonSerializer.Serialize(noiseModel))); + 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 (Exception ex) + { + throw new Exception($"Could not set noise model from JSON:\n{jsonData}", ex); + } } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="ideal_noise_model")] From 438d72cee0418484f4bfe9723d433a762fb18abc Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Feb 2021 22:05:55 -0800 Subject: [PATCH 026/158] Pass DumpMachine through OnDisplayableDiagnostic. --- .../OpenSystemsSimulator.cs | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index d4757690767..ca8581107da 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -22,6 +22,7 @@ public partial class OpenSystemsSimulator : SimulatorBase, IQSharpCore private readonly ulong Id; public override string Name => NativeInterface.Name; + public NoiseModel NoiseModel { get @@ -230,15 +231,44 @@ public void Dispose() { NativeInterface.Destroy(this.Id); } - // public void Reset__Body(Qubit target) - // { - // // The open systems simulator doesn't have a reset operation, so simulate - // // it via an M followed by a conditional X. - // var res = M__Body(target); - // if (res == Result.One) - // { - // ApplyUncontrolledX__Body(target); - // } - // } + + 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; + }; + } } } From 76f78c5d372dc8694799b6d34abde4c822e71564 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 09:36:20 -0800 Subject: [PATCH 027/158] Began addressing warnings and adding unit tests. --- .../OpenSystems/runtime/src/c_api.rs | 3 + .../runtime/src/common_matrices.rs | 56 ++++++++++++++++--- src/Simulation/OpenSystems/runtime/src/lib.rs | 5 +- .../OpenSystems/runtime/src/linalg.rs | 8 +++ .../OpenSystems/runtime/src/noise_model.rs | 24 ++++---- .../OpenSystems/runtime/src/states.rs | 6 +- .../OpenSystems/runtime/src/utils.rs | 4 +- 7 files changed, 81 insertions(+), 25 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs index 1e4c6c5e5c6..73041f0fac1 100644 --- a/src/Simulation/OpenSystems/runtime/src/c_api.rs +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use std::ffi::CStr; use std::ffi::CString; use std::os::raw::c_char; diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs index 5bc1cce386e..6fe47ede178 100644 --- a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs +++ b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs @@ -1,57 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use core::f64::consts::FRAC_1_SQRT_2; use crate::utils::*; use ndarray::Array2; +use crate::linalg::HasDagger; -pub fn I() -> Array2 { +pub fn i() -> Array2 { array![ [ONE_C, ZERO_C], [ZERO_C, ONE_C] ] } -pub fn X() -> Array2 { +pub fn x() -> Array2 { array![ [ZERO_C, ONE_C], [ONE_C, ZERO_C] ] } -pub fn Y() -> Array2 { +pub fn y() -> Array2 { array![ [ZERO_C, I_C], [-I_C, ZERO_C] ] } -pub fn Z() -> Array2 { +pub fn z() -> Array2 { array![ [ONE_C, ZERO_C], [ZERO_C, -ONE_C] ] } -pub fn H() -> Array2 { +pub fn h() -> Array2 { array![ [ONE_C, ONE_C], [ONE_C, -ONE_C] ] * FRAC_1_SQRT_2 } -pub fn T() -> Array2 { +pub fn t() -> Array2 { array![ [ONE_C, ZERO_C], [ZERO_C, C64::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2)] ] } -pub fn S() -> Array2 { +pub fn s() -> Array2 { array![ [ONE_C, ZERO_C], [ZERO_C, C64::new(0.0_f64, 1.0_f64)] ] } -pub fn CNOT() -> Array2 { +pub fn cnot() -> Array2 { array![ [ONE_C, ZERO_C, ZERO_C, ZERO_C], [ZERO_C, ONE_C, ZERO_C, ZERO_C], @@ -59,3 +63,39 @@ pub fn CNOT() -> Array2 { [ZERO_C, ZERO_C, ONE_C, ZERO_C] ] } + +#[cfg(test)] +mod tests { + use super::*; + + fn is_self_adjoint(arr: Array2) -> bool { + arr == arr.dag() + } + + fn are_equal_to_precision(actual: Array2, expected: Array2) -> bool { + // If we use assert_eq here, we'll get bitten by finite precision. + // We also can't use LAPACK, since that greatly complicates bindings, + // so we do an ad hoc implementation here. + (actual - expected).map(|x| x.norm()).sum() <= 1e-10 + } + + #[test] + fn h_is_self_adjoint() { + assert!(is_self_adjoint(h())); + } + + #[test] + fn x_is_self_adjoint() { + assert!(is_self_adjoint(x())); + } + + #[test] + fn s_squares_to_z() { + assert_eq!(s().dot(&s()), z()); + } + + #[test] + fn t_squares_to_s() { + assert!(are_equal_to_precision(t().dot(&t()), s())); + } +} diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index 9c10a6c8687..b39117cb2c4 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #[macro_use(array)] extern crate ndarray; @@ -12,8 +15,6 @@ mod linalg; mod channels; mod noise_model; mod instrument; -use num_complex::Complex; -use ndarray::{ Array1 }; pub use crate::channels::depolarizing_channel; pub use crate::noise_model::NoiseModel; diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 690be2d4360..01f1fb7b598 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use core::ops::Range; use num_traits::Zero; use ndarray::{ Array, Array1, Array2 }; @@ -7,6 +10,11 @@ use std::cmp; use crate::{ C64, log_message }; pub trait HasDagger { + /// Returns the hermitian conjugate (colloquially, the dagger) of a + /// borrowed reference as a new copy. + /// + /// For most types implementing this trait, the hermitian conjugate + /// is represented by the conjugate transpose. fn dag(self: &Self) -> Self; } diff --git a/src/Simulation/OpenSystems/runtime/src/noise_model.rs b/src/Simulation/OpenSystems/runtime/src/noise_model.rs index cbc6f35430a..7decc7c32fe 100644 --- a/src/Simulation/OpenSystems/runtime/src/noise_model.rs +++ b/src/Simulation/OpenSystems/runtime/src/noise_model.rs @@ -1,6 +1,6 @@ use num_traits::{ Zero, One }; use crate::C64; -use crate::common_matrices::{ I, X, Y, Z, H, T, CNOT, S }; +use crate::common_matrices; use crate::states::StateData::Mixed; use crate::states::State; use crate::channels::Channel; @@ -30,11 +30,11 @@ impl NoiseModel { pub fn ideal() -> NoiseModel { let i = Channel { n_qubits: 1, - data: Unitary(I()) + data: Unitary(common_matrices::i()) }; let z = Channel { n_qubits: 1, - data: Unitary(Z()) + data: Unitary(common_matrices::z()) }; let z_meas = Instrument { effects: vec![ @@ -61,41 +61,41 @@ impl NoiseModel { NoiseModel { initial_state: State { n_qubits: 1, - data: Mixed((I() + Z()) / 2.0) + data: Mixed((common_matrices::i() + common_matrices::z()) / 2.0) }, i: i, x: Channel { n_qubits: 1, - data: Unitary(X()) + data: Unitary(common_matrices::x()) }, y: Channel { n_qubits: 1, - data: Unitary(Y()) + data: Unitary(common_matrices::y()) }, z: z, h: Channel { n_qubits: 1, - data: Unitary(H()) + data: Unitary(common_matrices::h()) }, t: Channel { n_qubits: 1, - data: Unitary(T()) + data: Unitary(common_matrices::t()) }, t_adj: Channel { n_qubits: 1, - data: Unitary(T().dag()) + data: Unitary(common_matrices::t().dag()) }, s: Channel { n_qubits: 1, - data: Unitary(S()) + data: Unitary(common_matrices::s()) }, s_adj: Channel { n_qubits: 1, - data: Unitary(S().dag()) + data: Unitary(common_matrices::s().dag()) }, cnot: Channel { n_qubits: 2, - data: Unitary(CNOT()) + data: Unitary(common_matrices::cnot()) }, z_meas: z_meas } diff --git a/src/Simulation/OpenSystems/runtime/src/states.rs b/src/Simulation/OpenSystems/runtime/src/states.rs index b6035f9fc5c..7c9550c2250 100644 --- a/src/Simulation/OpenSystems/runtime/src/states.rs +++ b/src/Simulation/OpenSystems/runtime/src/states.rs @@ -1,5 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -use crate::Z; +use crate::common_matrices; use crate::linalg::extend_one_to_n; use core::fmt::Display; use num_traits::One; @@ -87,7 +89,7 @@ impl State { match &self.data { Pure(psi) => todo!(), Mixed(rho) => { - let meas_op = extend_one_to_n(&Z(), idx_qubit, self.n_qubits); + let meas_op = extend_one_to_n(&common_matrices::z(), idx_qubit, self.n_qubits); let expectation: C64 = rho.dot(&meas_op).trace(); (1.0 + expectation.re) / 2.0 } diff --git a/src/Simulation/OpenSystems/runtime/src/utils.rs b/src/Simulation/OpenSystems/runtime/src/utils.rs index af29baccad4..d83ba4e6552 100644 --- a/src/Simulation/OpenSystems/runtime/src/utils.rs +++ b/src/Simulation/OpenSystems/runtime/src/utils.rs @@ -1,6 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use num_traits::Num; use num_complex::Complex; -use std::fmt::Write; pub type C64 = Complex; From 2f3bde5c9007950ac33dd2c062908398acd32115 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 09:59:52 -0800 Subject: [PATCH 028/158] A couple more tests. --- .../runtime/src/common_matrices.rs | 36 +++++++++++++++++-- .../OpenSystems/runtime/src/states.rs | 18 +++------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs index 6fe47ede178..3faefff267d 100644 --- a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs +++ b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs @@ -3,8 +3,8 @@ use core::f64::consts::FRAC_1_SQRT_2; use crate::utils::*; -use ndarray::Array2; -use crate::linalg::HasDagger; +use ndarray::{ Array, Array1, Array2 }; +use num_traits::{ Zero, One }; pub fn i() -> Array2 { array![ @@ -64,9 +64,23 @@ pub fn cnot() -> Array2 { ] } + +pub fn elementary_vec(idx: usize, n: usize) -> Array1 { + Array::from_shape_fn(n, |i| if i == idx {T::one()} else {T::zero()}) +} + +pub fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (usize, usize)) -> Array2 { + Array::from_shape_fn((n, m), |(i, j)| if i == idx0 && j == idx1 { + T::one() + } else { + T::zero() + }) +} + #[cfg(test)] mod tests { use super::*; + use crate::linalg::HasDagger; fn is_self_adjoint(arr: Array2) -> bool { arr == arr.dag() @@ -98,4 +112,22 @@ mod tests { fn t_squares_to_s() { assert!(are_equal_to_precision(t().dot(&t()), s())); } + + #[test] + fn elementary_vec_is_correct() { + assert_eq!(elementary_vec::(2, 4), array![0i64, 0i64, 1i64, 0i64]) + } + + #[test] + fn elementary_matrix_is_correct() { + assert_eq!( + elementary_matrix::((1, 3), (4, 5)), + array![ + [0i64, 0i64, 0i64, 0i64, 0i64], + [0i64, 0i64, 0i64, 1i64, 0i64], + [0i64, 0i64, 0i64, 0i64, 0i64], + [0i64, 0i64, 0i64, 0i64, 0i64] + ] + ) + } } diff --git a/src/Simulation/OpenSystems/runtime/src/states.rs b/src/Simulation/OpenSystems/runtime/src/states.rs index 7c9550c2250..dad3867116f 100644 --- a/src/Simulation/OpenSystems/runtime/src/states.rs +++ b/src/Simulation/OpenSystems/runtime/src/states.rs @@ -5,7 +5,6 @@ use crate::common_matrices; use crate::linalg::extend_one_to_n; use core::fmt::Display; use num_traits::One; -use num_traits::Zero; use crate::states::StateData::Mixed; use crate::states::StateData::Pure; use crate::QubitSized; @@ -51,17 +50,8 @@ impl Display for StateData { } } -fn elementary_vec(idx: usize, n: usize) -> Array1 { - Array::from_shape_fn(n, |i| if i == idx {T::one()} else {T::zero()}) -} -fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (usize, usize)) -> Array2 { - Array::from_shape_fn((n, m), |(i, j)| if i == idx0 && j == idx1 { - T::one() - } else { - T::zero() - }) -} + impl State { pub fn extend(self: &Self, n_qubits: usize) -> State { @@ -69,8 +59,8 @@ impl State { State { n_qubits: self.n_qubits + n_qubits, data: match &self.data { - Pure(psi) => Pure(psi.tensor(&elementary_vec(0, new_dim))), - Mixed(rho) => Mixed(rho.tensor(&elementary_matrix((0, 0), (new_dim, new_dim)))) + Pure(psi) => Pure(psi.tensor(&common_matrices::elementary_vec(0, new_dim))), + Mixed(rho) => Mixed(rho.tensor(&common_matrices::elementary_matrix((0, 0), (new_dim, new_dim)))) } } } @@ -79,7 +69,7 @@ impl State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); State { n_qubits: n_qubits, - data: Mixed(elementary_matrix((0, 0), (new_dim, new_dim))) + data: Mixed(common_matrices::elementary_matrix((0, 0), (new_dim, new_dim))) } } From f4b04ca40f4bf3ffcb247a1a185c407579c48de3 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 11:22:01 -0800 Subject: [PATCH 029/158] Started enabling doctests for Rust runtime. --- .../runtime/src/common_matrices.rs | 44 ++++++++++++++++++- .../OpenSystems/runtime/src/linalg.rs | 6 +-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs index 3faefff267d..b67aebad6b2 100644 --- a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs +++ b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs @@ -1,11 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//! Definitions for commonly used vectors and matrices, such as the Pauli +//! matrices, common Clifford operators, and elementary matrices. + use core::f64::consts::FRAC_1_SQRT_2; -use crate::utils::*; +use std::convert::TryInto; + use ndarray::{ Array, Array1, Array2 }; use num_traits::{ Zero, One }; +use crate::utils::*; + pub fn i() -> Array2 { array![ [ONE_C, ZERO_C], @@ -64,7 +70,21 @@ pub fn cnot() -> Array2 { ] } - +/// Returns an elementary vector; that is, a vector with a one at a given +/// index and zeros everywhere else. +/// +/// # Examples +/// The following are equivalent: +/// ``` +/// # #[macro_use] extern crate ndarray; +/// # use opensim::elementary_vec; +/// let vec = elementary_vec::(2, 4); +/// ``` +/// and: +/// ``` +/// # #[macro_use] extern crate ndarray; +/// let vec = array![0i64, 0i64, 1i64, 0i64]; +/// ``` pub fn elementary_vec(idx: usize, n: usize) -> Array1 { Array::from_shape_fn(n, |i| if i == idx {T::one()} else {T::zero()}) } @@ -77,6 +97,26 @@ pub fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (u }) } +/// Returns an identity matrix that acts on state vectors of registers of +/// qubits with a given size. +/// +/// # Example +/// The following snippet defines a two-qubit identity matrix: +/// ``` +/// # #[macro_use] extern crate ndarray; +/// # use opensim::nq_eye; +/// let eye = nq_eye(2usize); +/// assert_eq!(eye, array![ +/// [Complex::new(1f64, 0f64), Complex::new(0f64, 0f64), Complex::new(0f64, 0f64), Complex::new(0f64, 0f64)], +/// [Complex::new(0f64, 0f64), Complex::new(1f64, 0f64), Complex::new(0f64, 0f64), Complex::new(0f64, 0f64)], +/// [Complex::new(0f64, 0f64), Complex::new(0f64, 0f64), Complex::new(1f64, 0f64), Complex::new(0f64, 0f64)], +/// [Complex::new(0f64, 0f64), Complex::new(0f64, 0f64), Complex::new(0f64, 0f64), Complex::new(1f64, 0f64)], +/// ]); +/// ``` +pub fn nq_eye(nq: usize) -> Array2 { + Array2::eye(2usize.pow(nq.try_into().unwrap())) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 01f1fb7b598..3a597af63a3 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -7,7 +7,8 @@ use ndarray::{ Array, Array1, Array2 }; use std::ops::Mul; use std::convert::TryInto; use std::cmp; -use crate::{ C64, log_message }; + +use crate::{ C64, log_message, nq_eye }; pub trait HasDagger { /// Returns the hermitian conjugate (colloquially, the dagger) of a @@ -84,9 +85,6 @@ impl Trace for &Array2 { } } -fn nq_eye(nq: usize) -> Array2 { - Array2::eye(2usize.pow(nq.try_into().unwrap())) -} // FIXME: weaken data to be a view so that to_owned isn't needed. // FIXME: modify to Result<..., String> so that errors can propagate to the C API. From 22a7c5524002dd45f8c8f8af3601167077ec42e9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 11:22:44 -0800 Subject: [PATCH 030/158] Fix new doctest. --- src/Simulation/OpenSystems/runtime/src/common_matrices.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs index b67aebad6b2..ebcaa6f7da1 100644 --- a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs +++ b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs @@ -105,6 +105,7 @@ pub fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (u /// ``` /// # #[macro_use] extern crate ndarray; /// # use opensim::nq_eye; +/// use num_complex::Complex; /// let eye = nq_eye(2usize); /// assert_eq!(eye, array![ /// [Complex::new(1f64, 0f64), Complex::new(0f64, 0f64), Complex::new(0f64, 0f64), Complex::new(0f64, 0f64)], From 59e0bf3998da2305ecc32826ab235c6adc335bd9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 11:53:54 -0800 Subject: [PATCH 031/158] Added more API doc comments. --- .../runtime/src/common_matrices.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs index ebcaa6f7da1..5e53920d582 100644 --- a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs +++ b/src/Simulation/OpenSystems/runtime/src/common_matrices.rs @@ -12,6 +12,7 @@ use num_traits::{ Zero, One }; use crate::utils::*; +/// Returns a copy of the single-qubit identity matrix. pub fn i() -> Array2 { array![ [ONE_C, ZERO_C], @@ -19,6 +20,7 @@ pub fn i() -> Array2 { ] } +/// Returns a copy of the Pauli 𝑋 operator. pub fn x() -> Array2 { array![ [ZERO_C, ONE_C], @@ -26,6 +28,7 @@ pub fn x() -> Array2 { ] } +/// Returns a copy of the Pauli 𝑌 operator. pub fn y() -> Array2 { array![ [ZERO_C, I_C], @@ -33,6 +36,7 @@ pub fn y() -> Array2 { ] } +/// Returns a copy of the Pauli 𝑍 operator. pub fn z() -> Array2 { array![ [ONE_C, ZERO_C], @@ -40,6 +44,7 @@ pub fn z() -> Array2 { ] } +/// Returns a copy of the single-qubit Hadamard transformation. pub fn h() -> Array2 { array![ [ONE_C, ONE_C], @@ -144,6 +149,21 @@ mod tests { assert!(is_self_adjoint(x())); } + #[test] + fn y_is_self_adjoint() { + assert!(is_self_adjoint(y())); + } + + #[test] + fn z_is_self_adjoint() { + assert!(is_self_adjoint(z())); + } + + #[test] + fn cnot_is_self_adjoint() { + assert!(is_self_adjoint(cnot())); + } + #[test] fn s_squares_to_z() { assert_eq!(s().dot(&s()), z()); From 9bb6b31e7d3486c4501a258b0b50c8e2388d5ae5 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 12:53:00 -0800 Subject: [PATCH 032/158] A bit of code cleanup. --- src/Simulation/OpenSystems/runtime/src/lib.rs | 35 --------------- .../OpenSystems/runtime/src/states.rs | 44 +++++++------------ 2 files changed, 15 insertions(+), 64 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index b39117cb2c4..4cd8b2f7270 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -33,38 +33,3 @@ pub struct QubitSized { n_qubits: usize, data: T } - -pub fn test() { - println!("Test!"); - let mut noise = NoiseModel::ideal(); - noise.h = noise.h * depolarizing_channel(0.025f64); - match &noise.h.data { - Unitary(u) => println!("{}", u), - KrausDecomposition(ks) => println!("{}", ks) - }; - let noise = noise; - - let mut rho = noise.initial_state.clone(); - // rho = rho.extend(1); - // rho = noise.h.apply_to(&[0], &rho).unwrap(); - // rho = noise.h.apply_to(&[0], &rho).unwrap(); - // rho = noise.h.apply_to(&[0], &rho).unwrap(); - // rho = noise.h.apply_to(&[0], &rho).unwrap(); - // rho = noise.h.apply_to(&[0], &rho).unwrap(); - - println!("{}", rho); - - let pi_0 = &noise.z_meas.effects[0]; - let rho_0 = pi_0.apply_to(&[0], &rho).unwrap(); - - println!("{}", rho_0); - - println!(""); - println!("{}", rho.ideal_z_meas_pr(0)); - println!("{}", rho.ideal_z_meas_pr(1)); - - println!(""); - println!("{:?}", depolarizing_channel(0.05f64)); - - // println!("{}", X()) -} diff --git a/src/Simulation/OpenSystems/runtime/src/states.rs b/src/Simulation/OpenSystems/runtime/src/states.rs index dad3867116f..b5b4297063c 100644 --- a/src/Simulation/OpenSystems/runtime/src/states.rs +++ b/src/Simulation/OpenSystems/runtime/src/states.rs @@ -2,20 +2,16 @@ // Licensed under the MIT License. use crate::common_matrices; -use crate::linalg::extend_one_to_n; use core::fmt::Display; use num_traits::One; use crate::states::StateData::Mixed; use crate::states::StateData::Pure; use crate::QubitSized; -use crate::ONE_C; use crate::linalg::Trace; -use ndarray::Array; use crate::C64; use ndarray::{ Array1, Array2 }; -use derive_more::{Display}; use std::convert::TryInto; -use crate::linalg::{ HasDagger, Tensor }; +use crate::linalg::{ Tensor }; use serde::{ Serialize, Deserialize }; #[derive(Clone, Debug, Serialize, Deserialize)] @@ -72,19 +68,6 @@ impl State { data: Mixed(common_matrices::elementary_matrix((0, 0), (new_dim, new_dim))) } } - - // TODO: deprecate this in favor of relying on the measurement probabilities - // returned by an instrument. - pub fn ideal_z_meas_pr(&self, idx_qubit: usize) -> f64 { - match &self.data { - Pure(psi) => todo!(), - Mixed(rho) => { - let meas_op = extend_one_to_n(&common_matrices::z(), idx_qubit, self.n_qubits); - let expectation: C64 = rho.dot(&meas_op).trace(); - (1.0 + expectation.re) / 2.0 - } - } - } } impl Trace for &State { @@ -98,14 +81,17 @@ impl Trace for &State { } } -// impl DensityOperator { -// fn new(n_qubits: usize) -> Self { -// let dim = 2usize.pow(n_qubits.try_into().unwrap()); -// let mut data = Array::zeros((dim, dim)); -// data[(0, 0)] = ONE_C; -// DensityOperator { -// n_qubits: n_qubits, -// data: data -// } -// } -// } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trace_pure_is_one() { + let pure = State { + n_qubits: 1usize, + data: Pure(common_matrices::elementary_vec(0, 2)) + }; + assert_eq!(pure.trace(), C64::one()); + } +} From 22cfe2111e3d500f38568d555a275d30d282c2a5 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 13:15:38 -0800 Subject: [PATCH 033/158] Start adding integration tests for experimental simulator in C# runtime. --- .../Circuits/UnitTests.qs | 1 + .../Simulators.Tests/Circuits/TeleportTest.qs | 71 ++++++++++--------- .../Simulators.Tests/Circuits/UnitTests.qs | 11 +-- .../IntrinsicTests/Arrays/Tests.qs | 1 + .../IntrinsicTests/Random/Tests.qs | 8 ++- 5 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs b/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs index fedf54f5c17..2db0121dfe1 100644 --- a/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs +++ b/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs @@ -19,6 +19,7 @@ namespace Microsoft.Quantum.Tests.UnitTests { @Test("QuantumSimulator") @Test("ToffoliSimulator") + @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") operation UnitTest1 () : Unit { } diff --git a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs index 64d4808065e..e20291541d2 100644 --- a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs +++ b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs @@ -1,42 +1,43 @@ -// 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); + + @Test("QuantumSimulator") + @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/Circuits/UnitTests.qs b/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs index 1b68749a058..c2bbb6ec49f 100644 --- a/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs +++ b/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs @@ -9,17 +9,18 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { @Test("QuantumSimulator") @Test("ToffoliSimulator") @Test("ResourcesEstimator") + @Test("QuantumSimulator") + @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") operation QSharpUnitTest() : Unit { - Message("Worked!"); - } + Message("Worked!"); + } - @Test("QuantumSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.TrivialSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.ModifiedTrivialSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.UnitTests.TrivialSimulator") operation ArbitraryUnitTestTarget() : Unit { - Message("Worked!"); - } + Message("Worked!"); + } } 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 aff82adbc8a..19172054b91 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 { let randomInt = DrawRandomInt(0, 45); if (randomInt > 45 or randomInt < 0) { From 0da2d8297c9c38bc27c024c3652cc0e42dc94319 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 14:13:13 -0800 Subject: [PATCH 034/158] Fix teleport test. --- src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs index e20291541d2..cc37f0abbe9 100644 --- a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs +++ b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs @@ -3,6 +3,7 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Diagnostics; @Test("QuantumSimulator") @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") From 500c19117068f1cf2dea139c0f4d0564eb526bfb Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 21:27:03 -0800 Subject: [PATCH 035/158] Attempt at fixing project-based tests. --- .../Simulators.Tests/Circuits/UnitTests.qs | 7 +++---- .../Simulators/Microsoft.Quantum.Simulators.csproj | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs b/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs index c2bbb6ec49f..9ed34836fd7 100644 --- a/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs +++ b/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs @@ -5,22 +5,21 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Diagnostics; - + @Test("QuantumSimulator") @Test("ToffoliSimulator") @Test("ResourcesEstimator") @Test("QuantumSimulator") - @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") operation QSharpUnitTest() : Unit { Message("Worked!"); } - + @Test("Microsoft.Quantum.Simulation.Simulators.Tests.TrivialSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.ModifiedTrivialSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.UnitTests.TrivialSimulator") operation ArbitraryUnitTestTarget() : Unit { Message("Worked!"); } - + } diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index 3540dcb3eb2..59e23eef2c9 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -1,4 +1,4 @@ - + @@ -44,18 +44,18 @@ PreserveNewest false - - runtimes\win-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll + + runtimes\win-x64\native\%(RecursiveDir)%(FileName)%(Extension) PreserveNewest false - - runtimes\osx-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll + + runtimes\osx-x64\native\%(RecursiveDir)%(FileName)%(Extension) PreserveNewest false - - runtimes\linux-x64\native\Microsoft.Quantum.Experimental.OpenSystemsSimulator.Native.dll + + runtimes\linux-x64\native\%(RecursiveDir)%(FileName)%(Extension) PreserveNewest false From 3a5e87f89f0f8d964668b548e441230a094984d0 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 21:27:19 -0800 Subject: [PATCH 036/158] One more Rust-side unit test. --- src/Simulation/OpenSystems/runtime/src/linalg.rs | 2 +- src/Simulation/OpenSystems/runtime/src/noise_model.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 3a597af63a3..15bd5aee777 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -8,7 +8,7 @@ use std::ops::Mul; use std::convert::TryInto; use std::cmp; -use crate::{ C64, log_message, nq_eye }; +use crate::{ C64, nq_eye }; pub trait HasDagger { /// Returns the hermitian conjugate (colloquially, the dagger) of a diff --git a/src/Simulation/OpenSystems/runtime/src/noise_model.rs b/src/Simulation/OpenSystems/runtime/src/noise_model.rs index 7decc7c32fe..eb0aa0de311 100644 --- a/src/Simulation/OpenSystems/runtime/src/noise_model.rs +++ b/src/Simulation/OpenSystems/runtime/src/noise_model.rs @@ -101,3 +101,14 @@ impl NoiseModel { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_serialize_noise_model() { + let noise_model = NoiseModel::ideal(); + let _json = serde_json::to_string(&noise_model); + } +} From f960430ee028eed8c0fa3dee8db23a35de9bab70 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 21:46:26 -0800 Subject: [PATCH 037/158] Added opensim to local dev props, and reverted bad change to unittests.qs. --- src/Simulation/Common/Simulators.Dev.props | 22 +++++++++++++++++++ .../Simulators.Tests/Circuits/UnitTests.qs | 16 +++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index b4eead59d87..96c52defb3e 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -5,8 +5,11 @@ bin\$(BuildConfiguration)\$(TargetFramework)\$(AssemblyName).xml $([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory)..\..\..\)) $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/Native/build/drop)) + $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/OpenSystems/runtime/target/$(Configuration.ToLowerInvariant()))) + + $([MSBuild]::NormalizePath($(NativeBuildPath)/libMicrosoft.Quantum.Simulator.Runtime.dylib)) $([MSBuild]::NormalizePath($(NativeBuildPath)/libMicrosoft.Quantum.Simulator.Runtime.so)) @@ -28,7 +31,26 @@ false + + + + $([MSBuild]::NormalizePath($(OpenSimBuildPath)/libopensim.dylib)) + $([MSBuild]::NormalizePath($(OpenSimBuildPath)/libopensim.so)) + $([MSBuild]::NormalizePath($(OpenSimBuildPath)/opensim.dll)) + $(OpenSimDllMac) + $(OpenSimDllLinux) + $(OpenSimDllWindows) + + + + + Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll + PreserveNewest + false + + + diff --git a/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs b/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs index 9ed34836fd7..1b68749a058 100644 --- a/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs +++ b/src/Simulation/Simulators.Tests/Circuits/UnitTests.qs @@ -5,21 +5,21 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Diagnostics; - + @Test("QuantumSimulator") @Test("ToffoliSimulator") @Test("ResourcesEstimator") - @Test("QuantumSimulator") operation QSharpUnitTest() : Unit { - Message("Worked!"); - } - + Message("Worked!"); + } + + @Test("QuantumSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.TrivialSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.ModifiedTrivialSimulator") @Test("Microsoft.Quantum.Simulation.Simulators.Tests.UnitTests.TrivialSimulator") operation ArbitraryUnitTestTarget() : Unit { - Message("Worked!"); - } - + Message("Worked!"); + } + } From 49c52cf1726404e086c9712e78ca331068988f92 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 21:52:07 -0800 Subject: [PATCH 038/158] Undo one other bad change. --- src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs b/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs index 2db0121dfe1..fedf54f5c17 100644 --- a/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs +++ b/src/Simulation/CSharpGeneration.Tests/Circuits/UnitTests.qs @@ -19,7 +19,6 @@ namespace Microsoft.Quantum.Tests.UnitTests { @Test("QuantumSimulator") @Test("ToffoliSimulator") - @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") operation UnitTest1 () : Unit { } From b273d1855f24556cfd3a787e2cf18e0f16727c15 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Feb 2021 21:53:56 -0800 Subject: [PATCH 039/158] Disable test that depends on Rz. --- src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs index cc37f0abbe9..829ee22d483 100644 --- a/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs +++ b/src/Simulation/Simulators.Tests/Circuits/TeleportTest.qs @@ -6,7 +6,8 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { open Microsoft.Quantum.Diagnostics; @Test("QuantumSimulator") - @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") + // TODO: Disabled until we have a noise model for Rz. + // @Test("Microsoft.Quantum.Experimental.OpenSystemsSimulator") operation TestTeleport() : Unit { use q1 = Qubit(); use q2 = Qubit(); From 90d7330720248a665140d2ff682fa74e5cddf27d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 22 Feb 2021 09:23:12 -0800 Subject: [PATCH 040/158] Started adding Rust-language integration tests. --- src/Simulation/OpenSystems/runtime/Cargo.lock | 11 ++++++++++ src/Simulation/OpenSystems/runtime/Cargo.toml | 3 +++ src/Simulation/OpenSystems/runtime/README.md | 14 ++++++++++++ .../OpenSystems/runtime/src/instrument.rs | 2 +- .../OpenSystems/runtime/src/noise_model.rs | 2 +- .../runtime/tests/serialization_tests.rs | 22 +++++++++++++++++++ 6 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/Simulation/OpenSystems/runtime/README.md create mode 100644 src/Simulation/OpenSystems/runtime/tests/serialization_tests.rs diff --git a/src/Simulation/OpenSystems/runtime/Cargo.lock b/src/Simulation/OpenSystems/runtime/Cargo.lock index c2c1b62026a..a451d203591 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.lock +++ b/src/Simulation/OpenSystems/runtime/Cargo.lock @@ -1,5 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "assert-json-diff" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f1c3703dd33532d7f0ca049168930e9099ecac238e23cf932f3a69c42f06da" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -155,6 +165,7 @@ dependencies = [ name = "opensim" version = "0.1.0" dependencies = [ + "assert-json-diff", "derive_more", "getrandom", "itertools", diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml index a3478b893c5..c81f3ce6f94 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -38,6 +38,9 @@ getrandom = { version = "0.1.15", features = ["wasm-bindgen"] } # We only need web-sys when compiling with the web-sys-log feature. web-sys = { version = "0.3.4", features = ['console'], optional = true } +[dev-dependencies] +assert-json-diff = "2.0.1" + [lib] name = "opensim" path = "src/lib.rs" diff --git a/src/Simulation/OpenSystems/runtime/README.md b/src/Simulation/OpenSystems/runtime/README.md new file mode 100644 index 00000000000..9782d2409e8 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/README.md @@ -0,0 +1,14 @@ +# 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. +- C++-language unit tests in the QIR runtime. + These tests ensure that the binding of the Rust library as a QIR simulator work as expected, and are defined in `qsharp-runtime/src/QirRuntime/test/OpenSystemsSimulator/*.cpp`. +- 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/OpenSystems/runtime/src/instrument.rs b/src/Simulation/OpenSystems/runtime/src/instrument.rs index b82af0b1bfd..9417824bc75 100644 --- a/src/Simulation/OpenSystems/runtime/src/instrument.rs +++ b/src/Simulation/OpenSystems/runtime/src/instrument.rs @@ -12,7 +12,7 @@ use serde::{ Serialize, Deserialize }; use crate::utils::log_message; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct Instrument { pub effects: Vec } diff --git a/src/Simulation/OpenSystems/runtime/src/noise_model.rs b/src/Simulation/OpenSystems/runtime/src/noise_model.rs index eb0aa0de311..b27269fe6b9 100644 --- a/src/Simulation/OpenSystems/runtime/src/noise_model.rs +++ b/src/Simulation/OpenSystems/runtime/src/noise_model.rs @@ -10,7 +10,7 @@ use crate::linalg::HasDagger; use serde::{Serialize, Deserialize}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct NoiseModel { pub initial_state: State, pub i: Channel, diff --git a/src/Simulation/OpenSystems/runtime/tests/serialization_tests.rs b/src/Simulation/OpenSystems/runtime/tests/serialization_tests.rs new file mode 100644 index 00000000000..6ea04a05132 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/tests/serialization_tests.rs @@ -0,0 +1,22 @@ +use serde_json::Value; +use opensim::{ NoiseModel }; +use assert_json_diff::assert_json_eq; + +// Use include_str! to store test case JSON as a string into the compiled +// test executable. +static IDEAL_NOISE_MODEL_JSON: &str = include_str!("data/ideal-noise-model.json"); + +#[test] +fn ideal_noise_model_serializes_correctly() { + let noise_model = NoiseModel::ideal(); + let expected: Value = serde_json::from_str(&*(serde_json::to_string(&noise_model).unwrap())).unwrap(); + + assert_json_eq!(noise_model, expected); +} + +#[test] +fn ideal_noise_model_deserializes_correctly() { + let actual: NoiseModel = serde_json::from_str(IDEAL_NOISE_MODEL_JSON).unwrap(); + + assert_json_eq!(actual, NoiseModel::ideal()); +} From e9e95220133e41420b006fc4a9cdc3a9e9d8a187 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 22 Feb 2021 10:30:31 -0800 Subject: [PATCH 041/158] Added more informative error message when R is not supported. --- .../OpenSystemsSimulator.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index ca8581107da..15c69da2f13 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -115,22 +115,34 @@ public Result Measure__Body(IQArray paulis, IQArray targets) public void R__Body(Pauli pauli, double angle, Qubit target) { - throw new NotImplementedException(); + if (pauli == Pauli.PauliI) + { + // Don't apply global phases on uncontrolled operations. + return; + } + throw new NotImplementedException("Arbitrary rotation with noise is not yet supported."); } public void R__AdjointBody(Pauli pauli, double angle, Qubit target) { - throw new NotImplementedException(); + R__Body(pauli, -angle, target); } public void R__ControlledBody(IQArray controls, Pauli pauli, double angle, Qubit target) { - throw new NotImplementedException(); + if (controls is { Count: 0 }) + { + R__Body(pauli, angle, target); + } + else + { + throw new NotImplementedException("Arbitrary controlled rotation with noise is not yet supported."); + } } public void R__ControlledAdjointBody(IQArray controls, Pauli pauli, double angle, Qubit target) { - throw new NotImplementedException(); + R__ControlledBody(controls, pauli, -angle, target); } public void S__Body(Qubit target) From c3dd0f779fe138d93e5fad198fc608abacf20487 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 22 Feb 2021 11:36:33 -0800 Subject: [PATCH 042/158] Forgot to add test data to prev commit. --- .../OpenSystems/runtime/tests/data/ideal-noise-model.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Simulation/OpenSystems/runtime/tests/data/ideal-noise-model.json diff --git a/src/Simulation/OpenSystems/runtime/tests/data/ideal-noise-model.json b/src/Simulation/OpenSystems/runtime/tests/data/ideal-noise-model.json new file mode 100644 index 00000000000..e73e2c99250 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/tests/data/ideal-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":{"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]]}}},"z_meas":{"effects":[{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[1,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],[1,0]]}}}]}} \ No newline at end of file From e806e1ae200e7218981c93c4f533ea420a24da02 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 16 Mar 2021 13:53:29 -0700 Subject: [PATCH 043/158] Start enabling performance reporting for opensim. --- src/Simulation/OpenSystems/runtime/Cargo.lock | 409 +++++++++++++++++- src/Simulation/OpenSystems/runtime/Cargo.toml | 5 + .../runtime/benches/c_api_benchmark.rs | 47 ++ .../noise-model-export-checkpoint.ipynb | 212 +++++++++ .../benches/data/benchmark-noise-model.json | 1 + .../benches/data/noise-model-export.ipynb | 212 +++++++++ .../OpenSystems/test-open-simulator.ps1 | 27 +- 7 files changed, 911 insertions(+), 2 deletions(-) create mode 100644 src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs create mode 100644 src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb create mode 100644 src/Simulation/OpenSystems/runtime/benches/data/benchmark-noise-model.json create mode 100644 src/Simulation/OpenSystems/runtime/benches/data/noise-model-export.ipynb diff --git a/src/Simulation/OpenSystems/runtime/Cargo.lock b/src/Simulation/OpenSystems/runtime/Cargo.lock index a451d203591..559b1e382ae 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.lock +++ b/src/Simulation/OpenSystems/runtime/Cargo.lock @@ -10,18 +10,62 @@ dependencies = [ "serde_json", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bstr" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cast" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" +dependencies = [ + "rustc_version", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -34,6 +78,120 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools 0.10.0", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" +dependencies = [ + "cast", + "itertools 0.9.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "derive_more" version = "0.99.11" @@ -63,6 +221,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + [[package]] name = "itertools" version = "0.9.0" @@ -72,6 +245,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.6" @@ -117,6 +299,21 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg", +] + [[package]] name = "ndarray" version = "0.13.1" @@ -161,14 +358,31 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opensim" version = "0.1.0" dependencies = [ "assert-json-diff", + "criterion", "derive_more", "getrandom", - "itertools", + "itertools 0.9.0", "lazy_static", "ndarray", "num-complex", @@ -179,6 +393,34 @@ dependencies = [ "web-sys", ] +[[package]] +name = "plotters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" + +[[package]] +name = "plotters-svg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" +dependencies = [ + "plotters-backend", +] + [[package]] name = "ppv-lite86" version = "0.2.9" @@ -250,12 +492,100 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.117" @@ -265,6 +595,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.117" @@ -298,12 +638,48 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -373,3 +749,34 @@ dependencies = [ "js-sys", "wasm-bindgen", ] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml index c81f3ce6f94..9ede8032254 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -40,8 +40,13 @@ web-sys = { version = "0.3.4", features = ['console'], optional = true } [dev-dependencies] assert-json-diff = "2.0.1" +criterion = { version = "0.3", features = ['html_reports', 'csv_output'] } [lib] name = "opensim" path = "src/lib.rs" crate-type = ["rlib", "staticlib", "cdylib"] + +[[bench]] +name = "c_api_benchmark" +harness = false diff --git a/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs b/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs new file mode 100644 index 00000000000..ebcf399e2d5 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs @@ -0,0 +1,47 @@ +// 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 std::ffi::CString; +use criterion::{criterion_group, criterion_main, Criterion}; +use opensim::{ c_api }; + +// 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; + c_api::m(sim_id, 0, &mut result); + })); +} + +fn ideal(c: &mut Criterion) { + let sim_id = c_api::init(3); + 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 sim_id = c_api::init(3); + 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/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb b/src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb new file mode 100644 index 00000000000..804cd28ccd4 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.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/OpenSystems/runtime/benches/data/benchmark-noise-model.json b/src/Simulation/OpenSystems/runtime/benches/data/benchmark-noise-model.json new file mode 100644 index 00000000000..e9d7f107f7c --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/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/OpenSystems/runtime/benches/data/noise-model-export.ipynb b/src/Simulation/OpenSystems/runtime/benches/data/noise-model-export.ipynb new file mode 100644 index 00000000000..804cd28ccd4 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/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/OpenSystems/test-open-simulator.ps1 b/src/Simulation/OpenSystems/test-open-simulator.ps1 index 6fdb212fdc0..678c8fd5340 100644 --- a/src/Simulation/OpenSystems/test-open-simulator.ps1 +++ b/src/Simulation/OpenSystems/test-open-simulator.ps1 @@ -1,3 +1,28 @@ +& (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); + Push-Location (Join-Path $PSScriptRoot runtime) - cargo test + # If running in CI, use cargo2junit to expose unit tests to the + # PublishTestResults task. + if ("$Env:TF_BUILD" -ne "") { + cargo install cargo2junit + cargo test -- -Z unstable-options --format json ` + | cargo2junit ` + <# We use this name to match the *_results.xml pattern that is used + to find test results in steps-wrap-up.yml. #> ` + | Out-File -FilePath opensim_results.xml -Encoding utf8NoBOM + } else { + # Outside of CI, show human-readable output. + cargo test + } + + # Run performance benchmarks as well. + cargo bench + + # This step isn't required, but we use it to upload run summaries. + $reportPath = (Join-Path "target" "criterion"); + $perfDest = (Join-Path $Env:DROPS_DIR "perf" "opensim"); + if (Get-Item -ErrorAction SilentlyContinue $reportPath) { + New-Item -Type Directory -Force -Path $perfDest; + Copy-Item -Recurse -Path $reportPath -Destination $perfDest; + } Pop-Location From ebd552aca3ec28fd4a75083b74c57043f1648e2b Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 16 Mar 2021 14:13:11 -0700 Subject: [PATCH 044/158] Adapt opensim / managed interface to #538. --- .../OpenSystemsSimulator.cs | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index 15c69da2f13..4b3b350f02f 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -56,9 +56,9 @@ protected override void Release(Qubit qubit, bool wasUsedOnlyForBorrowing) // design and make it customizable. // FIXME: In particular, this implementation uses a lot of // extraneous measurements. - if (this.Parent.Measure__Body(new QArray(Pauli.PauliZ), new QArray(qubit)) == Result.One) + if ((this.Parent as IIntrinsicMeasure).Body(new QArray(Pauli.PauliZ), new QArray(qubit)) == Result.One) { - this.Parent.X__Body(qubit); + (this.Parent as IIntrinsicX).Body(qubit); } } base.Release(qubit, wasUsedOnlyForBorrowing); @@ -70,37 +70,37 @@ public OpenSystemsSimulator(uint capacity = 3) : base(new QubitManager((long)cap this.Id = NativeInterface.Init(capacity); } - public void Exp__Body(IQArray paulis, double angle, IQArray targets) + void IIntrinsicExp.Body(IQArray paulis, double angle, IQArray targets) { throw new NotImplementedException(); } - public void Exp__AdjointBody(IQArray paulis, double angle, IQArray targets) + void IIntrinsicExp.AdjointBody(IQArray paulis, double angle, IQArray targets) { throw new NotImplementedException(); } - public void Exp__ControlledBody(IQArray controls, IQArray paulis, double angle, IQArray targets) + void IIntrinsicExp.ControlledBody(IQArray controls, IQArray paulis, double angle, IQArray targets) { throw new NotImplementedException(); } - public void Exp__ControlledAdjointBody(IQArray controls, IQArray paulis, double angle, IQArray targets) + void IIntrinsicExp.ControlledAdjointBody(IQArray controls, IQArray paulis, double angle, IQArray targets) { throw new NotImplementedException(); } - public void H__Body(Qubit target) + void IIntrinsicH.Body(Qubit target) { NativeInterface.H(this.Id, target); } - public void H__ControlledBody(IQArray controls, Qubit target) + void IIntrinsicH.ControlledBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } - public Result Measure__Body(IQArray paulis, IQArray targets) + Result IIntrinsicMeasure.Body(IQArray paulis, IQArray targets) { if (targets is { Count: 1 } && paulis is { Count: 1 } && paulis.Single() == Pauli.PauliZ) { @@ -113,7 +113,7 @@ public Result Measure__Body(IQArray paulis, IQArray targets) } } - public void R__Body(Pauli pauli, double angle, Qubit target) + void IIntrinsicR.Body(Pauli pauli, double angle, Qubit target) { if (pauli == Pauli.PauliI) { @@ -123,16 +123,16 @@ public void R__Body(Pauli pauli, double angle, Qubit target) throw new NotImplementedException("Arbitrary rotation with noise is not yet supported."); } - public void R__AdjointBody(Pauli pauli, double angle, Qubit target) + void IIntrinsicR.AdjointBody(Pauli pauli, double angle, Qubit target) { - R__Body(pauli, -angle, target); + (this as IIntrinsicR).Body(pauli, -angle, target); } - public void R__ControlledBody(IQArray controls, Pauli pauli, double angle, Qubit target) + void IIntrinsicR.ControlledBody(IQArray controls, Pauli pauli, double angle, Qubit target) { if (controls is { Count: 0 }) { - R__Body(pauli, angle, target); + (this as IIntrinsicR).Body(pauli, angle, target); } else { @@ -140,62 +140,62 @@ public void R__ControlledBody(IQArray controls, Pauli pauli, double angle } } - public void R__ControlledAdjointBody(IQArray controls, Pauli pauli, double angle, Qubit target) + void IIntrinsicR.ControlledAdjointBody(IQArray controls, Pauli pauli, double angle, Qubit target) { - R__ControlledBody(controls, pauli, -angle, target); + (this as IIntrinsicR).ControlledBody(controls, pauli, -angle, target); } - public void S__Body(Qubit target) + void IIntrinsicS.Body(Qubit target) { NativeInterface.S(this.Id, target); } - public void S__AdjointBody(Qubit target) + void IIntrinsicS.AdjointBody(Qubit target) { NativeInterface.SAdj(this.Id, target); } - public void S__ControlledBody(IQArray controls, Qubit target) + void IIntrinsicS.ControlledBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } - public void S__ControlledAdjointBody(IQArray controls, Qubit target) + void IIntrinsicS.ControlledAdjointBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } - public void T__Body(Qubit target) + void IIntrinsicT.Body(Qubit target) { NativeInterface.T(this.Id, target); } - public void T__AdjointBody(Qubit target) + void IIntrinsicT.AdjointBody(Qubit target) { NativeInterface.TAdj(this.Id, target); } - public void T__ControlledBody(IQArray controls, Qubit target) + void IIntrinsicT.ControlledBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } - public void T__ControlledAdjointBody(IQArray controls, Qubit target) + void IIntrinsicT.ControlledAdjointBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } - public void X__Body(Qubit target) + void IIntrinsicX.Body(Qubit target) { NativeInterface.X(this.Id, target); } - public void X__ControlledBody(IQArray controls, Qubit target) + void IIntrinsicX.ControlledBody(IQArray controls, Qubit target) { // TODO: pass off to decompositions for more than one control. if (controls is { Count: 0 }) { - X__Body(target); + (this as IIntrinsicX).Body(target); } else if (controls is { Count: 1 }) { @@ -207,27 +207,27 @@ public void X__ControlledBody(IQArray controls, Qubit target) } } - public void Y__Body(Qubit target) + void IIntrinsicY.Body(Qubit target) { NativeInterface.Y(this.Id, target); } - public void Y__ControlledBody(IQArray controls, Qubit target) + void IIntrinsicY.ControlledBody(IQArray controls, Qubit target) { throw new NotImplementedException(); } - public void Z__Body(Qubit target) + void IIntrinsicZ.Body(Qubit target) { NativeInterface.Z(this.Id, target); } - public void Z__ControlledBody(IQArray controls, Qubit target) + void IIntrinsicZ.ControlledBody(IQArray controls, Qubit target) { // TODO: pass off to decompositions for more than one control. if (controls is { Count: 0 }) { - Z__Body(target); + (this as IIntrinsicY).Body(target); } else if (controls is { Count: 1 }) { From f32af17c70157fd2864fa484bf6ae82f61cf3d69 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 16 Mar 2021 14:15:56 -0700 Subject: [PATCH 045/158] Adapt QIR / opensim interface to #553. --- .../lib/Simulators/OpenSystemsSimulator.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp index 088fa723dae..e8698ce3485 100644 --- a/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp +++ b/src/QirRuntime/lib/Simulators/OpenSystemsSimulator.cpp @@ -42,7 +42,7 @@ namespace Quantum { // FIXME: support methods from public IDiagnostics; they currently // just throw. - class OpenSystemSimulator : public ISimulator, public IQuantumGateSet, public IDiagnostics + class OpenSystemSimulator : public IRuntimeDriver, public IQuantumGateSet, public IDiagnostics { typedef void (*TSingleQubitGate)(size_t /*simulator id*/, size_t /*qubit id*/); typedef void (*TSingleQubitControlledGate)( @@ -101,15 +101,6 @@ namespace Quantum destroy(this->simulatorId); } - IQuantumGateSet* AsQuantumGateSet() override - { - return this; - } - IDiagnostics* AsDiagnostics() override - { - return this; - } - void GetState(TGetStateCallback callback) override { throw std::logic_error("operation_not_supported"); @@ -290,7 +281,7 @@ namespace Quantum } }; - std::unique_ptr CreateOpenSystemsSimulator() + std::unique_ptr CreateOpenSystemsSimulator() { return std::make_unique(); } From b7b8c96a1d3daf4787bece24e42246b3fa927d8d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Mar 2021 09:45:58 -0700 Subject: [PATCH 046/158] Apply rust-fmt, and reduce extraneous copying in conjugate_by. --- .../OpenSystems/runtime/src/c_api.rs | 152 +++++++++--------- .../OpenSystems/runtime/src/channels.rs | 123 +++++++------- .../OpenSystems/runtime/src/linalg.rs | 19 ++- 3 files changed, 157 insertions(+), 137 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs index 73041f0fac1..7294d7b16a9 100644 --- a/src/Simulation/OpenSystems/runtime/src/c_api.rs +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -1,18 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::{Channel, NoiseModel, State}; +use lazy_static::lazy_static; +use std::collections::HashMap; use std::ffi::CStr; use std::ffi::CString; use std::os::raw::c_char; use std::ptr; -use crate::{ Channel, NoiseModel, State }; -use lazy_static::lazy_static; use std::sync::Mutex; -use std::collections::HashMap; struct CApiState { register_state: State, - noise_model: NoiseModel + noise_model: NoiseModel, } lazy_static! { @@ -34,7 +34,11 @@ fn as_capi_err(result: Result<(), String>) -> i64 { } } -fn apply &Channel>(sim_id: usize, idxs: &[usize], channel_fn: F) -> Result<(), String> { +fn apply &Channel>( + sim_id: usize, + idxs: &[usize], + channel_fn: F, +) -> Result<(), String> { let state = &mut *STATE.lock().unwrap(); if let Some(sim_state) = state.get_mut(&sim_id) { let channel = channel_fn(&sim_state.noise_model); @@ -42,8 +46,8 @@ fn apply &Channel>(sim_id: usize, idxs: &[usize], channel_ Ok(new_state) => { sim_state.register_state = new_state; Ok(()) - }, - Err(err) => Err(err) + } + Err(err) => Err(err), } } else { return Err(format!("No simulator with id {}.", sim_id).to_string()); @@ -53,7 +57,7 @@ fn apply &Channel>(sim_id: usize, idxs: &[usize], channel_ // C API FUNCTIONS // #[no_mangle] -pub extern fn get_name() -> *const c_char { +pub extern "C" fn get_name() -> *const c_char { // There's a whole dance we have to do in order to allow the memory // allocated for a string to be deallocated on the .NET side. @@ -63,7 +67,7 @@ pub extern fn get_name() -> *const c_char { } #[no_mangle] -pub extern fn lasterr() -> *const c_char { +pub extern "C" fn lasterr() -> *const c_char { match &*LAST_ERROR.lock().unwrap() { None => ptr::null(), Some(msg) => { @@ -75,23 +79,21 @@ pub extern fn lasterr() -> *const c_char { } #[no_mangle] -pub extern fn init(initial_capacity: usize) -> usize { +pub extern "C" fn init(initial_capacity: usize) -> usize { let state = &mut *STATE.lock().unwrap(); - let id = 1 + state.keys().fold( - std::usize::MIN, |a, b| a.max(*b) - ); + let id = 1 + state.keys().fold(std::usize::MIN, |a, b| a.max(*b)); state.insert( id, CApiState { register_state: State::new_mixed(initial_capacity), - noise_model: NoiseModel::ideal() - } + noise_model: NoiseModel::ideal(), + }, ); id } #[no_mangle] -pub extern fn destroy(sim_id: usize) -> i64 { +pub extern "C" fn destroy(sim_id: usize) -> i64 { as_capi_err({ let state = &mut *STATE.lock().unwrap(); if state.contains_key(&sim_id) { @@ -104,26 +106,27 @@ pub extern fn destroy(sim_id: usize) -> i64 { } #[no_mangle] -pub extern fn dump_to_console(sim_id: usize) -> () { +pub extern "C" fn dump_to_console(sim_id: usize) -> () { // FIXME: implement this } #[no_mangle] -pub extern fn x(sim_id: usize, idx: usize) -> i64 { +pub extern "C" fn x(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.x)) } #[no_mangle] -pub extern fn y(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.y))} +pub extern "C" fn y(sim_id: usize, idx: usize) -> i64 { + as_capi_err(apply(sim_id, &[idx], |model| &model.y)) +} #[no_mangle] -pub extern fn z(sim_id: usize, idx: usize) -> i64 { +pub extern "C" fn z(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.z)) } #[no_mangle] -pub extern fn h(sim_id: usize, idx: usize) -> i64 { +pub extern "C" fn h(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.h)) } @@ -149,11 +152,13 @@ pub fn t_adj(sim_id: usize, idx: usize) -> i64 { #[no_mangle] pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx_control, idx_target], |model| &model.cnot)) + as_capi_err(apply(sim_id, &[idx_control, idx_target], |model| { + &model.cnot + })) } #[no_mangle] -pub extern fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { +pub extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { as_capi_err({ let state = &mut *STATE.lock().unwrap(); if let Some(sim_state) = state.get_mut(&sim_id) { @@ -171,12 +176,16 @@ pub extern fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { } #[no_mangle] -pub extern fn get_noise_model(sim_id: usize) -> *const c_char { +pub extern "C" fn get_noise_model(sim_id: usize) -> *const c_char { let state = &*STATE.lock().unwrap(); if let Some(sim_state) = state.get(&sim_id) { let serialized = CString::new( - serde_json::to_string(&sim_state.noise_model).unwrap().as_str() - ).unwrap().into_raw(); + serde_json::to_string(&sim_state.noise_model) + .unwrap() + .as_str(), + ) + .unwrap() + .into_raw(); // Need to forget that we hold this reference so that it doesn't // get released after returning to C. std::mem::forget(serialized); @@ -189,71 +198,70 @@ pub extern fn get_noise_model(sim_id: usize) -> *const c_char { /// Returns a JSON serialization of the ideal noise model (i.e.: a noise model /// that agrees with closed-system simulation). #[no_mangle] -pub extern fn ideal_noise_model() -> *const c_char { +pub extern "C" fn ideal_noise_model() -> *const c_char { let serialized = CString::new( - serde_json::to_string(&NoiseModel::ideal()).unwrap().as_str() - ).unwrap().into_raw(); + serde_json::to_string(&NoiseModel::ideal()) + .unwrap() + .as_str(), + ) + .unwrap() + .into_raw(); std::mem::forget(serialized); serialized } - #[no_mangle] -pub extern fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { +pub extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { if new_model.is_null() { return as_capi_err(Err("set_noise_model called with null pointer".to_string())); } - let c_str = unsafe { - CStr::from_ptr(new_model) - }; + let c_str = unsafe { CStr::from_ptr(new_model) }; - as_capi_err( - match c_str.to_str() { - Ok(serialized_noise_model) => { - match serde_json::from_str(serialized_noise_model) { - Ok(noise_model) => { - let state = &mut *STATE.lock().unwrap(); - if let Some(sim_state) = state.get_mut(&sim_id) { - sim_state.noise_model = noise_model; - Ok(()) - } else { - Err(format!("No simulator with id {} exists.", sim_id).to_string()) - } - }, - Err(serialization_error) => Err(format!( - "{} error deserializing noise model at line {}, column {}.", - match serialization_error.classify() { - serde_json::error::Category::Data => "Data / schema", - serde_json::error::Category::Eof => "End-of-file", - serde_json::error::Category::Io => "I/O", - serde_json::error::Category::Syntax => "Syntax", - }, - serialization_error.line(), - serialization_error.column() - )) + as_capi_err(match c_str.to_str() { + Ok(serialized_noise_model) => match serde_json::from_str(serialized_noise_model) { + Ok(noise_model) => { + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + sim_state.noise_model = noise_model; + Ok(()) + } else { + Err(format!("No simulator with id {} exists.", sim_id).to_string()) } - }, - Err(msg) => Err( - format!( - "UTF-8 error decoding serialized noise model; was valid until byte {}.", - msg.valid_up_to() - ) - ) - } - ) + } + Err(serialization_error) => Err(format!( + "{} error deserializing noise model at line {}, column {}.", + match serialization_error.classify() { + serde_json::error::Category::Data => "Data / schema", + serde_json::error::Category::Eof => "End-of-file", + serde_json::error::Category::Io => "I/O", + serde_json::error::Category::Syntax => "Syntax", + }, + serialization_error.line(), + serialization_error.column() + )), + }, + Err(msg) => Err(format!( + "UTF-8 error decoding serialized noise model; was valid until byte {}.", + msg.valid_up_to() + )), + }) } #[no_mangle] -pub extern fn get_current_state(sim_id: usize) -> *const c_char { +pub extern "C" fn get_current_state(sim_id: usize) -> *const c_char { let state = &mut *STATE.lock().unwrap(); if let Some(sim_state) = state.get_mut(&sim_id) { let serialized = CString::new( - serde_json::to_string(&sim_state.register_state).unwrap().as_str() - ).unwrap().into_raw(); + serde_json::to_string(&sim_state.register_state) + .unwrap() + .as_str(), + ) + .unwrap() + .into_raw(); std::mem::forget(serialized); serialized } else { ptr::null() } -} \ No newline at end of file +} diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs index 1ed8fc8b04d..2c1849d5982 100644 --- a/src/Simulation/OpenSystems/runtime/src/channels.rs +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -1,31 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -use crate::{ log_as_err, log_message }; -use crate::NoiseModel; -use crate::linalg::{ extend_one_to_n, extend_two_to_n }; -use itertools::zip; -use std::ops::Add; -use std::convert::TryInto; -use ndarray::Axis; +use crate::channels::ChannelData::{KrausDecomposition, Unitary}; +use crate::linalg::ConjBy; +use crate::linalg::{extend_one_to_n, extend_two_to_n}; +use crate::states::StateData::{Mixed, Pure}; use crate::zeros_like; -use std::ops::Mul; -use crate::channels::ChannelData::{ Unitary, KrausDecomposition }; -use crate::states::StateData::{ Pure, Mixed }; +use crate::NoiseModel; +use crate::QubitSized; use crate::State; use crate::C64; -use ndarray::{ Array, Array2, Array3 }; -use crate::QubitSized; -use crate::linalg::ConjBy; +use crate::{log_as_err, log_message}; +use itertools::zip; use itertools::Itertools; -use num_traits::{ Zero, One }; -use serde::{ Serialize, Deserialize }; +use ndarray::Axis; +use ndarray::{Array, Array2, Array3}; +use num_traits::{One, Zero}; +use serde::{Deserialize, Serialize}; +use std::convert::TryInto; +use std::ops::Add; +use std::ops::Mul; pub type Channel = QubitSized; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ChannelData { Unitary(Array2), - KrausDecomposition(Array3) - // TODO: Superoperator and Choi reps. + KrausDecomposition(Array3), // TODO: Superoperator and Choi reps. } impl Channel { @@ -37,17 +38,18 @@ impl Channel { data: match &state.data { Pure(psi) => todo!(), Mixed(rho) => match &self.data { - Unitary(u) => Mixed(rho.conjugate_by(u)), + Unitary(u) => Mixed(rho.conjugate_by(&u.into())), KrausDecomposition(ks) => Mixed({ - let mut sum: Array2 = Array::zeros((rho.shape()[0], rho.shape()[1])); + let mut sum: Array2 = + Array::zeros((rho.shape()[0], rho.shape()[1])); for k in ks.axis_iter(Axis(0)) { // FIXME: performance issue, this to_owned shouldn't be needed. - sum = sum + rho.conjugate_by(&k.to_owned()); + sum = sum + rho.conjugate_by(&k); } sum - }) - } - } + }), + }, + }, }) } else { Err(format!( @@ -80,7 +82,7 @@ impl Channel { return log_as_err(format!( "Qubit indices were specified as {:?}, but this channel only acts on {} qubits.", idx_qubits, self.n_qubits - )) + )); } // At this point we know that idx_qubits has self.n_qubits many unique @@ -89,11 +91,12 @@ impl Channel { // the ordinary apply method. let expanded = match self.n_qubits { 1 => self.extend_one_to_n(idx_qubits[0], state.n_qubits), - 2 => { - self.extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits) - }, + 2 => self.extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits), _ => { - log_message(&format!("Expanding {}-qubit channels is not yet implemented.", self.n_qubits)); + log_message(&format!( + "Expanding {}-qubit channels is not yet implemented.", + self.n_qubits + )); todo!(""); } }; @@ -112,30 +115,28 @@ impl Channel { let mut extended: Array3 = Array::zeros((n_kraus, new_dim, new_dim)); for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { let mut target = extended.index_axis_mut(Axis(0), idx_kraus); - let big_kraus = extend_one_to_n( - &kraus.to_owned(), - idx_qubit, n_qubits - ); + let big_kraus = extend_one_to_n(&kraus.to_owned(), idx_qubit, n_qubits); target.assign(&big_kraus); - }; + } KrausDecomposition(extended) } - } + }, } } - pub fn extend_two_to_n(self: &Self, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize) -> Channel { + pub fn extend_two_to_n( + self: &Self, + idx_qubit1: usize, + idx_qubit2: usize, + n_qubits: usize, + ) -> Channel { assert_eq!(self.n_qubits, 2); Channel { n_qubits: n_qubits, data: match &self.data { - Unitary(u) => { - Unitary(extend_two_to_n(u, idx_qubit1, idx_qubit2, n_qubits)) - }, - KrausDecomposition(ks) => { - todo!("extend_two_to_n for Kraus decompositions") - } - } + Unitary(u) => Unitary(extend_two_to_n(u, idx_qubit1, idx_qubit2, n_qubits)), + KrausDecomposition(ks) => todo!("extend_two_to_n for Kraus decompositions"), + }, } } } @@ -156,8 +157,8 @@ impl Mul<&Channel> for C64 { ks.index_axis_mut(Axis(0), 0).assign(&(self.sqrt() * u)); ks }), - KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks) - } + KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks), + }, } } } @@ -197,16 +198,15 @@ impl Mul<&Channel> for &Channel { // post-multiply each kraus operator by u. let mut post = zeros_like(ks); for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { - post.index_axis_mut(Axis(0), idx_kraus).assign( - &u.dot(&kraus) - ); + post.index_axis_mut(Axis(0), idx_kraus) + .assign(&u.dot(&kraus)); } KrausDecomposition(post) - }, + } // TODO: product of two kraus decompositions would be... not // fun. - _ => todo!() - } + _ => todo!(), + }, } } } @@ -223,23 +223,23 @@ impl Add<&Channel> for &Channel { let mut sum = Array::zeros([ ks1.shape()[0] + ks2.shape()[0], ks1.shape()[1], - ks1.shape()[2] + ks1.shape()[2], ]); for (idx_kraus, kraus) in ks1.axis_iter(Axis(0)).enumerate() { sum.index_axis_mut(Axis(0), idx_kraus).assign(&kraus); } for (idx_kraus, kraus) in ks2.axis_iter(Axis(0)).enumerate() { - sum.index_axis_mut(Axis(0), ks1.shape()[0] + idx_kraus).assign(&kraus); + sum.index_axis_mut(Axis(0), ks1.shape()[0] + idx_kraus) + .assign(&kraus); } KrausDecomposition(sum) - }, - _ => todo!() - } + } + _ => todo!(), + }, } } } - impl Mul for &Channel { type Output = Channel; @@ -298,14 +298,13 @@ pub fn amplitude_damping_channel(gamma: f64) -> Channel { n_qubits: 1, data: KrausDecomposition(array![ [ - [ C64::one(), C64::zero() ], - [ C64::zero(), C64::one() * (1.0 - gamma).sqrt()] + [C64::one(), C64::zero()], + [C64::zero(), C64::one() * (1.0 - gamma).sqrt()] ], - [ - [ C64::zero(), C64::one() * gamma.sqrt() ], - [ C64::zero(), C64::zero() ] + [C64::zero(), C64::one() * gamma.sqrt()], + [C64::zero(), C64::zero()] ] - ]) + ]), } } diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 15bd5aee777..81f873cb18f 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use ndarray::ArrayView2; use core::ops::Range; use num_traits::Zero; use ndarray::{ Array, Array1, Array2 }; @@ -11,26 +12,38 @@ use std::cmp; use crate::{ C64, nq_eye }; pub trait HasDagger { + type Output; + /// Returns the hermitian conjugate (colloquially, the dagger) of a /// borrowed reference as a new copy. /// /// For most types implementing this trait, the hermitian conjugate /// is represented by the conjugate transpose. - fn dag(self: &Self) -> Self; + fn dag(self: &Self) -> Self::Output; } impl HasDagger for Array2 { + type Output = Self; + fn dag(self: &Self) -> Self { self.t().map(|element| element.conj()) } } +impl HasDagger for ArrayView2<'_, C64> { + type Output = Array2; + + fn dag(self: &Self) -> Self::Output { + self.t().map(|element| element.conj()) + } +} + pub trait ConjBy { - fn conjugate_by(self: &Self, op: &Array2) -> Self; + fn conjugate_by(self: &Self, op: &ArrayView2) -> Self; } impl ConjBy for Array2 { - fn conjugate_by(self: &Self, op: &Array2) -> Self { + fn conjugate_by(self: &Self, op: &ArrayView2) -> Self { op.dot(self).dot(&op.dag()) } } From 29442098f101f404f0c03aa4f1db2dcd85da0f63 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Mar 2021 14:03:32 -0700 Subject: [PATCH 047/158] Started weakening from owned arrays to views. --- .../OpenSystems/runtime/src/channels.rs | 34 +++++++++++++++---- src/Simulation/OpenSystems/runtime/src/lib.rs | 4 +-- .../OpenSystems/runtime/src/linalg.rs | 33 +++++++++++++----- .../OpenSystems/runtime/src/states.rs | 21 +++++++++++- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs index 2c1849d5982..ecbcb295cf1 100644 --- a/src/Simulation/OpenSystems/runtime/src/channels.rs +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -11,10 +11,8 @@ use crate::QubitSized; use crate::State; use crate::C64; use crate::{log_as_err, log_message}; -use itertools::zip; use itertools::Itertools; -use ndarray::Axis; -use ndarray::{Array, Array2, Array3}; +use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; use num_traits::{One, Zero}; use serde::{Deserialize, Serialize}; use std::convert::TryInto; @@ -36,14 +34,25 @@ impl Channel { Ok(State { n_qubits: self.n_qubits, data: match &state.data { - Pure(psi) => todo!(), + Pure(psi) => match &self.data { + Unitary(u) => Pure(u.dot(psi)), + KrausDecomposition(ks) => { + // We can't apply a channel with more than one Kraus operator (Choi rank > 1) to a + // pure state directly, so if the Choi rank is bigger than 1, promote to + // Mixed and recurse. + if ks.shape()[0] == 1 { + Pure({ let k: ArrayView2 = ks.slice(s![0, .., ..]); k.dot(psi) }) + } else { + self.apply(&state.to_mixed())?.data + } + } + }, Mixed(rho) => match &self.data { Unitary(u) => Mixed(rho.conjugate_by(&u.into())), KrausDecomposition(ks) => Mixed({ let mut sum: Array2 = Array::zeros((rho.shape()[0], rho.shape()[1])); for k in ks.axis_iter(Axis(0)) { - // FIXME: performance issue, this to_owned shouldn't be needed. sum = sum + rho.conjugate_by(&k); } sum @@ -97,7 +106,7 @@ impl Channel { "Expanding {}-qubit channels is not yet implemented.", self.n_qubits )); - todo!(""); + unimplemented!(""); } }; expanded.apply(state) @@ -135,7 +144,18 @@ impl Channel { n_qubits: n_qubits, data: match &self.data { Unitary(u) => Unitary(extend_two_to_n(u, idx_qubit1, idx_qubit2, n_qubits)), - KrausDecomposition(ks) => todo!("extend_two_to_n for Kraus decompositions"), + KrausDecomposition(ks) => { + // TODO: consolidate with extend_one_to_n, above. + let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); + let n_kraus = ks.shape()[0]; + let mut extended: Array3 = Array::zeros((n_kraus, new_dim, new_dim)); + for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { + let mut target = extended.index_axis_mut(Axis(0), idx_kraus); + let big_kraus = extend_two_to_n(&kraus.to_owned(), idx_qubit1, idx_qubit2, n_qubits); + target.assign(&big_kraus); + } + KrausDecomposition(extended) + } }, } } diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index 4cd8b2f7270..6492d644717 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#[macro_use(array)] - +#[macro_use(array, s)] extern crate ndarray; + extern crate derive_more; extern crate serde; diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 81f873cb18f..6290d5b37f1 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use ndarray::ArrayView2; -use core::ops::Range; +use ndarray::{ArrayView1, ArrayView2}; use num_traits::Zero; use ndarray::{ Array, Array1, Array2 }; use std::ops::Mul; use std::convert::TryInto; -use std::cmp; use crate::{ C64, nq_eye }; @@ -49,11 +47,13 @@ impl ConjBy for Array2 { } pub trait Tensor { - fn tensor(self: &Self, other: &Self) -> Self; + type Output; + fn tensor(self: &Self, other: &Self) -> Self::Output; } -impl > Tensor for Array1 { - fn tensor(&self, other: &Self) -> Self { +impl > Tensor for ArrayView1<'_, T> { + type Output = Array1; + fn tensor(&self, other: &Self) -> Self::Output { let unflat = Array::from_shape_fn( (self.shape()[0], other.shape()[0]), |(i, j)| { @@ -64,8 +64,17 @@ impl > Tensor for Array1 { } } -impl > Tensor for Array2 { - fn tensor(&self, other: &Self) -> Self { +impl > Tensor for Array1 { + type Output = Array1; + + fn tensor(self: &Self, other: &Self) -> Self::Output { + self.view().tensor(&other.view()) + } +} + +impl > Tensor for ArrayView2<'_, T> { + type Output = Array2; + fn tensor(&self, other: &Self) -> Self::Output { let unflat = Array::from_shape_fn( (self.shape()[0], other.shape()[0], self.shape()[1], other.shape()[1]), |(i, j, k, l)| { @@ -76,6 +85,14 @@ impl > Tensor for Array2 { } } +impl > Tensor for Array2 { + type Output = Array2; + + fn tensor(self: &Self, other: &Self) -> Self::Output { + self.view().tensor(&other.view()) + } +} + pub trait Trace { type Output; diff --git a/src/Simulation/OpenSystems/runtime/src/states.rs b/src/Simulation/OpenSystems/runtime/src/states.rs index b5b4297063c..9a3a2160926 100644 --- a/src/Simulation/OpenSystems/runtime/src/states.rs +++ b/src/Simulation/OpenSystems/runtime/src/states.rs @@ -9,7 +9,7 @@ use crate::states::StateData::Pure; use crate::QubitSized; use crate::linalg::Trace; use crate::C64; -use ndarray::{ Array1, Array2 }; +use ndarray::{Array1, Array2, Axis}; use std::convert::TryInto; use crate::linalg::{ Tensor }; use serde::{ Serialize, Deserialize }; @@ -68,6 +68,25 @@ impl State { data: Mixed(common_matrices::elementary_matrix((0, 0), (new_dim, new_dim))) } } + + /// Returns a copy of this state, represented as a mixed state. + pub fn to_mixed(self: &Self) -> State { + State { + n_qubits: self.n_qubits, + data: match &self.data { + Mixed(rho) => Mixed(rho.clone()), + Pure(psi) => Mixed({ + // Take the outer product of psi with its complex conjugate + // by using insert_axis. + // Note that since we can't prove that this is a dim2 array, + // we can't use the HasDagger trait here yet; that's a possible + // improvement for the HasDagger trait itself. + let psi = psi.view().insert_axis(Axis(1)); + psi.t().map(|e| e.conj()) * psi + }) + } + } + } } impl Trace for &State { From fa18ebba6060dd9e14292bab28f9c75f5a1a1d9f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Mar 2021 15:07:43 -0700 Subject: [PATCH 048/158] Weakened tensor to views by owning views and borrowing owns. --- .../OpenSystems/runtime/src/channels.rs | 4 +- .../OpenSystems/runtime/src/linalg.rs | 40 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs index ecbcb295cf1..af92edce0a7 100644 --- a/src/Simulation/OpenSystems/runtime/src/channels.rs +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -117,14 +117,14 @@ impl Channel { Channel { n_qubits: n_qubits, data: match &self.data { - Unitary(u) => Unitary(extend_one_to_n(u, idx_qubit, n_qubits)), + Unitary(u) => Unitary(extend_one_to_n(u.view(), idx_qubit, n_qubits)), KrausDecomposition(ks) => { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); let n_kraus = ks.shape()[0]; let mut extended: Array3 = Array::zeros((n_kraus, new_dim, new_dim)); for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { let mut target = extended.index_axis_mut(Axis(0), idx_kraus); - let big_kraus = extend_one_to_n(&kraus.to_owned(), idx_qubit, n_qubits); + let big_kraus = extend_one_to_n(kraus.view(), idx_qubit, n_qubits); target.assign(&big_kraus); } KrausDecomposition(extended) diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 6290d5b37f1..60e968273ae 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -46,14 +46,15 @@ impl ConjBy for Array2 { } } -pub trait Tensor { +pub trait Tensor { type Output; - fn tensor(self: &Self, other: &Self) -> Self::Output; + fn tensor(self: Self, other: Other) -> Self::Output; } -impl > Tensor for ArrayView1<'_, T> { +impl , T: Copy + Mul> Tensor for ArrayView1<'_, T> { type Output = Array1; - fn tensor(&self, other: &Self) -> Self::Output { + fn tensor(self, other: Other) -> Self::Output { + let other: Self = other.into(); let unflat = Array::from_shape_fn( (self.shape()[0], other.shape()[0]), |(i, j)| { @@ -64,17 +65,19 @@ impl > Tensor for ArrayView1<'_, T> { } } -impl > Tensor for Array1 { +impl , T: Copy + Mul> Tensor for &Array1 { type Output = Array1; - fn tensor(self: &Self, other: &Self) -> Self::Output { - self.view().tensor(&other.view()) + fn tensor(self: Self, other: Other) -> Self::Output { + let other: Self = other.into(); + self.view().tensor(other).to_owned() } } -impl > Tensor for ArrayView2<'_, T> { +impl , T: Copy + Mul> Tensor for ArrayView2<'_, T> { type Output = Array2; - fn tensor(&self, other: &Self) -> Self::Output { + fn tensor(self, other: Other) -> Self::Output { + let other: Self = other.into(); let unflat = Array::from_shape_fn( (self.shape()[0], other.shape()[0], self.shape()[1], other.shape()[1]), |(i, j, k, l)| { @@ -85,11 +88,12 @@ impl > Tensor for ArrayView2<'_, T> { } } -impl > Tensor for Array2 { +impl , T: Copy + Mul> Tensor for &Array2 { type Output = Array2; - fn tensor(self: &Self, other: &Self) -> Self::Output { - self.view().tensor(&other.view()) + fn tensor(self: Self, other: Other) -> Self::Output { + let other: Self = other.into(); + self.view().tensor(other).to_owned() } } @@ -118,19 +122,23 @@ impl Trace for &Array2 { // FIXME: weaken data to be a view so that to_owned isn't needed. // FIXME: modify to Result<..., String> so that errors can propagate to the C API. -pub fn extend_one_to_n(data: &Array2, idx_qubit: usize, n_qubits: usize) -> Array2 { +pub fn extend_one_to_n<'a>(data: ArrayView2<'a, C64>, idx_qubit: usize, n_qubits: usize) -> Array2 { let n_left = idx_qubit; let n_right = n_qubits - idx_qubit - 1; match (n_left, n_right) { (0, _) => { let right_eye = nq_eye(n_right); - data.tensor(&right_eye) + data.view().tensor(&right_eye) }, (_, 0) => { let left_eye = Array2::eye(2usize.pow(n_left.try_into().unwrap())); - left_eye.tensor(data) + left_eye.view().tensor(&data) }, - (_, _) => nq_eye(n_left).tensor(&data.tensor(&nq_eye(n_right))) + (_, _) => { + let eye = nq_eye(n_right); + let right = data.view().tensor(&eye); + nq_eye(n_left).view().tensor(&right) + } } } From 5cf54ea0df90aec7f1ed757477bda542b4d79284 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Mar 2021 16:20:05 -0700 Subject: [PATCH 049/158] Started adding microbenchmarks. --- .../runtime/benches/c_api_benchmark.rs | 6 +- .../runtime/benches/microbenchmark.rs | 62 +++++++++++++++++++ src/Simulation/OpenSystems/runtime/src/lib.rs | 5 +- .../OpenSystems/runtime/src/linalg.rs | 4 +- 4 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs diff --git a/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs b/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs index ebcf399e2d5..45c58401aa8 100644 --- a/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs +++ b/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs @@ -1,6 +1,6 @@ -// 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. +//! 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 std::ffi::CString; use criterion::{criterion_group, criterion_main, Criterion}; diff --git a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs new file mode 100644 index 00000000000..5181da92db4 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs @@ -0,0 +1,62 @@ +//! 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, BenchmarkId, Criterion}; +use opensim::{common_matrices, linalg::extend_one_to_n, linalg::Tensor, nq_eye}; + +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); + }) + }, + ); + } + 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/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index 6492d644717..b294d014fb0 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -8,10 +8,10 @@ extern crate derive_more; extern crate serde; pub mod c_api; +pub mod linalg; +pub mod common_matrices; mod utils; -mod common_matrices; mod states; -mod linalg; mod channels; mod noise_model; mod instrument; @@ -26,6 +26,7 @@ pub use crate::channels::Channel; pub use crate::channels::ChannelData::KrausDecomposition; pub use crate::channels::ChannelData::Unitary; + use serde::{ Serialize, Deserialize }; #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 60e968273ae..639af7db0d4 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -119,9 +119,9 @@ impl Trace for &Array2 { } } - -// FIXME: weaken data to be a view so that to_owned isn't needed. // FIXME: modify to Result<..., String> so that errors can propagate to the C API. +// FIXME[perf]: This function is significantly slower than would be expected +// from microbenchmarks on tensor and nq_eye directly. pub fn extend_one_to_n<'a>(data: ArrayView2<'a, C64>, idx_qubit: usize, n_qubits: usize) -> Array2 { let n_left = idx_qubit; let n_right = n_qubits - idx_qubit - 1; From d83e3e6a13f197879cba9bca1014c8760f3b93d7 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Mar 2021 21:12:07 -0700 Subject: [PATCH 050/158] extend_two_to_n --- .../runtime/benches/microbenchmark.rs | 19 ++++++++++++++++++- .../OpenSystems/runtime/src/channels.rs | 4 ++-- .../OpenSystems/runtime/src/linalg.rs | 7 ++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs index 5181da92db4..a352ff4e6a7 100644 --- a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs +++ b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs @@ -6,7 +6,7 @@ //! performance in user code. use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use opensim::{common_matrices, linalg::extend_one_to_n, linalg::Tensor, nq_eye}; +use opensim::{common_matrices, linalg::Tensor, linalg::{extend_one_to_n, extend_two_to_n}, nq_eye}; fn linalg(c: &mut Criterion) { let mut group = c.benchmark_group("linalg"); @@ -34,6 +34,23 @@ fn linalg(c: &mut Criterion) { }, ); } + 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(); diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs index af92edce0a7..32e3637c43c 100644 --- a/src/Simulation/OpenSystems/runtime/src/channels.rs +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -143,7 +143,7 @@ impl Channel { Channel { n_qubits: n_qubits, data: match &self.data { - Unitary(u) => Unitary(extend_two_to_n(u, idx_qubit1, idx_qubit2, n_qubits)), + Unitary(u) => Unitary(extend_two_to_n(u.view(), idx_qubit1, idx_qubit2, n_qubits)), KrausDecomposition(ks) => { // TODO: consolidate with extend_one_to_n, above. let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); @@ -151,7 +151,7 @@ impl Channel { let mut extended: Array3 = Array::zeros((n_kraus, new_dim, new_dim)); for (idx_kraus, kraus) in ks.axis_iter(Axis(0)).enumerate() { let mut target = extended.index_axis_mut(Axis(0), idx_kraus); - let big_kraus = extend_two_to_n(&kraus.to_owned(), idx_qubit1, idx_qubit2, n_qubits); + let big_kraus = extend_two_to_n(kraus, idx_qubit1, idx_qubit2, n_qubits); target.assign(&big_kraus); } KrausDecomposition(extended) diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index 639af7db0d4..daf1d5da86e 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -142,7 +142,7 @@ pub fn extend_one_to_n<'a>(data: ArrayView2<'a, C64>, idx_qubit: usize, n_qubits } } -pub fn extend_two_to_n(data: &Array2, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize) -> Array2 { +pub fn extend_two_to_n(data: ArrayView2, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize) -> Array2 { // TODO: double check that data is 4x4. let mut permutation = Array::from((0..n_qubits).collect::>()); match (idx_qubit1, idx_qubit2) { @@ -159,9 +159,10 @@ pub fn extend_two_to_n(data: &Array2, idx_qubit1: usize, idx_qubit2: usize, // TODO: there is almost definitely a more elegant way to write this. if n_qubits == 2 { - permute_mtx(data, &permutation.to_vec()[..]) + // TODO[perf]: Eliminate the to_owned here by weakening permute_mtx. + permute_mtx(&data.to_owned(), &permutation.to_vec()[..]) } else { - permute_mtx(&data.tensor(&nq_eye(n_qubits - 2)), &permutation.to_vec()[..]) + permute_mtx(&data.view().tensor(&nq_eye(n_qubits - 2)), &permutation.to_vec()[..]) } } From 9b1707236db67a5bc00e35f15878661e79106151 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 17 Mar 2021 23:21:26 -0700 Subject: [PATCH 051/158] Clear diskspace and build microbenchmarks in CI. --- src/Simulation/OpenSystems/build-open-simulator.ps1 | 5 +++++ src/Simulation/OpenSystems/runtime/Cargo.toml | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/Simulation/OpenSystems/build-open-simulator.ps1 b/src/Simulation/OpenSystems/build-open-simulator.ps1 index 32c056dc38c..91e3b7ccca8 100644 --- a/src/Simulation/OpenSystems/build-open-simulator.ps1 +++ b/src/Simulation/OpenSystems/build-open-simulator.ps1 @@ -3,4 +3,9 @@ Push-Location (Join-Path $PSScriptRoot runtime) $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); cargo build @releaseFlag; + + # Free disk space by cleaning up target/${config}/deps. + # Note that this takes longer, but saves ~1 GB of space, which is + # exceptionally helpful in CI builds. + Remove-Item -Recurse (Join-Path . target ("$Env:BUILD_CONFIGURATION" -eq "Release" ? "release" : "debug") "deps"); Pop-Location diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml index 9ede8032254..cbcf8c1aae8 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -50,3 +50,7 @@ crate-type = ["rlib", "staticlib", "cdylib"] [[bench]] name = "c_api_benchmark" harness = false + +[[bench]] +name = "microbenchmark" +harness = false From 96cff4521e102853428ed5ca297e7a24d8bd7acf Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Mar 2021 08:12:43 -0700 Subject: [PATCH 052/158] Clean more aggressively to avoid disk space issues. --- src/Simulation/OpenSystems/build-open-simulator.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Simulation/OpenSystems/build-open-simulator.ps1 b/src/Simulation/OpenSystems/build-open-simulator.ps1 index 91e3b7ccca8..d24121846c4 100644 --- a/src/Simulation/OpenSystems/build-open-simulator.ps1 +++ b/src/Simulation/OpenSystems/build-open-simulator.ps1 @@ -7,5 +7,10 @@ Push-Location (Join-Path $PSScriptRoot runtime) # Free disk space by cleaning up target/${config}/deps. # Note that this takes longer, but saves ~1 GB of space, which is # exceptionally helpful in CI builds. - Remove-Item -Recurse (Join-Path . target ("$Env:BUILD_CONFIGURATION" -eq "Release" ? "release" : "debug") "deps"); + @("release", "debug") | ForEach-Object { + $config = $_; + @("deps", "build", "incremental") | ForEach-Object { + Remove-Item -Recurse (Join-Path . target $config $_) -ErrorAction Continue -Verbose; + } + } Pop-Location From dec89a1dbf90a5bdde95e394f3de43cfba4834a0 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 18 Mar 2021 16:55:31 -0700 Subject: [PATCH 053/158] Format, fix warnings, simplify Cargo.toml. --- src/Simulation/OpenSystems/runtime/Cargo.toml | 19 ++++---- .../runtime/benches/c_api_benchmark.rs | 3 ++ .../runtime/benches/microbenchmark.rs | 5 +- .../OpenSystems/runtime/src/c_api.rs | 5 -- .../OpenSystems/runtime/src/channels.rs | 31 +++++++++--- .../OpenSystems/runtime/src/instrument.rs | 48 +++++++++---------- src/Simulation/OpenSystems/runtime/src/lib.rs | 27 +++++------ 7 files changed, 78 insertions(+), 60 deletions(-) diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml index cbcf8c1aae8..2f81f02c96b 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -1,21 +1,25 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + [package] name = "opensim" version = "0.1.0" authors = ["Christopher Granade "] edition = "2018" +[lib] +name = "opensim" +path = "src/lib.rs" +crate-type = ["rlib", "staticlib", "cdylib"] + [features] default = [] - -# When enabled, web-sys-log allows for logging runtime error messages and -# warnings via console.log. web-sys-log = ["web-sys"] [profile.release] -opt-level = 'z' # Optimize for size. +opt-level = 3 codegen-units = 1 # Reduce number of codegen units to increase optimizations. panic = 'unwind' -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ndarray = { version = "0.13.1", features = ["serde"] } @@ -42,11 +46,6 @@ web-sys = { version = "0.3.4", features = ['console'], optional = true } assert-json-diff = "2.0.1" criterion = { version = "0.3", features = ['html_reports', 'csv_output'] } -[lib] -name = "opensim" -path = "src/lib.rs" -crate-type = ["rlib", "staticlib", "cdylib"] - [[bench]] name = "c_api_benchmark" harness = false diff --git a/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs b/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs index 45c58401aa8..058a8dd9f84 100644 --- a/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs +++ b/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs @@ -1,3 +1,6 @@ +// 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. diff --git a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs index a352ff4e6a7..1eb4bc88e15 100644 --- a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs +++ b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs @@ -1,3 +1,6 @@ +// 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 @@ -5,7 +8,7 @@ //! In particular, optimizing these benchmarks may not translate into improved //! performance in user code. -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use criterion::{criterion_group, criterion_main, Criterion}; use opensim::{common_matrices, linalg::Tensor, linalg::{extend_one_to_n, extend_two_to_n}, nq_eye}; fn linalg(c: &mut Criterion) { diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/OpenSystems/runtime/src/c_api.rs index 7294d7b16a9..be5c4c5033f 100644 --- a/src/Simulation/OpenSystems/runtime/src/c_api.rs +++ b/src/Simulation/OpenSystems/runtime/src/c_api.rs @@ -105,11 +105,6 @@ pub extern "C" fn destroy(sim_id: usize) -> i64 { }) } -#[no_mangle] -pub extern "C" fn dump_to_console(sim_id: usize) -> () { - // FIXME: implement this -} - #[no_mangle] pub extern "C" fn x(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.x)) diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs index 32e3637c43c..6dcfe99a198 100644 --- a/src/Simulation/OpenSystems/runtime/src/channels.rs +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -41,7 +41,10 @@ impl Channel { // pure state directly, so if the Choi rank is bigger than 1, promote to // Mixed and recurse. if ks.shape()[0] == 1 { - Pure({ let k: ArrayView2 = ks.slice(s![0, .., ..]); k.dot(psi) }) + Pure({ + let k: ArrayView2 = ks.slice(s![0, .., ..]); + k.dot(psi) + }) } else { self.apply(&state.to_mixed())?.data } @@ -98,9 +101,26 @@ impl Channel { // indices in ascending order, so we can proceed to make a new channel // that expands this channel to act on the full register and then use // the ordinary apply method. - let expanded = match self.n_qubits { - 1 => self.extend_one_to_n(idx_qubits[0], state.n_qubits), - 2 => self.extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits), + // TODO[perf]: For larger systems, this could be improved by using + // matrix multiplication kernels to avoid extending + // channels to larger Hilbert spaces. + // For smaller systems, extending channels and possibly + // caching them is likely to be more performant; need to + // tune to find crossover point. + match self.n_qubits { + 1 => { + if state.n_qubits == 1 { + self.apply(state) + } else { + self.extend_one_to_n(idx_qubits[0], state.n_qubits) + .apply(state) + } + } + // TODO[perf]: If the size of the register matches the size of the + // channel, permute rather than expanding. + 2 => self + .extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits) + .apply(state), _ => { log_message(&format!( "Expanding {}-qubit channels is not yet implemented.", @@ -108,8 +128,7 @@ impl Channel { )); unimplemented!(""); } - }; - expanded.apply(state) + } } pub fn extend_one_to_n(self: &Self, idx_qubit: usize, n_qubits: usize) -> Channel { diff --git a/src/Simulation/OpenSystems/runtime/src/instrument.rs b/src/Simulation/OpenSystems/runtime/src/instrument.rs index 9417824bc75..1b20980d778 100644 --- a/src/Simulation/OpenSystems/runtime/src/instrument.rs +++ b/src/Simulation/OpenSystems/runtime/src/instrument.rs @@ -1,42 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use crate::states::StateData::Mixed; use crate::Channel; -use std::iter::Iterator; use rand::Rng; +use std::iter::Iterator; -use crate::State; -use crate::channels::ChannelData; -use crate::QubitSized; use crate::linalg::Trace; +use crate::State; -use serde::{ Serialize, Deserialize }; -use crate::utils::log_message; - +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct Instrument { - pub effects: Vec + pub effects: Vec, } impl Instrument { pub fn sample(&self, idx_qubits: &[usize], state: &State) -> (usize, State) { - let mut possible_outcomes = self.effects.iter().enumerate().map(|(idx, effect)| { - let output_state = effect.apply_to(idx_qubits, state).unwrap(); - let tr = (&output_state).trace(); - ( - idx, - output_state, - tr.re - ) - }).collect::>(); + let mut possible_outcomes = self + .effects + .iter() + .enumerate() + .map(|(idx, effect)| { + let output_state = effect.apply_to(idx_qubits, state).unwrap(); + let tr = (&output_state).trace(); + (idx, output_state, tr.re) + }) + .collect::>(); let mut rng = rand::thread_rng(); let random_sample: f64 = rng.gen(); - for (idx, cum_pr) in possible_outcomes.iter().scan( - 0.0f64, - |acc, (_idx, _, pr)| { + for (idx, cum_pr) in possible_outcomes + .iter() + .scan(0.0f64, |acc, (_idx, _, pr)| { *acc = *acc + *pr; Some(*acc) - } - ).enumerate() { + }) + .enumerate() + { if random_sample < cum_pr { // In order to not have to copy the output state, we need // to be able to move it out from the vector. To do so, @@ -54,8 +55,7 @@ impl Instrument { } return (idx, output_state); } - }; - let idx_last = &possible_outcomes.len() - 1; + } let (idx, output_state, _) = possible_outcomes.pop().unwrap(); drop(possible_outcomes); (idx, output_state) diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index b294d014fb0..05253838842 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -8,29 +8,28 @@ extern crate derive_more; extern crate serde; pub mod c_api; -pub mod linalg; -pub mod common_matrices; -mod utils; -mod states; mod channels; -mod noise_model; +pub mod common_matrices; mod instrument; +pub mod linalg; +mod noise_model; +mod states; +mod utils; -pub use crate::channels::depolarizing_channel; -pub use crate::noise_model::NoiseModel; -pub use crate::utils::*; -pub use crate::common_matrices::*; -pub use crate::linalg::zeros_like; -pub use crate::states::State; pub use crate::channels::Channel; pub use crate::channels::ChannelData::KrausDecomposition; pub use crate::channels::ChannelData::Unitary; +pub use crate::channels::{amplitude_damping_channel, depolarizing_channel}; +pub use crate::common_matrices::*; +pub use crate::linalg::zeros_like; +pub use crate::noise_model::NoiseModel; +pub use crate::states::State; +pub use crate::utils::*; - -use serde::{ Serialize, Deserialize }; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct QubitSized { n_qubits: usize, - data: T + data: T, } From 37ed0c1e896effc27a885dc9d44181e4dece6191 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 13 Apr 2021 15:42:07 -0700 Subject: [PATCH 054/158] Fix choco rustup in prereqs. --- src/Qir/Runtime/prerequisites.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index d9e6e2bdb68..82320029b87 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -6,7 +6,7 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if (!(Get-Command clang -ErrorAction SilentlyContinue)) { choco install llvm choco install ninja - choco install rustup + choco install rustup.install } } elseif ($IsMacOS) { # temporary workaround for Bintray sunset From 0820d7563e535e23556bc60a096c5a1eb820bfe1 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 13 Apr 2021 16:45:50 -0700 Subject: [PATCH 055/158] Disable choco rustup. --- src/Qir/Runtime/prerequisites.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index 82320029b87..cdc63c78752 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -6,7 +6,9 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if (!(Get-Command clang -ErrorAction SilentlyContinue)) { choco install llvm choco install ninja - choco install rustup.install + # NB: The chocolatey package for rustup is currently nonfunctional, + # so we rely on Rust being installed via Azure Pipelines. + # choco install rustup.install } } elseif ($IsMacOS) { # temporary workaround for Bintray sunset From 0f75d25bf810ef26679c02c1fceae51df2ab0359 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 13 Apr 2021 18:48:07 -0700 Subject: [PATCH 056/158] Fix path to Cargo.toml. --- src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index 8c2dd74675f..b160ba1aedb 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -21,8 +21,7 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(Corrosion) - -corrosion_import_crate(MANIFEST_PATH ../../../Simulation/OpenSystems/runtime/Cargo.toml) +corrosion_import_crate(MANIFEST_PATH ../../../../Simulation/OpenSystems/runtime/Cargo.toml) #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on From 467d1600322f05ed579da207a5485a9e5702773a Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 13 Apr 2021 20:14:00 -0700 Subject: [PATCH 057/158] Don't check Rust-style names with clang-tidy. --- .../lib/Simulators/OpenSystemsSimulator.cpp | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp b/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp index e8698ce3485..8675c038ceb 100644 --- a/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp @@ -8,8 +8,9 @@ #include #include +#include "QirRuntimeApi_I.hpp" +#include "QSharpSimApi_I.hpp" #include "SimFactory.hpp" -#include "QuantumApi_I.hpp" using namespace std; typedef long long int i64; @@ -17,23 +18,25 @@ typedef long long int i64; // Define the API with the open systems simulator. extern "C" { - size_t init(size_t initialcapacity); - i64 destroy(size_t sim_id); - void dump_to_console(size_t sim_id); - i64 x(size_t sim_id, size_t idx); - i64 y(size_t sim_id, size_t idx); - i64 z(size_t sim_id, size_t idx); - i64 h(size_t sim_id, size_t idx); - i64 s(size_t sim_id, size_t idx); - i64 s_adj(size_t sim_id, size_t idx); - i64 t(size_t sim_id, size_t idx); - i64 t_adj(size_t sim_id, size_t idx); - i64 cnot(size_t sim_id, size_t idx_control, size_t idx_target); - i64 m(size_t sim_id, size_t idx, size_t* result_out); - const char* lasterr(); - const char* get_noise_model(size_t sim_id); - i64 set_noise_model(size_t sim_id, const char* new_model); - const char* get_current_state(size_t sim_id); + // NB: We disable clang-tidy rules for case conventions here, as the names + // reflect the Rust naming conventions used in the opensim crate. + size_t init(size_t initialcapacity); // NOLINT(readability-identifier-naming) + i64 destroy(size_t sim_id); // NOLINT(readability-identifier-naming) + void dump_to_console(size_t sim_id); // NOLINT(readability-identifier-naming) + i64 x(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 y(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 z(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 h(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 s(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 s_adj(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 t(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 t_adj(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) + i64 cnot(size_t sim_id, size_t idx_control, size_t idx_target); // NOLINT(readability-identifier-naming) + i64 m(size_t sim_id, size_t idx, size_t* result_out); // NOLINT(readability-identifier-naming) + const char* lasterr(); // NOLINT(readability-identifier-naming) + const char* get_noise_model(size_t sim_id); // NOLINT(readability-identifier-naming) + i64 set_noise_model(size_t sim_id, const char* new_model); // NOLINT(readability-identifier-naming) + const char* get_current_state(size_t sim_id); // NOLINT(readability-identifier-naming) } namespace Microsoft @@ -42,7 +45,7 @@ namespace Quantum { // FIXME: support methods from public IDiagnostics; they currently // just throw. - class OpenSystemSimulator : public IRuntimeDriver, public IQuantumGateSet, public IDiagnostics + class COpenSystemSimulator : public IRuntimeDriver, public IQuantumGateSet, public IDiagnostics { typedef void (*TSingleQubitGate)(size_t /*simulator id*/, size_t /*qubit id*/); typedef void (*TSingleQubitControlledGate)( @@ -91,12 +94,12 @@ namespace Quantum } public: - OpenSystemSimulator() + COpenSystemSimulator() { // FIXME: allow setting number of qubits. this->simulatorId = init(3); } - ~OpenSystemSimulator() + ~COpenSystemSimulator() { destroy(this->simulatorId); } @@ -283,7 +286,7 @@ namespace Quantum std::unique_ptr CreateOpenSystemsSimulator() { - return std::make_unique(); + return std::make_unique(); } } // namespace Quantum } // namespace Microsoft \ No newline at end of file From 8f47f94f93b2a01f3672a0fdbbcc27893131b5c9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 13 Apr 2021 21:43:25 -0700 Subject: [PATCH 058/158] M is no longer overriden. --- src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp b/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp index 8675c038ceb..5eb46727318 100644 --- a/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp @@ -141,7 +141,7 @@ namespace Quantum // TODO } - Result M(Qubit q) override + Result M(Qubit q) { size_t result; m(this->simulatorId, GetQubitId(q), &result); From 044f7ba8d11cd0f54b425d9378b7636f8806b2dc Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 13 Apr 2021 23:02:35 -0700 Subject: [PATCH 059/158] Temporarily disable QIR / opensim integration. --- src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 2 +- src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 4 +++- src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt index 191a99e0a3c..c7d11b083d7 100644 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(Microsoft.Quantum.Qir.QSharp.Core ${CMAKE_DL_LIBS} qsharp-core-qis-support-obj simulators-obj - opensim + # opensim "-L${CMAKE_BINARY_DIR}/lib/QIR" -lMicrosoft.Quantum.Qir.Runtime ) diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index b160ba1aedb..4c6ecfa0b9d 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -2,7 +2,9 @@ set(source_files "FullstateSimulator.cpp" "setup.cpp" "ToffoliSimulator.cpp" - "OpenSystemsSimulator.cpp" + # NB: Disabled temporarily until we can build crates into OBJECT CMake + # libraries. + # "OpenSystemsSimulator.cpp" ) set(includes diff --git a/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt b/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt index 5826a7e788a..00fac26da68 100644 --- a/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt +++ b/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt @@ -18,8 +18,8 @@ target_include_directories(open-systems-simulator-tests PUBLIC "${public_includes}" "${PROJECT_SOURCE_DIR}/lib/QIR" ) -add_dependencies(open-systems-simulator-tests qir_test_opensimulator_target) +# add_dependencies(open-systems-simulator-tests qir_test_opensimulator_target) -install(TARGETS open-systems-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(open-systems-simulator-tests) +# install(TARGETS open-systems-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +# add_unit_test(open-systems-simulator-tests) From 5e2ec23478df103b842200dcd67fc242ca25765f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 14 Apr 2021 08:21:22 -0700 Subject: [PATCH 060/158] Clean up extra cargo files after testing opensim. --- src/Simulation/OpenSystems/test-open-simulator.ps1 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Simulation/OpenSystems/test-open-simulator.ps1 b/src/Simulation/OpenSystems/test-open-simulator.ps1 index 678c8fd5340..e6499a098f8 100644 --- a/src/Simulation/OpenSystems/test-open-simulator.ps1 +++ b/src/Simulation/OpenSystems/test-open-simulator.ps1 @@ -25,4 +25,14 @@ Push-Location (Join-Path $PSScriptRoot runtime) New-Item -Type Directory -Force -Path $perfDest; Copy-Item -Recurse -Path $reportPath -Destination $perfDest; } + + # Free disk space by cleaning up target/${config}/deps. + # Note that this takes longer, but saves ~1 GB of space, which is + # exceptionally helpful in CI builds. + @("release", "debug") | ForEach-Object { + $config = $_; + @("deps", "build", "incremental") | ForEach-Object { + Remove-Item -Recurse (Join-Path . target $config $_) -ErrorAction Continue -Verbose; + } + } Pop-Location From 245b4446fb18622d87247648594a35a1c3d2b6a6 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 12 May 2021 13:41:44 -0700 Subject: [PATCH 061/158] Work on API docs and refactoring of Rust crate. --- .devcontainer/Dockerfile | 13 +- bootstrap.ps1 | 3 + src/Simulation/OpenSystems/runtime/Cargo.lock | 2 + src/Simulation/OpenSystems/runtime/Cargo.toml | 22 +++- src/Simulation/OpenSystems/runtime/README.md | 38 +++--- .../runtime/benches/microbenchmark.rs | 2 +- .../OpenSystems/runtime/src/channels.rs | 4 +- .../OpenSystems/runtime/src/instrument.rs | 5 + src/Simulation/OpenSystems/runtime/src/lib.rs | 22 ++-- .../OpenSystems/runtime/src/linalg.rs | 113 +++++++++++++----- .../OpenSystems/runtime/src/utils.rs | 2 +- src/Simulation/OpenSystems/runtime/testing.md | 14 +++ 12 files changed, 172 insertions(+), 68 deletions(-) create mode 100644 src/Simulation/OpenSystems/runtime/testing.md diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index bc9cabd0ec5..61f71f56bdc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,8 +1,11 @@ FROM mcr.microsoft.com/dotnet/sdk:3.1-focal RUN apt 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 -RUN apt-get install -y build-essential -CMD [ "pwsh" ] \ No newline at end of file + ninja-build \ + clang-11 \ + clang-tidy-11 \ + 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/bootstrap.ps1 b/bootstrap.ps1 index 7ad031ef42d..213fdfb7294 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -14,6 +14,9 @@ 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/OpenSystems") + .\build-open-simulator.ps1 + Pop-Location $Env:BUILD_CONFIGURATION = $null } diff --git a/src/Simulation/OpenSystems/runtime/Cargo.lock b/src/Simulation/OpenSystems/runtime/Cargo.lock index 559b1e382ae..7f16445d303 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.lock +++ b/src/Simulation/OpenSystems/runtime/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "assert-json-diff" version = "2.0.1" diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/OpenSystems/runtime/Cargo.toml index 2f81f02c96b..ee13245bdf2 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/OpenSystems/runtime/Cargo.toml @@ -14,7 +14,25 @@ crate-type = ["rlib", "staticlib", "cdylib"] [features] default = [] -web-sys-log = ["web-sys"] +wasm = ["web-sys"] + +# We use an empty feature to enable documentation generation, as is required +# until https://github.com/rust-lang/rust/pull/83366 is completed. +# See https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643 +# for discussion. +doc = [] + +# To make sure docs are enabled on docs.rs, we need to also pass the "doc" +# feature in our metadata. +[package.metadata.docs.rs] + +# Features to pass to Cargo (default: []) +features = ["doc"] + +# Enable LaTeX on docs.rs. +# See https://stackoverflow.com/a/54573800/267841 for why this works. +rustdoc-args = [ "--html-in-header", "docs/header.html" ] + [profile.release] opt-level = 3 @@ -39,7 +57,7 @@ lazy_static = "1.4.0" # by rand and rand_core. getrandom = { version = "0.1.15", features = ["wasm-bindgen"] } -# We only need web-sys when compiling with the web-sys-log feature. +# We only need web-sys when compiling with the wasm feature. web-sys = { version = "0.3.4", features = ['console'], optional = true } [dev-dependencies] diff --git a/src/Simulation/OpenSystems/runtime/README.md b/src/Simulation/OpenSystems/runtime/README.md index 9782d2409e8..63901bf6222 100644 --- a/src/Simulation/OpenSystems/runtime/README.md +++ b/src/Simulation/OpenSystems/runtime/README.md @@ -1,14 +1,24 @@ -# 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. -- C++-language unit tests in the QIR runtime. - These tests ensure that the binding of the Rust library as a QIR simulator work as expected, and are defined in `qsharp-runtime/src/QirRuntime/test/OpenSystemsSimulator/*.cpp`. -- 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. +This **experimental** crate contains 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. + +## Cargo Features + +- **`doc`**: Required to build documentation. +- **`python`**: Enables Python bindings for this crate. +- **`wasm`**: Ensures that the crate is compatible with usage from WebAssembly. + +## Representing open quantum systems + +This crate provides several different data structures for representing open quantum systems in a variety of different conventions: + +- [`State`]\: Represents pure or mixed states of a register of qubits. +- [`Channel`]\: Represents processes that map states to states. +- [`Instrument`]\: Represents quantum instruments, the most general form of measurement. + +## Noise model serialization + +TODO diff --git a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs index 1eb4bc88e15..d63909cf264 100644 --- a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs +++ b/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs @@ -9,7 +9,7 @@ //! performance in user code. use criterion::{criterion_group, criterion_main, Criterion}; -use opensim::{common_matrices, linalg::Tensor, linalg::{extend_one_to_n, extend_two_to_n}, nq_eye}; +use opensim::{common_matrices, Tensor, linalg::{extend_one_to_n, extend_two_to_n}, common_matrices::nq_eye}; fn linalg(c: &mut Criterion) { let mut group = c.benchmark_group("linalg"); diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/OpenSystems/runtime/src/channels.rs index 6dcfe99a198..04d3c4c82e4 100644 --- a/src/Simulation/OpenSystems/runtime/src/channels.rs +++ b/src/Simulation/OpenSystems/runtime/src/channels.rs @@ -3,9 +3,8 @@ use crate::channels::ChannelData::{KrausDecomposition, Unitary}; use crate::linalg::ConjBy; -use crate::linalg::{extend_one_to_n, extend_two_to_n}; +use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; use crate::states::StateData::{Mixed, Pure}; -use crate::zeros_like; use crate::NoiseModel; use crate::QubitSized; use crate::State; @@ -19,6 +18,7 @@ use std::convert::TryInto; use std::ops::Add; use std::ops::Mul; +// TODO: Rename to something that doesn't imply it's already CPTP. pub type Channel = QubitSized; #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/Simulation/OpenSystems/runtime/src/instrument.rs b/src/Simulation/OpenSystems/runtime/src/instrument.rs index 1b20980d778..8b3f2a2d865 100644 --- a/src/Simulation/OpenSystems/runtime/src/instrument.rs +++ b/src/Simulation/OpenSystems/runtime/src/instrument.rs @@ -12,7 +12,12 @@ use crate::State; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] +/// Represents a quantum instrument; that is, a process that accepts a quantum +/// state and returns the new state of a system and classical data extracted +/// from that system. pub struct Instrument { + /// The effects of the instrument, represented as completely positive + /// trace non-increasing (CPTNI) processes. pub effects: Vec, } diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/OpenSystems/runtime/src/lib.rs index 05253838842..7d72807c63d 100644 --- a/src/Simulation/OpenSystems/runtime/src/lib.rs +++ b/src/Simulation/OpenSystems/runtime/src/lib.rs @@ -1,33 +1,35 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +// The following two attributes include the README.md for this crate when +// the "doc" feature is turned on (requires +nightly). +// See https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643 +// for discussion. +#![cfg_attr(feature = "doc", feature(extended_key_value_attributes))] +#![cfg_attr(feature = "doc", cfg_attr(feature = "doc", doc = include_str!("../README.md")))] + #[macro_use(array, s)] extern crate ndarray; extern crate derive_more; extern crate serde; +use serde::{Deserialize, Serialize}; pub mod c_api; -mod channels; pub mod common_matrices; -mod instrument; pub mod linalg; +mod channels; +mod instrument; mod noise_model; mod states; mod utils; -pub use crate::channels::Channel; -pub use crate::channels::ChannelData::KrausDecomposition; -pub use crate::channels::ChannelData::Unitary; -pub use crate::channels::{amplitude_damping_channel, depolarizing_channel}; -pub use crate::common_matrices::*; -pub use crate::linalg::zeros_like; +pub use crate::channels::*; +pub use crate::instrument::*; pub use crate::noise_model::NoiseModel; pub use crate::states::State; pub use crate::utils::*; -use serde::{Deserialize, Serialize}; - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct QubitSized { n_qubits: usize, diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/OpenSystems/runtime/src/linalg.rs index daf1d5da86e..42c895a7480 100644 --- a/src/Simulation/OpenSystems/runtime/src/linalg.rs +++ b/src/Simulation/OpenSystems/runtime/src/linalg.rs @@ -1,15 +1,19 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use ndarray::{ArrayView1, ArrayView2}; +//! Provides common linear algebra functions and traits. + +use ndarray::{Array, Array1, Array2, ArrayView1, ArrayView2}; use num_traits::Zero; -use ndarray::{ Array, Array1, Array2 }; -use std::ops::Mul; -use std::convert::TryInto; +use std::{convert::TryInto, ops::Mul}; -use crate::{ C64, nq_eye }; +use crate::{common_matrices::nq_eye, C64}; +/// Represents types that have hermitian conjugates (e.g.: $A^\dagger$ for +/// a matrix $A$ is defined as the complex conjugate transpose of $A$, +/// $(A^\dagger)\_{ij} = A\_{ji}^*$). pub trait HasDagger { + /// The type of the hermitian conjugate. type Output; /// Returns the hermitian conjugate (colloquially, the dagger) of a @@ -46,26 +50,34 @@ impl ConjBy for Array2 { } } -pub trait Tensor { +/// The tensor product operator ($\otimes$). +pub trait Tensor { + /// The resulting type after applying the tensor product. type Output; - fn tensor(self: Self, other: Other) -> Self::Output; + + /// Performs the tensor product. + /// + /// # Example + /// ``` + /// // TODO + /// ``` + fn tensor(self: Self, rhs: Rhs) -> Self::Output; } -impl , T: Copy + Mul> Tensor for ArrayView1<'_, T> { +impl, T: Copy + Mul> Tensor for ArrayView1<'_, T> { type Output = Array1; fn tensor(self, other: Other) -> Self::Output { let other: Self = other.into(); - let unflat = Array::from_shape_fn( - (self.shape()[0], other.shape()[0]), - |(i, j)| { - self[(i)] * other[(j)] - } - ); - unflat.into_shape(self.shape()[0] * other.shape()[0]).unwrap() + let unflat = Array::from_shape_fn((self.shape()[0], other.shape()[0]), |(i, j)| { + self[(i)] * other[(j)] + }); + unflat + .into_shape(self.shape()[0] * other.shape()[0]) + .unwrap() } } -impl , T: Copy + Mul> Tensor for &Array1 { +impl, T: Copy + Mul> Tensor for &Array1 { type Output = Array1; fn tensor(self: Self, other: Other) -> Self::Output { @@ -74,21 +86,29 @@ impl , T: Copy + Mul> Tensor for &Array1 } } -impl , T: Copy + Mul> Tensor for ArrayView2<'_, T> { +impl, T: Copy + Mul> Tensor for ArrayView2<'_, T> { type Output = Array2; fn tensor(self, other: Other) -> Self::Output { let other: Self = other.into(); let unflat = Array::from_shape_fn( - (self.shape()[0], other.shape()[0], self.shape()[1], other.shape()[1]), - |(i, j, k, l)| { - self[(i, k)] * other[(j, l)] - } + ( + self.shape()[0], + other.shape()[0], + self.shape()[1], + other.shape()[1], + ), + |(i, j, k, l)| self[(i, k)] * other[(j, l)], ); - unflat.into_shape((self.shape()[0] * other.shape()[0], self.shape()[1] * other.shape()[1])).unwrap() + unflat + .into_shape(( + self.shape()[0] * other.shape()[0], + self.shape()[1] * other.shape()[1], + )) + .unwrap() } } -impl , T: Copy + Mul> Tensor for &Array2 { +impl, T: Copy + Mul> Tensor for &Array2 { type Output = Array2; fn tensor(self: Self, other: Other) -> Self::Output { @@ -97,13 +117,22 @@ impl , T: Copy + Mul> Tensor for &Array2 } } +/// Represents types for which the trace can be computed. pub trait Trace { + /// The type returned by the trace. type Output; + /// The trace (typically, the sum of the eigenvalues, + /// or the sum of the diagonal elements $\sum_i A_{ii}$). + /// + /// # Example + /// ``` + /// // TODO + /// ``` fn trace(self) -> Self::Output; } -impl Trace for Array2 { +impl Trace for Array2 { type Output = T; fn trace(self) -> Self::Output { @@ -111,7 +140,7 @@ impl Trace for Array2 { } } -impl Trace for &Array2 { +impl Trace for &Array2 { type Output = T; fn trace(self) -> Self::Output { @@ -122,18 +151,24 @@ impl Trace for &Array2 { // FIXME: modify to Result<..., String> so that errors can propagate to the C API. // FIXME[perf]: This function is significantly slower than would be expected // from microbenchmarks on tensor and nq_eye directly. -pub fn extend_one_to_n<'a>(data: ArrayView2<'a, C64>, idx_qubit: usize, n_qubits: usize) -> Array2 { +/// Given an array representing an operator acting on single-qubit states, +/// returns a new operator that acts on $n$-qubit states. +pub fn extend_one_to_n<'a>( + data: ArrayView2<'a, C64>, + idx_qubit: usize, + n_qubits: usize, +) -> Array2 { let n_left = idx_qubit; let n_right = n_qubits - idx_qubit - 1; match (n_left, n_right) { (0, _) => { let right_eye = nq_eye(n_right); data.view().tensor(&right_eye) - }, + } (_, 0) => { let left_eye = Array2::eye(2usize.pow(n_left.try_into().unwrap())); left_eye.view().tensor(&data) - }, + } (_, _) => { let eye = nq_eye(n_right); let right = data.view().tensor(&eye); @@ -142,7 +177,12 @@ pub fn extend_one_to_n<'a>(data: ArrayView2<'a, C64>, idx_qubit: usize, n_qubits } } -pub fn extend_two_to_n(data: ArrayView2, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize) -> Array2 { +pub fn extend_two_to_n( + data: ArrayView2, + idx_qubit1: usize, + idx_qubit2: usize, + n_qubits: usize, +) -> Array2 { // TODO: double check that data is 4x4. let mut permutation = Array::from((0..n_qubits).collect::>()); match (idx_qubit1, idx_qubit2) { @@ -150,7 +190,7 @@ pub fn extend_two_to_n(data: ArrayView2, idx_qubit1: usize, idx_qubit2: usi (_, 0) => { permutation.swap(1, idx_qubit2); permutation.swap(1, idx_qubit1); - }, + } _ => { permutation.swap(1, idx_qubit2); permutation.swap(0, idx_qubit1); @@ -162,7 +202,10 @@ pub fn extend_two_to_n(data: ArrayView2, idx_qubit1: usize, idx_qubit2: usi // TODO[perf]: Eliminate the to_owned here by weakening permute_mtx. permute_mtx(&data.to_owned(), &permutation.to_vec()[..]) } else { - permute_mtx(&data.view().tensor(&nq_eye(n_qubits - 2)), &permutation.to_vec()[..]) + permute_mtx( + &data.view().tensor(&nq_eye(n_qubits - 2)), + &permutation.to_vec()[..], + ) } } @@ -183,7 +226,12 @@ pub fn permute_mtx(data: &Array2, new_order: &[usize]) -> Array2 { // FIXME: there has to be a better way to make a vector that consists of // 2n copies of 2usize. - let new_dims: Vec = vec![2usize].iter().cycle().take(2 * n_qubits).map(|x| x.clone()).collect(); + let new_dims: Vec = vec![2usize] + .iter() + .cycle() + .take(2 * n_qubits) + .map(|x| x.clone()) + .collect(); // FIXME: make this a result and propagate the result out to the return. let tensor = data.clone().into_shared().reshape(new_dims); let mut permutation = new_order.to_vec(); @@ -194,7 +242,6 @@ pub fn permute_mtx(data: &Array2, new_order: &[usize]) -> Array2 { permuted.reshape([n_rows, n_rows]).into_owned() } - pub fn zeros_like(data: &Array) -> Array { Array::zeros(data.dim()) } diff --git a/src/Simulation/OpenSystems/runtime/src/utils.rs b/src/Simulation/OpenSystems/runtime/src/utils.rs index d83ba4e6552..3a3c86be06f 100644 --- a/src/Simulation/OpenSystems/runtime/src/utils.rs +++ b/src/Simulation/OpenSystems/runtime/src/utils.rs @@ -10,7 +10,7 @@ pub const ONE_C: C64 = Complex::new(1f64, 0f64); pub const ZERO_C: C64 = Complex::new(0f64, 0f64); pub const I_C: C64 = Complex::new(0f64, 1f64); -pub fn c(re: T, im: T) -> Complex { +fn c(re: T, im: T) -> Complex { Complex::new(re, im) } diff --git a/src/Simulation/OpenSystems/runtime/testing.md b/src/Simulation/OpenSystems/runtime/testing.md new file mode 100644 index 00000000000..9782d2409e8 --- /dev/null +++ b/src/Simulation/OpenSystems/runtime/testing.md @@ -0,0 +1,14 @@ +# 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. +- C++-language unit tests in the QIR runtime. + These tests ensure that the binding of the Rust library as a QIR simulator work as expected, and are defined in `qsharp-runtime/src/QirRuntime/test/OpenSystemsSimulator/*.cpp`. +- 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. From d6f4a7e0443372774fb2736288a3bab4ed477120 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 07:59:27 -0700 Subject: [PATCH 062/158] Separate out qdk_sim into its own crate. --- bootstrap.ps1 | 4 +- build/build.ps1 | 6 +- build/pack.ps1 | 28 +-- build/set-env.ps1 | 3 + src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 2 +- src/Simulation/Common/Simulators.Dev.props | 23 +- src/Simulation/OpenSystems/.gitignore | 1 - .../OpenSystems/build-open-simulator.ps1 | 16 -- .../noise-model-export-checkpoint.ipynb | 212 ------------------ .../Microsoft.Quantum.Simulators.csproj | 15 -- ...crosoft.Quantum.Simulators.nuspec.template | 3 - .../OpenSystemsSimulator/NativeInterface.cs | 2 +- .../runtime => qdk_sim_rs}/.gitignore | 1 + .../runtime => qdk_sim_rs}/Cargo.lock | 38 ++-- .../runtime => qdk_sim_rs}/Cargo.toml | 4 +- .../runtime => qdk_sim_rs}/README.md | 0 .../benches/c_api_benchmark.rs | 0 .../benches/data/benchmark-noise-model.json | 0 .../benches/data/noise-model-export.ipynb | 0 .../benches/microbenchmark.rs | 0 .../qdk_sim_rs/build-qdk-sim-rs.ps1 | 13 ++ .../runtime => qdk_sim_rs}/src/c_api.rs | 0 .../runtime => qdk_sim_rs}/src/channels.rs | 0 .../src/common_matrices.rs | 4 +- .../runtime => qdk_sim_rs}/src/instrument.rs | 0 .../runtime => qdk_sim_rs}/src/lib.rs | 0 .../runtime => qdk_sim_rs}/src/linalg.rs | 0 .../runtime => qdk_sim_rs}/src/noise_model.rs | 0 .../runtime => qdk_sim_rs}/src/states.rs | 0 .../runtime => qdk_sim_rs}/src/utils.rs | 0 .../test-qdk-sim-rs.ps1} | 13 +- .../runtime => qdk_sim_rs}/testing.md | 0 .../tests/data/ideal-noise-model.json | 0 .../tests/serialization_tests.rs | 0 34 files changed, 72 insertions(+), 316 deletions(-) delete mode 100644 src/Simulation/OpenSystems/.gitignore delete mode 100644 src/Simulation/OpenSystems/build-open-simulator.ps1 delete mode 100644 src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/.gitignore (69%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/Cargo.lock (99%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/Cargo.toml (98%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/README.md (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/benches/c_api_benchmark.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/benches/data/benchmark-noise-model.json (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/benches/data/noise-model-export.ipynb (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/benches/microbenchmark.rs (100%) create mode 100644 src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/c_api.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/channels.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/common_matrices.rs (98%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/instrument.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/lib.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/linalg.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/noise_model.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/states.rs (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/src/utils.rs (100%) rename src/Simulation/{OpenSystems/test-open-simulator.ps1 => qdk_sim_rs/test-qdk-sim-rs.ps1} (73%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/testing.md (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/tests/data/ideal-noise-model.json (100%) rename src/Simulation/{OpenSystems/runtime => qdk_sim_rs}/tests/serialization_tests.rs (100%) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 213fdfb7294..dd9f5a12d9f 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -14,8 +14,8 @@ 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/OpenSystems") - .\build-open-simulator.ps1 + Push-Location (Join-Path $PSScriptRoot "src/Simulation/qdk_sim") + .\build-qdk-sim-rs.ps1 Pop-Location $Env:BUILD_CONFIGURATION = $null } diff --git a/build/build.ps1 b/build/build.ps1 index f32b705e8a3..285d8f12de4 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -26,9 +26,9 @@ 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_OPENSIM -ne "false") { - $openSimulator = (Join-Path $PSScriptRoot "../src/Simulation/OpenSystems") - & "$openSimulator/build-open-simulator.ps1" +if ($Env:ENABLE_EXPERIMENTALSIM -ne "false") { + $expSim = (Join-Path $PSScriptRoot "../src/Simulation/qdk_sim_rs") + & "$openSimulator/build-qdk-sim-rs.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } diff --git a/build/pack.ps1 b/build/pack.ps1 index 7ce857256d0..af6a61a9710 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -7,34 +7,26 @@ $ErrorActionPreference = 'Stop' $all_ok = $True $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot ".."); -$DropNative = "$Env:DROP_NATIVE" -eq "" ? $RepoRoot : $Env:DROP_NATIVE; Write-Host "##[info]Copy Native simulator xplat binaries" pushd (Join-Path $PSScriptRoot ../src/Simulation/Native) If (-not (Test-Path 'osx')) { mkdir 'osx' } If (-not (Test-Path 'linux')) { mkdir 'linux' } - $DROP = "$DropNative/src/Simulation/Native/build/drop" + If (-not (Test-Path 'win10')) { mkdir 'win10' } + + $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 -Write-Host "##[info]Copy open systems simulator xplat binaries" -Push-Location (Join-Path $PSScriptRoot ../src/Simulation/OpenSystems/runtime) - If (-not (Test-Path 'osx')) { mkdir 'osx' } - If (-not (Test-Path 'linux')) { mkdir 'linux' } - If (-not (Test-Path 'win10')) { mkdir 'win10' } - $DROP = Join-Path ` - "$DropNative" "src" "Simulation" "OpenSystems" ` - "runtime" "target" ` - $Env:BUILD_CONFIGURATION.ToLowerInvariant(); - if (Test-Path "$DROP/libopensim.dylib") { - Copy-Item "$DROP/libopensim.dylib" "osx/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" + $DROP = "$Env:DROP_NATIVE/qdk_sim_rs"; + if (Test-Path "$DROP/libqdk_sim.dylib") { + Copy-Item "$DROP/libqdk_sim.dylib" "osx/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" } - if (Test-Path "$DROP/libopensim.so") { - Copy-Item "$DROP/libopensim.so" "linux/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" + if (Test-Path "$DROP/libqdk_sim.so") { + Copy-Item "$DROP/libqdk_sim.so" "linux/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" } - if (Test-Path "$DROP/opensim.dll") { - Copy-Item "$DROP/opensim.dll" "win10/Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll" + if (Test-Path "$DROP/qdk_sim.dll") { + Copy-Item "$DROP/qdk_sim.dll" "win10/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" } Pop-Location diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 9d9e79c6c83..186c77abac6 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -16,6 +16,9 @@ If (($Env:ENABLE_NATIVE -ne "false") -and ($Env:NATIVE_SIMULATOR -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 ".." "xplat") +} 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) } diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index 4c6ecfa0b9d..304e3c2ab9e 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -23,7 +23,7 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(Corrosion) -corrosion_import_crate(MANIFEST_PATH ../../../../Simulation/OpenSystems/runtime/Cargo.toml) +corrosion_import_crate(MANIFEST_PATH ../../../../Simulation/qdk_sim_rs/Cargo.toml) #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index d2fcdd7160d..149ebc96209 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -5,7 +5,7 @@ bin\$(BuildConfiguration)\$(TargetFramework)\$(AssemblyName).xml $([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory)..\..\..\)) $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/Native/build/drop)) - $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/OpenSystems/runtime/target/$(Configuration.ToLowerInvariant()))) + $([MSBuild]::NormalizePath($(EnlistmentRoot)/xplat/qdk_sim_rs)) @@ -32,20 +32,19 @@ - - - - $([MSBuild]::NormalizePath($(OpenSimBuildPath)/libopensim.dylib)) - $([MSBuild]::NormalizePath($(OpenSimBuildPath)/libopensim.so)) - $([MSBuild]::NormalizePath($(OpenSimBuildPath)/opensim.dll)) - $(OpenSimDllMac) - $(OpenSimDllLinux) - $(OpenSimDllWindows) + + + $([MSBuild]::NormalizePath($(ExperimentalSimBuildPath)/libqdk_sim.dylib)) + $([MSBuild]::NormalizePath($(ExperimentalSimBuildPath)/libqdk_sim.so)) + $([MSBuild]::NormalizePath($(ExperimentalSimBuildPath)/qdk_sim.dll)) + $(ExperimentalSimDllMac) + $(ExperimentalSimDllLinux) + $(ExperimentalSimDllWindows) - - Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll + + Microsoft.Quantum.Experimental.Simulators.Runtime.dll PreserveNewest false diff --git a/src/Simulation/OpenSystems/.gitignore b/src/Simulation/OpenSystems/.gitignore deleted file mode 100644 index eb5a316cbd1..00000000000 --- a/src/Simulation/OpenSystems/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/src/Simulation/OpenSystems/build-open-simulator.ps1 b/src/Simulation/OpenSystems/build-open-simulator.ps1 deleted file mode 100644 index d24121846c4..00000000000 --- a/src/Simulation/OpenSystems/build-open-simulator.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -& (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); - -Push-Location (Join-Path $PSScriptRoot runtime) - $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - cargo build @releaseFlag; - - # Free disk space by cleaning up target/${config}/deps. - # Note that this takes longer, but saves ~1 GB of space, which is - # exceptionally helpful in CI builds. - @("release", "debug") | ForEach-Object { - $config = $_; - @("deps", "build", "incremental") | ForEach-Object { - Remove-Item -Recurse (Join-Path . target $config $_) -ErrorAction Continue -Verbose; - } - } -Pop-Location diff --git a/src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb b/src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb deleted file mode 100644 index 804cd28ccd4..00000000000 --- a/src/Simulation/OpenSystems/runtime/benches/data/.ipynb_checkpoints/noise-model-export-checkpoint.ipynb +++ /dev/null @@ -1,212 +0,0 @@ -{ - "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/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index 2a5af1d31de..5518ed3b705 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -44,21 +44,6 @@ PreserveNewest false - - runtimes\win-x64\native\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - - - runtimes\osx-x64\native\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - - - runtimes\linux-x64\native\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template index e4542923ab2..3c72204cd23 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec.template @@ -20,9 +20,6 @@ - - - diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 6aa4d1ca9bc..b1de8a6b239 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -31,7 +31,7 @@ private static void CheckCall(Int64 errorCode) } - public const string DLL_NAME = "Microsoft.Quantum.Experimental.OpenSystemsSimulator.Runtime.dll"; + 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(); diff --git a/src/Simulation/OpenSystems/runtime/.gitignore b/src/Simulation/qdk_sim_rs/.gitignore similarity index 69% rename from src/Simulation/OpenSystems/runtime/.gitignore rename to src/Simulation/qdk_sim_rs/.gitignore index 5ad4050fa10..8f7943dd70e 100644 --- a/src/Simulation/OpenSystems/runtime/.gitignore +++ b/src/Simulation/qdk_sim_rs/.gitignore @@ -1,3 +1,4 @@ linux osx win10 +target diff --git a/src/Simulation/OpenSystems/runtime/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock similarity index 99% rename from src/Simulation/OpenSystems/runtime/Cargo.lock rename to src/Simulation/qdk_sim_rs/Cargo.lock index 7f16445d303..442a273bdaa 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -376,25 +376,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opensim" -version = "0.1.0" -dependencies = [ - "assert-json-diff", - "criterion", - "derive_more", - "getrandom", - "itertools 0.9.0", - "lazy_static", - "ndarray", - "num-complex", - "num-traits", - "rand", - "serde", - "serde_json", - "web-sys", -] - [[package]] name = "plotters" version = "0.3.0" @@ -438,6 +419,25 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "qdk_sim" +version = "0.1.0" +dependencies = [ + "assert-json-diff", + "criterion", + "derive_more", + "getrandom", + "itertools 0.9.0", + "lazy_static", + "ndarray", + "num-complex", + "num-traits", + "rand", + "serde", + "serde_json", + "web-sys", +] + [[package]] name = "quote" version = "1.0.7" diff --git a/src/Simulation/OpenSystems/runtime/Cargo.toml b/src/Simulation/qdk_sim_rs/Cargo.toml similarity index 98% rename from src/Simulation/OpenSystems/runtime/Cargo.toml rename to src/Simulation/qdk_sim_rs/Cargo.toml index ee13245bdf2..0a7c237ea45 100644 --- a/src/Simulation/OpenSystems/runtime/Cargo.toml +++ b/src/Simulation/qdk_sim_rs/Cargo.toml @@ -2,13 +2,13 @@ # Licensed under the MIT License. [package] -name = "opensim" +name = "qdk_sim" version = "0.1.0" authors = ["Christopher Granade "] edition = "2018" [lib] -name = "opensim" +name = "qdk_sim" path = "src/lib.rs" crate-type = ["rlib", "staticlib", "cdylib"] diff --git a/src/Simulation/OpenSystems/runtime/README.md b/src/Simulation/qdk_sim_rs/README.md similarity index 100% rename from src/Simulation/OpenSystems/runtime/README.md rename to src/Simulation/qdk_sim_rs/README.md diff --git a/src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/benches/c_api_benchmark.rs rename to src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs diff --git a/src/Simulation/OpenSystems/runtime/benches/data/benchmark-noise-model.json b/src/Simulation/qdk_sim_rs/benches/data/benchmark-noise-model.json similarity index 100% rename from src/Simulation/OpenSystems/runtime/benches/data/benchmark-noise-model.json rename to src/Simulation/qdk_sim_rs/benches/data/benchmark-noise-model.json diff --git a/src/Simulation/OpenSystems/runtime/benches/data/noise-model-export.ipynb b/src/Simulation/qdk_sim_rs/benches/data/noise-model-export.ipynb similarity index 100% rename from src/Simulation/OpenSystems/runtime/benches/data/noise-model-export.ipynb rename to src/Simulation/qdk_sim_rs/benches/data/noise-model-export.ipynb diff --git a/src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/benches/microbenchmark.rs rename to src/Simulation/qdk_sim_rs/benches/microbenchmark.rs 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..0ff71c4c12d --- /dev/null +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -0,0 +1,13 @@ +& (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); + +$DropPath = Join-Path $Env:DROP_NATIVE "qdk_sim_rs"; + +Push-Location $PSScriptRoot + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + cargo +nightly build -Z unstable-options @releaseFlag --out-dir $DropPath; + + # Free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space, which is + # exceptionally helpful in CI builds. + cargo clean; +Pop-Location diff --git a/src/Simulation/OpenSystems/runtime/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/c_api.rs rename to src/Simulation/qdk_sim_rs/src/c_api.rs diff --git a/src/Simulation/OpenSystems/runtime/src/channels.rs b/src/Simulation/qdk_sim_rs/src/channels.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/channels.rs rename to src/Simulation/qdk_sim_rs/src/channels.rs diff --git a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs b/src/Simulation/qdk_sim_rs/src/common_matrices.rs similarity index 98% rename from src/Simulation/OpenSystems/runtime/src/common_matrices.rs rename to src/Simulation/qdk_sim_rs/src/common_matrices.rs index 5e53920d582..1b09199481a 100644 --- a/src/Simulation/OpenSystems/runtime/src/common_matrices.rs +++ b/src/Simulation/qdk_sim_rs/src/common_matrices.rs @@ -82,7 +82,7 @@ pub fn cnot() -> Array2 { /// The following are equivalent: /// ``` /// # #[macro_use] extern crate ndarray; -/// # use opensim::elementary_vec; +/// # use opensim::common_matrices::elementary_vec; /// let vec = elementary_vec::(2, 4); /// ``` /// and: @@ -109,7 +109,7 @@ pub fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (u /// The following snippet defines a two-qubit identity matrix: /// ``` /// # #[macro_use] extern crate ndarray; -/// # use opensim::nq_eye; +/// # use opensim::common_matrices::nq_eye; /// use num_complex::Complex; /// let eye = nq_eye(2usize); /// assert_eq!(eye, array![ diff --git a/src/Simulation/OpenSystems/runtime/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/instrument.rs rename to src/Simulation/qdk_sim_rs/src/instrument.rs diff --git a/src/Simulation/OpenSystems/runtime/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/lib.rs rename to src/Simulation/qdk_sim_rs/src/lib.rs diff --git a/src/Simulation/OpenSystems/runtime/src/linalg.rs b/src/Simulation/qdk_sim_rs/src/linalg.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/linalg.rs rename to src/Simulation/qdk_sim_rs/src/linalg.rs diff --git a/src/Simulation/OpenSystems/runtime/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/noise_model.rs rename to src/Simulation/qdk_sim_rs/src/noise_model.rs diff --git a/src/Simulation/OpenSystems/runtime/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/states.rs rename to src/Simulation/qdk_sim_rs/src/states.rs diff --git a/src/Simulation/OpenSystems/runtime/src/utils.rs b/src/Simulation/qdk_sim_rs/src/utils.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/src/utils.rs rename to src/Simulation/qdk_sim_rs/src/utils.rs diff --git a/src/Simulation/OpenSystems/test-open-simulator.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 similarity index 73% rename from src/Simulation/OpenSystems/test-open-simulator.ps1 rename to src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index e6499a098f8..acf49e7047b 100644 --- a/src/Simulation/OpenSystems/test-open-simulator.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -1,6 +1,6 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); -Push-Location (Join-Path $PSScriptRoot runtime) +Push-Location $PSScriptRoot # If running in CI, use cargo2junit to expose unit tests to the # PublishTestResults task. if ("$Env:TF_BUILD" -ne "") { @@ -20,19 +20,14 @@ Push-Location (Join-Path $PSScriptRoot runtime) # This step isn't required, but we use it to upload run summaries. $reportPath = (Join-Path "target" "criterion"); - $perfDest = (Join-Path $Env:DROPS_DIR "perf" "opensim"); + $perfDest = (Join-Path $Env:DROPS_DIR "perf" "qdk_sim_rs"); if (Get-Item -ErrorAction SilentlyContinue $reportPath) { New-Item -Type Directory -Force -Path $perfDest; Copy-Item -Recurse -Path $reportPath -Destination $perfDest; } - # Free disk space by cleaning up target/${config}/deps. + # Free disk space by cleaning up. # Note that this takes longer, but saves ~1 GB of space, which is # exceptionally helpful in CI builds. - @("release", "debug") | ForEach-Object { - $config = $_; - @("deps", "build", "incremental") | ForEach-Object { - Remove-Item -Recurse (Join-Path . target $config $_) -ErrorAction Continue -Verbose; - } - } + cargo clean Pop-Location diff --git a/src/Simulation/OpenSystems/runtime/testing.md b/src/Simulation/qdk_sim_rs/testing.md similarity index 100% rename from src/Simulation/OpenSystems/runtime/testing.md rename to src/Simulation/qdk_sim_rs/testing.md diff --git a/src/Simulation/OpenSystems/runtime/tests/data/ideal-noise-model.json b/src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json similarity index 100% rename from src/Simulation/OpenSystems/runtime/tests/data/ideal-noise-model.json rename to src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json diff --git a/src/Simulation/OpenSystems/runtime/tests/serialization_tests.rs b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs similarity index 100% rename from src/Simulation/OpenSystems/runtime/tests/serialization_tests.rs rename to src/Simulation/qdk_sim_rs/tests/serialization_tests.rs From d741380b79733b496f45fb423506dbc0add486b3 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 08:37:58 -0700 Subject: [PATCH 063/158] Work on testing and enforcing rust fmt. --- build/build.ps1 | 4 +- build/steps-init.yml | 6 ++ build/test.ps1 | 11 ++++ .../qdk_sim_rs/benches/c_api_benchmark.rs | 48 +++++++++----- .../qdk_sim_rs/benches/microbenchmark.rs | 5 +- .../qdk_sim_rs/src/common_matrices.rs | 57 +++++++---------- src/Simulation/qdk_sim_rs/src/lib.rs | 4 +- src/Simulation/qdk_sim_rs/src/noise_model.rs | 62 +++++++++---------- src/Simulation/qdk_sim_rs/src/states.rs | 40 ++++++------ src/Simulation/qdk_sim_rs/src/utils.rs | 2 +- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 4 ++ .../qdk_sim_rs/tests/serialization_tests.rs | 7 ++- 12 files changed, 137 insertions(+), 113 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index 285d8f12de4..7c1e1f953c2 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -28,12 +28,12 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if ($Env:ENABLE_EXPERIMENTALSIM -ne "false") { $expSim = (Join-Path $PSScriptRoot "../src/Simulation/qdk_sim_rs") - & "$openSimulator/build-qdk-sim-rs.ps1" + & "$expSim/build-qdk-sim-rs.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } } else { - Write-Host "Skipping build of native simulator because ENABLE_OPENSIM variable is set to: $Env:ENABLE_OPENSIM." + Write-Host "Skipping build of experimental simulators because ENABLE_OPENSIM variable is set to: $Env:ENABLE_OPENSIM." } function Build-One { diff --git a/build/steps-init.yml b/build/steps-init.yml index 28268bd5294..f39aeb43bb6 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -26,6 +26,12 @@ steps: displayName: Windows install rust condition: eq( variables['Agent.OS'], 'Windows_NT' ) +- script: | + rustup install nightly + rustup component add rustfmt + rustup component add rustfmt --toolchain nightly + displayName: Enable Rust formatting and nightly options. + ## # Custom pre-reqs ## diff --git a/build/test.ps1 b/build/test.ps1 index 974c2a61615..9109434192c 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -57,6 +57,17 @@ 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") { + $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/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs index 058a8dd9f84..94365fb3ee5 100644 --- a/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs +++ b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs @@ -5,28 +5,39 @@ //! its C API, so as to gauge any potential issues for C-based consumers of the //! simulator. -use std::ffi::CString; use criterion::{criterion_group, criterion_main, Criterion}; -use opensim::{ c_api }; +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; - c_api::m(sim_id, 0, &mut result); - })); +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; + c_api::m(sim_id, 0, &mut result); + }) + }); } fn ideal(c: &mut Criterion) { @@ -39,7 +50,10 @@ fn ideal(c: &mut Criterion) { fn noisy(c: &mut Criterion) { let sim_id = c_api::init(3); - c_api::set_noise_model(sim_id, CString::new(BENCHMARK_NOISE_MODEL_JSON).unwrap().as_ptr()); + 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(); diff --git a/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs index d63909cf264..c1df02146fa 100644 --- a/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs +++ b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs @@ -9,7 +9,10 @@ //! performance in user code. use criterion::{criterion_group, criterion_main, Criterion}; -use opensim::{common_matrices, Tensor, linalg::{extend_one_to_n, extend_two_to_n}, common_matrices::nq_eye}; +use qdk_sim::{ + 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"); diff --git a/src/Simulation/qdk_sim_rs/src/common_matrices.rs b/src/Simulation/qdk_sim_rs/src/common_matrices.rs index 1b09199481a..5b78c2372e5 100644 --- a/src/Simulation/qdk_sim_rs/src/common_matrices.rs +++ b/src/Simulation/qdk_sim_rs/src/common_matrices.rs @@ -7,49 +7,34 @@ use core::f64::consts::FRAC_1_SQRT_2; use std::convert::TryInto; -use ndarray::{ Array, Array1, Array2 }; -use num_traits::{ Zero, One }; +use ndarray::{Array, Array1, Array2}; +use num_traits::{One, Zero}; use crate::utils::*; /// Returns a copy of the single-qubit identity matrix. pub fn i() -> Array2 { - array![ - [ONE_C, ZERO_C], - [ZERO_C, ONE_C] - ] + array![[ONE_C, ZERO_C], [ZERO_C, ONE_C]] } /// Returns a copy of the Pauli 𝑋 operator. pub fn x() -> Array2 { - array![ - [ZERO_C, ONE_C], - [ONE_C, ZERO_C] - ] + array![[ZERO_C, ONE_C], [ONE_C, ZERO_C]] } /// Returns a copy of the Pauli 𝑌 operator. pub fn y() -> Array2 { - array![ - [ZERO_C, I_C], - [-I_C, ZERO_C] - ] + array![[ZERO_C, I_C], [-I_C, ZERO_C]] } /// Returns a copy of the Pauli 𝑍 operator. pub fn z() -> Array2 { - array![ - [ONE_C, ZERO_C], - [ZERO_C, -ONE_C] - ] + array![[ONE_C, ZERO_C], [ZERO_C, -ONE_C]] } /// Returns a copy of the single-qubit Hadamard transformation. pub fn h() -> Array2 { - array![ - [ONE_C, ONE_C], - [ONE_C, -ONE_C] - ] * FRAC_1_SQRT_2 + array![[ONE_C, ONE_C], [ONE_C, -ONE_C]] * FRAC_1_SQRT_2 } pub fn t() -> Array2 { @@ -60,10 +45,7 @@ pub fn t() -> Array2 { } pub fn s() -> Array2 { - array![ - [ONE_C, ZERO_C], - [ZERO_C, C64::new(0.0_f64, 1.0_f64)] - ] + array![[ONE_C, ZERO_C], [ZERO_C, C64::new(0.0_f64, 1.0_f64)]] } pub fn cnot() -> Array2 { @@ -82,7 +64,7 @@ pub fn cnot() -> Array2 { /// The following are equivalent: /// ``` /// # #[macro_use] extern crate ndarray; -/// # use opensim::common_matrices::elementary_vec; +/// # use qdk_sim::common_matrices::elementary_vec; /// let vec = elementary_vec::(2, 4); /// ``` /// and: @@ -91,25 +73,30 @@ pub fn cnot() -> Array2 { /// let vec = array![0i64, 0i64, 1i64, 0i64]; /// ``` pub fn elementary_vec(idx: usize, n: usize) -> Array1 { - Array::from_shape_fn(n, |i| if i == idx {T::one()} else {T::zero()}) + Array::from_shape_fn(n, |i| if i == idx { T::one() } else { T::zero() }) } -pub fn elementary_matrix((idx0, idx1): (usize, usize), (n, m): (usize, usize)) -> Array2 { - Array::from_shape_fn((n, m), |(i, j)| if i == idx0 && j == idx1 { - T::one() - } else { - T::zero() +pub fn elementary_matrix( + (idx0, idx1): (usize, usize), + (n, m): (usize, usize), +) -> Array2 { + Array::from_shape_fn((n, m), |(i, j)| { + if i == idx0 && j == idx1 { + T::one() + } else { + T::zero() + } }) } /// Returns an identity matrix that acts on state vectors of registers of /// qubits with a given size. -/// +/// /// # Example /// The following snippet defines a two-qubit identity matrix: /// ``` /// # #[macro_use] extern crate ndarray; -/// # use opensim::common_matrices::nq_eye; +/// # use qdk_sim::common_matrices::nq_eye; /// use num_complex::Complex; /// let eye = nq_eye(2usize); /// assert_eq!(eye, array![ diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 7d72807c63d..4f7490ba248 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -16,10 +16,10 @@ extern crate serde; use serde::{Deserialize, Serialize}; pub mod c_api; -pub mod common_matrices; -pub mod linalg; mod channels; +pub mod common_matrices; mod instrument; +pub mod linalg; mod noise_model; mod states; mod utils; diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index b27269fe6b9..813e2fdc8d2 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -1,14 +1,14 @@ -use num_traits::{ Zero, One }; -use crate::C64; -use crate::common_matrices; -use crate::states::StateData::Mixed; -use crate::states::State; use crate::channels::Channel; -use crate::channels::ChannelData::{ Unitary, KrausDecomposition }; +use crate::channels::ChannelData::{KrausDecomposition, Unitary}; +use crate::common_matrices; use crate::instrument::Instrument; use crate::linalg::HasDagger; +use crate::states::State; +use crate::states::StateData::Mixed; +use crate::C64; +use num_traits::{One, Zero}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct NoiseModel { @@ -23,81 +23,77 @@ pub struct NoiseModel { pub t: Channel, pub t_adj: Channel, pub cnot: Channel, - pub z_meas: Instrument + pub z_meas: Instrument, } impl NoiseModel { pub fn ideal() -> NoiseModel { let i = Channel { n_qubits: 1, - data: Unitary(common_matrices::i()) + data: Unitary(common_matrices::i()), }; let z = Channel { n_qubits: 1, - data: Unitary(common_matrices::z()) + data: Unitary(common_matrices::z()), }; let z_meas = Instrument { effects: vec![ Channel { n_qubits: 1, - data: KrausDecomposition(array![ - [ - [C64::one(), C64::zero()], - [C64::zero(), C64::zero()] - ] - ]) + data: KrausDecomposition(array![[ + [C64::one(), C64::zero()], + [C64::zero(), C64::zero()] + ]]), }, Channel { n_qubits: 1, - data: KrausDecomposition(array![ - [ - [C64::zero(), C64::zero()], - [C64::zero(), C64::one()] - ] - ]) + data: KrausDecomposition(array![[ + [C64::zero(), C64::zero()], + [C64::zero(), C64::one()] + ]]), }, - ] + ], }; NoiseModel { initial_state: State { n_qubits: 1, - data: Mixed((common_matrices::i() + common_matrices::z()) / 2.0) + data: Mixed((common_matrices::i() + common_matrices::z()) / 2.0), }, i: i, x: Channel { n_qubits: 1, - data: Unitary(common_matrices::x()) + data: Unitary(common_matrices::x()), }, y: Channel { n_qubits: 1, - data: Unitary(common_matrices::y()) + data: Unitary(common_matrices::y()), }, z: z, h: Channel { n_qubits: 1, - data: Unitary(common_matrices::h()) + data: Unitary(common_matrices::h()), }, t: Channel { n_qubits: 1, - data: Unitary(common_matrices::t()) + data: Unitary(common_matrices::t()), }, t_adj: Channel { n_qubits: 1, - data: Unitary(common_matrices::t().dag()) + data: Unitary(common_matrices::t().dag()), }, s: Channel { n_qubits: 1, - data: Unitary(common_matrices::s()) + data: Unitary(common_matrices::s()), }, s_adj: Channel { n_qubits: 1, - data: Unitary(common_matrices::s().dag()) + data: Unitary(common_matrices::s().dag()), }, cnot: Channel { n_qubits: 2, - data: Unitary(common_matrices::cnot()) + data: Unitary(common_matrices::cnot()), }, - z_meas: z_meas + z_meas: z_meas, } } } diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 9a3a2160926..6eecadb33e7 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -2,22 +2,22 @@ // Licensed under the MIT License. use crate::common_matrices; -use core::fmt::Display; -use num_traits::One; +use crate::linalg::Tensor; +use crate::linalg::Trace; use crate::states::StateData::Mixed; use crate::states::StateData::Pure; use crate::QubitSized; -use crate::linalg::Trace; use crate::C64; +use core::fmt::Display; use ndarray::{Array1, Array2, Axis}; +use num_traits::One; +use serde::{Deserialize, Serialize}; use std::convert::TryInto; -use crate::linalg::{ Tensor }; -use serde::{ Serialize, Deserialize }; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum StateData { Pure(Array1), - Mixed(Array2) + Mixed(Array2), } pub type State = QubitSized; @@ -30,7 +30,7 @@ impl Display for State { self.n_qubits, match self.data { Pure(_) => "state vector", - Mixed(_) => "density operator" + Mixed(_) => "density operator", }, self.data ) @@ -41,14 +41,11 @@ impl Display for StateData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { match self { Pure(psi) => write!(f, "{}", psi), - Mixed(rho) => write!(f, "{}", rho) + Mixed(rho) => write!(f, "{}", rho), } } } - - - impl State { pub fn extend(self: &Self, n_qubits: usize) -> State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); @@ -56,8 +53,11 @@ impl State { n_qubits: self.n_qubits + n_qubits, data: match &self.data { Pure(psi) => Pure(psi.tensor(&common_matrices::elementary_vec(0, new_dim))), - Mixed(rho) => Mixed(rho.tensor(&common_matrices::elementary_matrix((0, 0), (new_dim, new_dim)))) - } + Mixed(rho) => Mixed(rho.tensor(&common_matrices::elementary_matrix( + (0, 0), + (new_dim, new_dim), + ))), + }, } } @@ -65,7 +65,10 @@ impl State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); State { n_qubits: n_qubits, - data: Mixed(common_matrices::elementary_matrix((0, 0), (new_dim, new_dim))) + data: Mixed(common_matrices::elementary_matrix( + (0, 0), + (new_dim, new_dim), + )), } } @@ -83,8 +86,8 @@ impl State { // improvement for the HasDagger trait itself. let psi = psi.view().insert_axis(Axis(1)); psi.t().map(|e| e.conj()) * psi - }) - } + }), + }, } } } @@ -95,12 +98,11 @@ impl Trace for &State { fn trace(self) -> Self::Output { match &self.data { Pure(_) => C64::one(), - Mixed(ref rho) => (&rho).trace() + Mixed(ref rho) => (&rho).trace(), } } } - #[cfg(test)] mod tests { use super::*; @@ -109,7 +111,7 @@ mod tests { fn trace_pure_is_one() { let pure = State { n_qubits: 1usize, - data: Pure(common_matrices::elementary_vec(0, 2)) + data: Pure(common_matrices::elementary_vec(0, 2)), }; assert_eq!(pure.trace(), C64::one()); } diff --git a/src/Simulation/qdk_sim_rs/src/utils.rs b/src/Simulation/qdk_sim_rs/src/utils.rs index 3a3c86be06f..cc259140808 100644 --- a/src/Simulation/qdk_sim_rs/src/utils.rs +++ b/src/Simulation/qdk_sim_rs/src/utils.rs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use num_traits::Num; use num_complex::Complex; +use num_traits::Num; pub type C64 = Complex; diff --git a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index acf49e7047b..8ecc4f30b8d 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -1,6 +1,10 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); Push-Location $PSScriptRoot + # Start with the quick check first and make sure that Rust sources + # meet formatting and style guide rules. + cargo fmt -- --check + # If running in CI, use cargo2junit to expose unit tests to the # PublishTestResults task. if ("$Env:TF_BUILD" -ne "") { diff --git a/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs index 6ea04a05132..6253b47f2c5 100644 --- a/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs +++ b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs @@ -1,6 +1,6 @@ -use serde_json::Value; -use opensim::{ NoiseModel }; use assert_json_diff::assert_json_eq; +use qdk_sim::NoiseModel; +use serde_json::Value; // Use include_str! to store test case JSON as a string into the compiled // test executable. @@ -9,7 +9,8 @@ static IDEAL_NOISE_MODEL_JSON: &str = include_str!("data/ideal-noise-model.json" #[test] fn ideal_noise_model_serializes_correctly() { let noise_model = NoiseModel::ideal(); - let expected: Value = serde_json::from_str(&*(serde_json::to_string(&noise_model).unwrap())).unwrap(); + let expected: Value = + serde_json::from_str(&*(serde_json::to_string(&noise_model).unwrap())).unwrap(); assert_json_eq!(noise_model, expected); } From 22132e09a5be4ed0421c2fe3eb2c1086339b8cb8 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 09:17:09 -0700 Subject: [PATCH 064/158] Run clippy in tests as well. --- build/steps-init.yml | 4 +- src/Simulation/qdk_sim_rs/src/c_api.rs | 71 +++++++++++--------- src/Simulation/qdk_sim_rs/src/channels.rs | 14 ++-- src/Simulation/qdk_sim_rs/src/instrument.rs | 2 +- src/Simulation/qdk_sim_rs/src/linalg.rs | 22 +++--- src/Simulation/qdk_sim_rs/src/noise_model.rs | 6 +- src/Simulation/qdk_sim_rs/src/states.rs | 6 +- src/Simulation/qdk_sim_rs/src/utils.rs | 11 +-- 8 files changed, 68 insertions(+), 68 deletions(-) diff --git a/build/steps-init.yml b/build/steps-init.yml index f39aeb43bb6..42d9df112aa 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -28,8 +28,8 @@ steps: - script: | rustup install nightly - rustup component add rustfmt - rustup component add rustfmt --toolchain nightly + rustup component add rustfmt clippy + rustup component add rustfmt clippy --toolchain nightly displayName: Enable Rust formatting and nightly options. ## diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index be5c4c5033f..8cbb83b457d 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -50,7 +50,7 @@ fn apply &Channel>( Err(err) => Err(err), } } else { - return Err(format!("No simulator with id {}.", sim_id).to_string()); + return Err(format!("No simulator with id {}.", sim_id)); } } @@ -58,12 +58,7 @@ fn apply &Channel>( #[no_mangle] pub extern "C" fn get_name() -> *const c_char { - // There's a whole dance we have to do in order to allow the memory - // allocated for a string to be deallocated on the .NET side. - - let serialized = CString::new("open_sim").unwrap().into_raw(); - std::mem::forget(serialized); - serialized + CString::new("open_sim").unwrap().into_raw() } #[no_mangle] @@ -71,9 +66,7 @@ pub extern "C" fn lasterr() -> *const c_char { match &*LAST_ERROR.lock().unwrap() { None => ptr::null(), Some(msg) => { - let wrapped_msg = CString::new(msg.as_str()).unwrap().into_raw(); - std::mem::forget(wrapped_msg); - wrapped_msg + CString::new(msg.as_str()).unwrap().into_raw() } } } @@ -100,7 +93,7 @@ pub extern "C" fn destroy(sim_id: usize) -> i64 { state.remove(&sim_id); Ok(()) } else { - Err(format!("No simulator with id {} exists.", sim_id).to_string()) + Err(format!("No simulator with id {} exists.", sim_id)) } }) } @@ -152,20 +145,25 @@ pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { })) } +/// Measures a single qubit in the $Z$-basis, returning the result by setting +/// the value at a given pointer. +/// +/// # Safety +/// This function is marked as unsafe as it is the caller's responsibility to +/// ensure that `result_out` is a valid pointer, and that the memory referenced +/// by `result_out` can be safely set. #[no_mangle] -pub extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { +pub unsafe extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { as_capi_err({ let state = &mut *STATE.lock().unwrap(); if let Some(sim_state) = state.get_mut(&sim_id) { let instrument = &sim_state.noise_model.z_meas; let (result, new_state) = instrument.sample(&[idx], &sim_state.register_state); sim_state.register_state = new_state; - unsafe { - *result_out = result; - }; + *result_out = result; Ok(()) } else { - Err(format!("No simulator with id {} exists.", sim_id).to_string()) + Err(format!("No simulator with id {} exists.", sim_id)) } }) } @@ -174,17 +172,13 @@ pub extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { pub extern "C" fn get_noise_model(sim_id: usize) -> *const c_char { let state = &*STATE.lock().unwrap(); if let Some(sim_state) = state.get(&sim_id) { - let serialized = CString::new( + CString::new( serde_json::to_string(&sim_state.noise_model) .unwrap() .as_str(), ) .unwrap() - .into_raw(); - // Need to forget that we hold this reference so that it doesn't - // get released after returning to C. - std::mem::forget(serialized); - serialized + .into_raw() } else { ptr::null() } @@ -194,24 +188,35 @@ pub extern "C" fn get_noise_model(sim_id: usize) -> *const c_char { /// that agrees with closed-system simulation). #[no_mangle] pub extern "C" fn ideal_noise_model() -> *const c_char { - let serialized = CString::new( + CString::new( serde_json::to_string(&NoiseModel::ideal()) .unwrap() .as_str(), ) .unwrap() - .into_raw(); - std::mem::forget(serialized); - serialized + .into_raw() } +/// Sets the noise model used by a given simulator instance, given a string +/// containing a JSON serialization of that noise model. +/// +/// # Safety +/// This function is marked as unsafe as the caller is responsible for ensuring +/// that `new_model`: +/// +/// - Is a valid pointer to a null-terminated array of C +/// characters. +/// - The pointer remains valid for at least the duration +/// of the call. +/// - No other thread may modify the memory referenced by `new_model` for at +/// least the duration of the call. #[no_mangle] -pub extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { +pub unsafe extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { if new_model.is_null() { return as_capi_err(Err("set_noise_model called with null pointer".to_string())); } - let c_str = unsafe { CStr::from_ptr(new_model) }; + let c_str = CStr::from_ptr(new_model); as_capi_err(match c_str.to_str() { Ok(serialized_noise_model) => match serde_json::from_str(serialized_noise_model) { @@ -221,7 +226,7 @@ pub extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i6 sim_state.noise_model = noise_model; Ok(()) } else { - Err(format!("No simulator with id {} exists.", sim_id).to_string()) + Err(format!("No simulator with id {} exists.", sim_id)) } } Err(serialization_error) => Err(format!( @@ -247,15 +252,15 @@ pub extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i6 pub extern "C" fn get_current_state(sim_id: usize) -> *const c_char { let state = &mut *STATE.lock().unwrap(); if let Some(sim_state) = state.get_mut(&sim_id) { - let serialized = CString::new( + CString::new( serde_json::to_string(&sim_state.register_state) .unwrap() .as_str(), ) .unwrap() - .into_raw(); - std::mem::forget(serialized); - serialized + // NB: into_raw implies transferring ownership to the C caller, + // and hence moves its self. + .into_raw() } else { ptr::null() } diff --git a/src/Simulation/qdk_sim_rs/src/channels.rs b/src/Simulation/qdk_sim_rs/src/channels.rs index 04d3c4c82e4..96a46d1bd84 100644 --- a/src/Simulation/qdk_sim_rs/src/channels.rs +++ b/src/Simulation/qdk_sim_rs/src/channels.rs @@ -29,7 +29,7 @@ pub enum ChannelData { impl Channel { // TODO: methods to forcibly convert representations. - pub fn apply(self: &Self, state: &State) -> Result { + pub fn apply(&self, state: &State) -> Result { if state.n_qubits == self.n_qubits { Ok(State { n_qubits: self.n_qubits, @@ -71,7 +71,7 @@ impl Channel { } } - pub fn apply_to(self: &Self, idx_qubits: &[usize], state: &State) -> Result { + pub fn apply_to(&self, idx_qubits: &[usize], state: &State) -> Result { // Fail if there's not enough qubits. if state.n_qubits < self.n_qubits { return log_as_err(format!( @@ -81,7 +81,7 @@ impl Channel { } // Fail if any indices are repeated. - if idx_qubits.into_iter().unique().count() < idx_qubits.len() { + if idx_qubits.iter().unique().count() < idx_qubits.len() { return log_as_err(format!( "List of qubit indices {:?} contained repeated elements.", idx_qubits @@ -131,10 +131,10 @@ impl Channel { } } - pub fn extend_one_to_n(self: &Self, idx_qubit: usize, n_qubits: usize) -> Channel { + pub fn extend_one_to_n(&self, idx_qubit: usize, n_qubits: usize) -> Channel { assert_eq!(self.n_qubits, 1); Channel { - n_qubits: n_qubits, + n_qubits, data: match &self.data { Unitary(u) => Unitary(extend_one_to_n(u.view(), idx_qubit, n_qubits)), KrausDecomposition(ks) => { @@ -153,14 +153,14 @@ impl Channel { } pub fn extend_two_to_n( - self: &Self, + &self, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize, ) -> Channel { assert_eq!(self.n_qubits, 2); Channel { - n_qubits: n_qubits, + n_qubits, data: match &self.data { Unitary(u) => Unitary(extend_two_to_n(u.view(), idx_qubit1, idx_qubit2, n_qubits)), KrausDecomposition(ks) => { diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 8b3f2a2d865..b311f78c1d8 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -38,7 +38,7 @@ impl Instrument { for (idx, cum_pr) in possible_outcomes .iter() .scan(0.0f64, |acc, (_idx, _, pr)| { - *acc = *acc + *pr; + *acc += *pr; Some(*acc) }) .enumerate() diff --git a/src/Simulation/qdk_sim_rs/src/linalg.rs b/src/Simulation/qdk_sim_rs/src/linalg.rs index 42c895a7480..e9e63ffd592 100644 --- a/src/Simulation/qdk_sim_rs/src/linalg.rs +++ b/src/Simulation/qdk_sim_rs/src/linalg.rs @@ -21,13 +21,13 @@ pub trait HasDagger { /// /// For most types implementing this trait, the hermitian conjugate /// is represented by the conjugate transpose. - fn dag(self: &Self) -> Self::Output; + fn dag(&self) -> Self::Output; } impl HasDagger for Array2 { type Output = Self; - fn dag(self: &Self) -> Self { + fn dag(&self) -> Self { self.t().map(|element| element.conj()) } } @@ -35,17 +35,17 @@ impl HasDagger for Array2 { impl HasDagger for ArrayView2<'_, C64> { type Output = Array2; - fn dag(self: &Self) -> Self::Output { + fn dag(&self) -> Self::Output { self.t().map(|element| element.conj()) } } pub trait ConjBy { - fn conjugate_by(self: &Self, op: &ArrayView2) -> Self; + fn conjugate_by(&self, op: &ArrayView2) -> Self; } impl ConjBy for Array2 { - fn conjugate_by(self: &Self, op: &ArrayView2) -> Self { + fn conjugate_by(&self, op: &ArrayView2) -> Self { op.dot(self).dot(&op.dag()) } } @@ -61,7 +61,7 @@ pub trait Tensor { /// ``` /// // TODO /// ``` - fn tensor(self: Self, rhs: Rhs) -> Self::Output; + fn tensor(self, rhs: Rhs) -> Self::Output; } impl, T: Copy + Mul> Tensor for ArrayView1<'_, T> { @@ -80,7 +80,7 @@ impl, T: Copy + Mul> Tensor for ArrayView1< impl, T: Copy + Mul> Tensor for &Array1 { type Output = Array1; - fn tensor(self: Self, other: Other) -> Self::Output { + fn tensor(self, other: Other) -> Self::Output { let other: Self = other.into(); self.view().tensor(other).to_owned() } @@ -111,7 +111,7 @@ impl, T: Copy + Mul> Tensor for ArrayView2< impl, T: Copy + Mul> Tensor for &Array2 { type Output = Array2; - fn tensor(self: Self, other: Other) -> Self::Output { + fn tensor(self, other: Other) -> Self::Output { let other: Self = other.into(); self.view().tensor(other).to_owned() } @@ -153,8 +153,8 @@ impl Trace for &Array2 { // from microbenchmarks on tensor and nq_eye directly. /// Given an array representing an operator acting on single-qubit states, /// returns a new operator that acts on $n$-qubit states. -pub fn extend_one_to_n<'a>( - data: ArrayView2<'a, C64>, +pub fn extend_one_to_n( + data: ArrayView2, idx_qubit: usize, n_qubits: usize, ) -> Array2 { @@ -230,7 +230,7 @@ pub fn permute_mtx(data: &Array2, new_order: &[usize]) -> Array2 { .iter() .cycle() .take(2 * n_qubits) - .map(|x| x.clone()) + .copied() .collect(); // FIXME: make this a result and propagate the result out to the return. let tensor = data.clone().into_shared().reshape(new_dims); diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 813e2fdc8d2..17be83812a9 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -59,7 +59,7 @@ impl NoiseModel { n_qubits: 1, data: Mixed((common_matrices::i() + common_matrices::z()) / 2.0), }, - i: i, + i, x: Channel { n_qubits: 1, data: Unitary(common_matrices::x()), @@ -68,7 +68,7 @@ impl NoiseModel { n_qubits: 1, data: Unitary(common_matrices::y()), }, - z: z, + z, h: Channel { n_qubits: 1, data: Unitary(common_matrices::h()), @@ -93,7 +93,7 @@ impl NoiseModel { n_qubits: 2, data: Unitary(common_matrices::cnot()), }, - z_meas: z_meas, + z_meas, } } } diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 6eecadb33e7..65dbc633814 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -47,7 +47,7 @@ impl Display for StateData { } impl State { - pub fn extend(self: &Self, n_qubits: usize) -> State { + pub fn extend(&self, n_qubits: usize) -> State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); State { n_qubits: self.n_qubits + n_qubits, @@ -64,7 +64,7 @@ impl State { pub fn new_mixed(n_qubits: usize) -> State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); State { - n_qubits: n_qubits, + n_qubits, data: Mixed(common_matrices::elementary_matrix( (0, 0), (new_dim, new_dim), @@ -73,7 +73,7 @@ impl State { } /// Returns a copy of this state, represented as a mixed state. - pub fn to_mixed(self: &Self) -> State { + pub fn to_mixed(&self) -> State { State { n_qubits: self.n_qubits, data: match &self.data { diff --git a/src/Simulation/qdk_sim_rs/src/utils.rs b/src/Simulation/qdk_sim_rs/src/utils.rs index cc259140808..55aeeace446 100644 --- a/src/Simulation/qdk_sim_rs/src/utils.rs +++ b/src/Simulation/qdk_sim_rs/src/utils.rs @@ -2,7 +2,6 @@ // Licensed under the MIT License. use num_complex::Complex; -use num_traits::Num; pub type C64 = Complex; @@ -10,21 +9,17 @@ pub const ONE_C: C64 = Complex::new(1f64, 0f64); pub const ZERO_C: C64 = Complex::new(0f64, 0f64); pub const I_C: C64 = Complex::new(0f64, 1f64); -fn c(re: T, im: T) -> Complex { - Complex::new(re, im) -} - #[cfg(feature = "web-sys-log")] -pub fn log_message(msg: &String) { +pub fn log_message(msg: &str) { web_sys::console::log_1(&msg.into()); } #[cfg(not(feature = "web-sys-log"))] -pub fn log_message(msg: &String) { +pub fn log_message(msg: &str) { println!("{}", msg); } pub fn log_as_err(msg: String) -> Result { - log_message(&msg); + log_message(&msg.as_str()); Err(msg) } From 4109e1181b77e097befc7792201b8733552e20fc Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 11:41:34 -0700 Subject: [PATCH 065/158] Add code quality checks to build/test. --- .../{testing.md => CONTRIBUTING.md} | 10 +- src/Simulation/qdk_sim_rs/Cargo.lock | 1 + src/Simulation/qdk_sim_rs/Cargo.toml | 1 + src/Simulation/qdk_sim_rs/README.md | 2 +- .../qdk_sim_rs/benches/c_api_benchmark.rs | 16 ++- .../qdk_sim_rs/benches/microbenchmark.rs | 1 + src/Simulation/qdk_sim_rs/src/c_api.rs | 47 ++++++- .../qdk_sim_rs/src/common_matrices.rs | 13 +- src/Simulation/qdk_sim_rs/src/instrument.rs | 6 +- src/Simulation/qdk_sim_rs/src/lib.rs | 17 ++- src/Simulation/qdk_sim_rs/src/linalg.rs | 7 + src/Simulation/qdk_sim_rs/src/noise_model.rs | 86 ++++++++---- .../src/{channels.rs => processes.rs} | 131 +++++++++++------- src/Simulation/qdk_sim_rs/src/states.rs | 15 ++ src/Simulation/qdk_sim_rs/src/utils.rs | 22 ++- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 6 + 16 files changed, 285 insertions(+), 96 deletions(-) rename src/Simulation/qdk_sim_rs/{testing.md => CONTRIBUTING.md} (75%) rename src/Simulation/qdk_sim_rs/src/{channels.rs => processes.rs} (77%) diff --git a/src/Simulation/qdk_sim_rs/testing.md b/src/Simulation/qdk_sim_rs/CONTRIBUTING.md similarity index 75% rename from src/Simulation/qdk_sim_rs/testing.md rename to src/Simulation/qdk_sim_rs/CONTRIBUTING.md index 9782d2409e8..ad3a58f9d5e 100644 --- a/src/Simulation/qdk_sim_rs/testing.md +++ b/src/Simulation/qdk_sim_rs/CONTRIBUTING.md @@ -1,4 +1,12 @@ -# Testing strategy +## 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: diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index 442a273bdaa..beb79824984 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -424,6 +424,7 @@ name = "qdk_sim" version = "0.1.0" dependencies = [ "assert-json-diff", + "cfg-if 1.0.0", "criterion", "derive_more", "getrandom", diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml b/src/Simulation/qdk_sim_rs/Cargo.toml index 0a7c237ea45..8c9ce2c5b79 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml +++ b/src/Simulation/qdk_sim_rs/Cargo.toml @@ -49,6 +49,7 @@ rand = "0.7.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" lazy_static = "1.4.0" +cfg-if = "1.0.0" # 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 diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index 63901bf6222..b984bd097af 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -16,7 +16,7 @@ The [`c_api`] module allows for using the simulation functionality in this crate This crate provides several different data structures for representing open quantum systems in a variety of different conventions: - [`State`]\: Represents pure or mixed states of a register of qubits. -- [`Channel`]\: Represents processes that map states to states. +- [`Process`]\: Represents processes that map states to states. - [`Instrument`]\: Represents quantum instruments, the most general form of measurement. ## Noise model serialization diff --git a/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs index 94365fb3ee5..f0b79f576fd 100644 --- a/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs +++ b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs @@ -35,7 +35,10 @@ fn with_test_suite( group.bench_function("measure", |b| { b.iter(|| { let mut result: usize = 0; - c_api::m(sim_id, 0, &mut result); + // NB: The C API is not in general safe. + unsafe { + c_api::m(sim_id, 0, &mut result); + } }) }); } @@ -50,10 +53,13 @@ fn ideal(c: &mut Criterion) { fn noisy(c: &mut Criterion) { let sim_id = c_api::init(3); - c_api::set_noise_model( - sim_id, - CString::new(BENCHMARK_NOISE_MODEL_JSON).unwrap().as_ptr(), - ); + // 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(); diff --git a/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs index c1df02146fa..89ccd07b809 100644 --- a/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs +++ b/src/Simulation/qdk_sim_rs/benches/microbenchmark.rs @@ -10,6 +10,7 @@ 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}, }; diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 8cbb83b457d..7001da7786a 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -1,7 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::{Channel, NoiseModel, State}; +//! 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. + +use crate::{Process, NoiseModel, State}; use lazy_static::lazy_static; use std::collections::HashMap; use std::ffi::CStr; @@ -34,7 +45,7 @@ fn as_capi_err(result: Result<(), String>) -> i64 { } } -fn apply &Channel>( +fn apply &Process>( sim_id: usize, idxs: &[usize], channel_fn: F, @@ -56,11 +67,17 @@ fn apply &Channel>( // C API FUNCTIONS // +// TODO: deprecate and replace with something that returns a JSON object +// with current version of the crate, target, etc. See the `built` crate. +/// Returns the name of this library; useful for debugging in the host +/// language. #[no_mangle] pub extern "C" fn get_name() -> *const c_char { CString::new("open_sim").unwrap().into_raw() } +/// Returns the last error message raised by a call to a C function. +/// If no error message has been raised, returns a null pointer. #[no_mangle] pub extern "C" fn lasterr() -> *const c_char { match &*LAST_ERROR.lock().unwrap() { @@ -71,6 +88,9 @@ pub extern "C" fn lasterr() -> *const c_char { } } +/// Allocate a new simulator with a given capacity, measured in the number of +/// qubits supported by that simulator. Returns an id that can be used to refer +/// to the new simulator in future function calls. #[no_mangle] pub extern "C" fn init(initial_capacity: usize) -> usize { let state = &mut *STATE.lock().unwrap(); @@ -85,6 +105,8 @@ pub extern "C" fn init(initial_capacity: usize) -> usize { id } +/// Deallocates the simulator with the given id, releasing any resources owned +/// by that simulator. #[no_mangle] pub extern "C" fn destroy(sim_id: usize) -> i64 { as_capi_err({ @@ -98,46 +120,66 @@ pub extern "C" fn destroy(sim_id: usize) -> i64 { }) } +// TODO[code quality]: refactor the following several functions into a macro. + +/// Applies the `X` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub extern "C" fn x(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.x)) } +/// Applies the `Y` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub extern "C" fn y(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.y)) } +/// Applies the `Z` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub extern "C" fn z(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.z)) } +/// Applies the `H` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub extern "C" fn h(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.h)) } +/// Applies the `S` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub fn s(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.s)) } +/// Applies the `Adjoint S` operation acting on a given qubit to a given +/// simulator, using the currently set noise model. #[no_mangle] pub fn s_adj(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.s_adj)) } +/// Applies the `T` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub fn t(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.t)) } +/// Applies the `Adjoint T` operation acting on a given qubit to a given simulator, +/// using the currently set noise model. #[no_mangle] pub fn t_adj(sim_id: usize, idx: usize) -> i64 { as_capi_err(apply(sim_id, &[idx], |model| &model.t_adj)) } +/// Applies the `CNOT` operation acting on two given qubits to a given simulator, +/// using the currently set noise model. #[no_mangle] pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { as_capi_err(apply(sim_id, &[idx_control, idx_target], |model| { @@ -248,6 +290,7 @@ pub unsafe extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char }) } +/// Returns the state of a given simulator, serialized as a JSON object. #[no_mangle] pub extern "C" fn get_current_state(sim_id: usize) -> *const c_char { let state = &mut *STATE.lock().unwrap(); diff --git a/src/Simulation/qdk_sim_rs/src/common_matrices.rs b/src/Simulation/qdk_sim_rs/src/common_matrices.rs index 5b78c2372e5..4247b1d6234 100644 --- a/src/Simulation/qdk_sim_rs/src/common_matrices.rs +++ b/src/Simulation/qdk_sim_rs/src/common_matrices.rs @@ -17,26 +17,27 @@ pub fn i() -> Array2 { array![[ONE_C, ZERO_C], [ZERO_C, ONE_C]] } -/// Returns a copy of the Pauli 𝑋 operator. +/// Returns a unitary matrix representing the `X` operation. pub fn x() -> Array2 { array![[ZERO_C, ONE_C], [ONE_C, ZERO_C]] } -/// Returns a copy of the Pauli 𝑌 operator. +/// Returns a unitary matrix representing the `Y` operation. pub fn y() -> Array2 { array![[ZERO_C, I_C], [-I_C, ZERO_C]] } -/// Returns a copy of the Pauli 𝑍 operator. +/// Returns a unitary matrix representing the `Z` operation. pub fn z() -> Array2 { array![[ONE_C, ZERO_C], [ZERO_C, -ONE_C]] } -/// Returns a copy of the single-qubit Hadamard transformation. +/// Returns a unitary matrix representing the single-qubit Hadamard transformation. pub fn h() -> Array2 { array![[ONE_C, ONE_C], [ONE_C, -ONE_C]] * FRAC_1_SQRT_2 } +/// Returns a unitary matrix representing the `T` operation. pub fn t() -> Array2 { array![ [ONE_C, ZERO_C], @@ -44,10 +45,12 @@ pub fn t() -> Array2 { ] } +/// Returns a unitary matrix representing the `S` operation. pub fn s() -> Array2 { array![[ONE_C, ZERO_C], [ZERO_C, C64::new(0.0_f64, 1.0_f64)]] } +/// Returns a unitary matrix representing the `CNOT` operation. pub fn cnot() -> Array2 { array![ [ONE_C, ZERO_C, ZERO_C, ZERO_C], @@ -76,6 +79,8 @@ pub fn elementary_vec(idx: usize, n: usize) -> Array1 { Array::from_shape_fn(n, |i| if i == idx { T::one() } else { T::zero() }) } +/// Returns an elementary matrix; that is, a matrix with a one at a given index +/// and zeros everywhere else. pub fn elementary_matrix( (idx0, idx1): (usize, usize), (n, m): (usize, usize), diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index b311f78c1d8..956ca393e3b 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use crate::states::StateData::Mixed; -use crate::Channel; +use crate::Process; use rand::Rng; use std::iter::Iterator; @@ -18,10 +18,12 @@ use serde::{Deserialize, Serialize}; pub struct Instrument { /// The effects of the instrument, represented as completely positive /// trace non-increasing (CPTNI) processes. - pub effects: Vec, + pub effects: Vec, } impl Instrument { + /// Samples from this instrument, returning the measurement result and + /// the new state of the system conditioned on that measurement result. pub fn sample(&self, idx_qubits: &[usize], state: &State) -> (usize, State) { let mut possible_outcomes = self .effects diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 4f7490ba248..9ef82c570c4 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -7,16 +7,20 @@ // for discussion. #![cfg_attr(feature = "doc", feature(extended_key_value_attributes))] #![cfg_attr(feature = "doc", cfg_attr(feature = "doc", doc = include_str!("../README.md")))] +#![cfg_attr(feature = "doc", deny(missing_docs))] +#![cfg_attr(feature = "doc", warn(missing_doc_code_examples))] #[macro_use(array, s)] extern crate ndarray; extern crate derive_more; extern crate serde; +use std::usize; + use serde::{Deserialize, Serialize}; pub mod c_api; -mod channels; +mod processes; pub mod common_matrices; mod instrument; pub mod linalg; @@ -24,14 +28,23 @@ mod noise_model; mod states; mod utils; -pub use crate::channels::*; +pub use crate::processes::*; pub use crate::instrument::*; pub use crate::noise_model::NoiseModel; pub use crate::states::State; pub use crate::utils::*; +/// Represents that a given type has a size that can be measured in terms +/// of a number of qubits (e.g.: [`State`]). #[derive(Clone, Debug, Serialize, Deserialize)] pub struct QubitSized { n_qubits: usize, data: T, } + +impl QubitSized { + /// Returns the number of qubits that this value relates to. + pub fn get_n_qubits(&self) -> usize { + self.n_qubits + } +} diff --git a/src/Simulation/qdk_sim_rs/src/linalg.rs b/src/Simulation/qdk_sim_rs/src/linalg.rs index e9e63ffd592..4205bf36c3e 100644 --- a/src/Simulation/qdk_sim_rs/src/linalg.rs +++ b/src/Simulation/qdk_sim_rs/src/linalg.rs @@ -40,7 +40,10 @@ impl HasDagger for ArrayView2<'_, C64> { } } +/// Represent types that can be conjugated by 2-dimensional arrays; that is, +/// as $UXU^{\dagger}$. pub trait ConjBy { + /// Conjugates this value by a given matrix, returning a copy. fn conjugate_by(&self, op: &ArrayView2) -> Self; } @@ -177,6 +180,8 @@ pub fn extend_one_to_n( } } +/// Given a view of an array representing a matrix acting on two-qubit states, +/// extends that array to act on $n$ qubits. pub fn extend_two_to_n( data: ArrayView2, idx_qubit1: usize, @@ -242,6 +247,8 @@ pub fn permute_mtx(data: &Array2, new_order: &[usize]) -> Array2 { permuted.reshape([n_rows, n_rows]).into_owned() } +/// Returns a new array of the same type and shape as a given array, but +/// containing only zeros. pub fn zeros_like(data: &Array) -> Array { Array::zeros(data.dim()) } diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 17be83812a9..549fc085f82 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -1,5 +1,5 @@ -use crate::channels::Channel; -use crate::channels::ChannelData::{KrausDecomposition, Unitary}; +use crate::processes::Process; +use crate::processes::ProcessData::{KrausDecomposition, Unitary}; use crate::common_matrices; use crate::instrument::Instrument; use crate::linalg::HasDagger; @@ -10,42 +10,80 @@ use num_traits::{One, Zero}; use serde::{Deserialize, Serialize}; +/// A description of the noise that applies to the state of a quantum system +/// as the result of applying operations. #[derive(Serialize, Deserialize, Debug)] pub struct NoiseModel { + /// The initial state that freshly allocated qubits start off in. pub initial_state: State, - pub i: Channel, - pub x: Channel, - pub y: Channel, - pub z: Channel, - pub h: Channel, - pub s: Channel, - pub s_adj: Channel, - pub t: Channel, - pub t_adj: Channel, - pub cnot: Channel, + + /// The process that applies to the state of a simulator + /// when the `I` operation is called. + pub i: Process, + + /// The process that applies to the state of a simulator + /// when the `X` operation is called. + pub x: Process, + + /// The process that applies to the state of a simulator + /// when the `Y` operation is called. + pub y: Process, + + /// The process that applies to the state of a simulator + /// when the `Z` operation is called. + pub z: Process, + + /// The process that applies to the state of a simulator + /// when the `H` operation is called. + pub h: Process, + + /// The process that applies to the state of a simulator + /// when the `S` operation is called. + pub s: Process, + + /// The process that applies to the state of a simulator + /// when the `Adjoint S` operation is called. + pub s_adj: Process, + + /// The process that applies to the state of a simulator + /// when the `T` operation is called. + pub t: Process, + + /// The process that applies to the state of a simulator + /// when the `Adjoint T` operation is called. + pub t_adj: Process, + + /// The process that applies to the state of a simulator + /// when the `CNOT` operation is called. + pub cnot: Process, + + /// The instrument that is used to the measure the state of a simulator + /// in the $Z$-basis. pub z_meas: Instrument, } impl NoiseModel { + /// Returns a copy of the ideal noise model; that is, a noise model + /// describing the case in which no noise acts on the quantum system. pub fn ideal() -> NoiseModel { - let i = Channel { + let i = Process { n_qubits: 1, data: Unitary(common_matrices::i()), }; - let z = Channel { + let z = Process { n_qubits: 1, data: Unitary(common_matrices::z()), }; let z_meas = Instrument { effects: vec![ - Channel { + Process { n_qubits: 1, data: KrausDecomposition(array![[ [C64::one(), C64::zero()], [C64::zero(), C64::zero()] ]]), }, - Channel { + Process { n_qubits: 1, data: KrausDecomposition(array![[ [C64::zero(), C64::zero()], @@ -60,36 +98,36 @@ impl NoiseModel { data: Mixed((common_matrices::i() + common_matrices::z()) / 2.0), }, i, - x: Channel { + x: Process { n_qubits: 1, data: Unitary(common_matrices::x()), }, - y: Channel { + y: Process { n_qubits: 1, data: Unitary(common_matrices::y()), }, z, - h: Channel { + h: Process { n_qubits: 1, data: Unitary(common_matrices::h()), }, - t: Channel { + t: Process { n_qubits: 1, data: Unitary(common_matrices::t()), }, - t_adj: Channel { + t_adj: Process { n_qubits: 1, data: Unitary(common_matrices::t().dag()), }, - s: Channel { + s: Process { n_qubits: 1, data: Unitary(common_matrices::s()), }, - s_adj: Channel { + s_adj: Process { n_qubits: 1, data: Unitary(common_matrices::s().dag()), }, - cnot: Channel { + cnot: Process { n_qubits: 2, data: Unitary(common_matrices::cnot()), }, diff --git a/src/Simulation/qdk_sim_rs/src/channels.rs b/src/Simulation/qdk_sim_rs/src/processes.rs similarity index 77% rename from src/Simulation/qdk_sim_rs/src/channels.rs rename to src/Simulation/qdk_sim_rs/src/processes.rs index 96a46d1bd84..9e03a6b67f7 100644 --- a/src/Simulation/qdk_sim_rs/src/channels.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::channels::ChannelData::{KrausDecomposition, Unitary}; +use crate::{log, processes::ProcessData::{KrausDecomposition, Unitary}}; use crate::linalg::ConjBy; use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; use crate::states::StateData::{Mixed, Pure}; @@ -9,7 +9,7 @@ use crate::NoiseModel; use crate::QubitSized; use crate::State; use crate::C64; -use crate::{log_as_err, log_message}; +use crate::{log_as_err}; use itertools::Itertools; use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; use num_traits::{One, Zero}; @@ -19,16 +19,32 @@ use std::ops::Add; use std::ops::Mul; // TODO: Rename to something that doesn't imply it's already CPTP. -pub type Channel = QubitSized; +/// A linear function from quantum states to quantum states. +/// +/// # Remarks +/// A process that is completely positive and trace preserving is a channel. +pub type Process = QubitSized; + +/// Data used to represent a given process. #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ChannelData { +pub enum ProcessData { + /// Representation of the process by a unitary matrix. Unitary(Array2), + + /// Representation of the process by the singular vectors of its Choi + /// representation (colloquially, the Kraus decomposition). + /// + /// The first index denotes each Kraus operator, with the second and third + /// indices representing the indices of each operator. KrausDecomposition(Array3), // TODO: Superoperator and Choi reps. } -impl Channel { +impl Process { // TODO: methods to forcibly convert representations. + + /// Applies this process to a quantum register with a given + /// state, returning the new state of that register. pub fn apply(&self, state: &State) -> Result { if state.n_qubits == self.n_qubits { Ok(State { @@ -71,6 +87,8 @@ impl Channel { } } + /// Applies this process to the given qubits in a register with a given + /// state, returning the new state of that register. pub fn apply_to(&self, idx_qubits: &[usize], state: &State) -> Result { // Fail if there's not enough qubits. if state.n_qubits < self.n_qubits { @@ -122,7 +140,7 @@ impl Channel { .extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits) .apply(state), _ => { - log_message(&format!( + log(&format!( "Expanding {}-qubit channels is not yet implemented.", self.n_qubits )); @@ -131,9 +149,11 @@ impl Channel { } } - pub fn extend_one_to_n(&self, idx_qubit: usize, n_qubits: usize) -> Channel { + /// Returns a copy of this process that applies to registers of a given + /// size. + pub fn extend_one_to_n(&self, idx_qubit: usize, n_qubits: usize) -> Process { assert_eq!(self.n_qubits, 1); - Channel { + Process { n_qubits, data: match &self.data { Unitary(u) => Unitary(extend_one_to_n(u.view(), idx_qubit, n_qubits)), @@ -152,14 +172,16 @@ impl Channel { } } + /// Returns a copy of this process that applies to registers of a given + /// size. pub fn extend_two_to_n( &self, idx_qubit1: usize, idx_qubit2: usize, n_qubits: usize, - ) -> Channel { + ) -> Process { assert_eq!(self.n_qubits, 2); - Channel { + Process { n_qubits, data: match &self.data { Unitary(u) => Unitary(extend_two_to_n(u.view(), idx_qubit1, idx_qubit2, n_qubits)), @@ -180,11 +202,11 @@ impl Channel { } } -impl Mul<&Channel> for C64 { - type Output = Channel; +impl Mul<&Process> for C64 { + type Output = Process; - fn mul(self, channel: &Channel) -> Self::Output { - Channel { + fn mul(self, channel: &Process) -> Self::Output { + Process { n_qubits: channel.n_qubits, data: match &channel.data { // Note that we need to multiply by the square root in @@ -202,34 +224,34 @@ impl Mul<&Channel> for C64 { } } -impl Mul for C64 { - type Output = Channel; - fn mul(self, channel: Channel) -> Self::Output { +impl Mul for C64 { + type Output = Process; + fn mul(self, channel: Process) -> Self::Output { self * (&channel) } } -impl Mul<&Channel> for f64 { - type Output = Channel; - fn mul(self, chanel: &Channel) -> Self::Output { +impl Mul<&Process> for f64 { + type Output = Process; + fn mul(self, chanel: &Process) -> Self::Output { C64::new(self, 0f64) * chanel } } -impl Mul for f64 { - type Output = Channel; - fn mul(self, channel: Channel) -> Self::Output { +impl Mul for f64 { + type Output = Process; + fn mul(self, channel: Process) -> Self::Output { self * (&channel) } } // Base case: both channels that we're composing are borrowed. -impl Mul<&Channel> for &Channel { - type Output = Channel; +impl Mul<&Process> for &Process { + type Output = Process; - fn mul(self, rhs: &Channel) -> Self::Output { + fn mul(self, rhs: &Process) -> Self::Output { assert_eq!(self.n_qubits, rhs.n_qubits); - Channel { + Process { n_qubits: self.n_qubits, data: match (&self.data, &rhs.data) { (Unitary(u), Unitary(v)) => Unitary(u.dot(v)), @@ -250,12 +272,12 @@ impl Mul<&Channel> for &Channel { } } -impl Add<&Channel> for &Channel { - type Output = Channel; +impl Add<&Process> for &Process { + type Output = Process; - fn add(self, rhs: &Channel) -> Self::Output { + fn add(self, rhs: &Process) -> Self::Output { assert_eq!(self.n_qubits, rhs.n_qubits); - Channel { + Process { n_qubits: self.n_qubits, data: match (&self.data, &rhs.data) { (KrausDecomposition(ks1), KrausDecomposition(ks2)) => { @@ -279,61 +301,66 @@ impl Add<&Channel> for &Channel { } } -impl Mul for &Channel { - type Output = Channel; +impl Mul for &Process { + type Output = Process; - fn mul(self, rhs: Channel) -> Self::Output { + fn mul(self, rhs: Process) -> Self::Output { self * &rhs } } -impl Mul<&Channel> for Channel { - type Output = Channel; +impl Mul<&Process> for Process { + type Output = Process; - fn mul(self, rhs: &Channel) -> Self::Output { + fn mul(self, rhs: &Process) -> Self::Output { &self * rhs } } -impl Mul for Channel { - type Output = Channel; +impl Mul for Process { + type Output = Process; - fn mul(self, rhs: Channel) -> Self::Output { + fn mul(self, rhs: Process) -> Self::Output { &self * &rhs } } -impl Add for &Channel { - type Output = Channel; +impl Add for &Process { + type Output = Process; - fn add(self, rhs: Channel) -> Self::Output { + fn add(self, rhs: Process) -> Self::Output { self + &rhs } } -impl Add<&Channel> for Channel { - type Output = Channel; +impl Add<&Process> for Process { + type Output = Process; - fn add(self, rhs: &Channel) -> Self::Output { + fn add(self, rhs: &Process) -> Self::Output { &self + rhs } } -impl Add for Channel { - type Output = Channel; +impl Add for Process { + type Output = Process; - fn add(self, rhs: Channel) -> Self::Output { + fn add(self, rhs: Process) -> Self::Output { &self + &rhs } } -pub fn depolarizing_channel(p: f64) -> Channel { +/// Returns a copy of a depolarizing channel of a given strength (that is, a +/// channel representing relaxation to the maximally mixed state). +pub fn depolarizing_channel(p: f64) -> Process { let ideal = NoiseModel::ideal(); (1.0 - p) * ideal.i + p / 3.0 * ideal.x + p / 3.0 * ideal.y + p / 3.0 * ideal.z } -pub fn amplitude_damping_channel(gamma: f64) -> Channel { - Channel { +/// Returns a copy of an amplitude damping channel of a given strength (that is, +/// a channel representing relaxation to the $\ket{0}$ state in a +/// characteristic time given by $1 / \gamma$). +pub fn amplitude_damping_channel(gamma: f64) -> Process { + Process { n_qubits: 1, data: KrausDecomposition(array![ [ diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 65dbc633814..224e88c03a4 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -20,6 +20,7 @@ pub enum StateData { Mixed(Array2), } +/// The state of a quantum system. pub type State = QubitSized; impl Display for State { @@ -47,6 +48,17 @@ impl Display for StateData { } impl State { + /// Extends this state to be a state on `n_qubits` additional qubits. + /// New qubits are added "to the right," e.g.: $\left|\psi\right\rangle$ + /// is extended to $\left|\psi 0\right\rangle$. + /// + /// # Example + /// + /// ``` + /// # use qdk_sim::State; + /// let rho = State::new_mixed(2); + /// assert_eq!(5, rho.extend(3).get_n_qubits()); + /// ``` pub fn extend(&self, n_qubits: usize) -> State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); State { @@ -61,6 +73,9 @@ impl State { } } + /// Returns a new mixed state on a given number of qubits. + /// By convention, new mixed states start off in the "all-zeros" state, + /// $\rho = \ket{00\cdots 0}\bra{00\cdots 0}. pub fn new_mixed(n_qubits: usize) -> State { let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); State { diff --git a/src/Simulation/qdk_sim_rs/src/utils.rs b/src/Simulation/qdk_sim_rs/src/utils.rs index 55aeeace446..df9df8e823a 100644 --- a/src/Simulation/qdk_sim_rs/src/utils.rs +++ b/src/Simulation/qdk_sim_rs/src/utils.rs @@ -3,23 +3,39 @@ use num_complex::Complex; +/// A complex number with two 64-bit floating point fields. +/// That is, the analogy of [`f64`] to complex values. pub type C64 = Complex; +/// The real unit 1, represented as a complex number with two 64-bit floating +/// point fields. pub const ONE_C: C64 = Complex::new(1f64, 0f64); + +/// The number zero, represented as a complex number with two 64-bit floating +/// point fields. pub const ZERO_C: C64 = Complex::new(0f64, 0f64); + +/// The imaginary unit $i$, represented as a complex number with two 64-bit +/// floating point fields. pub const I_C: C64 = Complex::new(0f64, 1f64); #[cfg(feature = "web-sys-log")] -pub fn log_message(msg: &str) { +fn log_message(msg: &str) { web_sys::console::log_1(&msg.into()); } #[cfg(not(feature = "web-sys-log"))] -pub fn log_message(msg: &str) { +fn log_message(msg: &str) { println!("{}", msg); } +/// Prints a message as an error, and returns it as a [`Result`]. pub fn log_as_err(msg: String) -> Result { - log_message(&msg.as_str()); + log(&msg); Err(msg) } + +/// Prints a message as an error. +pub fn log(msg: &String) -> () { + log_message(msg.as_str()); +} diff --git a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index 8ecc4f30b8d..11e46a3fb00 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -5,6 +5,12 @@ Push-Location $PSScriptRoot # meet formatting and style guide rules. cargo fmt -- --check + # Check linting rules defined by clippy. + # 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 + # If running in CI, use cargo2junit to expose unit tests to the # PublishTestResults task. if ("$Env:TF_BUILD" -ne "") { From 26c4a4001045e0377a8b4e94854ff761a1936ee5 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 11:50:06 -0700 Subject: [PATCH 066/158] Ignore jupyter temps and xplat drops. --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) 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 From fa7d2caee938bafaf030c9ac348614684531a39c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 13:01:34 -0700 Subject: [PATCH 067/158] Begin exposing build metadata through to C# callers. --- .../OpenSystemsSimulator/NativeInterface.cs | 18 ++- src/Simulation/qdk_sim_rs/Cargo.lock | 147 +++++++++++++++++- src/Simulation/qdk_sim_rs/Cargo.toml | 9 ++ src/Simulation/qdk_sim_rs/build.rs | 5 + src/Simulation/qdk_sim_rs/src/c_api.rs | 26 ++-- src/Simulation/qdk_sim_rs/src/lib.rs | 5 + 6 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/build.rs diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index b1de8a6b239..13743aec1fa 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -7,13 +7,14 @@ using System.Text.Json; using System.Runtime.InteropServices; using Microsoft.Quantum.Simulation.Core; +using Newtonsoft.Json.Linq; #nullable enable namespace Microsoft.Quantum.Experimental { /// - /// Abstracts away calls to and from libopensim. + /// Abstracts away calls to and from the experimental simulators DLL. /// internal static class NativeInterface { @@ -37,16 +38,21 @@ private static void CheckCall(Int64 errorCode) private static extern string _LastError(); - [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_name")] - private static extern string _GetName(); + [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_simulator_info")] + private static extern string _GetSimulatorInfo(); + + private static readonly JToken SimulatorInfo; + + static NativeInterface() + { + SimulatorInfo = JToken.Parse(_GetSimulatorInfo()); + } public static string Name { get { - // TODO: Add get_name to c_api and uncomment this. - LogCall("get_name"); - return _GetName();; + return SimulatorInfo.Value("name"); } } diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index beb79824984..c3bb69ccfef 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -47,6 +47,15 @@ dependencies = [ "serde", ] +[[package]] +name = "built" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76efeb50f902677353f8f7052c7da1cc8c4f65b46b30cc41fc1e3042249711c7" +dependencies = [ + "cargo-lock", +] + [[package]] name = "bumpalo" version = "3.4.0" @@ -59,6 +68,18 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cargo-lock" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f16e7adc20969298b1e137ac21ab3a7e7a9412fec71f963ff2fdc41663d70f" +dependencies = [ + "semver 0.11.0", + "serde", + "toml", + "url", +] + [[package]] name = "cast" version = "0.2.3" @@ -211,6 +232,16 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -238,6 +269,17 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "itertools" version = "0.9.0" @@ -292,6 +334,12 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "matrixmultiply" version = "0.2.3" @@ -376,6 +424,21 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "plotters" version = "0.3.0" @@ -424,6 +487,7 @@ name = "qdk_sim" version = "0.1.0" dependencies = [ "assert-json-diff", + "built", "cfg-if 1.0.0", "criterion", "derive_more", @@ -550,7 +614,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", ] [[package]] @@ -580,7 +644,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", + "serde", ] [[package]] @@ -589,6 +663,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.117" @@ -660,6 +743,54 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.8" @@ -672,6 +803,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "walkdir" version = "2.3.1" diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml b/src/Simulation/qdk_sim_rs/Cargo.toml index 8c9ce2c5b79..fc31dd01abe 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml +++ b/src/Simulation/qdk_sim_rs/Cargo.toml @@ -6,6 +6,10 @@ name = "qdk_sim" version = "0.1.0" authors = ["Christopher Granade "] 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" [lib] name = "qdk_sim" @@ -61,6 +65,11 @@ 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 } +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'] } diff --git a/src/Simulation/qdk_sim_rs/build.rs b/src/Simulation/qdk_sim_rs/build.rs new file mode 100644 index 00000000000..d9f19b31d9c --- /dev/null +++ b/src/Simulation/qdk_sim_rs/build.rs @@ -0,0 +1,5 @@ +use built; + +fn main() { + built::write_built_file().expect("Failed to acquire build-time information") +} diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 7001da7786a..fc7ebf90889 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -12,8 +12,9 @@ //! Please pay attention to any listed safety notes when calling into this C //! API. -use crate::{Process, NoiseModel, State}; +use crate::{NoiseModel, Process, State, built_info}; use lazy_static::lazy_static; +use serde_json::json; use std::collections::HashMap; use std::ffi::CStr; use std::ffi::CString; @@ -67,13 +68,20 @@ fn apply &Process>( // C API FUNCTIONS // -// TODO: deprecate and replace with something that returns a JSON object -// with current version of the crate, target, etc. See the `built` crate. -/// Returns the name of this library; useful for debugging in the host -/// language. +/// Returns information about how this simulator was built, serialized as a +/// JSON object. #[no_mangle] -pub extern "C" fn get_name() -> *const c_char { - CString::new("open_sim").unwrap().into_raw() +pub extern "C" fn get_simulator_info() -> *const c_char { + let build_info = json!({ + "name": "Microsoft.Quantum.Experimental.Simulators", + "version": built_info::PKG_VERSION, + "opt_level": built_info::OPT_LEVEL, + "features": built_info::FEATURES, + "target": built_info::TARGET + }); + CString::new(serde_json::to_string(&build_info).unwrap().as_str()) + .unwrap() + .into_raw() } /// Returns the last error message raised by a call to a C function. @@ -82,9 +90,7 @@ pub extern "C" fn get_name() -> *const c_char { pub extern "C" fn lasterr() -> *const c_char { match &*LAST_ERROR.lock().unwrap() { None => ptr::null(), - Some(msg) => { - CString::new(msg.as_str()).unwrap().into_raw() - } + Some(msg) => CString::new(msg.as_str()).unwrap().into_raw(), } } diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 9ef82c570c4..6dc1acf848b 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -48,3 +48,8 @@ impl QubitSized { self.n_qubits } } + +/// Metadata about how this crate was built. +pub mod built_info { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} From bf2147c04c895171cffb9c15b5dd2add674b8c03 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 14 May 2021 15:07:39 -0700 Subject: [PATCH 068/158] Start injecting version number from build into Cargo.toml. --- build/build.ps1 | 12 ++++++ build/set-env.ps1 | 2 +- omnisharp.json | 5 +++ .../qdk_sim_rs/.config/dotnet-tools.json | 12 ++++++ src/Simulation/qdk_sim_rs/.gitignore | 3 ++ src/Simulation/qdk_sim_rs/Cargo.lock | 2 +- .../{Cargo.toml => Cargo.toml.template} | 0 src/Simulation/qdk_sim_rs/NuGet.Config | 7 ++++ src/Simulation/qdk_sim_rs/inject-version.csx | 38 +++++++++++++++++++ 9 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 omnisharp.json create mode 100644 src/Simulation/qdk_sim_rs/.config/dotnet-tools.json rename src/Simulation/qdk_sim_rs/{Cargo.toml => Cargo.toml.template} (100%) create mode 100644 src/Simulation/qdk_sim_rs/NuGet.Config create mode 100644 src/Simulation/qdk_sim_rs/inject-version.csx diff --git a/build/build.ps1 b/build/build.ps1 index 7c1e1f953c2..7dd925b5672 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -6,6 +6,18 @@ $ErrorActionPreference = 'Stop' & "$PSScriptRoot/set-env.ps1" $all_ok = $True +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 ($Env:ENABLE_NATIVE -ne "false") { $nativeSimulator = (Join-Path $PSScriptRoot "../src/Simulation/Native") & "$nativeSimulator/build-native-simulator.ps1" diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 186c77abac6..fd5ae306ce1 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -9,7 +9,7 @@ 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: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") 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/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 index 8f7943dd70e..6ced2555d8e 100644 --- a/src/Simulation/qdk_sim_rs/.gitignore +++ b/src/Simulation/qdk_sim_rs/.gitignore @@ -2,3 +2,6 @@ linux osx win10 target + +# We inject version numbers into Cargo.toml, so don't want them stored in repo. +Cargo.toml diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index c3bb69ccfef..f73b9aba710 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -484,7 +484,7 @@ dependencies = [ [[package]] name = "qdk_sim" -version = "0.1.0" +version = "0.0.1-alpha" dependencies = [ "assert-json-diff", "built", diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml b/src/Simulation/qdk_sim_rs/Cargo.toml.template similarity index 100% rename from src/Simulation/qdk_sim_rs/Cargo.toml rename to src/Simulation/qdk_sim_rs/Cargo.toml.template diff --git a/src/Simulation/qdk_sim_rs/NuGet.Config b/src/Simulation/qdk_sim_rs/NuGet.Config new file mode 100644 index 00000000000..765346e5343 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + 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..a871efc7972 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/inject-version.csx @@ -0,0 +1,38 @@ +#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) => +{ + 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()); From b12a34f4d4667a931f8df95b27c49c1152814d7f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 17 May 2021 12:26:46 -0700 Subject: [PATCH 069/158] Started infrastructure to expose simulation functionality to Python. --- build/pack.ps1 | 47 +++++ build/set-env.ps1 | 6 + src/Simulation/qdk_sim_rs/Cargo.lock | 195 ++++++++++++++++++ src/Simulation/qdk_sim_rs/Cargo.toml.template | 14 +- src/Simulation/qdk_sim_rs/pyproject.toml | 3 + src/Simulation/qdk_sim_rs/qdk_sim/__init__.py | 5 + src/Simulation/qdk_sim_rs/setup.py | 12 ++ src/Simulation/qdk_sim_rs/src/lib.rs | 3 + src/Simulation/qdk_sim_rs/src/python.rs | 29 +++ src/Simulation/qdk_sim_rs/src/states.rs | 6 + 10 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 src/Simulation/qdk_sim_rs/pyproject.toml create mode 100644 src/Simulation/qdk_sim_rs/qdk_sim/__init__.py create mode 100644 src/Simulation/qdk_sim_rs/setup.py create mode 100644 src/Simulation/qdk_sim_rs/src/python.rs diff --git a/build/pack.ps1 b/build/pack.ps1 index af6a61a9710..752959647c9 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -75,6 +75,51 @@ function Pack-Dotnet() { } +function Pack-Crate() { + param( + [string] + $PackageDirectory, + + [string] + $OutPath + ); + + "##[info]Packing crate at $PackageDirectory to $OutPath..." | Write-Host + + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # 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 @releaseFlag; + 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' @@ -89,6 +134,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; if (-not $all_ok) { throw "At least one project failed to pack. Check the logs." diff --git a/build/set-env.ps1 b/build/set-env.ps1 index fd5ae306ce1..477209dbc2c 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -23,6 +23,12 @@ if ($Env:DROP_NATIVE -eq $null) { 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) } diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index f73b9aba710..8b1efe9c817 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -215,6 +215,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctor" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "derive_more" version = "0.99.11" @@ -254,6 +264,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghost" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5bcf1bbeab73aa4cf2fde60a846858dc036163c7c33bec309f8d17de785479" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "half" version = "1.7.1" @@ -280,6 +301,60 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indoc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "inventory" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0f7efb804ec95e33db9ad49e4252f049e37e8b0a4652e3cd61f7999f2eff7f" +dependencies = [ + "ctor", + "ghost", + "inventory-impl", +] + +[[package]] +name = "inventory-impl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c094e94816723ab936484666968f5b58060492e880f3c8d00489a1e244fa51" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "itertools" version = "0.9.0" @@ -325,6 +400,15 @@ version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.11" @@ -424,6 +508,50 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -473,6 +601,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -482,6 +616,45 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "pyo3" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4837b8e8e18a102c23f79d1e9a110b597ea3b684c95e874eb1ad88f8683109c3" +dependencies = [ + "cfg-if 1.0.0", + "ctor", + "indoc", + "inventory", + "libc", + "parking_lot", + "paste", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-macros" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47f2c300ceec3e58064fd5f8f5b61230f2ffd64bde4970c81fdd0563a2db1bb" +dependencies = [ + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87b097e5d84fcbe3e167f400fbedd657820a375b034c78bd852050749a575d66" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "qdk_sim" version = "0.0.1-alpha" @@ -497,6 +670,7 @@ dependencies = [ "ndarray", "num-complex", "num-traits", + "pyo3", "rand", "serde", "serde_json", @@ -584,6 +758,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.4.5" @@ -713,6 +896,12 @@ dependencies = [ "serde", ] +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "syn" version = "1.0.48" @@ -803,6 +992,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + [[package]] name = "url" version = "2.2.2" diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index fc31dd01abe..f537144259d 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -11,14 +11,22 @@ description = "Experimental simulators for use with the Quantum Development Kit. homepage = "https://github.com/microsoft/qsharp-runtime" repository = "https://github.com/microsoft/qsharp-runtime" +# Exclude files specific to QDK build pipelines. +exclude = [ + "*.template", "*.csx", "*.ps1", "NuGet.Config", "drops" +] + [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"] # We use an empty feature to enable documentation generation, as is required # until https://github.com/rust-lang/rust/pull/83366 is completed. @@ -54,7 +62,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" lazy_static = "1.4.0" cfg-if = "1.0.0" - # 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. @@ -65,6 +72,11 @@ 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 } + +# 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] 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/__init__.py b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py new file mode 100644 index 00000000000..fc4e7c62010 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py @@ -0,0 +1,5 @@ +import json +import qdk_sim._qdk_sim_rs as _native + +def build_info(): + return json.loads(_native.build_info_json()) diff --git a/src/Simulation/qdk_sim_rs/setup.py b/src/Simulation/qdk_sim_rs/setup.py new file mode 100644 index 00000000000..513529c8574 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/setup.py @@ -0,0 +1,12 @@ +from setuptools import setup +from setuptools_rust import Binding, RustExtension + +setup( + name="qdk_sim", + version="1.0", + rust_extensions=[RustExtension("qdk_sim._qdk_sim_rs", binding=Binding.PyO3, features=["python"])], + packages=["qdk_sim"], + # 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/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 6dc1acf848b..b73eb297be7 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -34,6 +34,9 @@ pub use crate::noise_model::NoiseModel; pub use crate::states::State; pub use crate::utils::*; +#[cfg(feature = "python")] +mod python; + /// Represents that a given type has a size that can be measured in terms /// of a number of qubits (e.g.: [`State`]). #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs new file mode 100644 index 00000000000..7eadcb39461 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -0,0 +1,29 @@ +// use crate::tableau::Tableau; +use crate::built_info; +use serde_json::json; +use pyo3::{ + prelude::*, +}; + +#[pymodule] +fn _qdk_sim_rs(_py: Python, m: &PyModule) -> PyResult<()> { + + /// Returns information about how this simulator was built, serialized as a + /// JSON object. + #[pyfn(m, "build_info_json")] + fn build_info_json_py(_py: Python) -> String { + // TODO[code quality]: Deduplicate this with the + // version in the c_api module. + let build_info = json!({ + "name": "Microsoft.Quantum.Experimental.Simulators", + "version": built_info::PKG_VERSION, + "opt_level": built_info::OPT_LEVEL, + "features": built_info::FEATURES, + "target": built_info::TARGET + }); + serde_json::to_string(&build_info).unwrap() + } + + // m.add_class::()?; + Ok(()) +} diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 224e88c03a4..a7313f16b88 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -16,8 +16,14 @@ use std::convert::TryInto; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum StateData { + /// A pure state, represented as a vector of complex numbers. Pure(Array1), + + /// A mixed state, represented as a density operator. Mixed(Array2), + + // /// A stabilizer state, represented as a stabilizer tableau. + // Stabilizer(Tableau) } /// The state of a quantum system. From 434cbb481b34218862db267688b122444f76e5c7 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 17 May 2021 12:27:38 -0700 Subject: [PATCH 070/158] Ignore Python temporaries and build artifacts. --- src/Simulation/qdk_sim_rs/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Simulation/qdk_sim_rs/.gitignore b/src/Simulation/qdk_sim_rs/.gitignore index 6ced2555d8e..f67c633ca75 100644 --- a/src/Simulation/qdk_sim_rs/.gitignore +++ b/src/Simulation/qdk_sim_rs/.gitignore @@ -5,3 +5,8 @@ target # We inject version numbers into Cargo.toml, so don't want them stored in repo. Cargo.toml + +# Ignore Python temporaries and build artifacts. +*.pyd +*.whl +*.egg-info From f404293c9339029994df061690c7eb0570d3a8b4 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 18 May 2021 16:15:10 -0700 Subject: [PATCH 071/158] More partial progress on exposing to Python. --- src/Simulation/qdk_sim_rs/Cargo.lock | 15 + src/Simulation/qdk_sim_rs/Cargo.toml.template | 4 +- src/Simulation/qdk_sim_rs/build.rs | 2 - src/Simulation/qdk_sim_rs/qdk_sim/__init__.py | 5 + src/Simulation/qdk_sim_rs/src/c_api.rs | 12 +- src/Simulation/qdk_sim_rs/src/instrument.rs | 4 + src/Simulation/qdk_sim_rs/src/lib.rs | 12 +- src/Simulation/qdk_sim_rs/src/linalg.rs | 6 +- src/Simulation/qdk_sim_rs/src/noise_model.rs | 13 +- src/Simulation/qdk_sim_rs/src/processes.rs | 10 +- src/Simulation/qdk_sim_rs/src/python.rs | 13 +- src/Simulation/qdk_sim_rs/src/states.rs | 16 +- src/Simulation/qdk_sim_rs/src/tableau.rs | 257 ++++++++++++++++++ src/Simulation/qdk_sim_rs/src/utils.rs | 80 +++++- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 2 +- 15 files changed, 413 insertions(+), 38 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/src/tableau.rs diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index 8b1efe9c817..d84d33fbc9b 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -502,6 +502,20 @@ dependencies = [ "libc", ] +[[package]] +name = "numpy" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7073fae1e0b82409533a29c6f804b79783d7b2d3c07728fdc4d884eda8cd4f0" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "ndarray", + "num-complex", + "num-traits", + "pyo3", +] + [[package]] name = "oorandom" version = "11.1.3" @@ -670,6 +684,7 @@ dependencies = [ "ndarray", "num-complex", "num-traits", + "numpy", "pyo3", "rand", "serde", diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index f537144259d..434fba7e817 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -26,7 +26,7 @@ crate-type = ["rlib", "staticlib", "cdylib"] default = [] wasm = ["web-sys"] # When Python bindings are enabled, we also need the pyo3 dependency. -python = ["pyo3"] +python = ["pyo3", "numpy"] # We use an empty feature to enable documentation generation, as is required # until https://github.com/rust-lang/rust/pull/83366 is completed. @@ -74,11 +74,13 @@ 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" diff --git a/src/Simulation/qdk_sim_rs/build.rs b/src/Simulation/qdk_sim_rs/build.rs index d9f19b31d9c..4306bb7873c 100644 --- a/src/Simulation/qdk_sim_rs/build.rs +++ b/src/Simulation/qdk_sim_rs/build.rs @@ -1,5 +1,3 @@ -use built; - fn main() { built::write_built_file().expect("Failed to acquire build-time information") } diff --git a/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py index fc4e7c62010..5a68398bbdf 100644 --- a/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py +++ b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py @@ -1,5 +1,10 @@ import json import qdk_sim._qdk_sim_rs as _native +# Re-export native classes. +from qdk_sim._qdk_sim_rs import ( + Tableau, NoiseModel, Instrument +) + def build_info(): return json.loads(_native.build_info_json()) diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index fc7ebf90889..58dfd5611b6 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -12,7 +12,7 @@ //! Please pay attention to any listed safety notes when calling into this C //! API. -use crate::{NoiseModel, Process, State, built_info}; +use crate::{built_info, NoiseModel, Process, State}; use lazy_static::lazy_static; use serde_json::json; use std::collections::HashMap; @@ -220,13 +220,9 @@ pub unsafe extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> pub extern "C" fn get_noise_model(sim_id: usize) -> *const c_char { let state = &*STATE.lock().unwrap(); if let Some(sim_state) = state.get(&sim_id) { - CString::new( - serde_json::to_string(&sim_state.noise_model) - .unwrap() - .as_str(), - ) - .unwrap() - .into_raw() + CString::new(sim_state.noise_model.as_json().as_str()) + .unwrap() + .into_raw() } else { ptr::null() } diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 956ca393e3b..36c893d51ac 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -11,6 +11,10 @@ use crate::State; use serde::{Deserialize, Serialize}; +#[cfg(feature = "python")] +use pyo3::prelude::*; + +#[cfg_attr(feature = "python", pyclass(name = "Instrument"))] #[derive(Serialize, Deserialize, Debug)] /// Represents a quantum instrument; that is, a process that accepts a quantum /// state and returns the new state of a system and classical data extracted diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index b73eb297be7..9c212cd092b 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -20,17 +20,18 @@ use std::usize; use serde::{Deserialize, Serialize}; pub mod c_api; -mod processes; pub mod common_matrices; mod instrument; pub mod linalg; mod noise_model; +mod processes; mod states; +mod tableau; mod utils; -pub use crate::processes::*; pub use crate::instrument::*; pub use crate::noise_model::NoiseModel; +pub use crate::processes::*; pub use crate::states::State; pub use crate::utils::*; @@ -52,6 +53,13 @@ impl QubitSized { } } +pub enum Pauli { + I = 0, + X = 1, + Z = 3, + Y = 2, +} + /// Metadata about how this crate was built. pub mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/src/Simulation/qdk_sim_rs/src/linalg.rs b/src/Simulation/qdk_sim_rs/src/linalg.rs index 4205bf36c3e..700af0c83e9 100644 --- a/src/Simulation/qdk_sim_rs/src/linalg.rs +++ b/src/Simulation/qdk_sim_rs/src/linalg.rs @@ -156,11 +156,7 @@ impl Trace for &Array2 { // from microbenchmarks on tensor and nq_eye directly. /// Given an array representing an operator acting on single-qubit states, /// returns a new operator that acts on $n$-qubit states. -pub fn extend_one_to_n( - data: ArrayView2, - idx_qubit: usize, - n_qubits: usize, -) -> Array2 { +pub fn extend_one_to_n(data: ArrayView2, idx_qubit: usize, n_qubits: usize) -> Array2 { let n_left = idx_qubit; let n_right = n_qubits - idx_qubit - 1; match (n_left, n_right) { diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 549fc085f82..7863a6b1668 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -1,8 +1,8 @@ -use crate::processes::Process; -use crate::processes::ProcessData::{KrausDecomposition, Unitary}; use crate::common_matrices; use crate::instrument::Instrument; use crate::linalg::HasDagger; +use crate::processes::Process; +use crate::processes::ProcessData::{KrausDecomposition, Unitary}; use crate::states::State; use crate::states::StateData::Mixed; use crate::C64; @@ -10,8 +10,12 @@ use num_traits::{One, Zero}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "python")] +use pyo3::prelude::*; + /// A description of the noise that applies to the state of a quantum system /// as the result of applying operations. +#[cfg_attr(feature = "python", pyclass(name = "NoiseModel"))] #[derive(Serialize, Deserialize, Debug)] pub struct NoiseModel { /// The initial state that freshly allocated qubits start off in. @@ -63,6 +67,11 @@ pub struct NoiseModel { } impl NoiseModel { + /// Returns a serialization of this noise model as a JSON object. + pub fn as_json(&self) -> String { + serde_json::to_string(&self).unwrap() + } + /// Returns a copy of the ideal noise model; that is, a noise model /// describing the case in which no noise acts on the quantum system. pub fn ideal() -> NoiseModel { diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 9e03a6b67f7..4c328107cef 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -1,15 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::{log, processes::ProcessData::{KrausDecomposition, Unitary}}; use crate::linalg::ConjBy; use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; +use crate::log_as_err; use crate::states::StateData::{Mixed, Pure}; use crate::NoiseModel; use crate::QubitSized; use crate::State; use crate::C64; -use crate::{log_as_err}; +use crate::{ + log, + processes::ProcessData::{KrausDecomposition, Unitary}, +}; use itertools::Itertools; use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; use num_traits::{One, Zero}; @@ -18,8 +21,6 @@ use std::convert::TryInto; use std::ops::Add; use std::ops::Mul; -// TODO: Rename to something that doesn't imply it's already CPTP. - /// A linear function from quantum states to quantum states. /// /// # Remarks @@ -77,6 +78,7 @@ impl Process { sum }), }, + _ => todo!(), }, }) } else { diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index 7eadcb39461..271080d646b 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -1,13 +1,10 @@ -// use crate::tableau::Tableau; -use crate::built_info; +use crate::tableau::Tableau; +use crate::{built_info, Instrument, NoiseModel}; +use pyo3::prelude::*; use serde_json::json; -use pyo3::{ - prelude::*, -}; #[pymodule] fn _qdk_sim_rs(_py: Python, m: &PyModule) -> PyResult<()> { - /// Returns information about how this simulator was built, serialized as a /// JSON object. #[pyfn(m, "build_info_json")] @@ -24,6 +21,8 @@ fn _qdk_sim_rs(_py: Python, m: &PyModule) -> PyResult<()> { serde_json::to_string(&build_info).unwrap() } - // m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; Ok(()) } diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index a7313f16b88..4dc1b194f2d 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -6,6 +6,7 @@ use crate::linalg::Tensor; use crate::linalg::Trace; use crate::states::StateData::Mixed; use crate::states::StateData::Pure; +use crate::tableau::Tableau; use crate::QubitSized; use crate::C64; use core::fmt::Display; @@ -14,6 +15,9 @@ use num_traits::One; use serde::{Deserialize, Serialize}; use std::convert::TryInto; +#[cfg(feature = "python")] +use pyo3::prelude::*; + #[derive(Clone, Debug, Serialize, Deserialize)] pub enum StateData { /// A pure state, represented as a vector of complex numbers. @@ -22,8 +26,8 @@ pub enum StateData { /// A mixed state, represented as a density operator. Mixed(Array2), - // /// A stabilizer state, represented as a stabilizer tableau. - // Stabilizer(Tableau) + /// A stabilizer state, represented as a tableau. + Stabilizer(Tableau), } /// The state of a quantum system. @@ -38,6 +42,7 @@ impl Display for State { match self.data { Pure(_) => "state vector", Mixed(_) => "density operator", + StateData::Stabilizer(_) => "stabilizer tableau", }, self.data ) @@ -49,6 +54,7 @@ impl Display for StateData { match self { Pure(psi) => write!(f, "{}", psi), Mixed(rho) => write!(f, "{}", rho), + StateData::Stabilizer(tableau) => write!(f, "{}", tableau), } } } @@ -59,7 +65,7 @@ impl State { /// is extended to $\left|\psi 0\right\rangle$. /// /// # Example - /// + /// /// ``` /// # use qdk_sim::State; /// let rho = State::new_mixed(2); @@ -75,6 +81,7 @@ impl State { (0, 0), (new_dim, new_dim), ))), + _ => todo!(), }, } } @@ -108,6 +115,7 @@ impl State { let psi = psi.view().insert_axis(Axis(1)); psi.t().map(|e| e.conj()) * psi }), + _ => todo!(), }, } } @@ -118,7 +126,7 @@ impl Trace for &State { fn trace(self) -> Self::Output { match &self.data { - Pure(_) => C64::one(), + Pure(_) | StateData::Stabilizer(_) => C64::one(), Mixed(ref rho) => (&rho).trace(), } } diff --git a/src/Simulation/qdk_sim_rs/src/tableau.rs b/src/Simulation/qdk_sim_rs/src/tableau.rs new file mode 100644 index 00000000000..64126252121 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/src/tableau.rs @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fmt::Display; + +use crate::utils::{set_row_to_row_sum, set_vec_to_row_sum, swap_columns}; +use ndarray::{s, Array, Array1, Array2}; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "python")] +use pyo3::{prelude::*, PyObjectProtocol}; + +#[cfg(feature = "python")] +use std::io::{Error, ErrorKind}; + +/// Represents a stabilizer group with logical dimension 1; +/// that is, a single stabilizer state expressed in terms +/// of the generators of its stabilizer group, and those +/// generators of the Pauli group that anticommute with +/// each stabilizer generator (colloquially, the destabilizers +/// of the represented state). +#[cfg_attr(feature = "python", pyclass(name = "Tableau"))] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Tableau { + n_qubits: usize, + table: Array2, +} + +impl Display for Tableau { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Stabilizer tableau on {} qubits:\n{:?}", + self.n_qubits, self.table + ) + } +} + +impl Tableau { + const fn idx_phase(&self) -> usize { + 2 * self.n_qubits + } + + /// Returns a new stabilizer tableau representing + /// the $\ket{00\cdots 0}$ state of an $n$-qubit + /// register. + pub fn new(n_qubits: usize) -> Self { + Tableau { + n_qubits, + table: Array::from_shape_fn((2 * n_qubits, 2 * n_qubits + 1), |(i, j)| i == j), + } + } + + fn idx_x(&self, idx_col: usize) -> usize { + idx_col + } + + fn idx_z(&self, idx_col: usize) -> usize { + idx_col + self.n_qubits + } + + /// Returns the determinstic result that would be obtained from measuring + /// the given qubit in the 𝑍 basis, or `None` if the result is random. + fn determinstic_result(&self, idx_target: usize) -> Option { + let determined = !self + .table + .slice(s![self.n_qubits.., idx_target]) + .iter() + .any(|b| *b); + if determined { + // What was it determined to be? + let mut vector: Array1 = Array::default(2 * self.n_qubits + 1); + for idx_destabilizer in 0..self.n_qubits { + if self.table[(idx_destabilizer, self.idx_x(idx_target))] { + set_vec_to_row_sum(&mut vector, &self.table, idx_destabilizer + self.n_qubits); + } + } + Some(vector[2 * self.n_qubits]) + } else { + None + } + } +} + +#[cfg_attr(feature = "python", pymethods)] +impl Tableau { + #[cfg(feature = "python")] + pub fn as_json(&self) -> PyResult { + serde_json::to_string(self) + .map_err(|e| PyErr::from(Error::new(ErrorKind::Other, e.to_string()))) + } + + pub fn apply_h_mut(&mut self, idx_target: usize) { + let idxs = (self.idx_x(idx_target), self.idx_z(idx_target)); + swap_columns(&mut self.table, idxs); + let idx_phase = self.idx_phase(); + for idx_row in 0..2 * self.n_qubits { + let a = self.table[(idx_row, self.idx_x(idx_target))]; + let b = self.table[(idx_row, self.idx_z(idx_target))]; + self.table[(idx_row, idx_phase)] ^= a && b; + } + } + + pub fn apply_s_mut(&mut self, idx_target: usize) { + let idx_phase = self.idx_phase(); + for idx_row in 0..2 * self.n_qubits { + self.table[(idx_row, idx_phase)] ^= self.table[(idx_row, self.idx_x(idx_target))] + && self.table[(idx_row, self.idx_z(idx_target))]; + } + + for idx_row in 0..2 * self.n_qubits { + let idx_x_target = self.idx_x(idx_target); + let idx_z_target = self.idx_z(idx_target); + self.table[(idx_row, idx_z_target)] ^= self.table[(idx_row, idx_x_target)]; + } + } + + pub fn apply_cnot_mut(&mut self, idx_control: usize, idx_target: usize) { + let idx_phase = self.idx_phase(); + for idx_row in 0..2 * self.n_qubits { + self.table[(idx_row, idx_phase)] ^= self.table[(idx_row, self.idx_x(idx_control))] + && self.table[(idx_row, self.idx_z(idx_target))] + && (self.table[(idx_row, self.idx_x(idx_target))] + ^ self.table[(idx_row, self.idx_z(idx_control))] + ^ true); + } + + for idx_row in 0..2 * self.n_qubits { + let idx_x_target = self.idx_x(idx_target); + let idx_x_control = self.idx_x(idx_control); + self.table[(idx_row, idx_x_target)] ^= self.table[(idx_row, idx_x_control)]; + } + + for idx_row in 0..2 * self.n_qubits { + let idx_z_target = self.idx_z(idx_target); + let idx_z_control = self.idx_z(idx_control); + self.table[(idx_row, idx_z_control)] ^= self.table[(idx_row, idx_z_target)]; + } + } + + pub fn apply_x_mut(&mut self, idx_target: usize) { + self.apply_h_mut(idx_target); + self.apply_z_mut(idx_target); + self.apply_h_mut(idx_target); + } + + pub fn apply_s_adj_mut(&mut self, idx_target: usize) { + self.apply_s_mut(idx_target); + self.apply_s_mut(idx_target); + self.apply_s_mut(idx_target); + } + + pub fn apply_y_mut(&mut self, idx_target: usize) { + self.apply_s_adj_mut(idx_target); + self.apply_x_mut(idx_target); + self.apply_s_mut(idx_target); + } + + pub fn apply_z_mut(&mut self, idx_target: usize) { + self.apply_s_mut(idx_target); + self.apply_s_mut(idx_target); + } + + pub fn apply_swap_mut(&mut self, idx_1: usize, idx_2: usize) { + self.apply_cnot_mut(idx_1, idx_2); + self.apply_cnot_mut(idx_2, idx_1); + self.apply_cnot_mut(idx_1, idx_2); + } + + pub fn meas_mut(&mut self, idx_target: usize) -> bool { + if let Some(result) = self.determinstic_result(idx_target) { + return result; + } + + // If we're still here, we know the measurement result is random; + // thus we need to pick a random result and use that to update the + // tableau. + let idx_phase = self.idx_phase(); + let result = rand::random(); + let collisions: Vec = self + .table + .slice(s![.., self.idx_x(idx_target)]) + .indexed_iter() + .filter(|(_i, b)| **b) + .map(|(i, _b)| i) + .collect(); + // Find the first stabilizer that intersects with idx_target in the X + // sector. + let idx_first: usize = self.n_qubits + + self + .table + .slice(s![self.n_qubits.., self.idx_x(idx_target)]) + .indexed_iter() + .find(|(_i, b)| **b) + .unwrap() + .0; + // Make an owned copy of the first colliding stabilizer, as we'll + // need that later. + let old_stab = self.table.slice(s![idx_first, ..]).to_owned(); + + // For all collisions other than the first stabilizer, take the row + // sum of that row with the first stabilizer. + for idx_collision in collisions.iter() { + if *idx_collision != idx_first { + set_row_to_row_sum(&mut self.table, *idx_collision, idx_first); + } + } + + // Move the old stabilizer into being a destabilizer, then make a new + // stabilizer that's constrained to agree with our random result. + self.table + .slice_mut(s![idx_first - self.n_qubits, ..]) + .assign(&old_stab); + self.table.slice_mut(s![idx_first, ..]).fill(false); + let idx_z_target = self.idx_z(idx_target); + self.table[(idx_first, idx_z_target)] = true; + self.table[(idx_first, idx_phase)] = result; + result + } +} + +// Forward the ::new method onto Python. +#[cfg(feature = "python")] +#[pymethods] +impl Tableau { + #[new] + pub fn new_py(n_qubits: usize) -> Self { + Self::new(n_qubits) + } +} + +#[cfg(feature = "python")] +#[pyproto] +impl PyObjectProtocol for Tableau { + fn __repr__(&self) -> String { + format!("<{:?}>", self) + } + + fn __str__(&self) -> String { + format!("{}", self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bell_pair_meas_agree() { + let mut t = Tableau::new(2); + t.apply_h_mut(0); + t.apply_cnot_mut(0, 1); + let left = t.meas_mut(0); + let right = t.meas_mut(1); + assert_eq!(left, right) + } +} diff --git a/src/Simulation/qdk_sim_rs/src/utils.rs b/src/Simulation/qdk_sim_rs/src/utils.rs index df9df8e823a..753ea0f5d46 100644 --- a/src/Simulation/qdk_sim_rs/src/utils.rs +++ b/src/Simulation/qdk_sim_rs/src/utils.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use ndarray::{s, Array1, Array2, ArrayView1}; use num_complex::Complex; /// A complex number with two 64-bit floating point fields. @@ -36,6 +37,81 @@ pub fn log_as_err(msg: String) -> Result { } /// Prints a message as an error. -pub fn log(msg: &String) -> () { - log_message(msg.as_str()); +pub fn log(msg: &str) { + log_message(msg); +} + +/// Given two columns in a two-dimensional array, swaps them in-place. +pub fn swap_columns(data: &mut Array2, idxs: (usize, usize)) { + // FIXME[perf]: can be accelerated for bool by using three XORs. + // FIXME[perf]: should be possible with one tmp instead of two + let tmp_a = data.slice(s![.., idxs.0]).to_owned(); + let tmp_b = data.slice(s![.., idxs.1]).to_owned(); + data.slice_mut(s![.., idxs.0]).assign(&tmp_b); + data.slice_mut(s![.., idxs.1]).assign(&tmp_a); +} + +/// Given a one-dimensional array, updates it to be the row-sum of that vector +/// and the given row of a matrix, taking into account the phase product +/// introduced by the binary symplectic product. +pub fn set_vec_to_row_sum(data: &mut Array1, matrix: &Array2, idx_source: usize) { + // FIXME[perf]: change to ndarray broadcast op. + // NB: we use - 1 in the range here, so that we don't also iterate over phases. + for idx_col in 0..matrix.shape()[1] - 1 { + data[idx_col] ^= matrix[(idx_source, idx_col)]; + } + + let idx_phase = data.shape()[0] - 1; + data[idx_phase] = phase_product(&data.slice(s![..]), &matrix.slice(s![idx_source, ..])); +} + +/// Given a two-dimensional array, updates a row of that matrix to be the +/// row-sum of that row and the given row of another matrix, taking into +/// account the phase product introduced by the binary symplectic product. +pub fn set_row_to_row_sum(data: &mut Array2, idx_source: usize, idx_target: usize) { + // FIXME[perf]: change to ndarray broadcast op. + // NB: we use - 1 in the range here, so that we don't also iterate over phases. + for idx_col in 0..data.shape()[1] - 1 { + data[(idx_target, idx_col)] ^= data[(idx_source, idx_col)]; + } + + let idx_phase = data.shape()[1] - 1; + data[(idx_target, idx_phase)] = phase_product( + &data.slice(s![idx_target, ..]), + &data.slice(s![idx_source, ..]), + ); +} + +fn g(x1: bool, z1: bool, x2: bool, z2: bool) -> i32 { + match (x1, z1) { + (false, false) => 0, + (true, true) => (if z2 { 1 } else { 0 }) - (if x2 { 1 } else { 0 }), + (true, false) => (if z2 { 1 } else { 0 }) * (if x2 { 1 } else { -1 }), + (false, true) => (if x2 { 1 } else { 0 }) * (if z2 { 1 } else { -1 }), + } +} + +/// Given a row of a binary symplectic matrix augmented with phase information, +/// returns its $X$, $Z$, and phase parts. +pub fn split_row(row: &ArrayView1) -> (Array1, Array1, bool) { + let n_qubits = (row.shape()[0] - 1) / 2; + // FIXME[perf]: relax to_owned call here. + ( + row.slice(s![0..n_qubits]).to_owned(), + row.slice(s![n_qubits..]).to_owned(), + row[2 * n_qubits], + ) +} + +/// Returns the phase introduced by the binary symplectic product of two rows. +pub fn phase_product(row1: &ArrayView1, row2: &ArrayView1) -> bool { + let mut acc = 0i32; + let (xs1, zs1, r1) = split_row(row1); + let (xs2, zs2, r2) = split_row(row2); + + for idx_col in 0..xs1.shape()[0] { + acc += g(xs1[idx_col], zs1[idx_col], xs2[idx_col], zs2[idx_col]); + } + + ((if r1 { 2 } else { 0 }) + (if r2 { 2 } else { 0 }) + acc) % 4 == 2 } diff --git a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index 11e46a3fb00..a60a5abb806 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -33,7 +33,7 @@ Push-Location $PSScriptRoot $perfDest = (Join-Path $Env:DROPS_DIR "perf" "qdk_sim_rs"); if (Get-Item -ErrorAction SilentlyContinue $reportPath) { New-Item -Type Directory -Force -Path $perfDest; - Copy-Item -Recurse -Path $reportPath -Destination $perfDest; + Copy-Item -Recurse -Force -Path $reportPath -Destination $perfDest; } # Free disk space by cleaning up. From 33b8732eb58060024005838b12cb2be86c2db45e Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 18 May 2021 16:34:00 -0700 Subject: [PATCH 072/158] Add initial precommit config. --- .pre-commit-config.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .pre-commit-config.yml diff --git a/.pre-commit-config.yml b/.pre-commit-config.yml new file mode 100644 index 00000000000..49b6a7ee3ce --- /dev/null +++ b/.pre-commit-config.yml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/doublify/pre-commit-rust + rev: master + hooks: + - id: fmt From 5a44d30d500c9990010c2b5b185b590e76f78f57 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 18 May 2021 16:38:33 -0700 Subject: [PATCH 073/158] Introduce fmt error. --- .pre-commit-config.yaml | 6 ++++++ .pre-commit-config.yml | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 .pre-commit-config.yaml delete mode 100644 .pre-commit-config.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000000..f657bcaa3b0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +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', '--'] diff --git a/.pre-commit-config.yml b/.pre-commit-config.yml deleted file mode 100644 index 49b6a7ee3ce..00000000000 --- a/.pre-commit-config.yml +++ /dev/null @@ -1,5 +0,0 @@ -repos: - - repo: https://github.com/doublify/pre-commit-rust - rev: master - hooks: - - id: fmt From 37218c164e8b07671d8414184f41b713c0af76ee Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 18 May 2021 16:43:18 -0700 Subject: [PATCH 074/158] Add new checks to pre-commit. --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f657bcaa3b0..9a2f084ec43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,3 +4,8 @@ repos: 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', '--'] + - id: clippy + args: ['--manifest-path', 'src/Simulation/qdk_sim_rs/Cargo.toml', '--'] + From 37756f72f52a93ee3de954a9ed480fbb9f6eb846 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 19 May 2021 15:15:15 -0700 Subject: [PATCH 075/158] Continued integrating CHP into simulator API. --- src/Simulation/qdk_sim_rs/Cargo.lock | 43 ++++- src/Simulation/qdk_sim_rs/Cargo.toml.template | 6 +- src/Simulation/qdk_sim_rs/src/lib.rs | 36 +++- src/Simulation/qdk_sim_rs/src/processes.rs | 172 +++++++++++++++++- src/Simulation/qdk_sim_rs/src/states.rs | 52 ++++-- src/Simulation/qdk_sim_rs/src/tableau.rs | 20 ++ .../qdk_sim_rs/tests/chp_simulation_tests.rs | 11 ++ 7 files changed, 303 insertions(+), 37 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index d84d33fbc9b..e73340a40f5 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -433,6 +433,15 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "matrixmultiply" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8a15b776d9dfaecd44b03c5828c2199cddff5247215858aac14624f8d6b741" +dependencies = [ + "rawpointer", +] + [[package]] name = "memchr" version = "2.3.4" @@ -454,8 +463,21 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" dependencies = [ - "matrixmultiply", - "num-complex", + "matrixmultiply 0.2.3", + "num-complex 0.2.4", + "num-integer", + "num-traits", + "rawpointer", +] + +[[package]] +name = "ndarray" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b2e4807aaa21dc6dcc3417e5902dc199c3648043bf27b7af4b202332fe4760" +dependencies = [ + "matrixmultiply 0.3.1", + "num-complex 0.4.0", "num-integer", "num-traits", "rawpointer", @@ -470,6 +492,15 @@ checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg", "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +dependencies = [ + "num-traits", "serde", ] @@ -510,8 +541,8 @@ checksum = "a7073fae1e0b82409533a29c6f804b79783d7b2d3c07728fdc4d884eda8cd4f0" dependencies = [ "cfg-if 0.1.10", "libc", - "ndarray", - "num-complex", + "ndarray 0.13.1", + "num-complex 0.2.4", "num-traits", "pyo3", ] @@ -681,8 +712,8 @@ dependencies = [ "getrandom", "itertools 0.9.0", "lazy_static", - "ndarray", - "num-complex", + "ndarray 0.15.2", + "num-complex 0.4.0", "num-traits", "numpy", "pyo3", diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index 434fba7e817..0ce1ba0dd10 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -52,12 +52,12 @@ codegen-units = 1 # Reduce number of codegen units to increase optimizations. panic = 'unwind' [dependencies] -ndarray = { version = "0.13.1", features = ["serde"] } -num-complex = { version = "0.2", features = ["serde"] } +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 = "0.7.3" +rand = { version = "0.7.3", features = ["alloc"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" lazy_static = "1.4.0" diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 9c212cd092b..888604e83bc 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -17,6 +17,8 @@ extern crate derive_more; extern crate serde; use std::usize; +use linalg::Tensor; +use ndarray::Array2; use serde::{Deserialize, Serialize}; pub mod c_api; @@ -32,7 +34,8 @@ mod utils; pub use crate::instrument::*; pub use crate::noise_model::NoiseModel; pub use crate::processes::*; -pub use crate::states::State; +pub use crate::states::{State, StateData}; +pub use crate::tableau::Tableau; pub use crate::utils::*; #[cfg(feature = "python")] @@ -53,11 +56,38 @@ impl QubitSized { } } +/// An element of the single-qubit Pauli group. +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum Pauli { I = 0, X = 1, - Z = 3, - Y = 2, + Y = 3, + Z = 2, +} + +/// Types that can be converted to unitary matrices. +pub trait AsUnitary { + /// Returns a representation as a unitary matrix. + fn as_unitary(&self) -> Array2; +} + +impl AsUnitary for Pauli { + fn as_unitary(&self) -> Array2 { + match self { + Pauli::I => common_matrices::nq_eye(1), + Pauli::X => common_matrices::x(), + Pauli::Y => common_matrices::y(), + Pauli::Z => common_matrices::z(), + } + } +} + +impl AsUnitary for Vec { + fn as_unitary(&self) -> Array2 { + let sq_unitaries = self.iter().map(|p| p.as_unitary()); + let result = sq_unitaries.reduce(|p, q| p.tensor(&q)); + result.unwrap() + } } /// Metadata about how this crate was built. diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 4c328107cef..09036f25830 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -1,21 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::linalg::ConjBy; use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; use crate::log_as_err; -use crate::states::StateData::{Mixed, Pure}; +use crate::states::StateData::{Mixed, Pure, Stabilizer}; use crate::NoiseModel; use crate::QubitSized; use crate::State; use crate::C64; +use crate::{linalg::ConjBy, AsUnitary, Pauli}; use crate::{ log, - processes::ProcessData::{KrausDecomposition, Unitary}, + processes::ProcessData::{KrausDecomposition, PauliChannel, Unitary}, }; use itertools::Itertools; -use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; +use ndarray::{Array, Array2, Array3, ArrayView2, Axis, NewAxis}; +use num_complex::Complex; use num_traits::{One, Zero}; +use rand::{distributions::WeightedIndex, prelude::Distribution, thread_rng}; use serde::{Deserialize, Serialize}; use std::convert::TryInto; use std::ops::Add; @@ -30,7 +32,12 @@ pub type Process = QubitSized; /// Data used to represent a given process. #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ProcessData { - /// Representation of the process by a unitary matrix. + /// Representation of a process as a mixture of Pauli operators + /// $\{(p_i, P_i)\}$ such that the channel acts as $\rho \mapsto + /// \sum_i p_i P_i \rho P_i^{\dagger}$. + PauliChannel(Vec<(f64, Vec)>), + + /// Representation of the process by an arbitrary unitary matrix. Unitary(Array2), /// Representation of the process by the singular vectors of its Choi @@ -42,6 +49,17 @@ pub enum ProcessData { } impl Process { + pub fn new_pauli_channel(data: T) -> Self { + let data = data.into_pauli_mixture(); + // How many qubits? + // TODO: check that every Pauli is supported on the same number of + // qubits. + let n_qubits = data[0].1.len(); + Process { + n_qubits, + data: PauliChannel(data), + } + } // TODO: methods to forcibly convert representations. /// Applies this process to a quantum register with a given @@ -65,6 +83,11 @@ impl Process { } else { self.apply(&state.to_mixed())?.data } + }, + PauliChannel(paulis) => { + // Promote and recurse. + let promoted = promote_pauli_channel(paulis); + promoted.apply(state)?.data } }, Mixed(rho) => match &self.data { @@ -77,8 +100,37 @@ impl Process { } sum }), + PauliChannel(paulis) => { + // Promote and recurse. + let promoted = promote_pauli_channel(paulis); + promoted.apply(state)?.data + } + }, + Stabilizer(tableau) => match &self.data { + PauliChannel(paulis) => { + // TODO[perf]: Introduce an apply_mut method to + // avoid extraneous cloning. + let mut new_tableau = tableau.clone(); + // Sample a Pauli and apply it. + let weighted = WeightedIndex::new( + paulis.iter().map(|(pr, _)| pr) + ).unwrap(); + let idx = weighted.sample(&mut thread_rng()); + let pauli = &(&paulis)[idx].1; + // TODO: Consider moving the following to a method + // on Tableau itself. + for (idx_qubit, p) in pauli.iter().enumerate() { + match p { + Pauli::I => (), + Pauli::X => new_tableau.apply_x_mut(idx_qubit), + Pauli::Y => new_tableau.apply_y_mut(idx_qubit), + Pauli::Z => new_tableau.apply_z_mut(idx_qubit) + } + } + Stabilizer(new_tableau) + }, + _ => unimplemented!("Promotion of stabilizer tableaus to pure or mixed states is not yet supported.") }, - _ => todo!(), }, }) } else { @@ -169,7 +221,20 @@ impl Process { target.assign(&big_kraus); } KrausDecomposition(extended) - } + }, + PauliChannel(paulis) => PauliChannel( + paulis.iter() + .map(|(pr, pauli)| { + if pauli.len() != 1 { + panic!("Pauli channel acts on more than one qubit, cannot extend to 𝑛 qubits."); + } + let p = pauli[0]; + let mut extended = std::iter::repeat(Pauli::I).take(n_qubits).collect_vec(); + extended[idx_qubit] = p; + (*pr, extended) + }) + .collect_vec() + ) }, } } @@ -198,7 +263,21 @@ impl Process { target.assign(&big_kraus); } KrausDecomposition(extended) - } + }, + PauliChannel(paulis) => PauliChannel( + paulis.iter() + .map(|(pr, pauli)| { + if pauli.len() != 2 { + panic!("Pauli channel acts on more than one qubit, cannot extend to 𝑛 qubits."); + } + let p = (pauli[0], pauli[1]); + let mut extended = std::iter::repeat(Pauli::I).take(n_qubits).collect_vec(); + extended[idx_qubit1] = p.0; + extended[idx_qubit2] = p.1; + (*pr, extended) + }) + .collect_vec() + ) }, } } @@ -221,6 +300,7 @@ impl Mul<&Process> for C64 { ks }), KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks), + PauliChannel(paulis) => (self * promote_pauli_channel(paulis)).data, }, } } @@ -376,3 +456,79 @@ pub fn amplitude_damping_channel(gamma: f64) -> Process { ]), } } + +/// A type that can be converted into a mixture of Pauli operators. +pub trait IntoPauliMixture { + fn into_pauli_mixture(self) -> Vec<(f64, Vec)>; +} + +impl IntoPauliMixture for Vec<(f64, Vec)> { + fn into_pauli_mixture(self) -> Vec<(f64, Vec)> { + self + } +} + +impl IntoPauliMixture for Vec { + fn into_pauli_mixture(self) -> Vec<(f64, Vec)> { + vec![(1.0, self)] + } +} + +impl IntoPauliMixture for Vec<(f64, Pauli)> { + fn into_pauli_mixture(self) -> Vec<(f64, Vec)> { + self.iter().map(|(pr, p)| (*pr, vec![*p])).collect_vec() + } +} + +impl IntoPauliMixture for Pauli { + fn into_pauli_mixture(self) -> Vec<(f64, Vec)> { + vec![(1.0, vec![self])] + } +} + +/// Given a vector of Paulis and the probability of applying each one, promotes +/// that vector to a process that acts on state vectors (e.g.: a unitary matrix, +/// or Kraus decomposition). +/// +/// This function is a private utility mainly used in handling the case where +/// a mixed Pauli channel is applied to a pure or mixed state. +fn promote_pauli_channel(paulis: &Vec<(f64, Vec)>) -> Process { + // TODO: Check that there's at least one Pauli... empty vectors aren't + // supported here. + if paulis.len() == 1 { + // Just one Pauli, so can box it up into a unitary. + let (_, pauli) = &paulis[0]; + // TODO[testing]: check that pr is 1.0. + Process { + n_qubits: pauli.len(), + data: Unitary(pauli.as_unitary()), + } + } else { + // To turn a mixed Pauli channel into a Kraus decomposition, we need + // to take the square root of each probability. + let matrices = paulis + .iter() + .map(|p| { + p.1.as_unitary() + * Complex { + re: p.0.sqrt(), + im: 0.0f64, + } + }) + .map(|u| { + let x = u.slice(s![NewAxis, .., ..]); + x.to_owned() + }) + .collect_vec(); + Process { + n_qubits: paulis[0].1.len(), + data: KrausDecomposition( + ndarray::concatenate( + Axis(0), + matrices.iter().map(|u| u.view()).collect_vec().as_slice(), + ) + .unwrap(), + ), + } + } +} diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 4dc1b194f2d..681f9caa7c0 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -1,14 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::common_matrices; use crate::linalg::Tensor; use crate::linalg::Trace; -use crate::states::StateData::Mixed; -use crate::states::StateData::Pure; +use crate::states::StateData::{Mixed, Pure, Stabilizer}; use crate::tableau::Tableau; use crate::QubitSized; use crate::C64; +use crate::{common_matrices, tableau}; use core::fmt::Display; use ndarray::{Array1, Array2, Axis}; use num_traits::One; @@ -60,6 +59,30 @@ impl Display for StateData { } impl State { + /// Returns a new mixed state on a given number of qubits. + /// By convention, new mixed states start off in the "all-zeros" state, + /// $\rho = \ket{00\cdots 0}\bra{00\cdots 0}$. + pub fn new_mixed(n_qubits: usize) -> State { + let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); + State { + n_qubits, + data: Mixed(common_matrices::elementary_matrix( + (0, 0), + (new_dim, new_dim), + )), + } + } + + /// Returns a new stabilizer state on a given number of qubits. + /// By convention, new stabilizer states start off in the "all-zeros" state, + /// $\left\langle Z_0, Z_1, \dots, Z_{n - 1} \right\rangle$. + pub fn new_stabilizer(n_qubits: usize) -> State { + State { + n_qubits, + data: Stabilizer(Tableau::new(n_qubits)), + } + } + /// Extends this state to be a state on `n_qubits` additional qubits. /// New qubits are added "to the right," e.g.: $\left|\psi\right\rangle$ /// is extended to $\left|\psi 0\right\rangle$. @@ -86,20 +109,6 @@ impl State { } } - /// Returns a new mixed state on a given number of qubits. - /// By convention, new mixed states start off in the "all-zeros" state, - /// $\rho = \ket{00\cdots 0}\bra{00\cdots 0}. - pub fn new_mixed(n_qubits: usize) -> State { - let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); - State { - n_qubits, - data: Mixed(common_matrices::elementary_matrix( - (0, 0), - (new_dim, new_dim), - )), - } - } - /// Returns a copy of this state, represented as a mixed state. pub fn to_mixed(&self) -> State { State { @@ -119,6 +128,15 @@ impl State { }, } } + + /// If the given state can be represented by a stabilizer tableau, returns + /// that tableau. + pub fn get_tableau(&self) -> Option<&Tableau> { + match self.data { + Stabilizer(ref tableau) => Some(tableau), + _ => None, + } + } } impl Trace for &State { diff --git a/src/Simulation/qdk_sim_rs/src/tableau.rs b/src/Simulation/qdk_sim_rs/src/tableau.rs index 64126252121..ca03419044a 100644 --- a/src/Simulation/qdk_sim_rs/src/tableau.rs +++ b/src/Simulation/qdk_sim_rs/src/tableau.rs @@ -167,6 +167,26 @@ impl Tableau { self.apply_cnot_mut(idx_1, idx_2); } + /// Asserts whether a hypothetical single-qubit $Z$-basis measurement + /// would agree with an expected result. + /// + /// If the assertion would pass, `Ok(())` is returned, otherwise an [`Err`] + /// describing the assertion failure is returned. + pub fn assert_meas(&self, idx_target: usize, expected: bool) -> Result<(), String> { + let actual = self.determinstic_result(idx_target).ok_or(format!( + "Expected {}, but measurement result would be random.", + expected + ))?; + if actual != expected { + Err(format!( + "Expected {}, but measurement result would actually be {}.", + expected, actual + )) + } else { + Ok(()) + } + } + pub fn meas_mut(&mut self, idx_target: usize) -> bool { if let Some(result) = self.determinstic_result(idx_target) { return result; diff --git a/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs b/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs new file mode 100644 index 00000000000..8b05ffc4bcd --- /dev/null +++ b/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs @@ -0,0 +1,11 @@ +use qdk_sim::{Pauli, Process, ProcessData, State, StateData, Tableau}; + +#[test] +fn pauli_channel_applies_correctly() -> Result<(), String> { + let x = Process::new_pauli_channel(Pauli::X); + let state = State::new_stabilizer(1); + let output_state = x.apply(&state)?; + + let tableau = output_state.get_tableau().unwrap(); + tableau.assert_meas(0, true) +} From c373fab17947466e2f46157339b3f6338517ae78 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 19 May 2021 15:17:08 -0700 Subject: [PATCH 076/158] Address clippy warnings. --- src/Simulation/qdk_sim_rs/src/processes.rs | 2 +- src/Simulation/qdk_sim_rs/src/states.rs | 2 +- src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 09036f25830..a4122b59191 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -492,7 +492,7 @@ impl IntoPauliMixture for Pauli { /// /// This function is a private utility mainly used in handling the case where /// a mixed Pauli channel is applied to a pure or mixed state. -fn promote_pauli_channel(paulis: &Vec<(f64, Vec)>) -> Process { +fn promote_pauli_channel(paulis: &[(f64, Vec)]) -> Process { // TODO: Check that there's at least one Pauli... empty vectors aren't // supported here. if paulis.len() == 1 { diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 681f9caa7c0..8c731ec4313 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::common_matrices; use crate::linalg::Tensor; use crate::linalg::Trace; use crate::states::StateData::{Mixed, Pure, Stabilizer}; use crate::tableau::Tableau; use crate::QubitSized; use crate::C64; -use crate::{common_matrices, tableau}; use core::fmt::Display; use ndarray::{Array1, Array2, Axis}; use num_traits::One; diff --git a/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs b/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs index 8b05ffc4bcd..7270a82f9b8 100644 --- a/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs +++ b/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs @@ -1,4 +1,4 @@ -use qdk_sim::{Pauli, Process, ProcessData, State, StateData, Tableau}; +use qdk_sim::{Pauli, Process, State, Tableau}; #[test] fn pauli_channel_applies_correctly() -> Result<(), String> { From 6c82ec6c3d833be483a1f75c993632b916a696ea Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 19 May 2021 18:04:48 -0700 Subject: [PATCH 077/158] Work on docs. --- src/Simulation/qdk_sim_rs/Cargo.toml.template | 5 +++-- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 5 +++++ src/Simulation/qdk_sim_rs/docs-includes/after.html | 12 ++++++++++++ src/Simulation/qdk_sim_rs/docs-includes/header.html | 3 +++ src/Simulation/qdk_sim_rs/src/c_api.rs | 2 ++ src/Simulation/qdk_sim_rs/src/lib.rs | 4 ++++ src/Simulation/qdk_sim_rs/src/processes.rs | 2 ++ src/Simulation/qdk_sim_rs/src/states.rs | 1 + src/Simulation/qdk_sim_rs/src/tableau.rs | 13 +++++++++++++ 9 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/docs-includes/after.html create mode 100644 src/Simulation/qdk_sim_rs/docs-includes/header.html diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index 0ce1ba0dd10..6e0f69c829b 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -42,8 +42,9 @@ doc = [] features = ["doc"] # Enable LaTeX on docs.rs. -# See https://stackoverflow.com/a/54573800/267841 for why this works. -rustdoc-args = [ "--html-in-header", "docs/header.html" ] +# See https://stackoverflow.com/a/54573800/267841 and +# https://github.com/rust-num/num/pull/226/files for why this works. +rustdoc-args = [ "--html-in-header", "docs-includes/header.html", "--html-after-content", "docs-includes/after.html" ] [profile.release] diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 0ff71c4c12d..1cf89ff7382 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -6,6 +6,11 @@ Push-Location $PSScriptRoot $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); cargo +nightly build -Z unstable-options @releaseFlag --out-dir $DropPath; + # 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 --features doc; + # Free disk space by cleaning up. # Note that this takes longer, but saves ~1 GB of space, which is # exceptionally helpful in CI builds. 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/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 58dfd5611b6..5aa5d0b3324 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -216,6 +216,8 @@ pub unsafe extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> }) } +/// Returns the currently configured noise model for a given simulator, +/// serialized as a string representing a JSON object. #[no_mangle] pub extern "C" fn get_noise_model(sim_id: usize) -> *const c_char { let state = &*STATE.lock().unwrap(); diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 888604e83bc..939019c25ca 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -59,9 +59,13 @@ impl QubitSized { /// An element of the single-qubit Pauli group. #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum Pauli { + /// The identity operator. I = 0, + /// The Pauli $X$ operator. X = 1, + /// The Pauli $Y$ operator. Y = 3, + /// The Pauli $Z$ operator. Z = 2, } diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index a4122b59191..985a1da2aac 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -49,6 +49,7 @@ pub enum ProcessData { } impl Process { + /// Returns a new Pauli channel, given a mixture of Pauli operators. pub fn new_pauli_channel(data: T) -> Self { let data = data.into_pauli_mixture(); // How many qubits? @@ -459,6 +460,7 @@ pub fn amplitude_damping_channel(gamma: f64) -> Process { /// A type that can be converted into a mixture of Pauli operators. pub trait IntoPauliMixture { + /// Convert this value into a mixture of multi-qubit Pauli operators. fn into_pauli_mixture(self) -> Vec<(f64, Vec)>; } diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 8c731ec4313..c1baf664b73 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -17,6 +17,7 @@ use std::convert::TryInto; #[cfg(feature = "python")] use pyo3::prelude::*; +/// Data used to represent a given state. #[derive(Clone, Debug, Serialize, Deserialize)] pub enum StateData { /// A pure state, represented as a vector of complex numbers. diff --git a/src/Simulation/qdk_sim_rs/src/tableau.rs b/src/Simulation/qdk_sim_rs/src/tableau.rs index ca03419044a..296461ca306 100644 --- a/src/Simulation/qdk_sim_rs/src/tableau.rs +++ b/src/Simulation/qdk_sim_rs/src/tableau.rs @@ -84,12 +84,14 @@ impl Tableau { #[cfg_attr(feature = "python", pymethods)] impl Tableau { + /// Returns a serialization of this stabilizer tableau as a JSON object. #[cfg(feature = "python")] pub fn as_json(&self) -> PyResult { serde_json::to_string(self) .map_err(|e| PyErr::from(Error::new(ErrorKind::Other, e.to_string()))) } + /// Applies a Hadamard operation in-place to the given qubit. pub fn apply_h_mut(&mut self, idx_target: usize) { let idxs = (self.idx_x(idx_target), self.idx_z(idx_target)); swap_columns(&mut self.table, idxs); @@ -101,6 +103,7 @@ impl Tableau { } } + /// Applies a phase operation ($S$) in-place to the given qubit. pub fn apply_s_mut(&mut self, idx_target: usize) { let idx_phase = self.idx_phase(); for idx_row in 0..2 * self.n_qubits { @@ -115,6 +118,8 @@ impl Tableau { } } + /// Applies a controlled-NOT operation in-place, given control and target + /// qubits. pub fn apply_cnot_mut(&mut self, idx_control: usize, idx_target: usize) { let idx_phase = self.idx_phase(); for idx_row in 0..2 * self.n_qubits { @@ -138,29 +143,35 @@ impl Tableau { } } + /// Applies a Pauli $X$ operation in-place to the given qubit. pub fn apply_x_mut(&mut self, idx_target: usize) { self.apply_h_mut(idx_target); self.apply_z_mut(idx_target); self.apply_h_mut(idx_target); } + /// Applies an adjoint phase operation ($S^{\dagger}$) in-place to the + /// given qubit. pub fn apply_s_adj_mut(&mut self, idx_target: usize) { self.apply_s_mut(idx_target); self.apply_s_mut(idx_target); self.apply_s_mut(idx_target); } + /// Applies a Pauli $Y$ operation in-place to the given qubit. pub fn apply_y_mut(&mut self, idx_target: usize) { self.apply_s_adj_mut(idx_target); self.apply_x_mut(idx_target); self.apply_s_mut(idx_target); } + /// Applies a Pauli $Z$ operation in-place to the given qubit. pub fn apply_z_mut(&mut self, idx_target: usize) { self.apply_s_mut(idx_target); self.apply_s_mut(idx_target); } + /// Applies a SWAP operation in-place between two qubits. pub fn apply_swap_mut(&mut self, idx_1: usize, idx_2: usize) { self.apply_cnot_mut(idx_1, idx_2); self.apply_cnot_mut(idx_2, idx_1); @@ -187,6 +198,8 @@ impl Tableau { } } + /// Measures a single qubit in the Pauli $Z$-basis, returning the result, + /// and updating the stabilizer tableau in-place. pub fn meas_mut(&mut self, idx_target: usize) -> bool { if let Some(result) = self.determinstic_result(idx_target) { return result; From ec8c4e5e83c6caa054f3efcac5fd2b738d5e9194 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 19 May 2021 18:17:25 -0700 Subject: [PATCH 078/158] Separate out paulis.rs. --- src/Simulation/qdk_sim_rs/Cargo.toml.template | 10 ---- src/Simulation/qdk_sim_rs/README.md | 25 +++++++-- src/Simulation/qdk_sim_rs/src/lib.rs | 53 +++---------------- src/Simulation/qdk_sim_rs/src/paulis.rs | 45 ++++++++++++++++ .../qdk_sim_rs/tests/chp_simulation_tests.rs | 3 ++ 5 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/src/paulis.rs diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index 6e0f69c829b..2da89075f3e 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -28,19 +28,10 @@ wasm = ["web-sys"] # When Python bindings are enabled, we also need the pyo3 dependency. python = ["pyo3", "numpy"] -# We use an empty feature to enable documentation generation, as is required -# until https://github.com/rust-lang/rust/pull/83366 is completed. -# See https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643 -# for discussion. -doc = [] - # To make sure docs are enabled on docs.rs, we need to also pass the "doc" # feature in our metadata. [package.metadata.docs.rs] -# Features to pass to Cargo (default: []) -features = ["doc"] - # 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. @@ -81,7 +72,6 @@ numpy = {version = "0.13.1", optional = true} # was built to C and Rust callers. built = "0.5.0" - [build-dependencies] built = "0.5.0" diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index b984bd097af..fdea8fb4cec 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -1,3 +1,11 @@ +# 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#504](https://github.com/microsoft/qsharp-runtime/issues/504) for more information. + This **experimental** crate contains simulation functionality for the Quantum Development Kit, including: - Open systems simulation @@ -7,18 +15,27 @@ The [`c_api`] module allows for using the simulation functionality in this crate ## Cargo Features -- **`doc`**: Required to build documentation. +- **`doc`**: Enables building documentation (requires `+nightly`). - **`python`**: Enables Python bindings for this crate. - **`wasm`**: Ensures that the crate is compatible with usage from WebAssembly. -## Representing open quantum systems +## Representing quantum systems -This crate provides several different data structures for representing open quantum systems in a variety of different conventions: +This crate provides several different data structures for representing quantum systems in a variety of different conventions: -- [`State`]\: Represents pure or mixed states of a register of qubits. +- [`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 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. diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 939019c25ca..970bf854ab6 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -5,27 +5,25 @@ // the "doc" feature is turned on (requires +nightly). // See https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643 // for discussion. -#![cfg_attr(feature = "doc", feature(extended_key_value_attributes))] -#![cfg_attr(feature = "doc", cfg_attr(feature = "doc", doc = include_str!("../README.md")))] -#![cfg_attr(feature = "doc", deny(missing_docs))] -#![cfg_attr(feature = "doc", warn(missing_doc_code_examples))] +#![cfg_attr(doc, feature(extended_key_value_attributes))] +#![cfg_attr(doc, cfg_attr(doc, doc = include_str!("../README.md")))] +#![cfg_attr(doc, deny(missing_docs))] +#![cfg_attr(doc, warn(missing_doc_code_examples))] #[macro_use(array, s)] extern crate ndarray; extern crate derive_more; extern crate serde; -use std::usize; - -use linalg::Tensor; -use ndarray::Array2; use serde::{Deserialize, Serialize}; +use std::usize; pub mod c_api; pub mod common_matrices; mod instrument; pub mod linalg; mod noise_model; +mod paulis; mod processes; mod states; mod tableau; @@ -33,6 +31,7 @@ mod utils; pub use crate::instrument::*; pub use crate::noise_model::NoiseModel; +pub use crate::paulis::*; pub use crate::processes::*; pub use crate::states::{State, StateData}; pub use crate::tableau::Tableau; @@ -56,44 +55,6 @@ impl QubitSized { } } -/// An element of the single-qubit Pauli group. -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum Pauli { - /// The identity operator. - I = 0, - /// The Pauli $X$ operator. - X = 1, - /// The Pauli $Y$ operator. - Y = 3, - /// The Pauli $Z$ operator. - Z = 2, -} - -/// Types that can be converted to unitary matrices. -pub trait AsUnitary { - /// Returns a representation as a unitary matrix. - fn as_unitary(&self) -> Array2; -} - -impl AsUnitary for Pauli { - fn as_unitary(&self) -> Array2 { - match self { - Pauli::I => common_matrices::nq_eye(1), - Pauli::X => common_matrices::x(), - Pauli::Y => common_matrices::y(), - Pauli::Z => common_matrices::z(), - } - } -} - -impl AsUnitary for Vec { - fn as_unitary(&self) -> Array2 { - let sq_unitaries = self.iter().map(|p| p.as_unitary()); - let result = sq_unitaries.reduce(|p, q| p.tensor(&q)); - result.unwrap() - } -} - /// Metadata about how this crate was built. pub mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/src/Simulation/qdk_sim_rs/src/paulis.rs b/src/Simulation/qdk_sim_rs/src/paulis.rs new file mode 100644 index 00000000000..73acc349274 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/src/paulis.rs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use ndarray::Array2; +use serde::{Deserialize, Serialize}; + +use crate::{common_matrices, linalg::Tensor, C64}; + +/// An element of the single-qubit Pauli group. +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum Pauli { + /// The identity operator. + I = 0, + /// The Pauli $X$ operator. + X = 1, + /// The Pauli $Y$ operator. + Y = 3, + /// The Pauli $Z$ operator. + Z = 2, +} + +/// Types that can be converted to unitary matrices. +pub trait AsUnitary { + /// Returns a representation as a unitary matrix. + fn as_unitary(&self) -> Array2; +} + +impl AsUnitary for Pauli { + fn as_unitary(&self) -> Array2 { + match self { + Pauli::I => common_matrices::nq_eye(1), + Pauli::X => common_matrices::x(), + Pauli::Y => common_matrices::y(), + Pauli::Z => common_matrices::z(), + } + } +} + +impl AsUnitary for Vec { + fn as_unitary(&self) -> Array2 { + let sq_unitaries = self.iter().map(|p| p.as_unitary()); + let result = sq_unitaries.reduce(|p, q| p.tensor(&q)); + result.unwrap() + } +} diff --git a/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs b/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs index 7270a82f9b8..c709d664cd3 100644 --- a/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs +++ b/src/Simulation/qdk_sim_rs/tests/chp_simulation_tests.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use qdk_sim::{Pauli, Process, State, Tableau}; #[test] From a44a135228ed8a02d668c83367f6cecd0ec63943 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 20 May 2021 14:56:16 -0700 Subject: [PATCH 079/158] Expose more simulation functionality to Python. --- src/Simulation/qdk_sim_rs/Cargo.lock | 43 +++++ src/Simulation/qdk_sim_rs/Cargo.toml.template | 1 + src/Simulation/qdk_sim_rs/README.md | 1 + src/Simulation/qdk_sim_rs/qdk_sim/__init__.py | 10 +- src/Simulation/qdk_sim_rs/src/instrument.rs | 126 +++++++++----- src/Simulation/qdk_sim_rs/src/noise_model.rs | 34 ++-- src/Simulation/qdk_sim_rs/src/paulis.rs | 4 +- src/Simulation/qdk_sim_rs/src/python.rs | 163 +++++++++++++++++- src/Simulation/qdk_sim_rs/src/tableau.rs | 42 ++--- 9 files changed, 337 insertions(+), 87 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock index e73340a40f5..997444259d9 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ b/src/Simulation/qdk_sim_rs/Cargo.lock @@ -225,6 +225,17 @@ dependencies = [ "syn", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" version = "0.99.11" @@ -533,6 +544,28 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "numpy" version = "0.13.1" @@ -646,6 +679,15 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -715,6 +757,7 @@ dependencies = [ "ndarray 0.15.2", "num-complex 0.4.0", "num-traits", + "num_enum", "numpy", "pyo3", "rand", diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index 2da89075f3e..2eb5986687b 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -54,6 +54,7 @@ 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. diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index fdea8fb4cec..03a31abe0f3 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -39,3 +39,4 @@ TODO - 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 [`std::Result`] returns instead. diff --git a/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py index 5a68398bbdf..f2129392806 100644 --- a/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py +++ b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py @@ -1,10 +1,18 @@ +from enum import Enum +import enum import json import qdk_sim._qdk_sim_rs as _native # Re-export native classes. from qdk_sim._qdk_sim_rs import ( - Tableau, NoiseModel, Instrument + Tableau, NoiseModel, Instrument, State, Process ) +class Pauli(enum.Enum): + I = 0 + X = 1 + Y = 3 + Z = 2 + def build_info(): return json.loads(_native.build_info_json()) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 36c893d51ac..89e922c6489 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::states::StateData::Mixed; use crate::Process; +use crate::{states::StateData::Mixed, StateData}; use rand::Rng; use std::iter::Iterator; @@ -11,64 +11,104 @@ use crate::State; use serde::{Deserialize, Serialize}; -#[cfg(feature = "python")] -use pyo3::prelude::*; +// TODO[design]: Instrument works pretty differently from State and Process; should +// likely refactor for consistency. -#[cfg_attr(feature = "python", pyclass(name = "Instrument"))] #[derive(Serialize, Deserialize, Debug)] /// Represents a quantum instrument; that is, a process that accepts a quantum /// state and returns the new state of a system and classical data extracted /// from that system. -pub struct Instrument { +pub enum Instrument { /// The effects of the instrument, represented as completely positive /// trace non-increasing (CPTNI) processes. - pub effects: Vec, + Effects(Vec), + + /// An instrument that measures a single qubit in the $Z$-basis, up to a + /// readout error (probability of result being flipped). + /// + /// Primarily useful when working with stabilizer states or other + /// subtheories. + ZMeasurement { + /// Probability with which a result is flipped. + pr_readout_error: f64, + }, } impl Instrument { /// Samples from this instrument, returning the measurement result and /// the new state of the system conditioned on that measurement result. pub fn sample(&self, idx_qubits: &[usize], state: &State) -> (usize, State) { - let mut possible_outcomes = self - .effects - .iter() - .enumerate() - .map(|(idx, effect)| { - let output_state = effect.apply_to(idx_qubits, state).unwrap(); - let tr = (&output_state).trace(); - (idx, output_state, tr.re) - }) - .collect::>(); - let mut rng = rand::thread_rng(); - let random_sample: f64 = rng.gen(); - for (idx, cum_pr) in possible_outcomes - .iter() - .scan(0.0f64, |acc, (_idx, _, pr)| { - *acc += *pr; - Some(*acc) - }) - .enumerate() - { - if random_sample < cum_pr { - // In order to not have to copy the output state, we need - // to be able to move it out from the vector. To do so, - // we retain only the element of the vector whose index - // is the one we want and then pop it, leaving an empty - // vector (that is, a vector that owns no data). - possible_outcomes.retain(|(i, _, _)| idx == *i); - let (_, mut output_state, tr) = possible_outcomes.pop().unwrap(); - if tr.abs() >= 1e-10 { - if let Mixed(ref rho) = output_state.data { - output_state.data = Mixed(rho * (1.0f64 / tr)); - } else { - panic!("Couldn't renormalize, expected mixed output from instrument."); + match self { + Instrument::Effects(ref effects) => sample_effects(effects, idx_qubits, state), + Instrument::ZMeasurement { pr_readout_error } => { + if idx_qubits.len() != 1 { + panic!("Z-basis measurement instruments only supported for single qubits."); + } + let idx_target = idx_qubits[0]; + match state.data { + StateData::Pure(ref _psi) => todo!(), + StateData::Mixed(ref _rho) => todo!(), + StateData::Stabilizer(ref tableau) => { + // TODO[perf]: allow instruments to sample in-place, + // reducing copying. + let mut new_tableau = tableau.clone(); + let result = new_tableau.meas_mut(idx_target) + ^ rand::thread_rng().gen_bool(*pr_readout_error); + ( + if result { 1 } else { 0 }, + State { + n_qubits: state.n_qubits, + data: StateData::Stabilizer(new_tableau), + }, + ) } } - return (idx, output_state); } } - let (idx, output_state, _) = possible_outcomes.pop().unwrap(); - drop(possible_outcomes); - (idx, output_state) } + + // TODO: Add more methods for making new instruments in convenient ways. +} + +fn sample_effects(effects: &Vec, idx_qubits: &[usize], state: &State) -> (usize, State) { + let mut possible_outcomes = effects + .iter() + .enumerate() + .map(|(idx, effect)| { + let output_state = effect.apply_to(idx_qubits, state).unwrap(); + let tr = (&output_state).trace(); + (idx, output_state, tr.re) + }) + .collect::>(); + let mut rng = rand::thread_rng(); + let random_sample: f64 = rng.gen(); + for (idx, cum_pr) in possible_outcomes + .iter() + .scan(0.0f64, |acc, (_idx, _, pr)| { + *acc += *pr; + Some(*acc) + }) + .enumerate() + { + if random_sample < cum_pr { + // In order to not have to copy the output state, we need + // to be able to move it out from the vector. To do so, + // we retain only the element of the vector whose index + // is the one we want and then pop it, leaving an empty + // vector (that is, a vector that owns no data). + possible_outcomes.retain(|(i, _, _)| idx == *i); + let (_, mut output_state, tr) = possible_outcomes.pop().unwrap(); + if tr.abs() >= 1e-10 { + if let Mixed(ref rho) = output_state.data { + output_state.data = Mixed(rho * (1.0f64 / tr)); + } else { + panic!("Couldn't renormalize, expected mixed output from instrument."); + } + } + return (idx, output_state); + } + } + let (idx, output_state, _) = possible_outcomes.pop().unwrap(); + drop(possible_outcomes); + (idx, output_state) } diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 7863a6b1668..5d1ea1bfc8b 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -83,24 +83,22 @@ impl NoiseModel { n_qubits: 1, data: Unitary(common_matrices::z()), }; - let z_meas = Instrument { - effects: vec![ - Process { - n_qubits: 1, - data: KrausDecomposition(array![[ - [C64::one(), C64::zero()], - [C64::zero(), C64::zero()] - ]]), - }, - Process { - n_qubits: 1, - data: KrausDecomposition(array![[ - [C64::zero(), C64::zero()], - [C64::zero(), C64::one()] - ]]), - }, - ], - }; + let z_meas = Instrument::Effects(vec![ + Process { + n_qubits: 1, + data: KrausDecomposition(array![[ + [C64::one(), C64::zero()], + [C64::zero(), C64::zero()] + ]]), + }, + Process { + n_qubits: 1, + data: KrausDecomposition(array![[ + [C64::zero(), C64::zero()], + [C64::zero(), C64::one()] + ]]), + }, + ]); NoiseModel { initial_state: State { n_qubits: 1, diff --git a/src/Simulation/qdk_sim_rs/src/paulis.rs b/src/Simulation/qdk_sim_rs/src/paulis.rs index 73acc349274..a5eec88965c 100644 --- a/src/Simulation/qdk_sim_rs/src/paulis.rs +++ b/src/Simulation/qdk_sim_rs/src/paulis.rs @@ -2,12 +2,14 @@ // Licensed under the MIT License. use ndarray::Array2; +use num_enum::{IntoPrimitive, TryFromPrimitive}; use serde::{Deserialize, Serialize}; use crate::{common_matrices, linalg::Tensor, C64}; /// An element of the single-qubit Pauli group. -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] pub enum Pauli { /// The identity operator. I = 0, diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index 271080d646b..b26edf6dcbc 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -1,6 +1,14 @@ -use crate::tableau::Tableau; -use crate::{built_info, Instrument, NoiseModel}; -use pyo3::prelude::*; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// FIXME: remove when the Python module is documented. +#![allow(missing_docs)] + +use std::convert::TryFrom; + +use crate::{built_info, Instrument, NoiseModel, Pauli, Process}; +use crate::{tableau::Tableau, State}; +use pyo3::{exceptions::PyRuntimeError, prelude::*, types::PyType, PyObjectProtocol}; use serde_json::json; #[pymodule] @@ -21,8 +29,155 @@ fn _qdk_sim_rs(_py: Python, m: &PyModule) -> PyResult<()> { serde_json::to_string(&build_info).unwrap() } + #[pyfn(m, "pauli_test")] + fn pauli_test(_py: Python, p: Pauli) -> Pauli { + p + } + m.add_class::()?; - m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; m.add_class::()?; Ok(()) } + +// Design notes: +// +// Some important Rust concepts, such as enums, do not forward "nicely" to +// Python types. When that happens, we make new types here to wrap the pure-Rust +// equivalents. + +#[pyclass(name = "State")] +#[derive(Debug, Clone)] +pub struct PyState { + data: State, +} + +#[pyproto] +impl PyObjectProtocol for PyState { + fn __repr__(&self) -> String { + format!("", self.data) + } +} + +#[pymethods] +impl PyState { + #[staticmethod] + pub fn new_mixed(n_qubits: usize) -> Self { + Self { + data: State::new_mixed(n_qubits), + } + } + + #[staticmethod] + pub fn new_stabilizer(n_qubits: usize) -> Self { + Self { + data: State::new_stabilizer(n_qubits), + } + } +} + +#[pyclass(name = "Process")] +#[derive(Debug)] +pub struct PyProcess { + data: Process, +} + +#[pyproto] +impl PyObjectProtocol for PyProcess { + fn __repr__(&self) -> String { + format!("", self.data) + } +} + +#[pymethods] +impl PyProcess { + #[staticmethod] + pub fn new_pauli_channel(data: Vec<(f64, Vec)>) -> Self { + PyProcess { + data: Process::new_pauli_channel(data), + } + } + + pub fn apply(&self, state: PyState) -> PyResult { + let data = self + .data + .apply(&state.data) + .map_err(|e| PyErr::new::(e))?; + Ok(PyState { data }) + } + + pub fn apply_to(&self, idx_qubits: Vec, state: PyState) -> PyResult { + let data = self + .data + .apply_to(idx_qubits.as_slice(), &state.data) + .map_err(|e| PyErr::new::(e))?; + Ok(PyState { data }) + } +} + +#[pyclass(name = "Instrument")] +#[derive(Debug)] +pub struct PyInstrument { + data: Instrument, +} + +#[pyproto] +impl PyObjectProtocol for PyInstrument { + fn __repr__(&self) -> String { + format!("", self.data) + } +} + +#[pymethods] +impl PyInstrument { + pub fn sample(&self, idx_qubits: Vec, state: &PyState) -> (usize, PyState) { + let (result, data) = self.data.sample(idx_qubits.as_slice(), &state.data); + (result, PyState { data }) + } + + #[staticmethod] + #[args(pr_readout_error = "0.0")] + pub fn new_z_measurement(pr_readout_error: f64) -> PyInstrument { + PyInstrument { + data: Instrument::ZMeasurement { pr_readout_error }, + } + } +} + +// See https://stackoverflow.com/q/67412827/267841 for why the following works +// to expose Pauli. If we have more enums like this, we could likewise expose +// them by using the macro from that SO question. +// +// We got through some extra hoops to make sure everything user-facing appears +// as a value of the Pauli enum on the Python side. +impl IntoPy for Pauli { + fn into_py(self, py: Python) -> PyObject { + // Import the root module containing this native extension, and find + // the enum definition there that we can call. + let root = PyModule::import(py, "qdk_sim").unwrap(); + let py_enum = root.get("Pauli").unwrap(); + let args = ((self as u8).into_py(py),); + py_enum.call1(args).unwrap().into_py(py) + } +} + +impl FromPyObject<'_> for Pauli { + fn extract(ob: &'_ PyAny) -> PyResult { + // We want to support either a primitive type that can extract to u8, + // or a value of type qdk_sim.Pauli from the root module. + Python::with_gil(|py| { + let root = PyModule::import(py, "qdk_sim").unwrap(); + let py_enum: &PyType = root.get("Pauli").unwrap().downcast().unwrap(); + let value: u8 = match py_enum.is_instance(ob) { + Ok(true) => ob.getattr("value")?, + _ => ob, + } + .extract()?; + let value = Pauli::try_from(value) + .map_err(|e| PyErr::new::(format!("{:?}", e)))?; + Ok(value) + }) + } +} diff --git a/src/Simulation/qdk_sim_rs/src/tableau.rs b/src/Simulation/qdk_sim_rs/src/tableau.rs index 296461ca306..c12ffe27a4f 100644 --- a/src/Simulation/qdk_sim_rs/src/tableau.rs +++ b/src/Simulation/qdk_sim_rs/src/tableau.rs @@ -82,6 +82,28 @@ impl Tableau { } } +impl Tableau { + /// Asserts whether a hypothetical single-qubit $Z$-basis measurement + /// would agree with an expected result. + /// + /// If the assertion would pass, `Ok(())` is returned, otherwise an [`Err`] + /// describing the assertion failure is returned. + pub fn assert_meas(&self, idx_target: usize, expected: bool) -> Result<(), String> { + let actual = self.determinstic_result(idx_target).ok_or(format!( + "Expected {}, but measurement result would be random.", + expected + ))?; + if actual != expected { + Err(format!( + "Expected {}, but measurement result would actually be {}.", + expected, actual + )) + } else { + Ok(()) + } + } +} + #[cfg_attr(feature = "python", pymethods)] impl Tableau { /// Returns a serialization of this stabilizer tableau as a JSON object. @@ -178,26 +200,6 @@ impl Tableau { self.apply_cnot_mut(idx_1, idx_2); } - /// Asserts whether a hypothetical single-qubit $Z$-basis measurement - /// would agree with an expected result. - /// - /// If the assertion would pass, `Ok(())` is returned, otherwise an [`Err`] - /// describing the assertion failure is returned. - pub fn assert_meas(&self, idx_target: usize, expected: bool) -> Result<(), String> { - let actual = self.determinstic_result(idx_target).ok_or(format!( - "Expected {}, but measurement result would be random.", - expected - ))?; - if actual != expected { - Err(format!( - "Expected {}, but measurement result would actually be {}.", - expected, actual - )) - } else { - Ok(()) - } - } - /// Measures a single qubit in the Pauli $Z$-basis, returning the result, /// and updating the stabilizer tableau in-place. pub fn meas_mut(&mut self, idx_target: usize) -> bool { From d91b7ae335ccff33dbf1502ff9930eb108981f44 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 20 May 2021 18:04:37 -0700 Subject: [PATCH 080/158] Generalize C API to allow using other state representations. --- .../OpenSystemsSimulator/NativeInterface.cs | 7 +- src/Simulation/qdk_sim_rs/src/c_api.rs | 149 +++++++++------ src/Simulation/qdk_sim_rs/src/instrument.rs | 29 ++- src/Simulation/qdk_sim_rs/src/lib.rs | 2 +- src/Simulation/qdk_sim_rs/src/processes.rs | 180 +++++++++++------- src/Simulation/qdk_sim_rs/src/states.rs | 11 ++ 6 files changed, 242 insertions(+), 136 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 13743aec1fa..9122392ecdc 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -57,12 +57,13 @@ public static string Name } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="init")] - private static extern ulong _Init(uint initialCapacity); + private static extern Int64 _Init(uint initialCapacity, string representation, out uint simulatorId); - public static ulong Init(uint initialCapacity) + public static ulong Init(uint initialCapacity, string representation = "mixed") { LogCall("init"); - return _Init(initialCapacity); + CheckCall(_Init(initialCapacity, representation, out var simulatorId)); + return simulatorId; } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="destroy")] diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 5aa5d0b3324..17beeea63b1 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -36,7 +36,8 @@ lazy_static! { /// Exposes a result to C callers by setting LAST_ERROR in the Error /// case, and generating an appropriate error code. -fn as_capi_err(result: Result<(), String>) -> i64 { +fn as_capi_err Result<(), String>>(result_fn: F) -> i64 { + let result = result_fn(); match result { Ok(_) => 0, Err(msg) => { @@ -97,25 +98,64 @@ pub extern "C" fn lasterr() -> *const c_char { /// Allocate a new simulator with a given capacity, measured in the number of /// qubits supported by that simulator. Returns an id that can be used to refer /// to the new simulator in future function calls. +/// +/// The initial state of the new simulator is populated using the +/// representation nominated by the `representation` argument: +/// +/// - **`pure`**: Creates the simulator with an initial state represented by +/// a state vector. +/// - **`mixed`**: Creates the simulator with an initial state represented by +/// a density operat. +/// - **`stabilizer`**: Creates the simulator with an initial state represented by +/// a stabilizer tableau. +/// +/// # Safety +/// The caller is responsible for ensuring that `sim_id_out` points to valid +/// memory, and that the lifetime of this pointer extends at least for the +/// duration of this call. #[no_mangle] -pub extern "C" fn init(initial_capacity: usize) -> usize { - let state = &mut *STATE.lock().unwrap(); - let id = 1 + state.keys().fold(std::usize::MIN, |a, b| a.max(*b)); - state.insert( - id, - CApiState { - register_state: State::new_mixed(initial_capacity), - noise_model: NoiseModel::ideal(), - }, - ); - id +pub unsafe extern "C" fn init( + initial_capacity: usize, + representation: *const c_char, + sim_id_out: *mut usize, +) -> i64 { + as_capi_err(|| { + if representation.is_null() { + return Err("init called with null pointer for representation".to_string()); + } + let representation = CStr::from_ptr(representation) + .to_str() + .map_err(|e| format!("UTF-8 error decoding representation argument: {}", e))?; + + let state = &mut *STATE.lock().unwrap(); + let id = 1 + state.keys().fold(std::usize::MIN, |a, b| a.max(*b)); + state.insert( + id, + CApiState { + register_state: match representation { + "mixed" => State::new_mixed(initial_capacity), + "pure" => State::new_pure(initial_capacity), + "stabilizer" => State::new_stabilizer(initial_capacity), + _ => { + return Err(format!( + "Unknown initial state representation {}.", + representation + )) + } + }, + noise_model: NoiseModel::ideal(), + }, + ); + *sim_id_out = id; + Ok(()) + }) } /// Deallocates the simulator with the given id, releasing any resources owned /// by that simulator. #[no_mangle] pub extern "C" fn destroy(sim_id: usize) -> i64 { - as_capi_err({ + as_capi_err(|| { let state = &mut *STATE.lock().unwrap(); if state.contains_key(&sim_id) { state.remove(&sim_id); @@ -132,65 +172,63 @@ pub extern "C" fn destroy(sim_id: usize) -> i64 { /// using the currently set noise model. #[no_mangle] pub extern "C" fn x(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.x)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.x)) } /// Applies the `Y` operation acting on a given qubit to a given simulator, /// using the currently set noise model. #[no_mangle] pub extern "C" fn y(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.y)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.y)) } /// Applies the `Z` operation acting on a given qubit to a given simulator, /// using the currently set noise model. #[no_mangle] pub extern "C" fn z(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.z)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.z)) } /// Applies the `H` operation acting on a given qubit to a given simulator, /// using the currently set noise model. #[no_mangle] pub extern "C" fn h(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.h)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.h)) } /// Applies the `S` operation acting on a given qubit to a given simulator, /// using the currently set noise model. #[no_mangle] pub fn s(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.s)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.s)) } /// Applies the `Adjoint S` operation acting on a given qubit to a given /// simulator, using the currently set noise model. #[no_mangle] pub fn s_adj(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.s_adj)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.s_adj)) } /// Applies the `T` operation acting on a given qubit to a given simulator, /// using the currently set noise model. #[no_mangle] pub fn t(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.t)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.t)) } /// Applies the `Adjoint T` operation acting on a given qubit to a given simulator, /// using the currently set noise model. #[no_mangle] pub fn t_adj(sim_id: usize, idx: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx], |model| &model.t_adj)) + as_capi_err(|| apply(sim_id, &[idx], |model| &model.t_adj)) } /// Applies the `CNOT` operation acting on two given qubits to a given simulator, /// using the currently set noise model. #[no_mangle] pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { - as_capi_err(apply(sim_id, &[idx_control, idx_target], |model| { - &model.cnot - })) + as_capi_err(|| apply(sim_id, &[idx_control, idx_target], |model| &model.cnot)) } /// Measures a single qubit in the $Z$-basis, returning the result by setting @@ -202,7 +240,7 @@ pub fn cnot(sim_id: usize, idx_control: usize, idx_target: usize) -> i64 { /// by `result_out` can be safely set. #[no_mangle] pub unsafe extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> i64 { - as_capi_err({ + as_capi_err(|| { let state = &mut *STATE.lock().unwrap(); if let Some(sim_state) = state.get_mut(&sim_id) { let instrument = &sim_state.noise_model.z_meas; @@ -258,39 +296,40 @@ pub extern "C" fn ideal_noise_model() -> *const c_char { /// least the duration of the call. #[no_mangle] pub unsafe extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char) -> i64 { - if new_model.is_null() { - return as_capi_err(Err("set_noise_model called with null pointer".to_string())); - } - - let c_str = CStr::from_ptr(new_model); + as_capi_err(|| { + if new_model.is_null() { + return Err("set_noise_model called with null pointer".to_string()); + } - as_capi_err(match c_str.to_str() { - Ok(serialized_noise_model) => match serde_json::from_str(serialized_noise_model) { - Ok(noise_model) => { - let state = &mut *STATE.lock().unwrap(); - if let Some(sim_state) = state.get_mut(&sim_id) { - sim_state.noise_model = noise_model; - Ok(()) - } else { - Err(format!("No simulator with id {} exists.", sim_id)) + let c_str = CStr::from_ptr(new_model); + match c_str.to_str() { + Ok(serialized_noise_model) => match serde_json::from_str(serialized_noise_model) { + Ok(noise_model) => { + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + sim_state.noise_model = noise_model; + Ok(()) + } else { + Err(format!("No simulator with id {} exists.", sim_id)) + } } - } - Err(serialization_error) => Err(format!( - "{} error deserializing noise model at line {}, column {}.", - match serialization_error.classify() { - serde_json::error::Category::Data => "Data / schema", - serde_json::error::Category::Eof => "End-of-file", - serde_json::error::Category::Io => "I/O", - serde_json::error::Category::Syntax => "Syntax", - }, - serialization_error.line(), - serialization_error.column() + Err(serialization_error) => Err(format!( + "{} error deserializing noise model at line {}, column {}.", + match serialization_error.classify() { + serde_json::error::Category::Data => "Data / schema", + serde_json::error::Category::Eof => "End-of-file", + serde_json::error::Category::Io => "I/O", + serde_json::error::Category::Syntax => "Syntax", + }, + serialization_error.line(), + serialization_error.column() + )), + }, + Err(msg) => Err(format!( + "UTF-8 error decoding serialized noise model; was valid until byte {}.", + msg.valid_up_to() )), - }, - Err(msg) => Err(format!( - "UTF-8 error decoding serialized noise model; was valid until byte {}.", - msg.valid_up_to() - )), + } }) } diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 89e922c6489..afa03277c9a 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::Process; use crate::{states::StateData::Mixed, StateData}; +use crate::{Process, ProcessData, C64}; +use num_traits::{One, Zero}; use rand::Rng; use std::iter::Iterator; @@ -46,8 +47,30 @@ impl Instrument { } let idx_target = idx_qubits[0]; match state.data { - StateData::Pure(ref _psi) => todo!(), - StateData::Mixed(ref _rho) => todo!(), + StateData::Pure(_) | StateData::Mixed(_) => { + // Get the ideal Z measurement instrument, apply it, + // and then assign a readout error. + // TODO[perf]: Cache this instrument as a lazy static. + let ideal_z_meas = Instrument::Effects(vec![ + Process { + n_qubits: 1, + data: ProcessData::KrausDecomposition(array![[ + [C64::one(), C64::zero()], + [C64::zero(), C64::zero()] + ]]), + }, + Process { + n_qubits: 1, + data: ProcessData::KrausDecomposition(array![[ + [C64::zero(), C64::zero()], + [C64::zero(), C64::one()] + ]]), + }, + ]); + let (result, new_state) = ideal_z_meas.sample(idx_qubits, state); + let result = (result == 1) ^ rand::thread_rng().gen_bool(*pr_readout_error); + (if result { 1 } else { 0 }, new_state) + } StateData::Stabilizer(ref tableau) => { // TODO[perf]: allow instruments to sample in-place, // reducing copying. diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 970bf854ab6..9f954aec52b 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. // The following two attributes include the README.md for this crate when -// the "doc" feature is turned on (requires +nightly). +// 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))] diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 985a1da2aac..7d48bf6e90a 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; use crate::log_as_err; use crate::states::StateData::{Mixed, Pure, Stabilizer}; use crate::NoiseModel; @@ -9,6 +8,10 @@ use crate::QubitSized; use crate::State; use crate::C64; use crate::{linalg::ConjBy, AsUnitary, Pauli}; +use crate::{ + linalg::{extend_one_to_n, extend_two_to_n, zeros_like}, + StateData, +}; use crate::{ log, processes::ProcessData::{KrausDecomposition, PauliChannel, Unitary}, @@ -46,6 +49,10 @@ pub enum ProcessData { /// The first index denotes each Kraus operator, with the second and third /// indices representing the indices of each operator. KrausDecomposition(Array3), // TODO: Superoperator and Choi reps. + + /// Represents a process that is not supported by a given noise model, + /// and thus always fails when applied. + Unsupported, } impl Process { @@ -66,80 +73,19 @@ impl Process { /// Applies this process to a quantum register with a given /// state, returning the new state of that register. pub fn apply(&self, state: &State) -> Result { - if state.n_qubits == self.n_qubits { - Ok(State { - n_qubits: self.n_qubits, - data: match &state.data { - Pure(psi) => match &self.data { - Unitary(u) => Pure(u.dot(psi)), - KrausDecomposition(ks) => { - // We can't apply a channel with more than one Kraus operator (Choi rank > 1) to a - // pure state directly, so if the Choi rank is bigger than 1, promote to - // Mixed and recurse. - if ks.shape()[0] == 1 { - Pure({ - let k: ArrayView2 = ks.slice(s![0, .., ..]); - k.dot(psi) - }) - } else { - self.apply(&state.to_mixed())?.data - } - }, - PauliChannel(paulis) => { - // Promote and recurse. - let promoted = promote_pauli_channel(paulis); - promoted.apply(state)?.data - } - }, - Mixed(rho) => match &self.data { - Unitary(u) => Mixed(rho.conjugate_by(&u.into())), - KrausDecomposition(ks) => Mixed({ - let mut sum: Array2 = - Array::zeros((rho.shape()[0], rho.shape()[1])); - for k in ks.axis_iter(Axis(0)) { - sum = sum + rho.conjugate_by(&k); - } - sum - }), - PauliChannel(paulis) => { - // Promote and recurse. - let promoted = promote_pauli_channel(paulis); - promoted.apply(state)?.data - } - }, - Stabilizer(tableau) => match &self.data { - PauliChannel(paulis) => { - // TODO[perf]: Introduce an apply_mut method to - // avoid extraneous cloning. - let mut new_tableau = tableau.clone(); - // Sample a Pauli and apply it. - let weighted = WeightedIndex::new( - paulis.iter().map(|(pr, _)| pr) - ).unwrap(); - let idx = weighted.sample(&mut thread_rng()); - let pauli = &(&paulis)[idx].1; - // TODO: Consider moving the following to a method - // on Tableau itself. - for (idx_qubit, p) in pauli.iter().enumerate() { - match p { - Pauli::I => (), - Pauli::X => new_tableau.apply_x_mut(idx_qubit), - Pauli::Y => new_tableau.apply_y_mut(idx_qubit), - Pauli::Z => new_tableau.apply_z_mut(idx_qubit) - } - } - Stabilizer(new_tableau) - }, - _ => unimplemented!("Promotion of stabilizer tableaus to pure or mixed states is not yet supported.") - }, - }, - }) - } else { - Err(format!( + if state.n_qubits != self.n_qubits { + return Err(format!( "Channel acts on {} qubits, but was applied to {}-qubit state.", self.n_qubits, state.n_qubits - )) + )); } + + return match &self.data { + Unitary(u) => apply_unitary(&u, state), + KrausDecomposition(ks) => apply_kraus_decomposition(&ks, state), + PauliChannel(paulis) => apply_pauli_channel(&paulis, state), + ProcessData::Unsupported => return Err(format!("Unsupported quantum process.")), + }; } /// Applies this process to the given qubits in a register with a given @@ -235,7 +181,8 @@ impl Process { (*pr, extended) }) .collect_vec() - ) + ), + ProcessData::Unsupported => ProcessData::Unsupported }, } } @@ -278,7 +225,8 @@ impl Process { (*pr, extended) }) .collect_vec() - ) + ), + ProcessData::Unsupported => ProcessData::Unsupported }, } } @@ -302,6 +250,7 @@ impl Mul<&Process> for C64 { }), KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks), PauliChannel(paulis) => (self * promote_pauli_channel(paulis)).data, + ProcessData::Unsupported => ProcessData::Unsupported, }, } } @@ -534,3 +483,86 @@ fn promote_pauli_channel(paulis: &[(f64, Vec)]) -> Process { } } } + +// Private functions for applying processes of each different representation. + +fn apply_unitary(u: &Array2, state: &State) -> Result { + Ok(State { + n_qubits: state.n_qubits, + data: match &state.data { + Pure(psi) => Pure(u.dot(psi)), + Mixed(rho) => Mixed(rho.conjugate_by(&u.into())), + Stabilizer(_tableau) => { + return Err(format!( + "TODO: Promote stabilizer state to state vector and recurse." + )) + } + }, + }) +} + +fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Result { + Ok(State { + n_qubits: state.n_qubits, + data: match &state.data { + Pure(psi) => { + // We can't apply a channel with more than one Kraus operator (Choi rank > 1) to a + // pure state directly, so if the Choi rank is bigger than 1, promote to + // Mixed and recurse. + if ks.shape()[0] == 1 { + Pure({ + let k: ArrayView2 = ks.slice(s![0, .., ..]); + k.dot(psi) + }) + } else { + apply_kraus_decomposition(ks, &state.to_mixed())?.data + } + } + Mixed(rho) => Mixed({ + let mut sum: Array2 = Array::zeros((rho.shape()[0], rho.shape()[1])); + for k in ks.axis_iter(Axis(0)) { + sum = sum + rho.conjugate_by(&k); + } + sum + }), + Stabilizer(_tableau) => { + return Err(format!( + "TODO: Promote stabilizer state to state vector and recurse." + )) + } + }, + }) +} + +fn apply_pauli_channel(paulis: &[(f64, Vec)], state: &State) -> Result { + Ok(State { + n_qubits: state.n_qubits, + data: match &state.data { + Pure(_) | Mixed(_) => { + // Promote and recurse. + let promoted = promote_pauli_channel(paulis); + return promoted.apply(state); + } + Stabilizer(tableau) => { + // TODO[perf]: Introduce an apply_mut method to + // avoid extraneous cloning. + let mut new_tableau = tableau.clone(); + // Sample a Pauli and apply it. + let weighted = WeightedIndex::new(paulis.iter().map(|(pr, _)| pr)).unwrap(); + let idx = weighted.sample(&mut thread_rng()); + let pauli = &(&paulis)[idx].1; + // TODO: Consider moving the following to a method + // on Tableau itself. + for (idx_qubit, p) in pauli.iter().enumerate() { + match p { + Pauli::I => (), + Pauli::X => new_tableau.apply_x_mut(idx_qubit), + Pauli::Y => new_tableau.apply_y_mut(idx_qubit), + Pauli::Z => new_tableau.apply_z_mut(idx_qubit), + } + } + Stabilizer(new_tableau) + } + }, + }) +} diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index c1baf664b73..3e2404e02ad 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -84,6 +84,17 @@ impl State { } } + /// Returns a new pure state on a given number of qubits. + /// By convention, new pure states start off in the "all-zeros" state, + /// $\ket{\psi} = \ket{00\cdots 0}$. + pub fn new_pure(n_qubits: usize) -> State { + let new_dim = 2usize.pow(n_qubits.try_into().unwrap()); + State { + n_qubits, + data: Pure(common_matrices::elementary_vec(0, new_dim)), + } + } + /// Extends this state to be a state on `n_qubits` additional qubits. /// New qubits are added "to the right," e.g.: $\left|\psi\right\rangle$ /// is extended to $\left|\psi 0\right\rangle$. From ebfd6e79f198be0d5f09a6dfa98a7ae8c4f5496c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 12:02:37 -0700 Subject: [PATCH 081/158] Enable CFG for C / Rust boundaries. --- src/Qir/Runtime/CMakeLists.txt | 2 +- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Qir/Runtime/CMakeLists.txt b/src/Qir/Runtime/CMakeLists.txt index 5aea1a8065f..39cc0ec0762 100644 --- a/src/Qir/Runtime/CMakeLists.txt +++ b/src/Qir/Runtime/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") if (WIN32) - add_link_options("LINKER:/guard:cf") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -cfguard") endif() # feel free to customize these flags for your local builds (don't check in) diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 1cf89ff7382..1c31a85ce84 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -4,7 +4,20 @@ $DropPath = Join-Path $Env:DROP_NATIVE "qdk_sim_rs"; Push-Location $PSScriptRoot $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - cargo +nightly build -Z unstable-options @releaseFlag --out-dir $DropPath; + + # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) + # for interoperating Rust and C. This feature is stable (hence -C), but + # only provides complete safety guarantees when the standard library is + # also rebuilt with the same rustc flags. + # 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"; + # To support rebuilding the Rust standard library with CFG enabled, we need + # the Rust source code to be installed with rustup. + rustup +nightly component add rust-src; + + # Actually run the build. + cargo +nightly build -Z unstable-options -Z build-std @releaseFlag --out-dir $DropPath; # Make sure docs are complete. $Env:RUSTDOCFLAGS = "--html-in-header $(Resolve-Path docs-includes/header.html) " + ` From 9cb438ec1f70d73cef873ec13676e5e0f3542c7c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 13:22:59 -0700 Subject: [PATCH 082/158] Move Cargo.toml injection into bootstrap. --- bootstrap.ps1 | 12 ++++++++++++ build/build.ps1 | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index dd9f5a12d9f..b27e204c44c 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" diff --git a/build/build.ps1 b/build/build.ps1 index 7dd925b5672..7c1e1f953c2 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -6,18 +6,6 @@ $ErrorActionPreference = 'Stop' & "$PSScriptRoot/set-env.ps1" $all_ok = $True -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 ($Env:ENABLE_NATIVE -ne "false") { $nativeSimulator = (Join-Path $PSScriptRoot "../src/Simulation/Native") & "$nativeSimulator/build-native-simulator.ps1" From 2f2b5e9de7f85280cbba44c60083553dd0c30fb2 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 13:32:22 -0700 Subject: [PATCH 083/158] Fix path from bootstrap to Cargo.toml injection, debug injection. --- bootstrap.ps1 | 2 +- src/Simulation/qdk_sim_rs/inject-version.csx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index b27e204c44c..9366835de06 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -7,7 +7,7 @@ Push-Location (Join-Path $PSScriptRoot "build") .\prerequisites.ps1 Pop-Location -Push-Location (Join-Path $PSScriptRoot "../src/Simulation/qdk_sim_rs") +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 diff --git a/src/Simulation/qdk_sim_rs/inject-version.csx b/src/Simulation/qdk_sim_rs/inject-version.csx index a871efc7972..ec71338f066 100644 --- a/src/Simulation/qdk_sim_rs/inject-version.csx +++ b/src/Simulation/qdk_sim_rs/inject-version.csx @@ -23,6 +23,7 @@ var rootCommand = new RootCommand // 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); From 83159613077dcb017485f8d727639250d28f874d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 13:36:01 -0700 Subject: [PATCH 084/158] One more path fix. --- bootstrap.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 9366835de06..66357218cf4 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -26,7 +26,7 @@ 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") + Push-Location (Join-Path $PSScriptRoot "src/Simulation/qdk-sim-rs") .\build-qdk-sim-rs.ps1 Pop-Location $Env:BUILD_CONFIGURATION = $null From 3906a49afc425ed79c85575b9c059b5889746ce4 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 13:36:52 -0700 Subject: [PATCH 085/158] One more path fix. --- bootstrap.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 66357218cf4..9663f59ee98 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -26,7 +26,7 @@ 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") + Push-Location (Join-Path $PSScriptRoot "src/Simulation/qdk_sim_rs") .\build-qdk-sim-rs.ps1 Pop-Location $Env:BUILD_CONFIGURATION = $null From 7943335ad43a3f642cd362eff025e875c6702605 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 14:27:45 -0700 Subject: [PATCH 086/158] Remove --release from cargo package. --- build/pack.ps1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/pack.ps1 b/build/pack.ps1 index 462e8c8ed71..d5ae965fa42 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -113,15 +113,13 @@ function Pack-Crate() { "##[info]Packing crate at $PackageDirectory to $OutPath..." | Write-Host - $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - # 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 @releaseFlag; + cargo package; Copy-Item -Force -Recurse (Join-Path . "target" "package") $OutPath; Pop-Location } From db0ab9ee3bb27e369ea74df92a499934478d6587 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 14:28:05 -0700 Subject: [PATCH 087/158] Revert CFG change to cmake. --- src/Qir/Runtime/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/Runtime/CMakeLists.txt b/src/Qir/Runtime/CMakeLists.txt index 39cc0ec0762..5aea1a8065f 100644 --- a/src/Qir/Runtime/CMakeLists.txt +++ b/src/Qir/Runtime/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") if (WIN32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -cfguard") + add_link_options("LINKER:/guard:cf") endif() # feel free to customize these flags for your local builds (don't check in) From 2035d67b84baf49eaddba04b65f05409bdaf7d5c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 14:32:14 -0700 Subject: [PATCH 088/158] Fix Rust build script. --- src/Simulation/qdk_sim_rs/Cargo.toml.template | 5 +---- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index 2eb5986687b..b31fcdddb0e 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -28,13 +28,10 @@ wasm = ["web-sys"] # When Python bindings are enabled, we also need the pyo3 dependency. python = ["pyo3", "numpy"] -# To make sure docs are enabled on docs.rs, we need to also pass the "doc" -# feature in our metadata. -[package.metadata.docs.rs] - # 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" ] diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 1c31a85ce84..a8815a512dc 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -17,12 +17,13 @@ Push-Location $PSScriptRoot rustup +nightly component add rust-src; # Actually run the build. - cargo +nightly build -Z unstable-options -Z build-std @releaseFlag --out-dir $DropPath; + # TODO: enable "-Z build-std" to fully support CFG. + cargo +nightly build -Z unstable-options @releaseFlag --out-dir $DropPath; # 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 --features doc; + cargo +nightly doc; # Free disk space by cleaning up. # Note that this takes longer, but saves ~1 GB of space, which is From 4b37b2ac82780e7563fe80d119c360864f569f97 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 17:16:35 -0700 Subject: [PATCH 089/158] Debug artifact paths. --- build/set-env.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 2204197c483..ba7c2d7e7bd 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -36,3 +36,11 @@ If (-not (Test-Path -Path $Env:WHEEL_OUTDIR)) { [IO.Directory]::CreateDirectory( 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 From 527ea8ef90b4e19d7ff98ded84513c658f65a0ee Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 17:17:20 -0700 Subject: [PATCH 090/158] Debug drop paths as well. --- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index a8815a512dc..d78b7d5fc25 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -18,6 +18,7 @@ Push-Location $PSScriptRoot # Actually run the build. # TODO: enable "-Z build-std" to fully support CFG. + Write-Host "##[info]Building qdk_sim_rs into $DropPath..."; cargo +nightly build -Z unstable-options @releaseFlag --out-dir $DropPath; # Make sure docs are complete. From cb792391a05f778361071faa5c9a49fad29473d9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 19:08:41 -0700 Subject: [PATCH 091/158] More path debugging. --- build/pack.ps1 | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/build/pack.ps1 b/build/pack.ps1 index d5ae965fa42..1b41bf0c67f 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -9,24 +9,31 @@ $all_ok = $True $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot ".."); Write-Host "##[info]Copy Native simulator xplat binaries" -pushd (Join-Path $PSScriptRoot ../src/Simulation/Native) +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" - 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" } + 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/qdk_sim_rs"; + Write-Host "##[info]Copying qdk_sim_rs files from $DROP..."; if (Test-Path "$DROP/libqdk_sim.dylib") { - Copy-Item "$DROP/libqdk_sim.dylib" "osx/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" + Copy-Item -Verbose "$DROP/libqdk_sim.dylib" "osx/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" } if (Test-Path "$DROP/libqdk_sim.so") { - Copy-Item "$DROP/libqdk_sim.so" "linux/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" + Copy-Item -Verbose "$DROP/libqdk_sim.so" "linux/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" } if (Test-Path "$DROP/qdk_sim.dll") { - Copy-Item "$DROP/qdk_sim.dll" "win10/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" + Copy-Item -Verbose "$DROP/qdk_sim.dll" "win10/Microsoft.Quantum.Experimental.Simulators.Runtime.dll" } Pop-Location From 335d9a02aa3d566fa1d506e46b1f209cf6f26489 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 2 Jun 2021 22:07:10 -0700 Subject: [PATCH 092/158] Fix build paths. --- build/pack.ps1 | 2 +- src/Simulation/Common/Simulators.Dev.props | 2 +- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 13 ++----------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/build/pack.ps1 b/build/pack.ps1 index 1b41bf0c67f..5b886701b5f 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -24,7 +24,7 @@ Push-Location (Join-Path $PSScriptRoot ../src/Simulation/Native) } - $DROP = "$Env:DROP_NATIVE/qdk_sim_rs"; + $DROP = "$Env:DROP_NATIVE/src/Simulation/qdk_sim_rs/target"; 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" diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index 149ebc96209..fcad9e356a2 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -5,7 +5,7 @@ bin\$(BuildConfiguration)\$(TargetFramework)\$(AssemblyName).xml $([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory)..\..\..\)) $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/Native/build/drop)) - $([MSBuild]::NormalizePath($(EnlistmentRoot)/xplat/qdk_sim_rs)) + $([MSBuild]::NormalizePath($(EnlistmentRoot)/src/Simulation/qdk_sim_rs/target)) diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index d78b7d5fc25..12853c1cc1f 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -1,25 +1,16 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); -$DropPath = Join-Path $Env:DROP_NATIVE "qdk_sim_rs"; - Push-Location $PSScriptRoot $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. This feature is stable (hence -C), but - # only provides complete safety guarantees when the standard library is - # also rebuilt with the same rustc flags. + # 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"; - # To support rebuilding the Rust standard library with CFG enabled, we need - # the Rust source code to be installed with rustup. - rustup +nightly component add rust-src; # Actually run the build. - # TODO: enable "-Z build-std" to fully support CFG. - Write-Host "##[info]Building qdk_sim_rs into $DropPath..."; - cargo +nightly build -Z unstable-options @releaseFlag --out-dir $DropPath; + cargo build @releaseFlag; # Make sure docs are complete. $Env:RUSTDOCFLAGS = "--html-in-header $(Resolve-Path docs-includes/header.html) " + ` From 0b279fd150a908346ac4e541ceaca6af7a6b10df Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 00:42:28 -0700 Subject: [PATCH 093/158] Don't cargo clean build artifacts away. --- src/Simulation/Common/Simulators.Dev.props | 2 +- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index fcad9e356a2..b5b4d77231f 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -5,7 +5,7 @@ bin\$(BuildConfiguration)\$(TargetFramework)\$(AssemblyName).xml $([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory)..\..\..\)) $([MSBuild]::NormalizePath($(EnlistmentRoot)src/Simulation/Native/build/drop)) - $([MSBuild]::NormalizePath($(EnlistmentRoot)/src/Simulation/qdk_sim_rs/target)) + $([MSBuild]::NormalizePath($(EnlistmentRoot)/src/Simulation/qdk_sim_rs/drop)) diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 12853c1cc1f..6b2358d5431 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -10,7 +10,7 @@ Push-Location $PSScriptRoot $Env:RUSTFLAGS = "-C control-flow-guard"; # Actually run the build. - cargo build @releaseFlag; + 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) " + ` From d2599696ff62a55b7544479f3a8001b4a227666d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 07:15:22 -0700 Subject: [PATCH 094/158] Fix one character typo in Simulators.Dev.props. --- src/Simulation/Common/Simulators.Dev.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index b5b4d77231f..f6313388c00 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -5,7 +5,7 @@ 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($(EnlistmentRoot)src/Simulation/qdk_sim_rs/drop)) From bddb2952f6b708fd0e26b2e3db19867400269368 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 07:18:32 -0700 Subject: [PATCH 095/158] Fix one more dev props typo. --- src/Simulation/Common/Simulators.Dev.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index f6313388c00..2466fba9c95 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -43,7 +43,7 @@ - + Microsoft.Quantum.Experimental.Simulators.Runtime.dll PreserveNewest false From e7dda55fed582ce4a912f6e1fa4b28a98ea9b79b Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 07:30:49 -0700 Subject: [PATCH 096/158] Eliminate unneeded copy. --- src/Simulation/qdk_sim_rs/src/linalg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/qdk_sim_rs/src/linalg.rs b/src/Simulation/qdk_sim_rs/src/linalg.rs index 700af0c83e9..98e41a02f8c 100644 --- a/src/Simulation/qdk_sim_rs/src/linalg.rs +++ b/src/Simulation/qdk_sim_rs/src/linalg.rs @@ -85,7 +85,7 @@ impl, T: Copy + Mul> Tensor for &Array1 fn tensor(self, other: Other) -> Self::Output { let other: Self = other.into(); - self.view().tensor(other).to_owned() + self.view().tensor(other) } } From e057dfa8055ee2d8598b9798a0ba44a8e1cb2185 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 07:31:52 -0700 Subject: [PATCH 097/158] Improve naming conventions in processes.rs. --- src/Simulation/qdk_sim_rs/src/processes.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 7d48bf6e90a..8a3451538b2 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; use crate::log_as_err; use crate::states::StateData::{Mixed, Pure, Stabilizer}; use crate::NoiseModel; @@ -8,13 +9,9 @@ use crate::QubitSized; use crate::State; use crate::C64; use crate::{linalg::ConjBy, AsUnitary, Pauli}; -use crate::{ - linalg::{extend_one_to_n, extend_two_to_n, zeros_like}, - StateData, -}; use crate::{ log, - processes::ProcessData::{KrausDecomposition, PauliChannel, Unitary}, + processes::ProcessData::{KrausDecomposition, MixedPauli, Unitary}, }; use itertools::Itertools; use ndarray::{Array, Array2, Array3, ArrayView2, Axis, NewAxis}; @@ -38,7 +35,7 @@ pub enum ProcessData { /// Representation of a process as a mixture of Pauli operators /// $\{(p_i, P_i)\}$ such that the channel acts as $\rho \mapsto /// \sum_i p_i P_i \rho P_i^{\dagger}$. - PauliChannel(Vec<(f64, Vec)>), + MixedPauli(Vec<(f64, Vec)>), /// Representation of the process by an arbitrary unitary matrix. Unitary(Array2), @@ -65,7 +62,7 @@ impl Process { let n_qubits = data[0].1.len(); Process { n_qubits, - data: PauliChannel(data), + data: MixedPauli(data), } } // TODO: methods to forcibly convert representations. @@ -83,7 +80,7 @@ impl Process { return match &self.data { Unitary(u) => apply_unitary(&u, state), KrausDecomposition(ks) => apply_kraus_decomposition(&ks, state), - PauliChannel(paulis) => apply_pauli_channel(&paulis, state), + MixedPauli(paulis) => apply_pauli_channel(&paulis, state), ProcessData::Unsupported => return Err(format!("Unsupported quantum process.")), }; } @@ -169,7 +166,7 @@ impl Process { } KrausDecomposition(extended) }, - PauliChannel(paulis) => PauliChannel( + MixedPauli(paulis) => MixedPauli( paulis.iter() .map(|(pr, pauli)| { if pauli.len() != 1 { @@ -212,7 +209,7 @@ impl Process { } KrausDecomposition(extended) }, - PauliChannel(paulis) => PauliChannel( + MixedPauli(paulis) => MixedPauli( paulis.iter() .map(|(pr, pauli)| { if pauli.len() != 2 { @@ -249,7 +246,7 @@ impl Mul<&Process> for C64 { ks }), KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks), - PauliChannel(paulis) => (self * promote_pauli_channel(paulis)).data, + MixedPauli(paulis) => (self * promote_pauli_channel(paulis)).data, ProcessData::Unsupported => ProcessData::Unsupported, }, } From a4ddaf871182f66dd409fff963c7e4f04a625bf2 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 08:51:18 -0700 Subject: [PATCH 098/158] Fix pack.ps1 to include qdk_sim_rs drops. --- build/pack.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pack.ps1 b/build/pack.ps1 index 5b886701b5f..9751b9e0047 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -24,7 +24,7 @@ Push-Location (Join-Path $PSScriptRoot ../src/Simulation/Native) } - $DROP = "$Env:DROP_NATIVE/src/Simulation/qdk_sim_rs/target"; + $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" From 8c02857f3af601b53830b50471dde896e0c62a07 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 09:16:32 -0700 Subject: [PATCH 099/158] Expose more information in exceptions. --- .../Simulators/OpenSystemsSimulator/NativeInterface.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 9122392ecdc..d25f98d78cc 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -119,9 +119,13 @@ public static void SetNoiseModel(ulong simId, NoiseModel noiseModel) { 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 (Exception ex) { - throw new Exception($"Could not set noise model from JSON:\n{jsonData}", ex); + throw new Exception($"Could not set noise model from JSON: {ex.Message}\nJSON data:\n{jsonData}", ex); } } From 7d4246f6b9344bc6f53a6377aba4726fd39a7107 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 10:50:12 -0700 Subject: [PATCH 100/158] Fix one more path. --- build/set-env.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/set-env.ps1 b/build/set-env.ps1 index ba7c2d7e7bd..8ac20b2e261 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -21,7 +21,7 @@ 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 ".." "xplat") + $Env:DROP_NATIVE = (Join-Path $PSScriptRoot "..") } If ($Env:NUGET_OUTDIR -eq $null) { $Env:NUGET_OUTDIR = (Join-Path $Env:DROPS_DIR "nugets") } From d04a0c66ff12f6839b07734f76c0b1b4d961b4ae Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 11:37:11 -0700 Subject: [PATCH 101/158] Add links on cargo-clippy. --- .pre-commit-config.yaml | 8 ++++++++ src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a2f084ec43..c12c6fd5236 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,7 @@ +# 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 @@ -6,6 +10,10 @@ repos: 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/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index a60a5abb806..245da5cdc1c 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -1,15 +1,22 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); +$script:allOk = $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. + # 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; # If running in CI, use cargo2junit to expose unit tests to the # PublishTestResults task. @@ -20,13 +27,16 @@ Push-Location $PSScriptRoot <# We use this name to match the *_results.xml pattern that is used to find test results in steps-wrap-up.yml. #> ` | Out-File -FilePath opensim_results.xml -Encoding utf8NoBOM + $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; } else { # Outside of CI, show human-readable output. cargo test + $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; } # Run performance benchmarks as well. cargo bench + $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; # This step isn't required, but we use it to upload run summaries. $reportPath = (Join-Path "target" "criterion"); @@ -41,3 +51,8 @@ Push-Location $PSScriptRoot # exceptionally helpful in CI builds. cargo clean Pop-Location + +if (-not $script:allOk) { + Write-Host "##vso[task.logissue type=error;]Failed to test qdk_sim_rs — please check logs above." + exit -1; +} From 906f9f95bde43687263f8c9c6cffe52dc3b7bc5d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 11:40:16 -0700 Subject: [PATCH 102/158] Fix issues in C API benchmarking. --- src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs index f0b79f576fd..d4ab3fbdc19 100644 --- a/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs +++ b/src/Simulation/qdk_sim_rs/benches/c_api_benchmark.rs @@ -44,7 +44,10 @@ fn with_test_suite( } fn ideal(c: &mut Criterion) { - let sim_id = c_api::init(3); + 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(); @@ -52,7 +55,10 @@ fn ideal(c: &mut Criterion) { } fn noisy(c: &mut Criterion) { - let sim_id = c_api::init(3); + 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( From 6d338a15265043d3c555e8ff6be97640623983c7 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 11:41:01 -0700 Subject: [PATCH 103/158] Improve Python bindings for NoiseModel. --- src/Simulation/qdk_sim_rs/src/noise_model.rs | 3 ++- src/Simulation/qdk_sim_rs/src/python.rs | 22 +++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 5d1ea1bfc8b..d9828084104 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -15,7 +15,6 @@ use pyo3::prelude::*; /// A description of the noise that applies to the state of a quantum system /// as the result of applying operations. -#[cfg_attr(feature = "python", pyclass(name = "NoiseModel"))] #[derive(Serialize, Deserialize, Debug)] pub struct NoiseModel { /// The initial state that freshly allocated qubits start off in. @@ -71,7 +70,9 @@ impl NoiseModel { pub fn as_json(&self) -> String { serde_json::to_string(&self).unwrap() } +} +impl NoiseModel { /// Returns a copy of the ideal noise model; that is, a noise model /// describing the case in which no noise acts on the quantum system. pub fn ideal() -> NoiseModel { diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index b26edf6dcbc..67239c96913 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -38,7 +38,7 @@ fn _qdk_sim_rs(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - m.add_class::()?; + m.add_class::()?; Ok(()) } @@ -146,6 +146,26 @@ impl PyInstrument { } } +#[pyclass(name = "NoiseModel")] +#[derive(Debug)] +pub struct PyNoiseModel { + data: NoiseModel, +} + +#[pymethods] +impl PyNoiseModel { + #[staticmethod] + pub fn ideal() -> PyNoiseModel { + PyNoiseModel { + data: NoiseModel::ideal(), + } + } + + pub fn as_json(&self) -> String { + self.data.as_json() + } +} + // See https://stackoverflow.com/q/67412827/267841 for why the following works // to expose Pauli. If we have more enums like this, we could likewise expose // them by using the macro from that SO question. From f7e167b55894bb48769f142d2fbde0f9233b772b Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 11:41:23 -0700 Subject: [PATCH 104/158] Improve readme for qdk_sim_rs crate. --- src/Simulation/qdk_sim_rs/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index 03a31abe0f3..6ec1c22ee2f 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -15,7 +15,6 @@ The [`c_api`] module allows for using the simulation functionality in this crate ## Cargo Features -- **`doc`**: Enables building documentation (requires `+nightly`). - **`python`**: Enables Python bindings for this crate. - **`wasm`**: Ensures that the crate is compatible with usage from WebAssembly. From 54c37b8e3d8906b5a8ba3eb9ac8e1a33a6434515 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 11:55:03 -0700 Subject: [PATCH 105/158] Improve noise model handling, add test coverage. --- .../NoiseModelSerializationTests.cs | 77 +++++++++++++++++++ .../DataModel/Instrument.cs | 55 ++++++++++++- .../DataModel/NoiseModel.cs | 20 ++--- .../DataModel/{Channel.cs => Process.cs} | 37 +++++---- 4 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs rename src/Simulation/Simulators/OpenSystemsSimulator/DataModel/{Channel.cs => Process.cs} (74%) diff --git a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs new file mode 100644 index 00000000000..84d42351f35 --- /dev/null +++ b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs @@ -0,0 +1,77 @@ +// 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 not 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); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + 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); + } + } + + 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],[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]]}}}]}}"; + + [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 = JsonSerializer.Deserialize(idealJson); + var roundtripJson = JsonSerializer.Serialize(idealNoiseModel); + + idealJson.AssertJsonIsEqualTo(roundtripJson); + } + } +} diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs index 0de9c2b3117..6327b567333 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs @@ -13,10 +13,61 @@ namespace Microsoft.Quantum.Experimental { - public class Instrument + 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(); + + var result = variant switch + { + "Effects" => new EffectsInstrument + { + Effects = 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) + { + writer.WriteStartObject(); + + switch (value) + { + case EffectsInstrument effectsInstrument: + writer.WritePropertyName("Effects"); + JsonSerializer.Serialize(writer, effectsInstrument.Effects); + break; + + default: + throw new JsonException($"Enum variant {value.GetType()} not yet supported."); + } + + writer.WriteEndObject(); + } + } + + [JsonConverter(typeof(InstrumentConverter))] + public abstract class Instrument + { + + } + + public class EffectsInstrument : Instrument { [JsonPropertyName("effects")] - public IList Effects { get; set; } = new List(); + public IList Effects { get; set; } = new List(); public override string ToString() => $"Instrument {{ Effects = {String.Join(", ", Effects.Select(effect => effect.ToString()))} }}"; diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs index 7936a4abed2..e29b2bff255 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs @@ -23,34 +23,34 @@ public class NoiseModel public State? InitialState { get; set; } [JsonPropertyName("cnot")] - public Channel? Cnot { get; set; } + public Process? Cnot { get; set; } [JsonPropertyName("i")] - public Channel? I { get; set; } + public Process? I { get; set; } [JsonPropertyName("s")] - public Channel? S { get; set; } + public Process? S { get; set; } [JsonPropertyName("s_adj")] - public Channel? SAdj { get; set; } + public Process? SAdj { get; set; } [JsonPropertyName("t")] - public Channel? T { get; set; } + public Process? T { get; set; } [JsonPropertyName("t_adj")] - public Channel? TAdj { get; set; } + public Process? TAdj { get; set; } [JsonPropertyName("h")] - public Channel? H { get; set; } + public Process? H { get; set; } [JsonPropertyName("x")] - public Channel? X { get; set; } + public Process? X { get; set; } [JsonPropertyName("y")] - public Channel? Y { get; set; } + public Process? Y { get; set; } [JsonPropertyName("z")] - public Channel? Z { get; set; } + public Process? Z { get; set; } [JsonPropertyName("z_meas")] public Instrument? ZMeas { get; set; } diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Channel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs similarity index 74% rename from src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Channel.cs rename to src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs index 395c544566c..62d5bb9e93d 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Channel.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs @@ -14,21 +14,21 @@ namespace Microsoft.Quantum.Experimental { - public class ChannelConverter : JsonConverter + public class ProcessConverter : JsonConverter { - public override Channel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Process Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var (nQubits, kind, data) = ComplexArrayConverter.ReadQubitSizedArray(ref reader, options); return kind switch { - "Unitary" => new UnitaryChannel(nQubits, data), - "KrausDecomposition" => new KrausDecompositionChannel(nQubits, data), + "Unitary" => new UnitaryProcess(nQubits, data), + "KrausDecomposition" => new KrausDecompositionProcess(nQubits, data), _ => throw new JsonException($"Unknown state kind {kind}.") }; } - public override void Write(Utf8JsonWriter writer, Channel value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerOptions options) { var arrayConverter = new ComplexArrayConverter(); @@ -40,8 +40,8 @@ public override void Write(Utf8JsonWriter writer, Channel value, JsonSerializerO writer.WritePropertyName( value switch { - UnitaryChannel _ => "Unitary", - KrausDecompositionChannel _ => "KrausDecomposition", + UnitaryProcess _ => "Unitary", + KrausDecompositionProcess _ => "KrausDecomposition", _ => throw new JsonException() } ); @@ -52,8 +52,8 @@ public override void Write(Utf8JsonWriter writer, Channel value, JsonSerializerO } } - [JsonConverter(typeof(ChannelConverter))] - public abstract class Channel + [JsonConverter(typeof(ProcessConverter))] + public abstract class Process { public int NQubits { get; } @@ -61,16 +61,21 @@ public abstract class Channel // means that the shape is at least fixed at initialization time. public NDArray Data { get; } - internal Channel(int nQubits, NDArray data) + internal Process(int nQubits, NDArray data) { NQubits = nQubits; Data = data; } } - public class UnitaryChannel : Channel + // TODO: Add class for mixed pauli processes as well. + // public class MixedPauliProcess : Process + // { + // } + + public class UnitaryProcess : Process { - public UnitaryChannel(int nQubits, NDArray data) : base(nQubits, data) + 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. @@ -82,11 +87,11 @@ public UnitaryChannel(int nQubits, NDArray data) : base(nQubits, data) } public override string ToString() => - $@"Unitary channel on {NQubits} qubits: {Data.AsTextMatrixOfComplex(rowSep: " ")}"; + $@"Unitary process on {NQubits} qubits: {Data.AsTextMatrixOfComplex(rowSep: " ")}"; } - public class KrausDecompositionChannel : Channel + public class KrausDecompositionProcess : Process { - public KrausDecompositionChannel(int nQubits, NDArray data) : base(nQubits, data) + 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 @@ -109,7 +114,7 @@ public override string ToString() Data.IterateOverLeftmostIndex() .Select(op => op.AsTextMatrixOfComplex(rowSep: " ")) ); - return $@"Kraus decomposition of channel on {NQubits} qubits: {ops}"; + return $@"Kraus decomposition of process on {NQubits} qubits: {ops}"; } } From afcdeb99a9e4d37b32e2f0cd82e28d6967542649 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 12:00:37 -0700 Subject: [PATCH 106/158] Update System.Text.Json to 4.7.2. --- src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index 0cc93611964..a3dd60f7333 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -25,7 +25,7 @@ - + From 9306a8c2e4806a044696b6fa30d5acf2a10e281c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 12:57:26 -0700 Subject: [PATCH 107/158] Address clippy warnings. --- src/Simulation/qdk_sim_rs/src/instrument.rs | 2 +- src/Simulation/qdk_sim_rs/src/processes.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index afa03277c9a..a8686502bf4 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -93,7 +93,7 @@ impl Instrument { // TODO: Add more methods for making new instruments in convenient ways. } -fn sample_effects(effects: &Vec, idx_qubits: &[usize], state: &State) -> (usize, State) { +fn sample_effects(effects: &[Process], idx_qubits: &[usize], state: &State) -> (usize, State) { let mut possible_outcomes = effects .iter() .enumerate() diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 8a3451538b2..8613291ef33 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -77,12 +77,12 @@ impl Process { )); } - return match &self.data { + match &self.data { Unitary(u) => apply_unitary(&u, state), KrausDecomposition(ks) => apply_kraus_decomposition(&ks, state), MixedPauli(paulis) => apply_pauli_channel(&paulis, state), - ProcessData::Unsupported => return Err(format!("Unsupported quantum process.")), - }; + ProcessData::Unsupported => Err("Unsupported quantum process.".to_string()), + } } /// Applies this process to the given qubits in a register with a given @@ -490,9 +490,9 @@ fn apply_unitary(u: &Array2, state: &State) -> Result { Pure(psi) => Pure(u.dot(psi)), Mixed(rho) => Mixed(rho.conjugate_by(&u.into())), Stabilizer(_tableau) => { - return Err(format!( - "TODO: Promote stabilizer state to state vector and recurse." - )) + return Err( + "TODO: Promote stabilizer state to state vector and recurse.".to_string(), + ) } }, }) @@ -523,9 +523,9 @@ fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Result { - return Err(format!( - "TODO: Promote stabilizer state to state vector and recurse." - )) + return Err( + "TODO: Promote stabilizer state to state vector and recurse.".to_string(), + ) } }, }) From f4106d6c0f807d997d2fe445b0dcf81e9b84aef0 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 12:59:55 -0700 Subject: [PATCH 108/158] Don't use new C# 9 features. --- .../SerializationTests/NoiseModelSerializationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs index 84d42351f35..41b1fcbeb78 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs @@ -16,7 +16,7 @@ internal static class SerializationExtensions { internal static void AssertTypeAnd(this object obj, Action then) { - if (obj is not T t) + if (!(obj is T t)) { Assert.IsType(obj); } From 65aa212224343e64b1248111ec42b92579e84440 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 13:07:13 -0700 Subject: [PATCH 109/158] Don't store cargo lockfile in repo for now. --- src/Simulation/qdk_sim_rs/.gitignore | 4 + src/Simulation/qdk_sim_rs/Cargo.lock | 1212 -------------------------- 2 files changed, 4 insertions(+), 1212 deletions(-) delete mode 100644 src/Simulation/qdk_sim_rs/Cargo.lock diff --git a/src/Simulation/qdk_sim_rs/.gitignore b/src/Simulation/qdk_sim_rs/.gitignore index f67c633ca75..55a98a1eba7 100644 --- a/src/Simulation/qdk_sim_rs/.gitignore +++ b/src/Simulation/qdk_sim_rs/.gitignore @@ -2,9 +2,13 @@ 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 diff --git a/src/Simulation/qdk_sim_rs/Cargo.lock b/src/Simulation/qdk_sim_rs/Cargo.lock deleted file mode 100644 index 997444259d9..00000000000 --- a/src/Simulation/qdk_sim_rs/Cargo.lock +++ /dev/null @@ -1,1212 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "assert-json-diff" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f1c3703dd33532d7f0ca049168930e9099ecac238e23cf932f3a69c42f06da" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bstr" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "built" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76efeb50f902677353f8f7052c7da1cc8c4f65b46b30cc41fc1e3042249711c7" -dependencies = [ - "cargo-lock", -] - -[[package]] -name = "bumpalo" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cargo-lock" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f16e7adc20969298b1e137ac21ab3a7e7a9412fec71f963ff2fdc41663d70f" -dependencies = [ - "semver 0.11.0", - "serde", - "toml", - "url", -] - -[[package]] -name = "cast" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "bitflags", - "textwrap", - "unicode-width", -] - -[[package]] -name = "criterion" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" -dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools 0.10.0", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" -dependencies = [ - "cast", - "itertools 0.9.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "lazy_static", -] - -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] -name = "ctor" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "0.99.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "getrandom" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "ghost" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5bcf1bbeab73aa4cf2fde60a846858dc036163c7c33bec309f8d17de785479" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "half" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" - -[[package]] -name = "hermit-abi" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indoc" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" -dependencies = [ - "indoc-impl", - "proc-macro-hack", -] - -[[package]] -name = "indoc-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", - "unindent", -] - -[[package]] -name = "instant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "inventory" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0f7efb804ec95e33db9ad49e4252f049e37e8b0a4652e3cd61f7999f2eff7f" -dependencies = [ - "ctor", - "ghost", - "inventory-impl", -] - -[[package]] -name = "inventory-impl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c094e94816723ab936484666968f5b58060492e880f3c8d00489a1e244fa51" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" - -[[package]] -name = "js-sys" -version = "0.3.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" - -[[package]] -name = "lock_api" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -dependencies = [ - "cfg-if 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - -[[package]] -name = "matrixmultiply" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f" -dependencies = [ - "rawpointer", -] - -[[package]] -name = "matrixmultiply" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8a15b776d9dfaecd44b03c5828c2199cddff5247215858aac14624f8d6b741" -dependencies = [ - "rawpointer", -] - -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "memoffset" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ndarray" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" -dependencies = [ - "matrixmultiply 0.2.3", - "num-complex 0.2.4", - "num-integer", - "num-traits", - "rawpointer", -] - -[[package]] -name = "ndarray" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b2e4807aaa21dc6dcc3417e5902dc199c3648043bf27b7af4b202332fe4760" -dependencies = [ - "matrixmultiply 0.3.1", - "num-complex 0.4.0", - "num-integer", - "num-traits", - "rawpointer", - "serde", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" -dependencies = [ - "derivative", - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "numpy" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7073fae1e0b82409533a29c6f804b79783d7b2d3c07728fdc4d884eda8cd4f0" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "ndarray 0.13.1", - "num-complex 0.2.4", - "num-traits", - "pyo3", -] - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "paste" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" -dependencies = [ - "paste-impl", - "proc-macro-hack", -] - -[[package]] -name = "paste-impl" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" -dependencies = [ - "proc-macro-hack", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "plotters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" - -[[package]] -name = "plotters-svg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "pyo3" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4837b8e8e18a102c23f79d1e9a110b597ea3b684c95e874eb1ad88f8683109c3" -dependencies = [ - "cfg-if 1.0.0", - "ctor", - "indoc", - "inventory", - "libc", - "parking_lot", - "paste", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-macros" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47f2c300ceec3e58064fd5f8f5b61230f2ffd64bde4970c81fdd0563a2db1bb" -dependencies = [ - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b097e5d84fcbe3e167f400fbedd657820a375b034c78bd852050749a575d66" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "qdk_sim" -version = "0.0.1-alpha" -dependencies = [ - "assert-json-diff", - "built", - "cfg-if 1.0.0", - "criterion", - "derive_more", - "getrandom", - "itertools 0.9.0", - "lazy_static", - "ndarray 0.15.2", - "num-complex 0.4.0", - "num-traits", - "num_enum", - "numpy", - "pyo3", - "rand", - "serde", - "serde_json", - "web-sys", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] - -[[package]] -name = "regex-syntax" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", - "serde", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_cbor" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "syn" -version = "1.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "unicode-bidi" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "unindent" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "walkdir" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasm-bindgen" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" - -[[package]] -name = "web-sys" -version = "0.3.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" From f4c404e2479e47e7e165387210883bb2276f0bcd Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 13:08:05 -0700 Subject: [PATCH 110/158] Remove outdated gitignore. --- src/Qir/Runtime/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/Qir/Runtime/.gitignore diff --git a/src/Qir/Runtime/.gitignore b/src/Qir/Runtime/.gitignore deleted file mode 100644 index 378eac25d31..00000000000 --- a/src/Qir/Runtime/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build From eb73bf72012b0393e2e9616d7aeca8a16851fa3e Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 13:24:03 -0700 Subject: [PATCH 111/158] Fix serialization test, false negatives. --- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 6 ++++-- src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index 245da5cdc1c..18b39a362b7 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -22,12 +22,14 @@ Push-Location $PSScriptRoot # PublishTestResults task. if ("$Env:TF_BUILD" -ne "") { cargo install cargo2junit - cargo test -- -Z unstable-options --format json ` + $testJson = cargo test -- -Z unstable-options --format json; + $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; + + $testJson ` | cargo2junit ` <# We use this name to match the *_results.xml pattern that is used to find test results in steps-wrap-up.yml. #> ` | Out-File -FilePath opensim_results.xml -Encoding utf8NoBOM - $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; } else { # Outside of CI, show human-readable output. cargo test diff --git a/src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json b/src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json index e73e2c99250..33c2d3089bb 100644 --- a/src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json +++ b/src/Simulation/qdk_sim_rs/tests/data/ideal-noise-model.json @@ -1 +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":{"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]]}}},"z_meas":{"effects":[{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[1,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],[1,0]]}}}]}} \ No newline at end of file +{"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":{"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]]}}},"z_meas":{"Effects":[{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[1,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],[1,0]]}}}]}} \ No newline at end of file From e12568e865e29485ec82b0eab0d8c5f55c7a1e2c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 14:08:19 -0700 Subject: [PATCH 112/158] Use +nightly to run doctests. --- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index 18b39a362b7..4b35b545b9b 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -22,7 +22,7 @@ Push-Location $PSScriptRoot # PublishTestResults task. if ("$Env:TF_BUILD" -ne "") { cargo install cargo2junit - $testJson = cargo test -- -Z unstable-options --format json; + $testJson = cargo +nightly test -- -Z unstable-options --format json; $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; $testJson ` @@ -32,7 +32,7 @@ Push-Location $PSScriptRoot | Out-File -FilePath opensim_results.xml -Encoding utf8NoBOM } else { # Outside of CI, show human-readable output. - cargo test + cargo +nightly test $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; } From b144ced3cf6a9513f93833b3a006beb2f1cdceea Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 14:54:44 -0700 Subject: [PATCH 113/158] Allow serializing and deserializing mixed pauli channels. --- .../DataModel/ArrayConverter.cs | 1 - .../DataModel/Extensions.cs | 63 +++++++++++++++++ .../DataModel/Instrument.cs | 20 +++++- .../OpenSystemsSimulator/DataModel/Process.cs | 70 ++++++++++++++----- .../DataModel/StateConverter.cs | 1 + 5 files changed, 132 insertions(+), 23 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs index 1d825d42a0c..67a23acd2af 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ArrayConverter.cs @@ -182,7 +182,6 @@ public override void Write(Utf8JsonWriter writer, NDArray value, JsonSerializerO public static (int nQubits, string kind, NDArray data) ReadQubitSizedArray(ref Utf8JsonReader reader, JsonSerializerOptions options) { - var arrayConverter = new ComplexArrayConverter(); int? nQubits = null; NDArray? data = null; diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs index 63ca08cac2b..db91935920e 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs @@ -5,9 +5,11 @@ 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) => @@ -72,5 +74,66 @@ public static void Require(this ref Utf8JsonReader reader, JsonTokenType type, b 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": + // Here, we expect an object with one property indicating + // the kind of data for the object. + reader.Require(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); + 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 index 6327b567333..b43b3c93606 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs @@ -25,12 +25,13 @@ public override Instrument Read(ref Utf8JsonReader reader, Type typeToConvert, J var variant = reader.GetString(); - var result = variant switch + 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.") }; @@ -41,20 +42,24 @@ public override Instrument Read(ref Utf8JsonReader reader, Type typeToConvert, J public override void Write(Utf8JsonWriter writer, Instrument value, JsonSerializerOptions options) { - writer.WriteStartObject(); switch (value) { case EffectsInstrument effectsInstrument: + writer.WriteStartObject(); writer.WritePropertyName("Effects"); JsonSerializer.Serialize(writer, effectsInstrument.Effects); + writer.WriteEndObject(); + break; + + case ZMeasurementInstrument zInstrument: + JsonSerializer.Serialize(writer, zInstrument); break; default: throw new JsonException($"Enum variant {value.GetType()} not yet supported."); } - writer.WriteEndObject(); } } @@ -72,4 +77,13 @@ public class EffectsInstrument : Instrument 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/Process.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs index 62d5bb9e93d..89429a0a2f4 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs @@ -9,6 +9,7 @@ using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.Quantum.Simulation.Core; using NumSharp; using static System.Math; @@ -16,16 +17,26 @@ namespace Microsoft.Quantum.Experimental { public class ProcessConverter : JsonConverter { - public override Process Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var (nQubits, kind, data) = ComplexArrayConverter.ReadQubitSizedArray(ref reader, options); - return kind switch - { - "Unitary" => new UnitaryProcess(nQubits, data), - "KrausDecomposition" => new KrausDecompositionProcess(nQubits, data), - _ => throw new JsonException($"Unknown state kind {kind}.") - }; + 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" => JsonSerializer.Deserialize)>>(ref reader).Bind( + (int nQubits, IList<(double, IList)> data) => new MixedPauliProcess(nQubits, data) + ), + _ => throw new JsonException() + } + ); } public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerOptions options) @@ -42,11 +53,19 @@ public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerO { UnitaryProcess _ => "Unitary", KrausDecompositionProcess _ => "KrausDecomposition", + MixedPauliProcess _ => "MixedPauli", _ => throw new JsonException() } ); - arrayConverter.Write(writer, value.Data, options); + if (value is ArrayProcess { Data: var data }) + { + arrayConverter.Write(writer, data, options); + } + else if (value is MixedPauliProcess mixedPauliProcess) + { + JsonSerializer.Serialize(writer, mixedPauliProcess.Operators); + } writer.WriteEndObject(); writer.WriteEndObject(); } @@ -55,25 +74,38 @@ public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerO [JsonConverter(typeof(ProcessConverter))] public abstract class Process { + [JsonPropertyName("n_qubits")] public int NQubits { get; } - // NB: NDArray instances are mutable, but marking this as read-only - // means that the shape is at least fixed at initialization time. + internal Process(int nQubits) + { + NQubits = nQubits; + } + } + + public abstract class ArrayProcess : Process + { + [JsonPropertyName("data")] public NDArray Data { get; } - internal Process(int nQubits, NDArray data) + internal ArrayProcess(int nQubits, NDArray data) : base(nQubits) { - NQubits = nQubits; - Data = data; + this.Data = data; } } // TODO: Add class for mixed pauli processes as well. - // public class MixedPauliProcess : Process - // { - // } + public class MixedPauliProcess : Process + { + public IList<(double, IList)> Operators; + + internal MixedPauliProcess(int nQubits, IList<(double, IList)> operators) : base(nQubits) + { + this.Operators = operators; + } + } - public class UnitaryProcess : Process + public class UnitaryProcess : ArrayProcess { public UnitaryProcess(int nQubits, NDArray data) : base(nQubits, data) { @@ -89,7 +121,7 @@ public UnitaryProcess(int nQubits, NDArray data) : base(nQubits, data) public override string ToString() => $@"Unitary process on {NQubits} qubits: {Data.AsTextMatrixOfComplex(rowSep: " ")}"; } - public class KrausDecompositionProcess : Process + public class KrausDecompositionProcess : ArrayProcess { public KrausDecompositionProcess(int nQubits, NDArray data) : base(nQubits, data) { diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs index b79254bb211..e8d0b286d55 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs @@ -47,6 +47,7 @@ public override State Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe { "Pure" => new PureState(nQubits, data), "Mixed" => new MixedState(nQubits, data), + // TODO: read tableaus here. _ => throw new JsonException($"Unknown state kind {kind}.") }; } From 36ebabd5329b175edee24812b26e62d1610b77e9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 14:57:19 -0700 Subject: [PATCH 114/158] Expose as_json to more Python classes. --- src/Simulation/qdk_sim_rs/src/instrument.rs | 5 +++++ src/Simulation/qdk_sim_rs/src/processes.rs | 5 +++++ src/Simulation/qdk_sim_rs/src/python.rs | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index a8686502bf4..0a580d69adb 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -91,6 +91,11 @@ impl Instrument { } // TODO: Add more methods for making new instruments in convenient ways. + + /// Returns a serialization of this instrument as a JSON object. + pub fn as_json(&self) -> String { + serde_json::to_string(&self).unwrap() + } } fn sample_effects(effects: &[Process], idx_qubits: &[usize], state: &State) -> (usize, State) { diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes.rs index 8613291ef33..3d35730800f 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes.rs @@ -67,6 +67,11 @@ impl Process { } // TODO: methods to forcibly convert representations. + /// Returns a serialization of this quantum process as a JSON object. + pub fn as_json(&self) -> String { + serde_json::to_string(&self).unwrap() + } + /// Applies this process to a quantum register with a given /// state, returning the new state of that register. pub fn apply(&self, state: &State) -> Result { diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index 67239c96913..7bc7a3ae236 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -100,6 +100,10 @@ impl PyProcess { } } + pub fn as_json(&self) -> String { + self.data.as_json() + } + pub fn apply(&self, state: PyState) -> PyResult { let data = self .data @@ -144,6 +148,10 @@ impl PyInstrument { data: Instrument::ZMeasurement { pr_readout_error }, } } + + pub fn as_json(&self) -> String { + self.data.as_json() + } } #[pyclass(name = "NoiseModel")] From e2efd448626c8461b5190801c19503a078fa797c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 3 Jun 2021 22:43:39 -0700 Subject: [PATCH 115/158] Added ideal_stabilizer noise model, split up processes, and start CHP decomp. --- .../qdk_sim_rs/src/chp_decompositions.rs | 37 ++++ src/Simulation/qdk_sim_rs/src/lib.rs | 1 + src/Simulation/qdk_sim_rs/src/noise_model.rs | 79 +++++++- .../qdk_sim_rs/src/processes/apply.rs | 161 +++++++++++++++ .../src/{processes.rs => processes/mod.rs} | 190 ++++-------------- src/Simulation/qdk_sim_rs/src/python.rs | 7 + 6 files changed, 319 insertions(+), 156 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/src/chp_decompositions.rs create mode 100644 src/Simulation/qdk_sim_rs/src/processes/apply.rs rename src/Simulation/qdk_sim_rs/src/{processes.rs => processes/mod.rs} (70%) diff --git a/src/Simulation/qdk_sim_rs/src/chp_decompositions.rs b/src/Simulation/qdk_sim_rs/src/chp_decompositions.rs new file mode 100644 index 00000000000..d896eef532f --- /dev/null +++ b/src/Simulation/qdk_sim_rs/src/chp_decompositions.rs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use serde::{Deserialize, Serialize}; + +/// Represents a single step of a decomposition of +/// a Clifford operation into CNOT, Hadamard, and phase operations. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum ChpOperation { + /// The controlled-NOT operation between two qubits. + Cnot(usize, usize), + + /// The Hadamard operation. + Hadamard(usize), + + /// The phase operation, represented by the matrix + /// $$ + /// \begin{align} + /// S = \left( \begin{matrix} + /// 1 & 0 \\\\ + /// 0 & i + /// \end{matrix} \right). + /// \end{align} + /// $$ + Phase(usize), + + /// The phase operation, represented by the matrix + /// $$ + /// \begin{align} + /// S = \left( \begin{matrix} + /// 1 & 0 \\\\ + /// 0 & i + /// \end{matrix} \right). + /// \end{align} + /// $$ + AdjointPhase(usize), +} diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 9f954aec52b..fee435f7e2d 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize}; use std::usize; pub mod c_api; +mod chp_decompositions; pub mod common_matrices; mod instrument; pub mod linalg; diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index d9828084104..b60e1e30ae6 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -1,10 +1,16 @@ +use crate::chp_decompositions::ChpOperation; use crate::common_matrices; use crate::instrument::Instrument; use crate::linalg::HasDagger; use crate::processes::Process; -use crate::processes::ProcessData::{KrausDecomposition, Unitary}; +use crate::processes::{ + ProcessData, + ProcessData::{KrausDecomposition, Unitary}, +}; use crate::states::State; use crate::states::StateData::Mixed; +use crate::StateData; +use crate::Tableau; use crate::C64; use num_traits::{One, Zero}; @@ -142,6 +148,77 @@ impl NoiseModel { z_meas, } } + + /// Returns a copy of the ideal noise model suitable for use with + /// stabilizer simulation; that is, a noise model + /// describing the case in which no noise acts on the quantum system, and + /// in which all channels can be represented by CHP decompositions. + pub fn ideal_stabilizer() -> NoiseModel { + NoiseModel { + initial_state: State { + n_qubits: 1, + data: StateData::Stabilizer(Tableau::new(1)), + }, + i: Process { + n_qubits: 1, + data: ProcessData::Sequence(vec![]), + }, + x: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ + ChpOperation::Hadamard(0), + ChpOperation::Phase(0), + ChpOperation::Phase(0), + ChpOperation::Hadamard(0), + ]), + }, + y: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ + ChpOperation::AdjointPhase(0), + ChpOperation::Hadamard(0), + ChpOperation::Phase(0), + ChpOperation::Phase(0), + ChpOperation::Hadamard(0), + ChpOperation::Phase(0), + ]), + }, + z: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ + ChpOperation::Phase(0), + ChpOperation::Phase(0), + ]), + }, + h: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ChpOperation::Hadamard(0)]), + }, + s: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ChpOperation::Phase(0)]), + }, + s_adj: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ChpOperation::AdjointPhase(0)]), + }, + t: Process { + n_qubits: 1, + data: ProcessData::Unsupported, + }, + t_adj: Process { + n_qubits: 1, + data: ProcessData::Unsupported, + }, + cnot: Process { + n_qubits: 1, + data: ProcessData::ChpDecomposition(vec![ChpOperation::Cnot(0, 1)]), + }, + z_meas: Instrument::ZMeasurement { + pr_readout_error: 0.0, + }, + } + } } #[cfg(test)] diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs new file mode 100644 index 00000000000..9bde3dd15cf --- /dev/null +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Public implementations and crate-private functions for applying processes +//! in each different representation. + +use itertools::Itertools; +use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; +use rand::{distributions::WeightedIndex, prelude::Distribution, thread_rng}; + +use crate::{linalg::ConjBy, log, log_as_err, Pauli, Process, State, StateData::*, C64}; + +use super::promote_pauli_channel; + +impl Process { + /// Applies this process to the given qubits in a register with a given + /// state, returning the new state of that register. + pub fn apply_to(&self, idx_qubits: &[usize], state: &State) -> Result { + // Fail if there's not enough qubits. + if state.n_qubits < self.n_qubits { + return log_as_err(format!( + "Channel acts on {} qubits, but a state on only {} qubits was given.", + self.n_qubits, state.n_qubits + )); + } + + // Fail if any indices are repeated. + if idx_qubits.iter().unique().count() < idx_qubits.len() { + return log_as_err(format!( + "List of qubit indices {:?} contained repeated elements.", + idx_qubits + )); + } + + // Make sure that there are only as many indices as qubits that this + // channel acts upon. + if idx_qubits.len() != self.n_qubits { + return log_as_err(format!( + "Qubit indices were specified as {:?}, but this channel only acts on {} qubits.", + idx_qubits, self.n_qubits + )); + } + + // At this point we know that idx_qubits has self.n_qubits many unique + // indices in ascending order, so we can proceed to make a new channel + // that expands this channel to act on the full register and then use + // the ordinary apply method. + // TODO[perf]: For larger systems, this could be improved by using + // matrix multiplication kernels to avoid extending + // channels to larger Hilbert spaces. + // For smaller systems, extending channels and possibly + // caching them is likely to be more performant; need to + // tune to find crossover point. + match self.n_qubits { + 1 => { + if state.n_qubits == 1 { + self.apply(state) + } else { + self.extend_one_to_n(idx_qubits[0], state.n_qubits) + .apply(state) + } + } + // TODO[perf]: If the size of the register matches the size of the + // channel, permute rather than expanding. + 2 => self + .extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits) + .apply(state), + _ => { + log(&format!( + "Expanding {}-qubit channels is not yet implemented.", + self.n_qubits + )); + unimplemented!(""); + } + } + } +} + +pub(crate) fn apply_unitary(u: &Array2, state: &State) -> Result { + Ok(State { + n_qubits: state.n_qubits, + data: match &state.data { + Pure(psi) => Pure(u.dot(psi)), + Mixed(rho) => Mixed(rho.conjugate_by(&u.into())), + Stabilizer(_tableau) => { + return Err( + "TODO: Promote stabilizer state to state vector and recurse.".to_string(), + ) + } + }, + }) +} + +pub(crate) fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Result { + Ok(State { + n_qubits: state.n_qubits, + data: match &state.data { + Pure(psi) => { + // We can't apply a channel with more than one Kraus operator (Choi rank > 1) to a + // pure state directly, so if the Choi rank is bigger than 1, promote to + // Mixed and recurse. + if ks.shape()[0] == 1 { + Pure({ + let k: ArrayView2 = ks.slice(s![0, .., ..]); + k.dot(psi) + }) + } else { + apply_kraus_decomposition(ks, &state.to_mixed())?.data + } + } + Mixed(rho) => Mixed({ + let mut sum: Array2 = Array::zeros((rho.shape()[0], rho.shape()[1])); + for k in ks.axis_iter(Axis(0)) { + sum = sum + rho.conjugate_by(&k); + } + sum + }), + Stabilizer(_tableau) => { + return Err( + "TODO: Promote stabilizer state to state vector and recurse.".to_string(), + ) + } + }, + }) +} + +pub(crate) fn apply_pauli_channel( + paulis: &[(f64, Vec)], + state: &State, +) -> Result { + Ok(State { + n_qubits: state.n_qubits, + data: match &state.data { + Pure(_) | Mixed(_) => { + // Promote and recurse. + let promoted = promote_pauli_channel(paulis); + return promoted.apply(state); + } + Stabilizer(tableau) => { + // TODO[perf]: Introduce an apply_mut method to + // avoid extraneous cloning. + let mut new_tableau = tableau.clone(); + // Sample a Pauli and apply it. + let weighted = WeightedIndex::new(paulis.iter().map(|(pr, _)| pr)).unwrap(); + let idx = weighted.sample(&mut thread_rng()); + let pauli = &(&paulis)[idx].1; + // TODO: Consider moving the following to a method + // on Tableau itself. + for (idx_qubit, p) in pauli.iter().enumerate() { + match p { + Pauli::I => (), + Pauli::X => new_tableau.apply_x_mut(idx_qubit), + Pauli::Y => new_tableau.apply_y_mut(idx_qubit), + Pauli::Z => new_tableau.apply_z_mut(idx_qubit), + } + } + Stabilizer(new_tableau) + } + }, + }) +} diff --git a/src/Simulation/qdk_sim_rs/src/processes.rs b/src/Simulation/qdk_sim_rs/src/processes/mod.rs similarity index 70% rename from src/Simulation/qdk_sim_rs/src/processes.rs rename to src/Simulation/qdk_sim_rs/src/processes/mod.rs index 3d35730800f..a441bcf0bd6 100644 --- a/src/Simulation/qdk_sim_rs/src/processes.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/mod.rs @@ -1,23 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +mod apply; + +use crate::chp_decompositions::ChpOperation; use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; -use crate::log_as_err; -use crate::states::StateData::{Mixed, Pure, Stabilizer}; +use crate::processes::apply::{apply_kraus_decomposition, apply_pauli_channel, apply_unitary}; +use crate::processes::ProcessData::{KrausDecomposition, MixedPauli, Unitary}; use crate::NoiseModel; use crate::QubitSized; use crate::State; use crate::C64; -use crate::{linalg::ConjBy, AsUnitary, Pauli}; -use crate::{ - log, - processes::ProcessData::{KrausDecomposition, MixedPauli, Unitary}, -}; +use crate::{AsUnitary, Pauli}; use itertools::Itertools; -use ndarray::{Array, Array2, Array3, ArrayView2, Axis, NewAxis}; +use ndarray::{Array, Array2, Array3, Axis, NewAxis}; use num_complex::Complex; use num_traits::{One, Zero}; -use rand::{distributions::WeightedIndex, prelude::Distribution, thread_rng}; use serde::{Deserialize, Serialize}; use std::convert::TryInto; use std::ops::Add; @@ -47,6 +45,13 @@ pub enum ProcessData { /// indices representing the indices of each operator. KrausDecomposition(Array3), // TODO: Superoperator and Choi reps. + /// Representation of a process as a sequence of other processes. + Sequence(Vec), + + /// Representation of a Clifford operation in terms of a decomposition + /// into CNOT, Hadamard, and phase operations. + ChpDecomposition(Vec), + /// Represents a process that is not supported by a given noise model, /// and thus always fails when applied. Unsupported, @@ -86,69 +91,15 @@ impl Process { Unitary(u) => apply_unitary(&u, state), KrausDecomposition(ks) => apply_kraus_decomposition(&ks, state), MixedPauli(paulis) => apply_pauli_channel(&paulis, state), - ProcessData::Unsupported => Err("Unsupported quantum process.".to_string()), - } - } - - /// Applies this process to the given qubits in a register with a given - /// state, returning the new state of that register. - pub fn apply_to(&self, idx_qubits: &[usize], state: &State) -> Result { - // Fail if there's not enough qubits. - if state.n_qubits < self.n_qubits { - return log_as_err(format!( - "Channel acts on {} qubits, but a state on only {} qubits was given.", - self.n_qubits, state.n_qubits - )); - } - - // Fail if any indices are repeated. - if idx_qubits.iter().unique().count() < idx_qubits.len() { - return log_as_err(format!( - "List of qubit indices {:?} contained repeated elements.", - idx_qubits - )); - } - - // Make sure that there are only as many indices as qubits that this - // channel acts upon. - if idx_qubits.len() != self.n_qubits { - return log_as_err(format!( - "Qubit indices were specified as {:?}, but this channel only acts on {} qubits.", - idx_qubits, self.n_qubits - )); - } - - // At this point we know that idx_qubits has self.n_qubits many unique - // indices in ascending order, so we can proceed to make a new channel - // that expands this channel to act on the full register and then use - // the ordinary apply method. - // TODO[perf]: For larger systems, this could be improved by using - // matrix multiplication kernels to avoid extending - // channels to larger Hilbert spaces. - // For smaller systems, extending channels and possibly - // caching them is likely to be more performant; need to - // tune to find crossover point. - match self.n_qubits { - 1 => { - if state.n_qubits == 1 { - self.apply(state) - } else { - self.extend_one_to_n(idx_qubits[0], state.n_qubits) - .apply(state) + ProcessData::Sequence(processes) => { + let mut acc_state = state.clone(); + for process in processes { + acc_state = process.apply(state)?; } + Ok(acc_state) } - // TODO[perf]: If the size of the register matches the size of the - // channel, permute rather than expanding. - 2 => self - .extend_two_to_n(idx_qubits[0], idx_qubits[1], state.n_qubits) - .apply(state), - _ => { - log(&format!( - "Expanding {}-qubit channels is not yet implemented.", - self.n_qubits - )); - unimplemented!(""); - } + ProcessData::ChpDecomposition(_operations) => todo!(), + ProcessData::Unsupported => Err("Unsupported quantum process.".to_string()), } } @@ -184,7 +135,11 @@ impl Process { }) .collect_vec() ), - ProcessData::Unsupported => ProcessData::Unsupported + ProcessData::Unsupported => ProcessData::Unsupported, + ProcessData::Sequence(processes) => ProcessData::Sequence( + processes.iter().map(|p| p.extend_one_to_n(idx_qubit, n_qubits)).collect() + ), + ProcessData::ChpDecomposition(_) => todo!(), }, } } @@ -228,7 +183,11 @@ impl Process { }) .collect_vec() ), - ProcessData::Unsupported => ProcessData::Unsupported + ProcessData::Unsupported => ProcessData::Unsupported, + ProcessData::Sequence(processes) => ProcessData::Sequence( + processes.iter().map(|p| p.extend_two_to_n(idx_qubit1, idx_qubit2, n_qubits)).collect() + ), + ProcessData::ChpDecomposition(_) => todo!(), }, } } @@ -253,6 +212,10 @@ impl Mul<&Process> for C64 { KrausDecomposition(ks) => KrausDecomposition(self.sqrt() * ks), MixedPauli(paulis) => (self * promote_pauli_channel(paulis)).data, ProcessData::Unsupported => ProcessData::Unsupported, + ProcessData::Sequence(processes) => { + ProcessData::Sequence(processes.iter().map(|p| self * p).collect()) + } + ProcessData::ChpDecomposition(_) => todo!(), }, } } @@ -485,86 +448,3 @@ fn promote_pauli_channel(paulis: &[(f64, Vec)]) -> Process { } } } - -// Private functions for applying processes of each different representation. - -fn apply_unitary(u: &Array2, state: &State) -> Result { - Ok(State { - n_qubits: state.n_qubits, - data: match &state.data { - Pure(psi) => Pure(u.dot(psi)), - Mixed(rho) => Mixed(rho.conjugate_by(&u.into())), - Stabilizer(_tableau) => { - return Err( - "TODO: Promote stabilizer state to state vector and recurse.".to_string(), - ) - } - }, - }) -} - -fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Result { - Ok(State { - n_qubits: state.n_qubits, - data: match &state.data { - Pure(psi) => { - // We can't apply a channel with more than one Kraus operator (Choi rank > 1) to a - // pure state directly, so if the Choi rank is bigger than 1, promote to - // Mixed and recurse. - if ks.shape()[0] == 1 { - Pure({ - let k: ArrayView2 = ks.slice(s![0, .., ..]); - k.dot(psi) - }) - } else { - apply_kraus_decomposition(ks, &state.to_mixed())?.data - } - } - Mixed(rho) => Mixed({ - let mut sum: Array2 = Array::zeros((rho.shape()[0], rho.shape()[1])); - for k in ks.axis_iter(Axis(0)) { - sum = sum + rho.conjugate_by(&k); - } - sum - }), - Stabilizer(_tableau) => { - return Err( - "TODO: Promote stabilizer state to state vector and recurse.".to_string(), - ) - } - }, - }) -} - -fn apply_pauli_channel(paulis: &[(f64, Vec)], state: &State) -> Result { - Ok(State { - n_qubits: state.n_qubits, - data: match &state.data { - Pure(_) | Mixed(_) => { - // Promote and recurse. - let promoted = promote_pauli_channel(paulis); - return promoted.apply(state); - } - Stabilizer(tableau) => { - // TODO[perf]: Introduce an apply_mut method to - // avoid extraneous cloning. - let mut new_tableau = tableau.clone(); - // Sample a Pauli and apply it. - let weighted = WeightedIndex::new(paulis.iter().map(|(pr, _)| pr)).unwrap(); - let idx = weighted.sample(&mut thread_rng()); - let pauli = &(&paulis)[idx].1; - // TODO: Consider moving the following to a method - // on Tableau itself. - for (idx_qubit, p) in pauli.iter().enumerate() { - match p { - Pauli::I => (), - Pauli::X => new_tableau.apply_x_mut(idx_qubit), - Pauli::Y => new_tableau.apply_y_mut(idx_qubit), - Pauli::Z => new_tableau.apply_z_mut(idx_qubit), - } - } - Stabilizer(new_tableau) - } - }, - }) -} diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index 7bc7a3ae236..289bb35a174 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -169,6 +169,13 @@ impl PyNoiseModel { } } + #[staticmethod] + pub fn ideal_stabilizer() -> PyNoiseModel { + PyNoiseModel { + data: NoiseModel::ideal_stabilizer(), + } + } + pub fn as_json(&self) -> String { self.data.as_json() } From 2342e87e53826f52154fdbdbbc363722400f1de3 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 08:48:55 -0700 Subject: [PATCH 116/158] Allow applying CHP decompositions. --- src/Simulation/qdk_sim_rs/src/noise_model.rs | 3 - .../qdk_sim_rs/src/processes/apply.rs | 85 ++++++++++++++++++- .../qdk_sim_rs/src/processes/mod.rs | 28 ------ 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index b60e1e30ae6..92e1d3594f3 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -16,9 +16,6 @@ use num_traits::{One, Zero}; use serde::{Deserialize, Serialize}; -#[cfg(feature = "python")] -use pyo3::prelude::*; - /// A description of the noise that applies to the state of a quantum system /// as the result of applying operations. #[derive(Serialize, Deserialize, Debug)] diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index 9bde3dd15cf..45fab01ba17 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -8,14 +8,54 @@ use itertools::Itertools; use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; use rand::{distributions::WeightedIndex, prelude::Distribution, thread_rng}; -use crate::{linalg::ConjBy, log, log_as_err, Pauli, Process, State, StateData::*, C64}; +use crate::{ + chp_decompositions::ChpOperation, linalg::ConjBy, log, log_as_err, Pauli, Process, + ProcessData::*, State, StateData::*, Tableau, C64, +}; use super::promote_pauli_channel; impl Process { + /// Applies this process to a quantum register with a given + /// state, returning the new state of that register. + pub fn apply(&self, state: &State) -> Result { + if state.n_qubits != self.n_qubits { + return Err(format!( + "Channel acts on {} qubits, but was applied to {}-qubit state.", + self.n_qubits, state.n_qubits + )); + } + + match &self.data { + Unitary(u) => apply_unitary(&u, state), + KrausDecomposition(ks) => apply_kraus_decomposition(&ks, state), + MixedPauli(paulis) => apply_pauli_channel(&paulis, state), + Sequence(processes) => { + // TODO[perf]: eliminate the extraneous clone here. + let mut acc_state = state.clone(); + for process in processes { + acc_state = process.apply(state)?; + } + Ok(acc_state) + } + ChpDecomposition(_operations) => todo!(), + Unsupported => Err("Unsupported quantum process.".to_string()), + } + } + /// Applies this process to the given qubits in a register with a given /// state, returning the new state of that register. pub fn apply_to(&self, idx_qubits: &[usize], state: &State) -> Result { + // If we have a sequence, we can apply each in turn and exit early. + if let Sequence(channels) = &self.data { + // TODO[perf]: eliminate the extraneous clone here. + let mut acc_state = state.clone(); + for channel in channels { + acc_state = channel.apply_to(idx_qubits, &acc_state)?; + } + return Ok(acc_state); + } + // Fail if there's not enough qubits. if state.n_qubits < self.n_qubits { return log_as_err(format!( @@ -42,15 +82,31 @@ impl Process { } // At this point we know that idx_qubits has self.n_qubits many unique - // indices in ascending order, so we can proceed to make a new channel + // indices, such that we can meaningfully apply the channel to the + // qubits described by idx_qubits. + // + // To do so in general, we can proceed to make a new channel // that expands this channel to act on the full register and then use // the ordinary apply method. - // TODO[perf]: For larger systems, this could be improved by using + // + // In some cases, however, we can do so more efficiently by working + // with the small channel directly, so we check for those cases first + // before falling through to the general case. + + // TODO[perf]: For larger systems, we could add another "fast path" using // matrix multiplication kernels to avoid extending // channels to larger Hilbert spaces. // For smaller systems, extending channels and possibly // caching them is likely to be more performant; need to // tune to find crossover point. + if let ChpDecomposition(operations) = &self.data { + if let Stabilizer(tableau) = &state.data { + return apply_chp_decomposition_to(operations, self.n_qubits, idx_qubits, tableau); + } + } + + // Having tried fast paths above, we now fall back to the most general + // case. match self.n_qubits { 1 => { if state.n_qubits == 1 { @@ -76,6 +132,29 @@ impl Process { } } +fn apply_chp_decomposition_to( + operations: &Vec, + n_qubits: usize, + idx_qubits: &[usize], + tableau: &Tableau, +) -> Result { + let mut new_tableau = tableau.clone(); + for operation in operations { + match operation { + &ChpOperation::Phase(idx) => new_tableau.apply_s_mut(idx_qubits[idx]), + &ChpOperation::AdjointPhase(idx) => new_tableau.apply_s_adj_mut(idx_qubits[idx]), + &ChpOperation::Hadamard(idx) => new_tableau.apply_h_mut(idx_qubits[idx]), + &ChpOperation::Cnot(idx_control, idx_target) => { + new_tableau.apply_cnot_mut(idx_qubits[idx_control], idx_qubits[idx_target]) + } + }; + } + Ok(State { + n_qubits, + data: Stabilizer(new_tableau), + }) +} + pub(crate) fn apply_unitary(u: &Array2, state: &State) -> Result { Ok(State { n_qubits: state.n_qubits, diff --git a/src/Simulation/qdk_sim_rs/src/processes/mod.rs b/src/Simulation/qdk_sim_rs/src/processes/mod.rs index a441bcf0bd6..4aacc049d96 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/mod.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/mod.rs @@ -5,11 +5,9 @@ mod apply; use crate::chp_decompositions::ChpOperation; use crate::linalg::{extend_one_to_n, extend_two_to_n, zeros_like}; -use crate::processes::apply::{apply_kraus_decomposition, apply_pauli_channel, apply_unitary}; use crate::processes::ProcessData::{KrausDecomposition, MixedPauli, Unitary}; use crate::NoiseModel; use crate::QubitSized; -use crate::State; use crate::C64; use crate::{AsUnitary, Pauli}; use itertools::Itertools; @@ -77,32 +75,6 @@ impl Process { serde_json::to_string(&self).unwrap() } - /// Applies this process to a quantum register with a given - /// state, returning the new state of that register. - pub fn apply(&self, state: &State) -> Result { - if state.n_qubits != self.n_qubits { - return Err(format!( - "Channel acts on {} qubits, but was applied to {}-qubit state.", - self.n_qubits, state.n_qubits - )); - } - - match &self.data { - Unitary(u) => apply_unitary(&u, state), - KrausDecomposition(ks) => apply_kraus_decomposition(&ks, state), - MixedPauli(paulis) => apply_pauli_channel(&paulis, state), - ProcessData::Sequence(processes) => { - let mut acc_state = state.clone(); - for process in processes { - acc_state = process.apply(state)?; - } - Ok(acc_state) - } - ProcessData::ChpDecomposition(_operations) => todo!(), - ProcessData::Unsupported => Err("Unsupported quantum process.".to_string()), - } - } - /// Returns a copy of this process that applies to registers of a given /// size. pub fn extend_one_to_n(&self, idx_qubit: usize, n_qubits: usize) -> Process { From ab667214c465f4c9ed2d9e9f640f81fcc794a269 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 09:36:31 -0700 Subject: [PATCH 117/158] Fix clippy warnings. --- src/Simulation/qdk_sim_rs/src/processes/apply.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index 45fab01ba17..c65cd321d92 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -133,18 +133,18 @@ impl Process { } fn apply_chp_decomposition_to( - operations: &Vec, + operations: &[ChpOperation], n_qubits: usize, idx_qubits: &[usize], tableau: &Tableau, ) -> Result { let mut new_tableau = tableau.clone(); for operation in operations { - match operation { - &ChpOperation::Phase(idx) => new_tableau.apply_s_mut(idx_qubits[idx]), - &ChpOperation::AdjointPhase(idx) => new_tableau.apply_s_adj_mut(idx_qubits[idx]), - &ChpOperation::Hadamard(idx) => new_tableau.apply_h_mut(idx_qubits[idx]), - &ChpOperation::Cnot(idx_control, idx_target) => { + match *operation { + ChpOperation::Phase(idx) => new_tableau.apply_s_mut(idx_qubits[idx]), + ChpOperation::AdjointPhase(idx) => new_tableau.apply_s_adj_mut(idx_qubits[idx]), + ChpOperation::Hadamard(idx) => new_tableau.apply_h_mut(idx_qubits[idx]), + ChpOperation::Cnot(idx_control, idx_target) => { new_tableau.apply_cnot_mut(idx_qubits[idx_control], idx_qubits[idx_target]) } }; From d53e26c566b7ee10501dbc23967ccd8df95ec525 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 12:22:20 -0700 Subject: [PATCH 118/158] Check error conditions on get_noise_model. --- .../NativeInterfaceTests.cs | 33 +++++ .../DataModel/NoiseModel.cs | 15 ++- .../OpenSystemsSimulator/NativeInterface.cs | 51 +++++--- src/Simulation/qdk_sim_rs/src/c_api.rs | 120 ++++++++++++++---- 4 files changed, 178 insertions(+), 41 deletions(-) create mode 100644 src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs diff --git a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs new file mode 100644 index 00000000000..b955e2b4295 --- /dev/null +++ b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs @@ -0,0 +1,33 @@ +// 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 GetNoiseModelByNameWorks() + { + var ideal = NativeInterface.GetNoiseModelByName("ideal"); + 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/OpenSystemsSimulator/DataModel/NoiseModel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs index e29b2bff255..55b9f4c55dc 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs @@ -10,6 +10,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using NumSharp; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Quantum.Experimental { @@ -55,6 +56,18 @@ public class NoiseModel [JsonPropertyName("z_meas")] public Instrument? ZMeas { get; set; } - public static NoiseModel Ideal => NativeInterface.IdealNoiseModel(); + public static bool TryGetByName(string name, [NotNullWhen(true)] out NoiseModel? model) + { + try + { + model = NativeInterface.GetNoiseModelByName(name); + return true; + } + catch + { + model = null; + return false; + } + } } } diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index d25f98d78cc..293e102b7b8 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -13,6 +13,26 @@ 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. /// @@ -27,7 +47,7 @@ private static void CheckCall(Int64 errorCode) if (errorCode != 0) { var error = _LastError(); - throw new Exception($"Exception in native open systems simulator runtime: {error}"); + throw new SimulationException($"Exception in native open systems simulator runtime: {error}", DLL_NAME); } } @@ -93,14 +113,24 @@ public static State GetCurrentState(ulong simId) return JsonSerializer.Deserialize(_GetCurrentState(simId)); } - [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_noise_model")] - private static extern string _GetNoiseModel(ulong 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"); - return JsonSerializer.Deserialize(_GetNoiseModel(simId)); + CheckCall(_GetNoiseModel(simId, out var noiseModelJson)); + return JsonSerializer.Deserialize(noiseModelJson); } [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="set_noise_model")] @@ -123,23 +153,12 @@ public static void SetNoiseModel(ulong simId, NoiseModel noiseModel) { throw new Exception($"Could not serialize noise model: {ex.Message}", ex); } - catch (Exception 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="ideal_noise_model")] - private static extern string _IdealNoiseModel(); - - - public static NoiseModel IdealNoiseModel() - { - LogCall("ideal_noise_model"); - return JsonSerializer.Deserialize(_IdealNoiseModel()); - } - - [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="h")] private static extern Int64 _H(ulong simId, uint idx); diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 17beeea63b1..40aa66e8084 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -110,9 +110,13 @@ pub extern "C" fn lasterr() -> *const c_char { /// a stabilizer tableau. /// /// # Safety -/// The caller is responsible for ensuring that `sim_id_out` points to valid -/// memory, and that the lifetime of this pointer extends at least for the -/// duration of this call. +/// The caller is responsible for: +/// +/// - Ensuring that `sim_id_out` points to valid +/// memory, and that the lifetime of this pointer extends at least for the +/// duration of this call. +/// - Ensuring that `representation` is a valid pointer to a null-terminated +/// string of Unicode characters, encoded as UTF-8. #[no_mangle] pub unsafe extern "C" fn init( initial_capacity: usize, @@ -254,31 +258,99 @@ pub unsafe extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> }) } -/// Returns the currently configured noise model for a given simulator, -/// serialized as a string representing a JSON object. -#[no_mangle] -pub extern "C" fn get_noise_model(sim_id: usize) -> *const c_char { - let state = &*STATE.lock().unwrap(); - if let Some(sim_state) = state.get(&sim_id) { - CString::new(sim_state.noise_model.as_json().as_str()) - .unwrap() - .into_raw() - } else { - ptr::null() +impl NoiseModel { + /// Private function to help look up common noise models by name. + fn get_by_name(name: &str) -> Result { + match name { + "ideal" => Ok(NoiseModel::ideal()), + "ideal_stabilizer" => Ok(NoiseModel::ideal_stabilizer()), + _ => Err(format!("Unrecognized noise model name {}.", name)), + } } } -/// Returns a JSON serialization of the ideal noise model (i.e.: a noise model -/// that agrees with closed-system simulation). +/// Gets the noise model corresponding to a particular name, serialized as a +/// string representing a JSON object. +/// +/// Currently recognized names: +/// - `ideal` +/// - `ideal_stabilizer` +/// +/// # Safety +/// As this is a C-language API, the Rust compiler cannot guarantee safety when +/// calling into this function. The caller is responsible for ensuring that: +/// +/// - `name` is a pointer to a null-terminated string of Unicode characters, +/// encoded as UTF-8, and that the pointer remains valid for the lifetime of +/// this call. +/// - `noise_model_json` is a valid pointer whose lifetime extends for the +/// duration of this function call. +/// +/// After this call completes, this function guarantees that either of the two +/// conditions below holds: +/// +/// - The return value is negative, in which case calling `lasterr` will return +/// an actionable error message, or +/// - The return value is `0`, and `*noise_model_json` is a valid pointer to a +/// null-terminated string of Unicode characters, encoded as UTF-8. In this +/// case, the caller is considered to own the memory allocated for this +/// string. +#[no_mangle] +pub extern "C" fn get_noise_model_by_name( + name: *const c_char, + noise_model_json: *mut *const c_char, +) -> i64 { + as_capi_err(|| { + let name = unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(|e| format!("UTF-8 error decoding representation argument: {}", e))?; + let noise_model = NoiseModel::get_by_name(name)?; + let noise_model = CString::new(noise_model.as_json()).unwrap(); + unsafe { + *noise_model_json = noise_model.into_raw(); + } + Ok(()) + }) +} + +// TODO: convert to as_capi_err call. +/// Returns the currently configured noise model for a given simulator, +/// serialized as a string representing a JSON object. +/// +/// # Safety +/// As this is a C-language API, the Rust compiler cannot guarantee safety when +/// calling into this function. The caller is responsible for ensuring that: +/// +/// - `noise_model_json` is a valid pointer whose lifetime extends for the +/// duration of this function call. +/// +/// After this call completes, this function guarantees that either of the two +/// conditions below holds: +/// +/// - The return value is negative, in which case calling `lasterr` will return +/// an actionable error message, or +/// - The return value is `0`, and `*noise_model_json` is a valid pointer to a +/// null-terminated string of Unicode characters, encoded as UTF-8. In this +/// case, the caller is considered to own the memory allocated for this +/// string. #[no_mangle] -pub extern "C" fn ideal_noise_model() -> *const c_char { - CString::new( - serde_json::to_string(&NoiseModel::ideal()) - .unwrap() - .as_str(), - ) - .unwrap() - .into_raw() +pub extern "C" fn get_noise_model(sim_id: usize, noise_model_json: *mut *const c_char) -> i64 { + as_capi_err(|| { + let state = &*STATE + .lock() + .map_err(|e| format!("Lock poisoning error: {}", e))?; + if let Some(sim_state) = state.get(&sim_id) { + let c_str = CString::new(sim_state.noise_model.as_json().as_str()).map_err(|e| { + format!("Null error while converting noise model to C string: {}", e) + })?; + unsafe { + *noise_model_json = c_str.into_raw(); + } + } else { + return Err(format!("No simulator with id {} exists.", sim_id)); + } + Ok(()) + }) } /// Sets the noise model used by a given simulator instance, given a string From 65b568c66d781aca2c2b5f2a808e9f8f098eb85d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 12:50:29 -0700 Subject: [PATCH 119/158] Allow for getting/setting noise models by name. --- .../NativeInterfaceTests.cs | 8 ++++- .../OpenSystemsSimulator/NativeInterface.cs | 10 ++++++ src/Simulation/qdk_sim_rs/src/c_api.rs | 35 ++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs index b955e2b4295..70e6b2c2a70 100644 --- a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs +++ b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs @@ -14,9 +14,15 @@ namespace Microsoft.Quantum.Experimental public partial class NativeInterfaceTests { [Fact] - public void GetNoiseModelByNameWorks() + public void GetIdealNoiseModelByNameWorks() { var ideal = NativeInterface.GetNoiseModelByName("ideal"); + // TODO: Add assertions here to check properties of the above noise model. + } + + [Fact(Skip="Deserializing stabilizer tableaus is not yet supported.")] + public void GetIdealStabilizerNoiseModelByNameWorks() + { var idealStabilizer = NativeInterface.GetNoiseModelByName("ideal_stabilizer"); // TODO: Add assertions here to check properties of each noise model. } diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 293e102b7b8..17f751e9e79 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -159,6 +159,16 @@ public static void SetNoiseModel(ulong simId, NoiseModel noiseModel) } } + [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); diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 40aa66e8084..b1402a292d8 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -313,7 +313,6 @@ pub extern "C" fn get_noise_model_by_name( }) } -// TODO: convert to as_capi_err call. /// Returns the currently configured noise model for a given simulator, /// serialized as a string representing a JSON object. /// @@ -405,6 +404,40 @@ pub unsafe extern "C" fn set_noise_model(sim_id: usize, new_model: *const c_char }) } +/// Sets the noise model used by a given simulator instance, given a string +/// containing the name of a built-in noise model. +/// +/// # Safety +/// This function is marked as unsafe as the caller is responsible for ensuring +/// that `name`: +/// +/// - Is a valid pointer to a null-terminated array of C +/// characters. +/// - The pointer remains valid for at least the duration +/// of the call. +/// - No other thread may modify the memory referenced by `new_model` for at +/// least the duration of the call. +#[no_mangle] +pub unsafe extern "C" fn set_noise_model_by_name(sim_id: usize, name: *const c_char) -> i64 { + as_capi_err(|| { + if name.is_null() { + return Err("set_noise_model_by_name called with null pointer".to_string()); + } + + let name = CStr::from_ptr(name) + .to_str() + .map_err(|e| format!("UTF-8 error decoding name: {}", e))?; + let noise_model = NoiseModel::get_by_name(name)?; + let state = &mut *STATE.lock().unwrap(); + if let Some(sim_state) = state.get_mut(&sim_id) { + sim_state.noise_model = noise_model; + Ok(()) + } else { + Err(format!("No simulator with id {} exists.", sim_id)) + } + }) +} + /// Returns the state of a given simulator, serialized as a JSON object. #[no_mangle] pub extern "C" fn get_current_state(sim_id: usize) -> *const c_char { From 620fa1d1560d1c3923166277a895d709820d9cdd Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 13:03:35 -0700 Subject: [PATCH 120/158] Expose as_json for State to Python. --- src/Simulation/qdk_sim_rs/src/python.rs | 4 ++++ src/Simulation/qdk_sim_rs/src/states.rs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index 289bb35a174..29c3d18b73d 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -76,6 +76,10 @@ impl PyState { data: State::new_stabilizer(n_qubits), } } + + pub fn as_json(&self) -> String { + self.data.as_json() + } } #[pyclass(name = "Process")] diff --git a/src/Simulation/qdk_sim_rs/src/states.rs b/src/Simulation/qdk_sim_rs/src/states.rs index 3e2404e02ad..3bf69611212 100644 --- a/src/Simulation/qdk_sim_rs/src/states.rs +++ b/src/Simulation/qdk_sim_rs/src/states.rs @@ -95,6 +95,11 @@ impl State { } } + /// Returns a serialization of this quantum state as a JSON object. + pub fn as_json(&self) -> String { + serde_json::to_string(&self).unwrap() + } + /// Extends this state to be a state on `n_qubits` additional qubits. /// New qubits are added "to the right," e.g.: $\left|\psi\right\rangle$ /// is extended to $\left|\psi 0\right\rangle$. From 06bead50ac5f4a5df24336f1a76b1f2fdfe5ee0d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 13:37:04 -0700 Subject: [PATCH 121/158] Added stabilizer states to data model for serialization. --- .../NativeInterfaceTests.cs | 2 +- .../NoiseModelSerializationTests.cs | 33 ----------- .../SerializationExtensions.cs | 47 +++++++++++++++ .../StateSerializationTests.cs | 47 +++++++++++++++ .../OpenSystemsSimulator/DataModel/State.cs | 58 +++++++++++++++++-- .../DataModel/StateConverter.cs | 40 ++++++++++--- 6 files changed, 179 insertions(+), 48 deletions(-) create mode 100644 src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs create mode 100644 src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs diff --git a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs index 70e6b2c2a70..cb95eaba9af 100644 --- a/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs +++ b/src/Simulation/Simulators.Tests/OpenSystemsSimulatorTests/NativeInterfaceTests.cs @@ -20,7 +20,7 @@ public void GetIdealNoiseModelByNameWorks() // TODO: Add assertions here to check properties of the above noise model. } - [Fact(Skip="Deserializing stabilizer tableaus is not yet supported.")] + [Fact] public void GetIdealStabilizerNoiseModelByNameWorks() { var idealStabilizer = NativeInterface.GetNoiseModelByName("ideal_stabilizer"); diff --git a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs index 41b1fcbeb78..a1f008cd4d7 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs @@ -12,39 +12,6 @@ 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); - return Encoding.UTF8.GetString(stream.ToArray()); - } - - 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); - } - } - 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],[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]]}}}]}}"; diff --git a/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs b/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs new file mode 100644 index 00000000000..4777142dadc --- /dev/null +++ b/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs @@ -0,0 +1,47 @@ +// 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); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + 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..eca344659e3 --- /dev/null +++ b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs @@ -0,0 +1,47 @@ +// 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(1, 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"": 1, + ""data"": { + ""Stabilizer"": { + ""n_qubits"": 1, + ""table"": { + ""v"": 1, + ""dim"": [2, 3], + ""data"": [true,false,false,false,true,false] + } + } + } + }"; + + expectedJson.AssertJsonIsEqualTo(json); + } + } +} diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs index 59568e31ab6..c21402282ba 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs @@ -19,16 +19,64 @@ public abstract class State { public int NQubits { get; } + 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. + internal class TableArray + { + [JsonPropertyName("v")] + internal int SchemaVersion { get; set; } = 1; + + [JsonPropertyName("dim")] + internal List? Dimensions { get; set; } + + [JsonPropertyName("data")] + internal List? Data { get; set; } + + internal NDArray? AsArray => + Dimensions == null || Data == null + ? null + : np.ndarray( + new Shape(Dimensions.ToArray()), + typeof(bool), + Data.ToArray() + ); + } + public NDArray Data { get; } - internal State(int nQubits, NDArray data) + internal StabilizerState(int nQubits, TableArray data) : base(nQubits) { - NQubits = nQubits; - Data = data; + var array = data.AsArray; + if (array == null) + { + throw new Exception($"Data deserialized for stabilizer state was null. Dimensions: {data.Dimensions}, data: {data.Data}"); + } + Data = array!; + } + } + + public abstract class ArrayState : State + { + public NDArray Data { get; } + + internal ArrayState(int nQubits, NDArray data) : base(nQubits) + { + this.Data = data; } } - public class PureState : State + public class PureState : ArrayState { public PureState(int nQubits, NDArray data) : base(nQubits, data) { @@ -43,7 +91,7 @@ public PureState(int nQubits, NDArray data) : base(nQubits, data) // TODO: Override ToString here! } - public class MixedState : State + public class MixedState : ArrayState { public MixedState(int nQubits, NDArray data) : base(nQubits, data) { diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs index e8d0b286d55..2abbbacb857 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs @@ -42,14 +42,24 @@ to interpret data. public override State Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var (nQubits, kind, data) = ComplexArrayConverter.ReadQubitSizedArray(ref reader, options); - return kind switch - { - "Pure" => new PureState(nQubits, data), - "Mixed" => new MixedState(nQubits, data), - // TODO: read tableaus here. - _ => throw new JsonException($"Unknown state kind {kind}.") - }; + 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.TableArray data) => new StabilizerState(nQubits, data) + ), + _ => throw new JsonException($"Unknown state kind {kind}.") + } + ); } public override void Write(Utf8JsonWriter writer, State value, JsonSerializerOptions options) @@ -70,7 +80,19 @@ public override void Write(Utf8JsonWriter writer, State value, JsonSerializerOpt } ); - arrayConverter.Write(writer, value.Data, options); + 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() + }; + JsonSerializer.Serialize(writer, array); + } writer.WriteEndObject(); writer.WriteEndObject(); } From 72977df6a9ddbc17b9dcecc3dfb7fd355f348a81 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 17:19:53 -0700 Subject: [PATCH 122/158] Significantly expanded documentation. --- .../Decompositions/README.md | 2 +- .../OpenSystemsSimulator.cs | 4 +- .../Simulators/OpenSystemsSimulator/README.md | 65 ++++++++ src/Simulation/qdk_sim_rs/.gitignore | 6 + src/Simulation/qdk_sim_rs/Cargo.toml.template | 2 + src/Simulation/qdk_sim_rs/README.md | 102 +++++++++++- src/Simulation/qdk_sim_rs/build.rs | 16 +- src/Simulation/qdk_sim_rs/cbindgen.toml | 1 + src/Simulation/qdk_sim_rs/docs/c-api.md | 145 ++++++++++++++++++ src/Simulation/qdk_sim_rs/docs/python-api.md | 45 ++++++ src/Simulation/qdk_sim_rs/src/c_api.rs | 27 +--- src/Simulation/qdk_sim_rs/src/lib.rs | 10 +- src/Simulation/qdk_sim_rs/src/noise_model.rs | 25 ++- src/Simulation/qdk_sim_rs/src/python.rs | 21 +++ src/Simulation/qdk_sim_rs/src/tableau.rs | 3 + 15 files changed, 444 insertions(+), 30 deletions(-) create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/README.md create mode 100644 src/Simulation/qdk_sim_rs/cbindgen.toml create mode 100644 src/Simulation/qdk_sim_rs/docs/c-api.md create mode 100644 src/Simulation/qdk_sim_rs/docs/python-api.md diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md index e3ff7b78b41..f687ce8d0d6 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md +++ b/src/Simulation/Simulators/OpenSystemsSimulator/Decompositions/README.md @@ -1,4 +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#. \ No newline at end of file +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/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index 4b3b350f02f..56326b710b9 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -65,9 +65,9 @@ protected override void Release(Qubit qubit, bool wasUsedOnlyForBorrowing) } } - public OpenSystemsSimulator(uint capacity = 3) : base(new QubitManager((long)capacity)) + public OpenSystemsSimulator(uint capacity = 3, string representation = "mixed") : base(new QubitManager((long)capacity)) { - this.Id = NativeInterface.Init(capacity); + this.Id = NativeInterface.Init(capacity, representation); } void IIntrinsicExp.Body(IQArray paulis, double angle, IQArray targets) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/README.md b/src/Simulation/Simulators/OpenSystemsSimulator/README.md new file mode 100644 index 00000000000..59d2d1a1f4e --- /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 reuturned 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/qdk_sim_rs/.gitignore b/src/Simulation/qdk_sim_rs/.gitignore index 55a98a1eba7..a740f3ce698 100644 --- a/src/Simulation/qdk_sim_rs/.gitignore +++ b/src/Simulation/qdk_sim_rs/.gitignore @@ -14,3 +14,9 @@ Cargo.lock *.pyd *.whl *.egg-info + +# Ignore generated C/C++ headers. +include/ + +# Don't ignore documentation here. +!docs diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index b31fcdddb0e..26908f887a4 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -72,6 +72,8 @@ built = "0.5.0" [build-dependencies] built = "0.5.0" +cbindgen = "0.19.0" + [dev-dependencies] assert-json-diff = "2.0.1" diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index 6ec1c22ee2f..6669c5e812c 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -1,3 +1,7 @@ + + # Quantum Development Kit Experimental Simulators > ## **⚠** WARNING **⚠** @@ -13,6 +17,8 @@ This **experimental** crate contains simulation functionality for the Quantum De 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. @@ -28,6 +34,100 @@ This crate provides several different data structures for representing quantum s ## 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 @@ -38,4 +138,4 @@ TODO - 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 [`std::Result`] returns instead. +- Too many APIs `panic!` or `unwrap`, and need replaced with `Result` returns instead. diff --git a/src/Simulation/qdk_sim_rs/build.rs b/src/Simulation/qdk_sim_rs/build.rs index 4306bb7873c..cc5fb1c8731 100644 --- a/src/Simulation/qdk_sim_rs/build.rs +++ b/src/Simulation/qdk_sim_rs/build.rs @@ -1,3 +1,15 @@ -fn main() { - built::write_built_file().expect("Failed to acquire build-time information") +extern crate cbindgen; + +use std::env; + +fn main() -> Result<(), String> { + built::write_built_file().expect("Failed to acquire build-time information"); + + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + cbindgen::generate(crate_dir) + .map_err(|e| e.to_string())? + .write_to_file("include/qdk_sim.h"); + + 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/c-api.md b/src/Simulation/qdk_sim_rs/docs/c-api.md new file mode 100644 index 00000000000..11d152bafe3 --- /dev/null +++ b/src/Simulation/qdk_sim_rs/docs/c-api.md @@ -0,0 +1,145 @@ +# 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 build +``` + +This will generate `include/qdk_sim.h`, which can then be used from C callers: + +```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 docs/example.c -Iinclude -Ltarget/debug -lqdk_sim -lws2_32 -lAdvapi32 -lUserenv +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()); + } + + 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()); +} +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/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index b1402a292d8..0fa40a4bd04 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -1,16 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! 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. +// The following two attributes include the README.md for this crate 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; @@ -258,17 +254,6 @@ pub unsafe extern "C" fn m(sim_id: usize, idx: usize, result_out: *mut usize) -> }) } -impl NoiseModel { - /// Private function to help look up common noise models by name. - fn get_by_name(name: &str) -> Result { - match name { - "ideal" => Ok(NoiseModel::ideal()), - "ideal_stabilizer" => Ok(NoiseModel::ideal_stabilizer()), - _ => Err(format!("Unrecognized noise model name {}.", name)), - } - } -} - /// Gets the noise model corresponding to a particular name, serialized as a /// string representing a JSON object. /// diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index fee435f7e2d..9bab0e9b352 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -7,6 +7,9 @@ // for discussion. #![cfg_attr(doc, feature(extended_key_value_attributes))] #![cfg_attr(doc, cfg_attr(doc, doc = include_str!("../README.md")))] +// Set linting rules for documentation. We will stop the build on missing docs, +// or docs with known broken links. +#![cfg_attr(doc, deny(rustdoc::broken_intra_doc_links))] #![cfg_attr(doc, deny(missing_docs))] #![cfg_attr(doc, warn(missing_doc_code_examples))] @@ -38,8 +41,13 @@ pub use crate::states::{State, StateData}; pub use crate::tableau::Tableau; pub use crate::utils::*; -#[cfg(feature = "python")] +// When documenting, we want to make sure to expose the Python module as +// public so that rustdoc can see its documentation. When the "python" crate +// feature is off, the module shouldn't even be private. +#[cfg(all(not(doc), feature = "python"))] mod python; +#[cfg(all(doc, feature = "python"))] +pub mod python; /// Represents that a given type has a size that can be measured in terms /// of a number of qubits (e.g.: [`State`]). diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 92e1d3594f3..8f935bb7c85 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -73,9 +73,30 @@ impl NoiseModel { pub fn as_json(&self) -> String { serde_json::to_string(&self).unwrap() } -} -impl NoiseModel { + /// Given the name of a built-in noise model, returns either that noise + /// model if it exists, or an [`Err`] variant if no such model exists. + /// + /// Currently accepted noise model names: + /// + /// - `"ideal"`: A full-state noise model in which all operations are + /// ideal (that is, without errors). + /// - `"ideal_stabilizer"`: A noise model in which all operations except + /// [`NoiseModel::t`] and [`NoiseModel::t_adj`] are ideal, and + /// represented in a form compatible with stabilizer simulation. + /// + /// # Example + /// ``` + /// let noise_model = NoiseModel::get_by_name("ideal"); + /// ``` + pub fn get_by_name(name: &str) -> Result { + match name { + "ideal" => Ok(NoiseModel::ideal()), + "ideal_stabilizer" => Ok(NoiseModel::ideal_stabilizer()), + _ => Err(format!("Unrecognized noise model name {}.", name)), + } + } + /// Returns a copy of the ideal noise model; that is, a noise model /// describing the case in which no noise acts on the quantum system. pub fn ideal() -> NoiseModel { diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index 29c3d18b73d..da12c751fbd 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -1,6 +1,12 @@ // 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/python-api.md")))] // FIXME: remove when the Python module is documented. #![allow(missing_docs)] @@ -164,6 +170,13 @@ pub struct PyNoiseModel { data: NoiseModel, } +#[pyproto] +impl PyObjectProtocol for PyNoiseModel { + fn __repr__(&self) -> String { + format!("", self.data) + } +} + #[pymethods] impl PyNoiseModel { #[staticmethod] @@ -180,6 +193,14 @@ impl PyNoiseModel { } } + #[staticmethod] + pub fn get_by_name(name: &str) -> PyResult { + Ok(PyNoiseModel { + data: NoiseModel::get_by_name(name) + .map_err(|e| PyErr::new::(e))?, + }) + } + pub fn as_json(&self) -> String { self.data.as_json() } diff --git a/src/Simulation/qdk_sim_rs/src/tableau.rs b/src/Simulation/qdk_sim_rs/src/tableau.rs index c12ffe27a4f..dabc9e81988 100644 --- a/src/Simulation/qdk_sim_rs/src/tableau.rs +++ b/src/Simulation/qdk_sim_rs/src/tableau.rs @@ -258,6 +258,9 @@ impl Tableau { #[cfg(feature = "python")] #[pymethods] impl Tableau { + /// Returns a new stabilizer tableau representing + /// the $\ket{00\cdots 0}$ state of an $n$-qubit + /// register. #[new] pub fn new_py(n_qubits: usize) -> Self { Self::new(n_qubits) From bb09fc4ac12eea3453dd0929a4cde1d4e82e1384 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 17:22:45 -0700 Subject: [PATCH 123/158] Added header explaining how to call cargo doc. --- src/Simulation/qdk_sim_rs/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index 6669c5e812c..6343b16123c 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -1,5 +1,10 @@ # Quantum Development Kit Experimental Simulators From 7b08bff6f7f12c6e78dabb9cd694b365ff2b4e75 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 18:12:32 -0700 Subject: [PATCH 124/158] Fix doctest. --- src/Simulation/qdk_sim_rs/src/noise_model.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index 8f935bb7c85..b8852736233 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -87,6 +87,7 @@ impl NoiseModel { /// /// # Example /// ``` + /// # use qdk_sim::NoiseModel; /// let noise_model = NoiseModel::get_by_name("ideal"); /// ``` pub fn get_by_name(name: &str) -> Result { From 1042e5128cda540959883bf5554ba719e3812908 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 18:24:59 -0700 Subject: [PATCH 125/158] Add extra assertions. --- src/Simulation/qdk_sim_rs/src/instrument.rs | 4 ++++ src/Simulation/qdk_sim_rs/src/processes/apply.rs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 0a580d69adb..a88c2839603 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -133,6 +133,10 @@ fn sample_effects(effects: &[Process], idx_qubits: &[usize], state: &State) -> ( panic!("Couldn't renormalize, expected mixed output from instrument."); } } + assert!( + (output_state.trace() - 1.0).norm() <= 1e-10, + "Expected output of instrument to be trace 1." + ); return (idx, output_state); } } diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index c65cd321d92..daf4ce77694 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -9,8 +9,13 @@ use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; use rand::{distributions::WeightedIndex, prelude::Distribution, thread_rng}; use crate::{ - chp_decompositions::ChpOperation, linalg::ConjBy, log, log_as_err, Pauli, Process, - ProcessData::*, State, StateData::*, Tableau, C64, + chp_decompositions::ChpOperation, + linalg::{ConjBy, Trace}, + log, log_as_err, Pauli, Process, + ProcessData::*, + State, + StateData::*, + Tableau, C64, }; use super::promote_pauli_channel; @@ -192,6 +197,10 @@ pub(crate) fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Resu for k in ks.axis_iter(Axis(0)) { sum = sum + rho.conjugate_by(&k); } + assert!( + ((&sum).trace() - 1.0).norm() <= 1e-10, + "Expected output of applying Kraus decomposition to be trace 1." + ); sum }), Stabilizer(_tableau) => { From 633d88f58466e7ba0874378a9be2bee84422bcb3 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 19:09:08 -0700 Subject: [PATCH 126/158] Add more debug outputs to assertion failure. --- src/Simulation/qdk_sim_rs/src/processes/apply.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index daf4ce77694..b6efca20e3e 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -199,7 +199,8 @@ pub(crate) fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Resu } assert!( ((&sum).trace() - 1.0).norm() <= 1e-10, - "Expected output of applying Kraus decomposition to be trace 1." + "Expected output of applying Kraus decomposition to be trace 1.\nKraus decomposition:\n{}\n\nInput state:\n{}\n\nOutput state:\n{}", + ks, state, sum ); sum }), From b33e9d1c61f5fcbf39188e21fa2b9404ec2c32d9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 19:16:56 -0700 Subject: [PATCH 127/158] Fixed assertion. --- src/Simulation/qdk_sim_rs/src/instrument.rs | 7 ++++++- src/Simulation/qdk_sim_rs/src/processes/apply.rs | 5 ----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index a88c2839603..9bb5565e93d 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -105,9 +105,14 @@ fn sample_effects(effects: &[Process], idx_qubits: &[usize], state: &State) -> ( .map(|(idx, effect)| { let output_state = effect.apply_to(idx_qubits, state).unwrap(); let tr = (&output_state).trace(); - (idx, output_state, tr.re) + (idx, output_state, tr.norm()) }) .collect::>(); + debug_assert!( + possible_outcomes.iter().any(|post_state| post_state.1.trace().norm() >= 1e-10), + "Expected output of applying instrument to be nonzero trace.\nInstrument effects:\n{:?}\n\nInput state:\n{}\n\nPostselected states:\n{:?}", + effects, state, possible_outcomes + ); let mut rng = rand::thread_rng(); let random_sample: f64 = rng.gen(); for (idx, cum_pr) in possible_outcomes diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index b6efca20e3e..d457ac30262 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -197,11 +197,6 @@ pub(crate) fn apply_kraus_decomposition(ks: &Array3, state: &State) -> Resu for k in ks.axis_iter(Axis(0)) { sum = sum + rho.conjugate_by(&k); } - assert!( - ((&sum).trace() - 1.0).norm() <= 1e-10, - "Expected output of applying Kraus decomposition to be trace 1.\nKraus decomposition:\n{}\n\nInput state:\n{}\n\nOutput state:\n{}", - ks, state, sum - ); sum }), Stabilizer(_tableau) => { From 72938eabb0a47bed3962b78e1c8e04817c0f0401 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 19:21:19 -0700 Subject: [PATCH 128/158] Fixed assert. --- src/Simulation/qdk_sim_rs/src/instrument.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 9bb5565e93d..61dc265e7bc 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -108,7 +108,9 @@ fn sample_effects(effects: &[Process], idx_qubits: &[usize], state: &State) -> ( (idx, output_state, tr.norm()) }) .collect::>(); - debug_assert!( + // TODO[perf]: Downgrade this to a debug_assert!, and configure the CI + // build to enable debug_assertions at full_validation. + assert!( possible_outcomes.iter().any(|post_state| post_state.1.trace().norm() >= 1e-10), "Expected output of applying instrument to be nonzero trace.\nInstrument effects:\n{:?}\n\nInput state:\n{}\n\nPostselected states:\n{:?}", effects, state, possible_outcomes From c1d7350bba23e4ecf174caeb206b25fad1bc5c15 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 4 Jun 2021 21:39:52 -0700 Subject: [PATCH 129/158] Fix clippy warning. --- src/Simulation/qdk_sim_rs/src/processes/apply.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index d457ac30262..c65cd321d92 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -9,13 +9,8 @@ use ndarray::{Array, Array2, Array3, ArrayView2, Axis}; use rand::{distributions::WeightedIndex, prelude::Distribution, thread_rng}; use crate::{ - chp_decompositions::ChpOperation, - linalg::{ConjBy, Trace}, - log, log_as_err, Pauli, Process, - ProcessData::*, - State, - StateData::*, - Tableau, C64, + chp_decompositions::ChpOperation, linalg::ConjBy, log, log_as_err, Pauli, Process, + ProcessData::*, State, StateData::*, Tableau, C64, }; use super::promote_pauli_channel; From dbebdeaa732ac44688ca8084681b224d2502970c Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Sun, 6 Jun 2021 09:17:49 -0700 Subject: [PATCH 130/158] Update src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs Co-authored-by: Robin Kuzmin --- .../Simulators/OpenSystemsSimulator/NativeInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 17f751e9e79..9d1e2473c3c 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -91,7 +91,7 @@ public static ulong Init(uint initialCapacity, string representation = "mixed") public static void Destroy(ulong simId) { - LogCall("init"); + LogCall("destroy"); CheckCall(_Destroy(simId)); } From 623cf46d68f02300cddbc689eea2a570fce6d423 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Sun, 6 Jun 2021 09:24:19 -0700 Subject: [PATCH 131/158] Fixed C# serialization. --- .../ProcessSerializationTests.cs | 79 +++++++++++ .../StateSerializationTests.cs | 35 ++++- .../DataModel/ChpDecomposition.cs | 132 ++++++++++++++++++ .../DataModel/Extensions.cs | 45 ++++-- .../OpenSystemsSimulator/DataModel/Process.cs | 91 +++++++++--- .../OpenSystemsSimulator/DataModel/State.cs | 29 ++-- .../DataModel/StateConverter.cs | 8 +- 7 files changed, 365 insertions(+), 54 deletions(-) create mode 100644 src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs create mode 100644 src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs diff --git a/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs new file mode 100644 index 00000000000..662e17171d5 --- /dev/null +++ b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs @@ -0,0 +1,79 @@ +// 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 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/StateSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs index eca344659e3..e5d20be2987 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs @@ -18,14 +18,18 @@ public class StateSerializationTests [Fact] public void StabilizerStateSerializesCorrectly() { - var state = new StabilizerState(1, new StabilizerState.TableArray + var state = new StabilizerState { - Data = new List + NQubits = 2, + Table = new StabilizerState.TableArray { - true, false, false, false, true, false - }, - Dimensions = new List { 2, 3 } - }); + Data = new List + { + true, false, false, false, true, false + }, + Dimensions = new List { 2, 3 } + } + }; var json = JsonSerializer.Serialize(state); var expectedJson = @"{ ""n_qubits"": 1, @@ -43,5 +47,24 @@ public void StabilizerStateSerializesCorrectly() 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/OpenSystemsSimulator/DataModel/ChpDecomposition.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs new file mode 100644 index 00000000000..36da8e149ad --- /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/Extensions.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs index db91935920e..670234325ab 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Extensions.cs @@ -68,7 +68,18 @@ public static void Require(this ref Utf8JsonReader reader, JsonTokenType type, b if (read) reader.Read(); if (reader.TokenType != type) { - throw new JsonException($"Expected a JSON token of type {type}, got {reader.TokenType} instead."); + // 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) => @@ -112,17 +123,31 @@ internal static TResult ReadQubitSizedData(this ref Utf8JsonReader read break; case "data": - // Here, we expect an object with one property indicating + // In most cases, we expect an object with one property + // indicating // the kind of data for the object. - reader.Require(JsonTokenType.StartObject); - reader.Require(JsonTokenType.PropertyName); - var kind = reader.GetString(); - + // For variants of type unit, it's just the string. reader.Read(); - completion = readData(ref reader, kind); - - // Finally, require an end to the object. - reader.Require(JsonTokenType.EndObject); + 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: diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs index 89429a0a2f4..7c1cdfbb9af 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs @@ -34,7 +34,16 @@ public override Process Read(ref Utf8JsonReader reader, Type typeToConvert, Json "MixedPauli" => JsonSerializer.Deserialize)>>(ref reader).Bind( (int nQubits, IList<(double, IList)> data) => new MixedPauliProcess(nQubits, data) ), - _ => throw new JsonException() + "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.") } ); } @@ -47,26 +56,43 @@ public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerO writer.WriteNumber("n_qubits", value.NQubits); writer.WritePropertyName("data"); - writer.WriteStartObject(); - writer.WritePropertyName( - value switch + 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) + { + JsonSerializer.Serialize(writer, mixedPauliProcess.Operators); + } + else if (value is ChpDecompositionProcess chpDecomposition) { - UnitaryProcess _ => "Unitary", - KrausDecompositionProcess _ => "KrausDecomposition", - MixedPauliProcess _ => "MixedPauli", - _ => throw new JsonException() + JsonSerializer.Serialize(writer, chpDecomposition.Operations); } - ); - - if (value is ArrayProcess { Data: var data }) - { - arrayConverter.Write(writer, data, options); - } - else if (value is MixedPauliProcess mixedPauliProcess) - { - JsonSerializer.Serialize(writer, mixedPauliProcess.Operators); - } - writer.WriteEndObject(); + else if (value is SequenceProcess sequence) + { + JsonSerializer.Serialize(writer, sequence.Processes); + } + writer.WriteEndObject(); + } writer.WriteEndObject(); } } @@ -94,7 +120,6 @@ internal ArrayProcess(int nQubits, NDArray data) : base(nQubits) } } - // TODO: Add class for mixed pauli processes as well. public class MixedPauliProcess : Process { public IList<(double, IList)> Operators; @@ -105,6 +130,32 @@ internal MixedPauliProcess(int nQubits, IList<(double, IList)> 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) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs index c21402282ba..3e1406c3c98 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs @@ -17,7 +17,11 @@ namespace Microsoft.Quantum.Experimental [JsonConverter(typeof(StateConverter))] public abstract class State { - public int NQubits { get; } + [JsonPropertyName("n_qubits")] + public int NQubits { get; set; } = 1; + + internal State() + { } internal State(int nQubits) { @@ -32,18 +36,18 @@ public class StabilizerState : State // 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. - internal class TableArray + public class TableArray { [JsonPropertyName("v")] - internal int SchemaVersion { get; set; } = 1; + public int SchemaVersion { get; set; } = 1; [JsonPropertyName("dim")] - internal List? Dimensions { get; set; } + public List? Dimensions { get; set; } [JsonPropertyName("data")] - internal List? Data { get; set; } + public List? Data { get; set; } - internal NDArray? AsArray => + public NDArray? AsArray => Dimensions == null || Data == null ? null : np.ndarray( @@ -53,17 +57,10 @@ internal class TableArray ); } - public NDArray Data { get; } + [JsonPropertyName("table")] + public TableArray? Table { get; set; } - internal StabilizerState(int nQubits, TableArray data) : base(nQubits) - { - var array = data.AsArray; - if (array == null) - { - throw new Exception($"Data deserialized for stabilizer state was null. Dimensions: {data.Dimensions}, data: {data.Data}"); - } - Data = array!; - } + public NDArray Data => Table?.AsArray; } public abstract class ArrayState : State diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs index 2abbbacb857..ad0be705a46 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs @@ -54,8 +54,12 @@ public override State Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe "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.TableArray data) => new StabilizerState(nQubits, data) + "Stabilizer" => JsonSerializer.Deserialize(ref reader).Bind( + (int nQubits, StabilizerState state) => + { + System.Diagnostics.Debug.Assert(nQubits == state.NQubits); + return state; + } ), _ => throw new JsonException($"Unknown state kind {kind}.") } From c7529caa56d498e3f3224ada1e808045d77527e0 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Sun, 6 Jun 2021 09:24:42 -0700 Subject: [PATCH 132/158] Slight comment fixes. --- src/Simulation/qdk_sim_rs/src/c_api.rs | 2 +- src/Simulation/qdk_sim_rs/src/tableau.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Simulation/qdk_sim_rs/src/c_api.rs b/src/Simulation/qdk_sim_rs/src/c_api.rs index 0fa40a4bd04..bf88c09065e 100644 --- a/src/Simulation/qdk_sim_rs/src/c_api.rs +++ b/src/Simulation/qdk_sim_rs/src/c_api.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// The following two attributes include the README.md for this crate when +// 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. diff --git a/src/Simulation/qdk_sim_rs/src/tableau.rs b/src/Simulation/qdk_sim_rs/src/tableau.rs index dabc9e81988..ccbf5ddd98f 100644 --- a/src/Simulation/qdk_sim_rs/src/tableau.rs +++ b/src/Simulation/qdk_sim_rs/src/tableau.rs @@ -22,6 +22,9 @@ use std::io::{Error, ErrorKind}; #[cfg_attr(feature = "python", pyclass(name = "Tableau"))] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Tableau { + // TODO[code quality]: This is redundant with the n_qubits field in + // QubitSized, such that this type should be refactored + // to only have the table itself. n_qubits: usize, table: Array2, } From 5a658e2c71e0a4593e07ccf4012213fe188a0804 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Sun, 6 Jun 2021 10:30:39 -0700 Subject: [PATCH 133/158] Fix build failure. --- .../OpenSystemsSimulator/DataModel/ChpDecomposition.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs index 36da8e149ad..85900a3e4a7 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/ChpDecomposition.cs @@ -71,10 +71,10 @@ public override void Write(Utf8JsonWriter writer, ChpOperation value, JsonSerial writer.WritePropertyName( value switch { - ChpOperation.Cnot => "Cnot", - ChpOperation.Hadamard => "Hadamard", - ChpOperation.Phase => "Phase", - ChpOperation.AdjointPhase => "AdjointPhase", + ChpOperation.Cnot _ => "Cnot", + ChpOperation.Hadamard _ => "Hadamard", + ChpOperation.Phase _ => "Phase", + ChpOperation.AdjointPhase _ => "AdjointPhase", _ => throw new JsonException() } ); From 8376fc3bbfab7536e5ba2e786cf463333787391b Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 08:42:34 -0700 Subject: [PATCH 134/158] Address review comments. --- bootstrap.ps1 | 9 ++++++- build/build.ps1 | 25 ++++++++++++++++--- src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 4 +++ src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 1 + src/Simulation/Common/Simulators.Dev.props | 1 - src/Simulation/qdk_sim_rs/NuGet.Config | 6 +++++ 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 305e16e8792..0ca15813a99 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -27,7 +27,14 @@ if (-not (Test-Path Env:AGENT_OS)) { .\build-native-simulator.ps1 Pop-Location Push-Location (Join-Path $PSScriptRoot "src/Simulation/qdk_sim_rs") - .\build-qdk-sim-rs.ps1 + # 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 } diff --git a/build/build.ps1 b/build/build.ps1 index 7c1e1f953c2..177b5eb6a8b 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -27,10 +27,27 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { } if ($Env:ENABLE_EXPERIMENTALSIM -ne "false") { - $expSim = (Join-Path $PSScriptRoot "../src/Simulation/qdk_sim_rs") - & "$expSim/build-qdk-sim-rs.ps1" - if ($LastExitCode -ne 0) { - $script:all_ok = $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." diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt index 2a2ef2cef0d..6d53af78503 100644 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt @@ -26,6 +26,10 @@ target_link_libraries(Microsoft.Quantum.Qir.QSharp.Core ${CMAKE_DL_LIBS} qsharp-core-qis-support-obj simulators-obj + # NB: Experimental simulator support is not yet enabled for QIR integration. + # See https://github.com/microsoft/qsharp-runtime/issues/713. + # Once QIR integration is available, the following line should be + # uncommented. # opensim "-L${CMAKE_BINARY_DIR}/lib/QIR" -lMicrosoft.Quantum.Qir.Runtime diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index 13af18ae8df..173d35fc4a1 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -24,6 +24,7 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(Corrosion) +set(Rust_TOOLCHAIN "nightly" CACHE STRING "Control flow guard support requires nightly option -Z build-std.") corrosion_import_crate(MANIFEST_PATH ../../../../Simulation/qdk_sim_rs/Cargo.toml) #=============================================================================== diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index 2466fba9c95..3efc2a5f4ef 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -9,7 +9,6 @@ - $([MSBuild]::NormalizePath($(NativeBuildPath)/libMicrosoft.Quantum.Simulator.Runtime.dylib)) $([MSBuild]::NormalizePath($(NativeBuildPath)/libMicrosoft.Quantum.Simulator.Runtime.so)) diff --git a/src/Simulation/qdk_sim_rs/NuGet.Config b/src/Simulation/qdk_sim_rs/NuGet.Config index 765346e5343..8b638f09a31 100644 --- a/src/Simulation/qdk_sim_rs/NuGet.Config +++ b/src/Simulation/qdk_sim_rs/NuGet.Config @@ -1,4 +1,10 @@ + From 42c03efb180786feb2d61589a1c46a61206209d3 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 09:18:18 -0700 Subject: [PATCH 135/158] =?UTF-8?q?qdk=5Fsim=20=E2=86=92=20qdk=5Fsim=5Fexp?= =?UTF-8?q?erimental,improve=20Python=20version=20handling.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/set-env.ps1 | 1 + src/Simulation/qdk_sim_rs/qdk_sim/__init__.py | 18 ------- .../qdk_sim_experimental/__init__.py | 53 +++++++++++++++++++ .../qdk_sim_experimental/version.py | 9 ++++ src/Simulation/qdk_sim_rs/setup.py | 47 +++++++++++++--- 5 files changed, 104 insertions(+), 24 deletions(-) delete mode 100644 src/Simulation/qdk_sim_rs/qdk_sim/__init__.py create mode 100644 src/Simulation/qdk_sim_rs/qdk_sim_experimental/__init__.py create mode 100644 src/Simulation/qdk_sim_rs/qdk_sim_experimental/version.py diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 8ac20b2e261..1173a7a0a07 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -9,6 +9,7 @@ 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: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) ) { diff --git a/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py b/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py deleted file mode 100644 index f2129392806..00000000000 --- a/src/Simulation/qdk_sim_rs/qdk_sim/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from enum import Enum -import enum -import json -import qdk_sim._qdk_sim_rs as _native - -# Re-export native classes. -from qdk_sim._qdk_sim_rs import ( - Tableau, NoiseModel, Instrument, State, Process -) - -class Pauli(enum.Enum): - I = 0 - X = 1 - Y = 3 - Z = 2 - -def build_info(): - return json.loads(_native.build_info_json()) 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 index 513529c8574..6d3fad30592 100644 --- a/src/Simulation/qdk_sim_rs/setup.py +++ b/src/Simulation/qdk_sim_rs/setup.py @@ -1,11 +1,46 @@ -from setuptools import setup +#!/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} +''') -setup( - name="qdk_sim", - version="1.0", - rust_extensions=[RustExtension("qdk_sim._qdk_sim_rs", binding=Binding.PyO3, features=["python"])], - packages=["qdk_sim"], +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, From f3c038258b54eec549169d1717f3dd8ec2411da6 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 09:21:03 -0700 Subject: [PATCH 136/158] Revert src/Qir to main. QIR integration deferred to #713. --- src/Qir/Runtime/lib/QIR/CMakeLists.txt | 1 + src/Qir/Runtime/lib/QIR/QubitManager.cpp | 522 ++++++++++++++++++ src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 5 - src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 18 - .../lib/Simulators/FullstateSimulator.cpp | 21 +- src/Qir/Runtime/prerequisites.ps1 | 3 - src/Qir/Runtime/public/QubitManager.hpp | 255 +++++++++ src/Qir/Runtime/public/SimFactory.hpp | 1 - src/Qir/Runtime/unittests/CMakeLists.txt | 1 + .../Runtime/unittests/QubitManagerTests.cpp | 253 +++++++++ .../FullstateSimulatorTests.cpp | 30 + 11 files changed, 1075 insertions(+), 35 deletions(-) create mode 100644 src/Qir/Runtime/lib/QIR/QubitManager.cpp create mode 100644 src/Qir/Runtime/public/QubitManager.hpp create mode 100644 src/Qir/Runtime/unittests/QubitManagerTests.cpp diff --git a/src/Qir/Runtime/lib/QIR/CMakeLists.txt b/src/Qir/Runtime/lib/QIR/CMakeLists.txt index 9dbd2b9cd74..c9812d73d6b 100644 --- a/src/Qir/Runtime/lib/QIR/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QIR/CMakeLists.txt @@ -19,6 +19,7 @@ set(rt_sup_source_files delegated.cpp strings.cpp utils.cpp + QubitManager.cpp ) # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp new file mode 100644 index 00000000000..7e200aa5c44 --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/QubitManager.cpp @@ -0,0 +1,522 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "QubitManager.hpp" +#include "QirRuntime.hpp" // For quantum__rt__fail_cstr +#include // For memcpy + +namespace Microsoft +{ +namespace Quantum +{ + +// +// Failing in case of errors +// + +[[noreturn]] static void FailNow(const char* message) +{ + quantum__rt__fail_cstr(message); +} + +static void FailIf(bool condition, const char* message) +{ + if (condition) + { + quantum__rt__fail_cstr(message); + } +} + +// +// QubitListInSharedArray +// + +CQubitManager::QubitListInSharedArray::QubitListInSharedArray( + QubitIdType startId, + QubitIdType endId, + QubitIdType* sharedQubitStatusArray): + firstElement(startId), + lastElement(endId) +{ + FailIf(startId > endId || startId < 0 || endId == MaximumQubitCapacity, + "Incorrect boundaries in the linked list initialization."); + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + for (QubitIdType i = startId; i < endId; i++) { + sharedQubitStatusArray[i] = i + 1; // Current element points to the next element. + } + sharedQubitStatusArray[endId] = NoneMarker; // Last element ends the chain. +} + +bool CQubitManager::QubitListInSharedArray::IsEmpty() const +{ + return firstElement == NoneMarker; +} + +void CQubitManager::QubitListInSharedArray::AddQubit(QubitIdType id, bool addToFront, QubitIdType* sharedQubitStatusArray) +{ + FailIf(id == NoneMarker, "Incorrect qubit id, cannot add it to the list."); + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + // If the list is empty, we initialize it with the new element. + if (IsEmpty()) + { + firstElement = id; + lastElement = id; + sharedQubitStatusArray[id] = NoneMarker; // List with a single elemenet in the chain. + return; + } + + if (addToFront) + { + sharedQubitStatusArray[id] = firstElement; // The new element will point to the former first element. + firstElement = id; // The new element is now the first in the chain. + } else + { + sharedQubitStatusArray[lastElement] = id; // The last element will point to the new element. + sharedQubitStatusArray[id] = NoneMarker; // The new element will end the chain. + lastElement = id; // The new element will be the last element in the chain. + } +} + +CQubitManager::QubitIdType CQubitManager::QubitListInSharedArray::TakeQubitFromFront(QubitIdType* sharedQubitStatusArray) +{ + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + // First element will be returned. It is 'NoneMarker' if the list is empty. + QubitIdType result = firstElement; + + // Advance list start to the next element if list is not empty. + if (!IsEmpty()) + { + firstElement = sharedQubitStatusArray[firstElement]; // The second element will be the first. + } + + // Drop pointer to the last element if list becomes empty. + if (IsEmpty()) + { + lastElement = NoneMarker; + } + + if (result != NoneMarker) + { + sharedQubitStatusArray[result] = AllocatedMarker; + } + + return result; +} + +void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, QubitIdType* sharedQubitStatusArray) +{ + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + // No need to do anthing if source is empty. + if (source.IsEmpty()) + { + return; + } + + if (this->IsEmpty()) + { + // If this list is empty, we'll just set it to the source list. + lastElement = source.lastElement; + } else + { + // Attach source at the beginning of the list if both lists aren't empty. + sharedQubitStatusArray[source.lastElement] = firstElement; // The last element of the source chain will point to the first element of this chain. + } + firstElement = source.firstElement; // The first element from the source chain will be the first element of this chain. + + // Remove all elements from source. + source.firstElement = NoneMarker; + source.lastElement = NoneMarker; +} + + +// +// RestrictedReuseArea +// + +CQubitManager::RestrictedReuseArea::RestrictedReuseArea( + QubitListInSharedArray freeQubits): + FreeQubitsReuseProhibited(), // Default costructor + FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. +{ +} + + +// +// CRestrictedReuseAreaStack +// + +void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) +{ + FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); + this->insert(this->end(), area); +} + +CQubitManager::RestrictedReuseArea CQubitManager::CRestrictedReuseAreaStack::PopFromBack() +{ + FailIf(this->empty(), "Cannot remove restricted reuse area from an empty set."); + RestrictedReuseArea result = this->back(); + this->pop_back(); + return result; +} + +CQubitManager::RestrictedReuseArea& CQubitManager::CRestrictedReuseAreaStack::PeekBack() +{ + return this->back(); +} + +int32_t CQubitManager::CRestrictedReuseAreaStack::Count() const +{ + // The size should never exceed int32_t. + return static_cast(this->size()); +} + +// +// CQubitManager +// + +CQubitManager::CQubitManager( + QubitIdType initialQubitCapacity, + bool mayExtendCapacity, + bool encourageReuse): + mayExtendCapacity(mayExtendCapacity), + encourageReuse(encourageReuse), + qubitCapacity(initialQubitCapacity) +{ + FailIf(qubitCapacity <= 0, "Qubit capacity must be positive."); + sharedQubitStatusArray = new QubitIdType[qubitCapacity]; + + // These objects are passed by value (copies are created) + QubitListInSharedArray FreeQubitsFresh(0, qubitCapacity - 1, sharedQubitStatusArray); + RestrictedReuseArea outermostArea(FreeQubitsFresh); + freeQubitsInAreas.PushToBack(outermostArea); + + freeQubitCount = qubitCapacity; +} + +CQubitManager::~CQubitManager() +{ + if (sharedQubitStatusArray != nullptr) + { + delete[] sharedQubitStatusArray; + sharedQubitStatusArray = nullptr; + } + // freeQubitsInAreas - direct member of the class, no need to delete. +} + +// Although it is not necessary to pass area IDs to these functions, such support may be added for extra checks. +void CQubitManager::StartRestrictedReuseArea() +{ + RestrictedReuseArea newArea; + freeQubitsInAreas.PushToBack(newArea); +} + +void CQubitManager::NextRestrictedReuseSegment() +{ + FailIf(freeQubitsInAreas.Count() <= 0, "Internal error! No reuse areas."); + FailIf(freeQubitsInAreas.Count() == 1, "NextRestrictedReuseSegment() without an active area."); + RestrictedReuseArea& currentArea = freeQubitsInAreas.PeekBack(); + // When new segment starts, reuse of all free qubits in the current area becomes prohibited. + currentArea.FreeQubitsReuseProhibited.MoveAllQubitsFrom(currentArea.FreeQubitsReuseAllowed, sharedQubitStatusArray); +} + +void CQubitManager::EndRestrictedReuseArea() +{ + FailIf(freeQubitsInAreas.Count() < 2, "EndRestrictedReuseArea() without an active area."); + RestrictedReuseArea areaAboutToEnd = freeQubitsInAreas.PopFromBack(); + RestrictedReuseArea& containingArea = freeQubitsInAreas.PeekBack(); + // When area ends, reuse of all free qubits from this area becomes allowed. + containingArea.FreeQubitsReuseAllowed.MoveAllQubitsFrom(areaAboutToEnd.FreeQubitsReuseProhibited, sharedQubitStatusArray); + containingArea.FreeQubitsReuseAllowed.MoveAllQubitsFrom(areaAboutToEnd.FreeQubitsReuseAllowed, sharedQubitStatusArray); +} + +Qubit CQubitManager::Allocate() +{ + QubitIdType newQubitId = AllocateQubitId(); + FailIf(newQubitId == NoneMarker, "Not enough qubits."); + return CreateQubitObject(newQubitId); +} + +void CQubitManager::Allocate(Qubit* qubitsToAllocate, int32_t qubitCountToAllocate) +{ + if (qubitCountToAllocate == 0) + { + return; + } + FailIf(qubitCountToAllocate < 0, "Cannot allocate negative number of qubits."); + FailIf(qubitsToAllocate == nullptr, "No array provided for qubits to be allocated."); + + // Consider optimization for initial allocation of a large array at once + for (int32_t i = 0; i < qubitCountToAllocate; i++) + { + QubitIdType newQubitId = AllocateQubitId(); + if (newQubitId == NoneMarker) + { + for (int32_t k = 0; k < i; k++) + { + Release(qubitsToAllocate[k]); + } + FailNow("Not enough qubits."); + } + qubitsToAllocate[i] = CreateQubitObject(newQubitId); + } +} + +void CQubitManager::Release(Qubit qubit) +{ + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + ReleaseQubitId(QubitToId(qubit)); + DeleteQubitObject(qubit); +} + +void CQubitManager::Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease) { + if (qubitCountToRelease == 0) + { + return; + } + FailIf(qubitCountToRelease < 0, "Cannot release negative number of qubits."); + FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); + + for (int32_t i = 0; i < qubitCountToRelease; i++) + { + Release(qubitsToRelease[i]); + qubitsToRelease[i] = nullptr; + } +} + +Qubit CQubitManager::Borrow() +{ + // We don't support true borrowing/returning at the moment. + return Allocate(); +} + +void CQubitManager::Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow) +{ + // We don't support true borrowing/returning at the moment. + return Allocate(qubitsToBorrow, qubitCountToBorrow); +} + +void CQubitManager::Return(Qubit qubit) +{ + // We don't support true borrowing/returning at the moment. + Release(qubit); +} + +void CQubitManager::Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn) +{ + // We don't support true borrowing/returning at the moment. + Release(qubitsToReturn, qubitCountToReturn); +} + +void CQubitManager::Disable(Qubit qubit) +{ + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + QubitIdType id = QubitToId(qubit); + + // We can only disable explicitly allocated qubits that were not borrowed. + FailIf(!IsExplicitlyAllocatedId(id), "Cannot disable qubit that is not explicitly allocated."); + sharedQubitStatusArray[id] = DisabledMarker; + + disabledQubitCount++; + FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); +} + +void CQubitManager::Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable) +{ + if (qubitCountToDisable == 0) + { + return; + } + FailIf(qubitCountToDisable < 0, "Cannot disable negative number of qubits."); + FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); + + for (int32_t i = 0; i < qubitCountToDisable; i++) + { + Disable(qubitsToDisable[i]); + } +} + +bool CQubitManager::IsValidId(QubitIdType id) const +{ + return (id >= 0) && (id < qubitCapacity); +} + +bool CQubitManager::IsDisabledId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] == DisabledMarker; +} + +bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] == AllocatedMarker; +} + +bool CQubitManager::IsFreeId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] >= 0; +} + + +bool CQubitManager::IsValidQubit(Qubit qubit) const +{ + return IsValidId(QubitToId(qubit)); +} + +bool CQubitManager::IsDisabledQubit(Qubit qubit) const +{ + return IsValidQubit(qubit) && IsDisabledId(QubitToId(qubit)); +} + +bool CQubitManager::IsExplicitlyAllocatedQubit(Qubit qubit) const +{ + return IsValidQubit(qubit) && IsExplicitlyAllocatedId(QubitToId(qubit)); +} + +bool CQubitManager::IsFreeQubitId(QubitIdType id) const +{ + return IsValidId(id) && IsFreeId(id); +} + +CQubitManager::QubitIdType CQubitManager::GetQubitId(Qubit qubit) const +{ + FailIf(!IsValidQubit(qubit), "Not a valid qubit."); + return QubitToId(qubit); +} + + +Qubit CQubitManager::CreateQubitObject(QubitIdType id) +{ + // Make sure the static_cast won't overflow: + FailIf(id < 0 || id > std::numeric_limits::max(), "Qubit id is out of range."); + intptr_t pointerSizedId = static_cast(id); + return reinterpret_cast(pointerSizedId); +} + +void CQubitManager::DeleteQubitObject(Qubit qubit) +{ + // Do nothing. By default we store qubit Id in place of a pointer to a qubit. +} + +CQubitManager::QubitIdType CQubitManager::QubitToId(Qubit qubit) const +{ + intptr_t pointerSizedId = reinterpret_cast(qubit); + // Make sure the static_cast won't overflow: + FailIf(pointerSizedId < 0 || pointerSizedId > std::numeric_limits::max(), "Qubit id is out of range."); + return static_cast(pointerSizedId); +} + +void CQubitManager::EnsureCapacity(QubitIdType requestedCapacity) +{ + FailIf(requestedCapacity <= 0, "Requested qubit capacity must be positive."); + if (requestedCapacity <= qubitCapacity) + { + return; + } + // We need to reallocate shared status array, but there's no need to adjust + // existing values (NonMarker or indexes in the array). + + // Prepare new shared status array + QubitIdType* newStatusArray = new QubitIdType[requestedCapacity]; + memcpy(newStatusArray, sharedQubitStatusArray, qubitCapacity * sizeof(newStatusArray[0])); + QubitListInSharedArray newFreeQubits(qubitCapacity, requestedCapacity - 1, newStatusArray); + + // Set new data. All fresh new qubits are added to the free qubits in the outermost area. + freeQubitCount += requestedCapacity - qubitCapacity; + FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); + delete[] sharedQubitStatusArray; + sharedQubitStatusArray = newStatusArray; + qubitCapacity = requestedCapacity; + freeQubitsInAreas[0].FreeQubitsReuseAllowed.MoveAllQubitsFrom(newFreeQubits, sharedQubitStatusArray); +} + +CQubitManager::QubitIdType CQubitManager::TakeFreeQubitId() +{ + // Possible future optimization: we may store and maintain links to the next + // area with non-empty free list. Need to check amortized complexity... + + QubitIdType id = NoneMarker; + if (encourageReuse) + { + // When reuse is encouraged, we start with the innermost area + for (CRestrictedReuseAreaStack::reverse_iterator rit = freeQubitsInAreas.rbegin(); rit != freeQubitsInAreas.rend(); ++rit) + { + id = rit->FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); + if (id != NoneMarker) + { + break; + } + } + } else + { + // When reuse is discouraged, we start with the outermost area + for (CRestrictedReuseAreaStack::iterator it = freeQubitsInAreas.begin(); it != freeQubitsInAreas.end(); ++it) + { + id = it->FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); + if (id != NoneMarker) + { + break; + } + } + } + if (id != NoneMarker) { + FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Allocated invalid qubit."); + allocatedQubitCount++; + FailIf(allocatedQubitCount <= 0, "Incorrect allocated qubit count."); + freeQubitCount--; + FailIf(freeQubitCount < 0, "Incorrect free qubit count."); + } + return id; +} + +CQubitManager::QubitIdType CQubitManager::AllocateQubitId() +{ + QubitIdType newQubitId = TakeFreeQubitId(); + if (newQubitId == NoneMarker && mayExtendCapacity) + { + QubitIdType newQubitCapacity = qubitCapacity * 2; + FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); + EnsureCapacity(newQubitCapacity); + newQubitId = TakeFreeQubitId(); + } + return newQubitId; +} + +void CQubitManager::ReleaseQubitId(QubitIdType id) +{ + FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Cannot release an invalid qubit."); + if (IsDisabledId(id)) + { + // Nothing to do. Qubit will stay disabled. + return; + } + + FailIf(!IsExplicitlyAllocatedId(id), "Attempt to free qubit that has not been allocated."); + + if (mayExtendCapacity && !encourageReuse) + { + // We can extend capcity and don't want reuse => Qubits will never be reused => Discard qubit. + // We put it in its own "free" list, this list will never be found again and qubit will not be reused. + sharedQubitStatusArray[id] = NoneMarker; + } else + { + // Released qubits are added to reuse area/segment in which they were released + // (rather than area/segment where they are allocated). + // Although counterintuitive, this makes code simple. + // This is reasonable because qubits should be allocated and released in the same segment. (This is not enforced) + freeQubitsInAreas.PeekBack().FreeQubitsReuseAllowed.AddQubit(id, encourageReuse, sharedQubitStatusArray); + } + + freeQubitCount++; + FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); +} + + +} +} diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt index 6d53af78503..abf35258ede 100644 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt @@ -26,11 +26,6 @@ target_link_libraries(Microsoft.Quantum.Qir.QSharp.Core ${CMAKE_DL_LIBS} qsharp-core-qis-support-obj simulators-obj - # NB: Experimental simulator support is not yet enabled for QIR integration. - # See https://github.com/microsoft/qsharp-runtime/issues/713. - # Once QIR integration is available, the following line should be - # uncommented. - # opensim "-L${CMAKE_BINARY_DIR}/lib/QIR" -lMicrosoft.Quantum.Qir.Runtime ) diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index 173d35fc4a1..589b53283e2 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -1,9 +1,6 @@ set(source_files "FullstateSimulator.cpp" "ToffoliSimulator.cpp" - # NB: Disabled temporarily until we can build crates into OBJECT CMake - # libraries. - # "OpenSystemsSimulator.cpp" ) set(includes @@ -12,21 +9,6 @@ set(includes "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src/simulator" ) -#=============================================================================== -# Import Rust crates into CMake. - -include(FetchContent) - -FetchContent_Declare( - Corrosion - GIT_REPOSITORY https://github.com/AndrewGaspar/corrosion.git - GIT_TAG origin/master # Optionally specify a version tag or branch here -) - -FetchContent_MakeAvailable(Corrosion) -set(Rust_TOOLCHAIN "nightly" CACHE STRING "Control flow guard support requires nightly option -Z build-std.") -corrosion_import_crate(MANIFEST_PATH ../../../../Simulation/qdk_sim_rs/Cargo.toml) - #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp index 6d9103d6779..b6da62d9443 100644 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp @@ -17,6 +17,7 @@ #include "QSharpSimApi_I.hpp" #include "SimFactory.hpp" #include "OutputStream.hpp" +#include "QubitManager.hpp" #ifdef _WIN32 #include @@ -107,11 +108,13 @@ namespace Quantum const QUANTUM_SIMULATOR handle = 0; unsigned simulatorId = -1; - unsigned nextQubitId = 0; // the QuantumSimulator expects contiguous ids, starting from 0 + // the QuantumSimulator expects contiguous ids, starting from 0 + std::unique_ptr qubitManager; unsigned GetQubitId(Qubit qubit) const { - return static_cast(reinterpret_cast(qubit)); + // Qubit manager uses unsigned range of int32_t for qubit ids. + return static_cast(qubitManager->GetQubitId(qubit)); } std::vector GetQubitIds(long num, Qubit* qubits) const @@ -120,7 +123,7 @@ namespace Quantum ids.reserve(num); for (long i = 0; i < num; i++) { - ids.push_back(static_cast(reinterpret_cast(qubits[i]))); + ids.push_back(GetQubitId(qubits[i])); } return ids; } @@ -156,6 +159,7 @@ namespace Quantum typedef unsigned (*TInit)(); static TInit initSimulatorInstance = reinterpret_cast(this->GetProc("init")); + qubitManager = std::make_unique(); this->simulatorId = initSimulatorInstance(); } ~CFullstateSimulator() @@ -195,10 +199,10 @@ namespace Quantum typedef void (*TAllocateQubit)(unsigned, unsigned); static TAllocateQubit allocateQubit = reinterpret_cast(this->GetProc("allocateQubit")); - const unsigned id = this->nextQubitId; - allocateQubit(this->simulatorId, id); - this->nextQubitId++; - return reinterpret_cast(id); + Qubit q = qubitManager->Allocate(); // Allocate qubit in qubit manager. + unsigned id = GetQubitId(q); // Get its id. + allocateQubit(this->simulatorId, id); // Allocate it in the simulator. + return q; } void ReleaseQubit(Qubit q) override @@ -206,7 +210,8 @@ namespace Quantum typedef void (*TReleaseQubit)(unsigned, unsigned); static TReleaseQubit releaseQubit = reinterpret_cast(this->GetProc("release")); - releaseQubit(this->simulatorId, GetQubitId(q)); + releaseQubit(this->simulatorId, GetQubitId(q)); // Release qubit in the simulator. + qubitManager->Release(q); // Release it in the qubit manager. } Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index d0f222f081c..48395d667c2 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -8,9 +8,6 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { } if (!(Get-Command ninja -ErrorAction SilentlyContinue)) { choco install ninja - # NB: The chocolatey package for rustup is currently nonfunctional, - # so we rely on Rust being installed via Azure Pipelines. - # choco install rustup.install } if (!(Get-Command cmake -ErrorAction SilentlyContinue)) { choco install cmake diff --git a/src/Qir/Runtime/public/QubitManager.hpp b/src/Qir/Runtime/public/QubitManager.hpp new file mode 100644 index 00000000000..c45a74ce292 --- /dev/null +++ b/src/Qir/Runtime/public/QubitManager.hpp @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include + +#include "CoreTypes.hpp" + +namespace Microsoft +{ +namespace Quantum +{ + // CQubitManager maintains mapping between user qubit objects and + // underlying qubit identifiers (Ids). When user program allocates + // a qubit, Qubit Manager decides whether to allocate a fresh id or + // reuse existing id that was previously freed. When user program + // releases a qubit, Qubit Manager tracks it as a free qubit id. + // Decision to reuse a qubit id is influenced by restricted reuse + // areas. When a qubit id is freed in one section of a restricted + // reuse area, it cannot be reused in other sections of the same area. + // True borrowing of qubits is not supported and is currently + // implemented as a plain allocation. + class QIR_SHARED_API CQubitManager + { + public: + using QubitIdType = ::int32_t; + + // We want status array to be reasonably large. + constexpr static QubitIdType DefaultQubitCapacity = 8; + + // Indexes in the status array can potentially be in range 0 .. QubitIdType.MaxValue-1. + // This gives maximum capacity as QubitIdType.MaxValue. Actual configured capacity may be less than this. + // Index equal to QubitIdType.MaxValue doesn't exist and is reserved for 'NoneMarker' - list terminator. + constexpr static QubitIdType MaximumQubitCapacity = std::numeric_limits::max(); + + public: + CQubitManager( + QubitIdType initialQubitCapacity = DefaultQubitCapacity, + bool mayExtendCapacity = true, + bool encourageReuse = true); + + // No complex scenarios for now. Don't need to support copying/moving. + CQubitManager(const CQubitManager&) = delete; + CQubitManager& operator = (const CQubitManager&) = delete; + virtual ~CQubitManager(); + + // Restricted reuse area control + void StartRestrictedReuseArea(); + void NextRestrictedReuseSegment(); + void EndRestrictedReuseArea(); + + // Allocate a qubit. Extend capacity if necessary and possible. + // Fail if the qubit cannot be allocated. + // Computation complexity is O(number of nested restricted reuse areas). + Qubit Allocate(); + // Allocate qubitCountToAllocate qubits and store them in the provided array. Extend manager capacity if necessary and possible. + // Fail without allocating any qubits if the qubits cannot be allocated. + // Caller is responsible for providing array of sufficient size to hold qubitCountToAllocate. + void Allocate(Qubit* qubitsToAllocate, int32_t qubitCountToAllocate); + + // Releases a given qubit. + void Release(Qubit qubit); + // Releases qubitCountToRelease qubits in the provided array. + // Caller is responsible for managing memory used by the array itself (i.e. delete[] array if it was dynamically allocated). + void Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease); + + // Borrow (We treat borrowing as allocation currently) + Qubit Borrow(); + void Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow); + // Return (We treat returning as release currently) + void Return(Qubit qubit); + void Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn); + + // Disables a given qubit. + // Once a qubit is disabled it can never be "enabled" or reallocated. + void Disable(Qubit qubit); + // Disables a set of given qubits. + // Once a qubit is disabled it can never be "enabled" or reallocated. + void Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable); + + bool IsValidQubit(Qubit qubit) const; + bool IsDisabledQubit(Qubit qubit) const; + bool IsExplicitlyAllocatedQubit(Qubit qubit) const; + bool IsFreeQubitId(QubitIdType id) const; + + QubitIdType GetQubitId(Qubit qubit) const; + + // Qubit counts: + + // Number of qubits that are disabled. When an explicitly allocated qubit + // gets disabled, it is removed from allocated count and is added to + // disabled count immediately. Subsequent Release doesn't affect counts. + int32_t GetDisabledQubitCount() const { return disabledQubitCount; } + + // Number of qubits that are explicitly allocated. This counter gets + // increased on allocation of a qubit and decreased on release of a qubit. + // Note that we treat borrowing as allocation now. + int32_t GetAllocatedQubitCount() const { return allocatedQubitCount; } + + // Number of free qubits that are currently tracked by this qubit manager. + // Note that when qubit manager may extend capacity, this doesn't account + // for qubits that may be potentially added in future via capacity extension. + // If qubit manager may extend capacity and reuse is discouraged, released + // qubits still increase this number even though they cannot be reused. + int32_t GetFreeQubitCount() const { return freeQubitCount; } + + // Total number of qubits that are currently tracked by this qubit manager. + int32_t GetQubitCapacity() const { return qubitCapacity; } + bool GetMayExtendCapacity() const { return mayExtendCapacity; } + bool GetEncourageReuse() const { return encourageReuse; } + + protected: + // May be overriden to create a custom Qubit object. + // When not overriden, it just stores qubit Id in place of a pointer to a qubit. + // id: unique qubit id + // Returns a newly instantiated qubit. + virtual Qubit CreateQubitObject(QubitIdType id); + + // May be overriden to delete a custom Qubit object. + // Must be overriden if CreateQubitObject is overriden. + // When not overriden, it does nothing. + // qubit: pointer to QUBIT + virtual void DeleteQubitObject(Qubit qubit); + + // May be overriden to get a qubit id from a custom qubit object. + // Must be overriden if CreateQubitObject is overriden. + // When not overriden, it just reinterprets pointer to qubit as a qubit id. + // qubit: pointer to QUBIT + // Returns id of a qubit pointed to by qubit. + virtual QubitIdType QubitToId(Qubit qubit) const; + + private: + // The end of free lists are marked with NoneMarker value. It is used like null for pointers. + // This value is non-negative just like other values in the free lists. See sharedQubitStatusArray. + constexpr static QubitIdType NoneMarker = std::numeric_limits::max(); + + // Explicitly allocated qubits are marked with AllocatedMarker value. + // If borrowing is implemented, negative values may be used for refcounting. + // See sharedQubitStatusArray. + constexpr static QubitIdType AllocatedMarker = std::numeric_limits::min(); + + // Disabled qubits are marked with this value. See sharedQubitStatusArray. + constexpr static QubitIdType DisabledMarker = -1; + + // QubitListInSharedArray implements a singly-linked list with "pointers" + // to the first and the last element stored. Pointers are the indexes + // in a single shared array. Shared array isn't sotored in this class + // because it can be reallocated. This class maintains status of elements + // in the list by virtue of linking them as part of this list. This class + // sets Allocated status of elementes taken from the list (via TakeQubitFromFront). + // This class is small, contains no C++ pointers and relies on default shallow copying/destruction. + struct QubitListInSharedArray final + { + private: + QubitIdType firstElement = NoneMarker; + QubitIdType lastElement = NoneMarker; + // We are not storing pointer to shared array because it can be reallocated. + // Indexes and special values remain the same on such reallocations. + + public: + // Initialize empty list + QubitListInSharedArray() = default; + + // Initialize as a list with sequential elements from startId to endId inclusve. + QubitListInSharedArray(QubitIdType startId, QubitIdType endId, QubitIdType* sharedQubitStatusArray); + + bool IsEmpty() const; + void AddQubit(QubitIdType id, bool addToFront, QubitIdType* sharedQubitStatusArray); + QubitIdType TakeQubitFromFront(QubitIdType* sharedQubitStatusArray); + void MoveAllQubitsFrom(QubitListInSharedArray& source, QubitIdType* sharedQubitStatusArray); + }; + + // Restricted reuse area consists of multiple segments. Qubits released + // in one segment cannot be reused in another. One restricted reuse area + // can be nested in a segment of another restricted reuse area. This class + // tracks current segment of an area by maintaining a list of free qubits + // in a shared status array FreeQubitsReuseAllowed. Previous segments are + // tracked collectively (not individually) by maintaining FreeQubitsReuseProhibited. + // This class is small, contains no C++ pointers and relies on default shallow copying/destruction. + struct RestrictedReuseArea final + { + public: + QubitListInSharedArray FreeQubitsReuseProhibited; + QubitListInSharedArray FreeQubitsReuseAllowed; + + RestrictedReuseArea() = default; + RestrictedReuseArea(QubitListInSharedArray freeQubits); + }; + + // This is NOT a pure stack! We modify it only by push/pop, but we also iterate over elements. + class CRestrictedReuseAreaStack final : public std::vector + { + public: + // No complex scenarios for now. Don't need to support copying/moving. + CRestrictedReuseAreaStack() = default; + CRestrictedReuseAreaStack(const CRestrictedReuseAreaStack&) = delete; + CRestrictedReuseAreaStack& operator = (const CRestrictedReuseAreaStack&) = delete; + ~CRestrictedReuseAreaStack() = default; + + void PushToBack(RestrictedReuseArea area); + RestrictedReuseArea PopFromBack(); + RestrictedReuseArea& PeekBack(); + int32_t Count() const; + }; + + private: + void EnsureCapacity(QubitIdType requestedCapacity); + + // Take free qubit id from a free list without extending capacity. + // First non-empty free list among nested restricted reuse areas are considered. + QubitIdType TakeFreeQubitId(); + // Allocate free qubit id extending capacity if necessary and possible. + QubitIdType AllocateQubitId(); + // Put qubit id back into a free list for the current restricted reuse area. + void ReleaseQubitId(QubitIdType id); + + bool IsValidId(QubitIdType id) const; + bool IsDisabledId(QubitIdType id) const; + bool IsFreeId(QubitIdType id) const; + bool IsExplicitlyAllocatedId(QubitIdType id) const; + + // Configuration Properties: + bool mayExtendCapacity = true; + bool encourageReuse = true; + + // State: + // sharedQubitStatusArray is used to store statuses of all known qubits. + // Integer value at the index of the qubit id represents the status of that qubit. + // (Ex: sharedQubitStatusArray[4] is the status of qubit with id = 4). + // Therefore qubit ids are in the range of [0..qubitCapacity). + // Capacity may be extended if MayExtendCapacity = true. + // If qubit X is allocated, sharedQubitStatusArray[X] = AllocatedMarker (negative number) + // If qubit X is disabled, sharedQubitStatusArray[X] = DisabledMarker (negative number) + // If qubit X is free, sharedQubitStatusArray[X] is a non-negative number, denote it Next(X). + // Next(X) is either the index of the next element in the list or the list terminator - NoneMarker. + // All free qubits form disjoint singly linked lists bound to to respective resricted reuse areas. + // Each area has two lists of free qubits - see RestrictedReuseArea. + QubitIdType* sharedQubitStatusArray = nullptr; + // qubitCapacity is always equal to the array size. + QubitIdType qubitCapacity = 0; + // All nested restricted reuse areas at the current moment. + // Fresh Free Qubits are added to the outermost area: freeQubitsInAreas[0].FreeQubitsReuseAllowed + CRestrictedReuseAreaStack freeQubitsInAreas; + + // Counts: + int32_t disabledQubitCount = 0; + int32_t allocatedQubitCount = 0; + int32_t freeQubitCount = 0; + }; + +} +} diff --git a/src/Qir/Runtime/public/SimFactory.hpp b/src/Qir/Runtime/public/SimFactory.hpp index 8165dca1182..14128657262 100644 --- a/src/Qir/Runtime/public/SimFactory.hpp +++ b/src/Qir/Runtime/public/SimFactory.hpp @@ -18,6 +18,5 @@ namespace Quantum // Full State Simulator QIR_SHARED_API std::unique_ptr CreateFullstateSimulator(); - QIR_SHARED_API std::unique_ptr CreateOpenSystemsSimulator(); } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Runtime/unittests/CMakeLists.txt b/src/Qir/Runtime/unittests/CMakeLists.txt index 40c41776d2a..664fc359a8f 100644 --- a/src/Qir/Runtime/unittests/CMakeLists.txt +++ b/src/Qir/Runtime/unittests/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(qir-runtime-unittests QirRuntimeTests.cpp ToffoliTests.cpp TracerTests.cpp + QubitManagerTests.cpp $ $ $ diff --git a/src/Qir/Runtime/unittests/QubitManagerTests.cpp b/src/Qir/Runtime/unittests/QubitManagerTests.cpp new file mode 100644 index 00000000000..966b36f9645 --- /dev/null +++ b/src/Qir/Runtime/unittests/QubitManagerTests.cpp @@ -0,0 +1,253 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include "catch.hpp" + +#include "QubitManager.hpp" + +using namespace Microsoft::Quantum; + +TEST_CASE("Simple allocation and release of one qubit", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(); + Qubit q = qm->Allocate(); + qm->Release(q); +} + +TEST_CASE("Allocation and reallocation of one qubit", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(1, false, true); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + Qubit q = qm->Allocate(); + REQUIRE(qm->GetQubitId(q) == 0); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + REQUIRE_THROWS(qm->Allocate()); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + qm->Release(q); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + Qubit q0 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + qm->Release(q0); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 0); +} + +TEST_CASE("Qubit Status", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(2, false, true); + + Qubit q0 = qm->Allocate(); + CQubitManager::QubitIdType q0id = qm->GetQubitId(q0); + REQUIRE(qm->IsValidQubit(q0)); + REQUIRE(qm->IsExplicitlyAllocatedQubit(q0)); + REQUIRE(!qm->IsDisabledQubit(q0)); + REQUIRE(!qm->IsFreeQubitId(q0id)); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + + qm->Disable(q0); + REQUIRE(qm->IsValidQubit(q0)); + REQUIRE(!qm->IsExplicitlyAllocatedQubit(q0)); + REQUIRE(qm->IsDisabledQubit(q0)); + REQUIRE(!qm->IsFreeQubitId(q0id)); + REQUIRE(qm->GetDisabledQubitCount() == 1); + + qm->Release(q0); + REQUIRE(!qm->IsFreeQubitId(q0id)); + REQUIRE(qm->GetFreeQubitCount() == 1); + + Qubit q1 = qm->Allocate(); + CQubitManager::QubitIdType q1id = qm->GetQubitId(q1); + REQUIRE(q0id != q1id); + + REQUIRE(qm->IsValidQubit(q1)); + REQUIRE(qm->IsExplicitlyAllocatedQubit(q1)); + REQUIRE(!qm->IsDisabledQubit(q1)); + REQUIRE(!qm->IsFreeQubitId(q1id)); + + REQUIRE(qm->GetAllocatedQubitCount() == 1); + REQUIRE(qm->GetDisabledQubitCount() == 1); + REQUIRE(qm->GetFreeQubitCount() == 0); + + qm->Release(q1); + REQUIRE(qm->IsFreeQubitId(q1id)); +} + +TEST_CASE("Qubit Counts", "[QubitManagerBasic]") +{ + constexpr int totalQubitCount = 100; + constexpr int disabledQubitCount = 29; + constexpr int extraQubitCount = 43; + static_assert(extraQubitCount <= totalQubitCount); + static_assert(disabledQubitCount <= totalQubitCount); + // We don't want capacity to be extended at first... + static_assert(extraQubitCount + disabledQubitCount <= totalQubitCount); + + std::unique_ptr qm = std::make_unique(totalQubitCount, true, true); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == 0); + + Qubit* qubits = new Qubit[disabledQubitCount]; + qm->Allocate(qubits, disabledQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == disabledQubitCount); + REQUIRE(qm->GetDisabledQubitCount() == 0); + + qm->Disable(qubits, disabledQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + + qm->Release(qubits, disabledQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + delete[] qubits; + + qubits = new Qubit[extraQubitCount]; + qm->Allocate(qubits, extraQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount-extraQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == extraQubitCount); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + + qm->Release(qubits, extraQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + delete[] qubits; + + qubits = new Qubit[totalQubitCount]; + qm->Allocate(qubits, totalQubitCount); + REQUIRE(qm->GetQubitCapacity() > totalQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == totalQubitCount); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + + qm->Release(qubits, totalQubitCount); + REQUIRE(qm->GetQubitCapacity() > totalQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + delete[] qubits; +} + +TEST_CASE("Allocation of released qubits when reuse is encouraged", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(2, false, true); + REQUIRE(qm->GetFreeQubitCount() == 2); + Qubit q0 = qm->Allocate(); + Qubit q1 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); // Qubit ids should be in order + REQUIRE(qm->GetQubitId(q1) == 1); + REQUIRE_THROWS(qm->Allocate()); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 2); + + qm->Release(q0); + Qubit q0a = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0a) == 0); // It was the only one available + REQUIRE_THROWS(qm->Allocate()); + + qm->Release(q1); + qm->Release(q0a); + REQUIRE(qm->GetFreeQubitCount() == 2); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + + Qubit q0b = qm->Allocate(); + Qubit q1a = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0b) == 0); // By default reuse is encouraged, LIFO is used + REQUIRE(qm->GetQubitId(q1a) == 1); + REQUIRE_THROWS(qm->Allocate()); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 2); + + qm->Release(q0b); + qm->Release(q1a); +} + +TEST_CASE("Extending capacity", "[QubitManager]") +{ + std::unique_ptr qm = std::make_unique(1, true, true); + + Qubit q0 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); + Qubit q1 = qm->Allocate(); // This should double capacity + REQUIRE(qm->GetQubitId(q1) == 1); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 2); + + qm->Release(q0); + Qubit q0a = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0a) == 0); + Qubit q2 = qm->Allocate(); // This should double capacity again + REQUIRE(qm->GetQubitId(q2) == 2); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 3); + + qm->Release(q1); + qm->Release(q0a); + qm->Release(q2); + REQUIRE(qm->GetFreeQubitCount() == 4); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + + Qubit* qqq = new Qubit[3]; + qm->Allocate(qqq, 3); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 3); + qm->Release(qqq, 3); + delete[] qqq; + REQUIRE(qm->GetFreeQubitCount() == 4); + REQUIRE(qm->GetAllocatedQubitCount() == 0); +} + +TEST_CASE("Restricted Area", "[QubitManager]") +{ + std::unique_ptr qm = std::make_unique(3, false, true); + + Qubit q0 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); + + qm->StartRestrictedReuseArea(); + + // Allocates fresh qubit + Qubit q1 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q1) == 1); + qm->Release(q1); // Released, but cannot be used in the next segment. + + qm->NextRestrictedReuseSegment(); + + // Allocates fresh qubit, q1 cannot be reused - it belongs to a differen segment. + Qubit q2 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q2) == 2); + qm->Release(q2); + + Qubit q2a = qm->Allocate(); // Getting the same one as the one that's just released. + REQUIRE(qm->GetQubitId(q2a) == 2); + qm->Release(q2a); // Released, but cannot be used in the next segment. + + qm->NextRestrictedReuseSegment(); + + // There's no qubits left here. q0 is allocated, q1 and q2 are from different segments. + REQUIRE_THROWS(qm->Allocate()); + + qm->EndRestrictedReuseArea(); + + // Qubits 1 and 2 are available here again. + Qubit* qqq = new Qubit[2]; + qm->Allocate(qqq, 2); + // OK to destruct qubit manager while qubits are still allocated. + REQUIRE_THROWS(qm->Allocate()); +} + diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index f9369c87fa6..7fe5e789d80 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -77,6 +77,36 @@ TEST_CASE("Fullstate simulator: X and measure", "[fullstate_simulator]") sim->ReleaseResult(r2); } +TEST_CASE("Fullstate simulator: X, M, reuse, M", "[fullstate_simulator]") +{ + std::unique_ptr sim = CreateFullstateSimulator(); + IQuantumGateSet* iqa = dynamic_cast(sim.get()); + + Qubit q = sim->AllocateQubit(); + Result r1 = MZ(iqa, q); + REQUIRE(Result_Zero == sim->GetResultValue(r1)); + REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); + + iqa->X(q); + Result r2 = MZ(iqa, q); + REQUIRE(Result_One == sim->GetResultValue(r2)); + REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); + + sim->ReleaseQubit(q); + sim->ReleaseResult(r1); + sim->ReleaseResult(r2); + + Qubit qq = sim->AllocateQubit(); + Result r3 = MZ(iqa, qq); + // Allocated qubit should always be in |0> state even though we released + // q in |1> state, and qq is likely reusing the same underlying qubit as q. + REQUIRE(Result_Zero == sim->GetResultValue(r3)); + REQUIRE(sim->AreEqualResults(r3, sim->UseZero())); + + sim->ReleaseQubit(qq); + sim->ReleaseResult(r3); +} + TEST_CASE("Fullstate simulator: measure Bell state", "[fullstate_simulator]") { std::unique_ptr sim = CreateFullstateSimulator(); From a80fb6fe52a4ffdd74ba33231cc77ad23ddc189f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 09:43:09 -0700 Subject: [PATCH 137/158] Add prerequisites.ps1. --- src/Simulation/qdk_sim_rs/CONTRIBUTING.md | 12 +++++++-- src/Simulation/qdk_sim_rs/Cargo.toml.template | 2 +- src/Simulation/qdk_sim_rs/prerequisites.ps1 | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/Simulation/qdk_sim_rs/prerequisites.ps1 diff --git a/src/Simulation/qdk_sim_rs/CONTRIBUTING.md b/src/Simulation/qdk_sim_rs/CONTRIBUTING.md index ad3a58f9d5e..894a188cb9b 100644 --- a/src/Simulation/qdk_sim_rs/CONTRIBUTING.md +++ b/src/Simulation/qdk_sim_rs/CONTRIBUTING.md @@ -1,3 +1,13 @@ +# 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: @@ -14,8 +24,6 @@ Tests for the open systems simulator consist of five distinct parts: 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. -- C++-language unit tests in the QIR runtime. - These tests ensure that the binding of the Rust library as a QIR simulator work as expected, and are defined in `qsharp-runtime/src/QirRuntime/test/OpenSystemsSimulator/*.cpp`. - 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. diff --git a/src/Simulation/qdk_sim_rs/Cargo.toml.template b/src/Simulation/qdk_sim_rs/Cargo.toml.template index 26908f887a4..64e8483c780 100644 --- a/src/Simulation/qdk_sim_rs/Cargo.toml.template +++ b/src/Simulation/qdk_sim_rs/Cargo.toml.template @@ -2,7 +2,7 @@ # Licensed under the MIT License. [package] -name = "qdk_sim" +name = "qdk_sim_experimental" version = "0.1.0" authors = ["Christopher Granade "] edition = "2018" 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 From b2208ad577cd0e4e2b4ad6e0a5426ce5af8f4ee8 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 09:57:32 -0700 Subject: [PATCH 138/158] Finish reverting QIR. --- .../lib/Simulators/OpenSystemsSimulator.cpp | 292 ---- .../Tests/OpenSystemsSimulator/CMakeLists.txt | 25 - .../OpenSystemsSimulatorTests.cpp | 36 - .../qir-test-opensimulator.ll | 1398 ----------------- .../qir-test-opensimulator.qs | 82 - 5 files changed, 1833 deletions(-) delete mode 100644 src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp delete mode 100644 src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt delete mode 100644 src/Qir/Tests/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp delete mode 100644 src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.ll delete mode 100644 src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.qs diff --git a/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp b/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp deleted file mode 100644 index 5eb46727318..00000000000 --- a/src/Qir/Runtime/lib/Simulators/OpenSystemsSimulator.cpp +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" - -using namespace std; -typedef long long int i64; - -// Define the API with the open systems simulator. -extern "C" -{ - // NB: We disable clang-tidy rules for case conventions here, as the names - // reflect the Rust naming conventions used in the opensim crate. - size_t init(size_t initialcapacity); // NOLINT(readability-identifier-naming) - i64 destroy(size_t sim_id); // NOLINT(readability-identifier-naming) - void dump_to_console(size_t sim_id); // NOLINT(readability-identifier-naming) - i64 x(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 y(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 z(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 h(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 s(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 s_adj(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 t(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 t_adj(size_t sim_id, size_t idx); // NOLINT(readability-identifier-naming) - i64 cnot(size_t sim_id, size_t idx_control, size_t idx_target); // NOLINT(readability-identifier-naming) - i64 m(size_t sim_id, size_t idx, size_t* result_out); // NOLINT(readability-identifier-naming) - const char* lasterr(); // NOLINT(readability-identifier-naming) - const char* get_noise_model(size_t sim_id); // NOLINT(readability-identifier-naming) - i64 set_noise_model(size_t sim_id, const char* new_model); // NOLINT(readability-identifier-naming) - const char* get_current_state(size_t sim_id); // NOLINT(readability-identifier-naming) -} - -namespace Microsoft -{ -namespace Quantum -{ - // FIXME: support methods from public IDiagnostics; they currently - // just throw. - class COpenSystemSimulator : public IRuntimeDriver, public IQuantumGateSet, public IDiagnostics - { - typedef void (*TSingleQubitGate)(size_t /*simulator id*/, size_t /*qubit id*/); - typedef void (*TSingleQubitControlledGate)( - unsigned /*simulator id*/, - unsigned /*number of controls*/, - unsigned* /*controls*/, - unsigned /*qubit id*/); - - // QuantumSimulator defines paulis as: - // enum Basis - // { - // PauliI = 0, - // PauliX = 1, - // PauliY = 3, - // PauliZ = 2 - // }; - // which (surprise!) matches our definition of PauliId enum - static inline unsigned GetBasis(PauliId pauli) - { - return static_cast(pauli); - } - - unsigned simulatorId = -1; - unsigned nextQubitId = 0; // the QuantumSimulator expects contiguous ids, starting from 0 - - unsigned GetQubitId(Qubit qubit) const - { - return static_cast(reinterpret_cast(qubit)); - } - - vector GetQubitIds(long num, Qubit* qubits) const - { - vector ids; - ids.reserve(num); - for (long i = 0; i < num; i++) - { - ids.push_back(static_cast(reinterpret_cast(qubits[i]))); - } - return ids; - } - - // use for debugging the simulator - void DumpState() - { - dump_to_console(simulatorId); - } - - public: - COpenSystemSimulator() - { - // FIXME: allow setting number of qubits. - this->simulatorId = init(3); - } - ~COpenSystemSimulator() - { - destroy(this->simulatorId); - } - - void GetState(TGetStateCallback callback) override - { - throw std::logic_error("operation_not_supported"); - } - bool Assert(long numTargets, PauliId* bases, Qubit* targets, Result result, const char* failureMessage) override - { - throw std::logic_error("operation_not_supported"); - } - - bool AssertProbability( - long numTargets, - PauliId bases[], - Qubit targets[], - double probabilityOfZero, - double precision, - const char* failureMessage) override - { - throw std::logic_error("operation_not_supported"); - } - - virtual std::string QubitToString(Qubit q) override - { - return std::to_string(GetQubitId(q)); - } - - Qubit AllocateQubit() override - { - const unsigned id = this->nextQubitId; - this->nextQubitId++; - return reinterpret_cast(id); - } - - void ReleaseQubit(Qubit q) override - { - // TODO - } - - Result M(Qubit q) - { - size_t result; - m(this->simulatorId, GetQubitId(q), &result); - return reinterpret_cast(result); - } - - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override - { - throw std::logic_error("not yet implemented"); - } - - void ReleaseResult(Result r) override {} - - ResultValue GetResultValue(Result r) override - { - const unsigned val = static_cast(reinterpret_cast(r)); - assert(val == 0 || val == 1); - return (val == 0) ? Result_Zero : Result_One; - } - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } - - bool AreEqualResults(Result r1, Result r2) override - { - return (r1 == r2); - } - - void X(Qubit q) override - { - x(this->simulatorId, GetQubitId(q)); - } - - void ControlledX(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void Y(Qubit q) override - { - y(this->simulatorId, GetQubitId(q)); - } - - void ControlledY(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void Z(Qubit q) override - { - z(this->simulatorId, GetQubitId(q)); - } - - void ControlledZ(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void H(Qubit q) override - { - h(this->simulatorId, GetQubitId(q)); - } - - void ControlledH(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void S(Qubit q) override - { - s(this->simulatorId, GetQubitId(q)); - } - - void ControlledS(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void AdjointS(Qubit q) override - { - s_adj(this->simulatorId, GetQubitId(q)); - } - - void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void T(Qubit q) override - { - t(this->simulatorId, GetQubitId(q)); - } - - void ControlledT(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void AdjointT(Qubit q) override - { - t_adj(this->simulatorId, GetQubitId(q)); - } - - void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not yet implemented"); - } - - void R(PauliId axis, Qubit target, double theta) override - { - throw std::logic_error("not yet implemented"); - } - - void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override - { - throw std::logic_error("not yet implemented"); - } - - void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override - { - throw std::logic_error("not yet implemented"); - } - - void ControlledExp( - long numControls, - Qubit controls[], - long numTargets, - PauliId paulis[], - Qubit targets[], - double theta) override - { - throw std::logic_error("not yet implemented"); - } - }; - - std::unique_ptr CreateOpenSystemsSimulator() - { - return std::make_unique(); - } -} // namespace Quantum -} // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt b/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt deleted file mode 100644 index 00fac26da68..00000000000 --- a/src/Qir/Tests/OpenSystemsSimulator/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ - -compile_from_qir(qir-test-opensimulator qir_test_opensimulator_target) - -add_executable(open-systems-simulator-tests - OpenSystemsSimulatorTests.cpp) - -target_link_libraries(open-systems-simulator-tests PUBLIC - ${QIR_UTILITY_LIB} # created by compile_from_qir - ${QIR_BRIDGE_UTILITY_LIB} - ${QIR_BRIDGE_QIS_UTILITY_LIB} - qir-rt-support - qir-qis-support - simulators -) - -target_include_directories(open-systems-simulator-tests PUBLIC - "${test_includes}" - "${public_includes}" - "${PROJECT_SOURCE_DIR}/lib/QIR" -) -# add_dependencies(open-systems-simulator-tests qir_test_opensimulator_target) - -# install(TARGETS open-systems-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -# add_unit_test(open-systems-simulator-tests) - diff --git a/src/Qir/Tests/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp b/src/Qir/Tests/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp deleted file mode 100644 index a70a7f4c288..00000000000 --- a/src/Qir/Tests/OpenSystemsSimulator/OpenSystemsSimulatorTests.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -#include "QuantumApi_I.hpp" -#include "SimFactory.hpp" -#include "context.hpp" - -using namespace Microsoft::Quantum; -using namespace std; - -TEST_CASE("Open systems simulator: X and measure", "[open_simulator]") -{ - std::unique_ptr sim = CreateOpenSystemsSimulator(); - IQuantumGateSet* iqa = sim->AsQuantumGateSet(); - - Qubit q = sim->AllocateQubit(); - Result r1 = sim->M(q); - REQUIRE(Result_Zero == sim->GetResultValue(r1)); - REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); - - iqa->X(q); - Result r2 = sim->M(q); - REQUIRE(Result_One == sim->GetResultValue(r2)); - REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); - - sim->ReleaseQubit(q); - sim->ReleaseResult(r1); - sim->ReleaseResult(r2); -} diff --git a/src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.ll b/src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.ll deleted file mode 100644 index 915936863f3..00000000000 --- a/src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.ll +++ /dev/null @@ -1,1398 +0,0 @@ - -%Result = type opaque -%Range = type { i64, i64, i64 } -%Tuple = type opaque -%Qubit = type opaque -%Array = type opaque -%Callable = type opaque - -@ResultZero = external global %Result* -@ResultOne = external global %Result* -@PauliI = constant i2 0 -@PauliX = constant i2 1 -@PauliY = constant i2 -1 -@PauliZ = constant i2 -2 -@EmptyRange = internal constant %Range { i64 0, i64 1, i64 -1 } -@Microsoft__Quantum__Intrinsic__X = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__X__ctladj__wrapper] -@Microsoft__Quantum__Intrinsic__Y = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Y__ctladj__wrapper] -@Microsoft__Quantum__Intrinsic__Z = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__Z__ctladj__wrapper] -@Microsoft__Quantum__Intrinsic__H = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__H__ctladj__wrapper] -@Microsoft__Quantum__Intrinsic__S = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__S__ctladj__wrapper] -@Microsoft__Quantum__Intrinsic__T = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__T__ctladj__wrapper] -@Microsoft__Quantum__Intrinsic__R = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Intrinsic__R__ctladj__wrapper] -@PartialApplication__1 = constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__adj__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__ctl__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* @Lifted__PartialApplication__1__ctladj__wrapper] -@MemoryManagement__1 = constant [2 x void (%Tuple*, i64)*] [void (%Tuple*, i64)* @MemoryManagement__1__RefCount, void (%Tuple*, i64)* @MemoryManagement__1__AliasCount] - -@Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS = alias i64 (), i64 ()* @Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body - -define void @Microsoft__Quantum__Intrinsic__X__body(%Qubit* %qb) { -entry: - call void @__quantum__qis__x__body(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__x__body(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__X__adj(%Qubit* %qb) { -entry: - call void @__quantum__qis__x__body(%Qubit* %qb) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__X__ctl(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__x__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__rt__array_update_alias_count(%Array*, i64) - -declare void @__quantum__qis__x__ctl(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__X__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__x__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -define %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %qb) { -entry: - %bases__inline__1 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 1) - %0 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__1, i64 0) - %1 = bitcast i8* %0 to i2* - %2 = load i2, i2* @PauliZ - store i2 %2, i2* %1 - call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__1, i64 1) - %qubits__inline__1 = call %Array* @__quantum__rt__array_create_1d(i32 8, i64 1) - %3 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qubits__inline__1, i64 0) - %4 = bitcast i8* %3 to %Qubit** - store %Qubit* %qb, %Qubit** %4 - call void @__quantum__rt__array_update_alias_count(%Array* %qubits__inline__1, i64 1) - %5 = call %Result* @__quantum__qis__measure__body(%Array* %bases__inline__1, %Array* %qubits__inline__1) - call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__1, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits__inline__1, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %bases__inline__1, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %qubits__inline__1, i64 -1) - ret %Result* %5 -} - -declare %Array* @__quantum__rt__array_create_1d(i32, i64) - -declare i8* @__quantum__rt__array_get_element_ptr_1d(%Array*, i64) - -declare %Result* @__quantum__qis__measure__body(%Array*, %Array*) - -declare void @__quantum__rt__array_update_reference_count(%Array*, i64) - -define void @Microsoft__Quantum__Intrinsic__S__body(%Qubit* %qb) { -entry: - call void @__quantum__qis__s__body(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__s__body(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__S__adj(%Qubit* %qb) { -entry: - call void @__quantum__qis__s__adj(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__s__adj(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__S__ctl(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__s__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__s__ctl(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__S__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__s__ctladj(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__s__ctladj(%Array*, %Qubit*) - -define %Result* @Microsoft__Quantum__Intrinsic__Measure__body(%Array* %bases, %Array* %qubits) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %bases, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) - %0 = call %Result* @__quantum__qis__measure__body(%Array* %bases, %Array* %qubits) - call void @__quantum__rt__array_update_alias_count(%Array* %bases, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) - ret %Result* %0 -} - -define void @Microsoft__Quantum__Intrinsic__T__body(%Qubit* %qb) { -entry: - call void @__quantum__qis__t__body(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__t__body(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__T__adj(%Qubit* %qb) { -entry: - call void @__quantum__qis__t__adj(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__t__adj(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__T__ctl(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__t__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__t__ctl(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__T__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__t__ctladj(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__t__ctladj(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %qb) { -entry: - call void @__quantum__qis__h__body(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__h__body(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__H__adj(%Qubit* %qb) { -entry: - call void @__quantum__qis__h__body(%Qubit* %qb) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__H__ctl(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__h__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__h__ctl(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__H__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__h__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Y__body(%Qubit* %qb) { -entry: - call void @__quantum__qis__y__body(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__y__body(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__Y__adj(%Qubit* %qb) { -entry: - call void @__quantum__qis__y__body(%Qubit* %qb) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Y__ctl(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__y__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__y__ctl(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__Y__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__y__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__R__body(i2 %pauli, double %theta, %Qubit* %qubit) { -entry: - call void @__quantum__qis__r__body(i2 %pauli, double %theta, %Qubit* %qubit) - ret void -} - -declare void @__quantum__qis__r__body(i2, double, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__R__adj(i2 %pauli, double %theta, %Qubit* %qubit) { -entry: - call void @__quantum__qis__r__adj(i2 %pauli, double %theta, %Qubit* %qubit) - ret void -} - -declare void @__quantum__qis__r__adj(i2, double, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__R__ctl(%Array* %__controlQubits__, { i2, double, %Qubit* }* %0) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 - %pauli = load i2, i2* %1 - %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 - %theta = load double, double* %2 - %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 - %qubit = load %Qubit*, %Qubit** %3 - %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) - %5 = bitcast %Tuple* %4 to { i2, double, %Qubit* }* - %6 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 0 - %7 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 1 - %8 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 2 - store i2 %pauli, i2* %6 - store double %theta, double* %7 - store %Qubit* %qubit, %Qubit** %8 - call void @__quantum__qis__r__ctl(%Array* %__controlQubits__, { i2, double, %Qubit* }* %5) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) - ret void -} - -declare void @__quantum__qis__r__ctl(%Array*, { i2, double, %Qubit* }*) - -declare %Tuple* @__quantum__rt__tuple_create(i64) - -declare void @__quantum__rt__tuple_update_reference_count(%Tuple*, i64) - -define void @Microsoft__Quantum__Intrinsic__R__ctladj(%Array* %__controlQubits__, { i2, double, %Qubit* }* %0) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 - %pauli = load i2, i2* %1 - %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 - %theta = load double, double* %2 - %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 - %qubit = load %Qubit*, %Qubit** %3 - %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) - %5 = bitcast %Tuple* %4 to { i2, double, %Qubit* }* - %6 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 0 - %7 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 1 - %8 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %5, i64 0, i32 2 - store i2 %pauli, i2* %6 - store double %theta, double* %7 - store %Qubit* %qubit, %Qubit** %8 - call void @__quantum__qis__r__ctladj(%Array* %__controlQubits__, { i2, double, %Qubit* }* %5) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) - ret void -} - -declare void @__quantum__qis__r__ctladj(%Array*, { i2, double, %Qubit* }*) - -define void @Microsoft__Quantum__Intrinsic__Exp__body(%Array* %paulis, double %theta, %Array* %qubits) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) - call void @__quantum__qis__exp__body(%Array* %paulis, double %theta, %Array* %qubits) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) - ret void -} - -declare void @__quantum__qis__exp__body(%Array*, double, %Array*) - -define void @Microsoft__Quantum__Intrinsic__Exp__adj(%Array* %paulis, double %theta, %Array* %qubits) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) - call void @__quantum__qis__exp__adj(%Array* %paulis, double %theta, %Array* %qubits) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) - ret void -} - -declare void @__quantum__qis__exp__adj(%Array*, double, %Array*) - -define void @Microsoft__Quantum__Intrinsic__Exp__ctl(%Array* %__controlQubits__, { %Array*, double, %Array* }* %0) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - %1 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 0 - %paulis = load %Array*, %Array** %1 - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) - %2 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 1 - %theta = load double, double* %2 - %3 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 2 - %qubits = load %Array*, %Array** %3 - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) - %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) - %5 = bitcast %Tuple* %4 to { %Array*, double, %Array* }* - %6 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 0 - %7 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 1 - %8 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 2 - store %Array* %paulis, %Array** %6 - store double %theta, double* %7 - store %Array* %qubits, %Array** %8 - call void @__quantum__qis__exp__ctl(%Array* %__controlQubits__, { %Array*, double, %Array* }* %5) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) - ret void -} - -declare void @__quantum__qis__exp__ctl(%Array*, { %Array*, double, %Array* }*) - -define void @Microsoft__Quantum__Intrinsic__Exp__ctladj(%Array* %__controlQubits__, { %Array*, double, %Array* }* %0) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - %1 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 0 - %paulis = load %Array*, %Array** %1 - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 1) - %2 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 1 - %theta = load double, double* %2 - %3 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %0, i64 0, i32 2 - %qubits = load %Array*, %Array** %3 - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 1) - %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) - %5 = bitcast %Tuple* %4 to { %Array*, double, %Array* }* - %6 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 0 - %7 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 1 - %8 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %5, i64 0, i32 2 - store %Array* %paulis, %Array** %6 - store double %theta, double* %7 - store %Array* %qubits, %Array** %8 - call void @__quantum__qis__exp__ctladj(%Array* %__controlQubits__, { %Array*, double, %Array* }* %5) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) - ret void -} - -declare void @__quantum__qis__exp__ctladj(%Array*, { %Array*, double, %Array* }*) - -define void @Microsoft__Quantum__Intrinsic__Z__body(%Qubit* %qb) { -entry: - call void @__quantum__qis__z__body(%Qubit* %qb) - ret void -} - -declare void @__quantum__qis__z__body(%Qubit*) - -define void @Microsoft__Quantum__Intrinsic__Z__adj(%Qubit* %qb) { -entry: - call void @__quantum__qis__z__body(%Qubit* %qb) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Z__ctl(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__z__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -declare void @__quantum__qis__z__ctl(%Array*, %Qubit*) - -define void @Microsoft__Quantum__Intrinsic__Z__ctladj(%Array* %__controlQubits__, %Qubit* %qb) { -entry: - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 1) - call void @__quantum__qis__z__ctl(%Array* %__controlQubits__, %Qubit* %qb) - call void @__quantum__rt__array_update_alias_count(%Array* %__controlQubits__, i64 -1) - ret void -} - -define i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %op) { -entry: - call void @__quantum__rt__callable_memory_management(i32 1, %Callable* %op, i64 1) - call void @__quantum__rt__callable_update_alias_count(%Callable* %op, i64 1) - %res = alloca i64 - store i64 0, i64* %res - %target = call %Qubit* @__quantum__rt__qubit_allocate() - %ctls = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) - %0 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64)) - %1 = bitcast %Tuple* %0 to { %Qubit* }* - %2 = getelementptr { %Qubit* }, { %Qubit* }* %1, i64 0, i32 0 - store %Qubit* %target, %Qubit** %2 - call void @__quantum__rt__callable_invoke(%Callable* %op, %Tuple* %0, %Tuple* null) - %3 = call %Callable* @__quantum__rt__callable_copy(%Callable* %op, i1 false) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 1) - call void @__quantum__rt__callable_make_adjoint(%Callable* %3) - %4 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64)) - %5 = bitcast %Tuple* %4 to { %Qubit* }* - %6 = getelementptr { %Qubit* }, { %Qubit* }* %5, i64 0, i32 0 - store %Qubit* %target, %Qubit** %6 - call void @__quantum__rt__callable_invoke(%Callable* %3, %Tuple* %4, %Tuple* null) - %7 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %target) - %8 = load %Result*, %Result** @ResultZero - %9 = call i1 @__quantum__rt__result_equal(%Result* %7, %Result* %8) - %10 = xor i1 %9, true - br i1 %10, label %then0__1, label %else__1 - -then0__1: ; preds = %entry - store i64 1, i64* %res - br label %continue__1 - -else__1: ; preds = %entry - %11 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) - %12 = bitcast i8* %11 to %Qubit** - %qb__inline__1 = load %Qubit*, %Qubit** %12 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__1) - %13 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) - %14 = bitcast i8* %13 to %Qubit** - %qb__inline__2 = load %Qubit*, %Qubit** %14 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__2) - %15 = call %Callable* @__quantum__rt__callable_copy(%Callable* %op, i1 false) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 1) - call void @__quantum__rt__callable_make_controlled(%Callable* %15) - %16 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) - %17 = bitcast %Tuple* %16 to { %Array*, %Qubit* }* - %18 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %17, i64 0, i32 0 - %19 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %17, i64 0, i32 1 - store %Array* %ctls, %Array** %18 - store %Qubit* %target, %Qubit** %19 - call void @__quantum__rt__callable_invoke(%Callable* %15, %Tuple* %16, %Tuple* null) - %20 = call %Callable* @__quantum__rt__callable_copy(%Callable* %op, i1 false) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %20, i64 1) - call void @__quantum__rt__callable_make_controlled(%Callable* %20) - call void @__quantum__rt__callable_make_adjoint(%Callable* %20) - %21 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) - %22 = bitcast %Tuple* %21 to { %Array*, %Qubit* }* - %23 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %22, i64 0, i32 0 - %24 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %22, i64 0, i32 1 - store %Array* %ctls, %Array** %23 - store %Qubit* %target, %Qubit** %24 - call void @__quantum__rt__callable_invoke(%Callable* %20, %Tuple* %21, %Tuple* null) - %25 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %target) - %26 = load %Result*, %Result** @ResultZero - %27 = call i1 @__quantum__rt__result_equal(%Result* %25, %Result* %26) - %28 = xor i1 %27, true - br i1 %28, label %then0__2, label %continue__2 - -then0__2: ; preds = %else__1 - store i64 2, i64* %res - br label %continue__2 - -continue__2: ; preds = %then0__2, %else__1 - %29 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) - %30 = bitcast i8* %29 to %Qubit** - %qb__inline__3 = load %Qubit*, %Qubit** %30 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__3) - %31 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) - %32 = bitcast i8* %31 to %Qubit** - %qb__inline__4 = load %Qubit*, %Qubit** %32 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__4) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %16, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %20, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %20, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %21, i64 -1) - call void @__quantum__rt__result_update_reference_count(%Result* %25, i64 -1) - br label %continue__1 - -continue__1: ; preds = %continue__2, %then0__1 - call void @__quantum__rt__qubit_release(%Qubit* %target) - call void @__quantum__rt__qubit_release_array(%Array* %ctls) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %ctls, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %4, i64 -1) - call void @__quantum__rt__result_update_reference_count(%Result* %7, i64 -1) - %33 = load i64, i64* %res - call void @__quantum__rt__callable_memory_management(i32 1, %Callable* %op, i64 -1) - call void @__quantum__rt__callable_update_alias_count(%Callable* %op, i64 -1) - ret i64 %33 -} - -declare void @__quantum__rt__callable_memory_management(i32, %Callable*, i64) - -declare void @__quantum__rt__callable_update_alias_count(%Callable*, i64) - -declare %Qubit* @__quantum__rt__qubit_allocate() - -declare %Array* @__quantum__rt__qubit_allocate_array(i64) - -declare void @__quantum__rt__callable_invoke(%Callable*, %Tuple*, %Tuple*) - -declare %Callable* @__quantum__rt__callable_copy(%Callable*, i1) - -declare void @__quantum__rt__callable_make_adjoint(%Callable*) - -declare i1 @__quantum__rt__result_equal(%Result*, %Result*) - -declare void @__quantum__rt__callable_make_controlled(%Callable*) - -declare void @__quantum__rt__callable_update_reference_count(%Callable*, i64) - -declare void @__quantum__rt__result_update_reference_count(%Result*, i64) - -declare void @__quantum__rt__qubit_release(%Qubit*) - -declare void @__quantum__rt__qubit_release_array(%Array*) - -define i64 @Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body() #0 { -entry: - %res = alloca i64 - store i64 0, i64* %res - %0 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__X, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %1 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %0) - store i64 %1, i64* %res - %2 = icmp ne i64 %1, 0 - br i1 %2, label %then0__1, label %continue__1 - -then0__1: ; preds = %entry - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - ret i64 %1 - -continue__1: ; preds = %entry - %3 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__Y, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %4 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %3) - store i64 %4, i64* %res - %5 = icmp ne i64 %4, 0 - br i1 %5, label %then0__2, label %continue__2 - -then0__2: ; preds = %continue__1 - %6 = add i64 10, %4 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - ret i64 %6 - -continue__2: ; preds = %continue__1 - %7 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__Z, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %8 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %7) - store i64 %8, i64* %res - %9 = icmp ne i64 %8, 0 - br i1 %9, label %then0__3, label %continue__3 - -then0__3: ; preds = %continue__2 - %10 = add i64 20, %8 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - ret i64 %10 - -continue__3: ; preds = %continue__2 - %11 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__H, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %12 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %11) - store i64 %12, i64* %res - %13 = icmp ne i64 %12, 0 - br i1 %13, label %then0__4, label %continue__4 - -then0__4: ; preds = %continue__3 - %14 = add i64 30, %12 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) - ret i64 %14 - -continue__4: ; preds = %continue__3 - %15 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__S, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %16 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %15) - store i64 %16, i64* %res - %17 = icmp ne i64 %16, 0 - br i1 %17, label %then0__5, label %continue__5 - -then0__5: ; preds = %continue__4 - %18 = add i64 40, %16 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - ret i64 %18 - -continue__5: ; preds = %continue__4 - %19 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__T, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %20 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %19) - store i64 %20, i64* %res - %21 = icmp ne i64 %20, 0 - br i1 %21, label %then0__6, label %continue__6 - -then0__6: ; preds = %continue__5 - %22 = add i64 50, %20 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) - ret i64 %22 - -continue__6: ; preds = %continue__5 - %23 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Callable*, i2, double }* getelementptr ({ %Callable*, i2, double }, { %Callable*, i2, double }* null, i32 1) to i64)) - %24 = bitcast %Tuple* %23 to { %Callable*, i2, double }* - %25 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %24, i64 0, i32 0 - %26 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %24, i64 0, i32 1 - %27 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %24, i64 0, i32 2 - %28 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Intrinsic__R, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) - %29 = load i2, i2* @PauliX - store %Callable* %28, %Callable** %25 - store i2 %29, i2* %26 - store double 4.200000e-01, double* %27 - %30 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @PartialApplication__1, [2 x void (%Tuple*, i64)*]* @MemoryManagement__1, %Tuple* %23) - %31 = call i64 @Microsoft__Quantum__Testing__QIR__InvokeAllVariants__body(%Callable* %30) - store i64 %31, i64* %res - %32 = icmp ne i64 %31, 0 - br i1 %32, label %then0__7, label %continue__7 - -then0__7: ; preds = %continue__6 - %33 = add i64 60, %31 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %30, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %30, i64 -1) - ret i64 %33 - -continue__7: ; preds = %continue__6 - %targets = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) - %ctls = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) - %paulis__inline__1 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) - %34 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__1, i64 0) - %35 = bitcast i8* %34 to i2* - %36 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__1, i64 1) - %37 = bitcast i8* %36 to i2* - %38 = load i2, i2* @PauliX - %39 = load i2, i2* @PauliY - store i2 %38, i2* %35 - store i2 %39, i2* %37 - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__1, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) - call void @__quantum__qis__exp__body(%Array* %paulis__inline__1, double 4.200000e-01, %Array* %targets) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__1, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__1, i64 -1) - %paulis__inline__2 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) - %40 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__2, i64 0) - %41 = bitcast i8* %40 to i2* - %42 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__2, i64 1) - %43 = bitcast i8* %42 to i2* - %44 = load i2, i2* @PauliX - %45 = load i2, i2* @PauliY - store i2 %44, i2* %41 - store i2 %45, i2* %43 - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__2, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) - call void @__quantum__qis__exp__adj(%Array* %paulis__inline__2, double 4.200000e-01, %Array* %targets) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__2, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__2, i64 -1) - %46 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 0) - %47 = bitcast i8* %46 to %Qubit** - %48 = load %Qubit*, %Qubit** %47 - %49 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %48) - %50 = load %Result*, %Result** @ResultZero - %51 = call i1 @__quantum__rt__result_equal(%Result* %49, %Result* %50) - %52 = xor i1 %51, true - %53 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 1) - %54 = bitcast i8* %53 to %Qubit** - %55 = load %Qubit*, %Qubit** %54 - %56 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %55) - %57 = load %Result*, %Result** @ResultZero - %58 = call i1 @__quantum__rt__result_equal(%Result* %56, %Result* %57) - %59 = xor i1 %58, true - %60 = or i1 %52, %59 - br i1 %60, label %then0__8, label %else__1 - -then0__8: ; preds = %continue__7 - store i64 1, i64* %res - br label %continue__8 - -else__1: ; preds = %continue__7 - %61 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) - %62 = bitcast i8* %61 to %Qubit** - %qb__inline__3 = load %Qubit*, %Qubit** %62 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__3) - %63 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) - %64 = bitcast i8* %63 to %Qubit** - %qb__inline__4 = load %Qubit*, %Qubit** %64 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__4) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) - %paulis__inline__5 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) - %65 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__5, i64 0) - %66 = bitcast i8* %65 to i2* - %67 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__5, i64 1) - %68 = bitcast i8* %67 to i2* - %69 = load i2, i2* @PauliX - %70 = load i2, i2* @PauliY - store i2 %69, i2* %66 - store i2 %70, i2* %68 - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__5, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) - %71 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) - %72 = bitcast %Tuple* %71 to { %Array*, double, %Array* }* - %73 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %72, i64 0, i32 0 - %74 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %72, i64 0, i32 1 - %75 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %72, i64 0, i32 2 - store %Array* %paulis__inline__5, %Array** %73 - store double 4.200000e-01, double* %74 - store %Array* %targets, %Array** %75 - call void @__quantum__qis__exp__ctl(%Array* %ctls, { %Array*, double, %Array* }* %72) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__5, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__5, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %71, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 1) - %paulis__inline__6 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 2) - %76 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__6, i64 0) - %77 = bitcast i8* %76 to i2* - %78 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %paulis__inline__6, i64 1) - %79 = bitcast i8* %78 to i2* - %80 = load i2, i2* @PauliX - %81 = load i2, i2* @PauliY - store i2 %80, i2* %77 - store i2 %81, i2* %79 - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__6, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 1) - %82 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Array*, double, %Array* }* getelementptr ({ %Array*, double, %Array* }, { %Array*, double, %Array* }* null, i32 1) to i64)) - %83 = bitcast %Tuple* %82 to { %Array*, double, %Array* }* - %84 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %83, i64 0, i32 0 - %85 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %83, i64 0, i32 1 - %86 = getelementptr { %Array*, double, %Array* }, { %Array*, double, %Array* }* %83, i64 0, i32 2 - store %Array* %paulis__inline__6, %Array** %84 - store double 4.200000e-01, double* %85 - store %Array* %targets, %Array** %86 - call void @__quantum__qis__exp__ctladj(%Array* %ctls, { %Array*, double, %Array* }* %83) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %paulis__inline__6, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %paulis__inline__6, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %82, i64 -1) - %87 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 0) - %88 = bitcast i8* %87 to %Qubit** - %qb__inline__7 = load %Qubit*, %Qubit** %88 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__7) - %89 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %ctls, i64 1) - %90 = bitcast i8* %89 to %Qubit** - %qb__inline__8 = load %Qubit*, %Qubit** %90 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__8) - %91 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 0) - %92 = bitcast i8* %91 to %Qubit** - %93 = load %Qubit*, %Qubit** %92 - %94 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %93) - %95 = load %Result*, %Result** @ResultZero - %96 = call i1 @__quantum__rt__result_equal(%Result* %94, %Result* %95) - %97 = xor i1 %96, true - %98 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %targets, i64 1) - %99 = bitcast i8* %98 to %Qubit** - %100 = load %Qubit*, %Qubit** %99 - %101 = call %Result* @Microsoft__Quantum__Intrinsic__M__body(%Qubit* %100) - %102 = load %Result*, %Result** @ResultZero - %103 = call i1 @__quantum__rt__result_equal(%Result* %101, %Result* %102) - %104 = xor i1 %103, true - %105 = or i1 %97, %104 - br i1 %105, label %then0__9, label %continue__9 - -then0__9: ; preds = %else__1 - store i64 72, i64* %res - br label %continue__9 - -continue__9: ; preds = %then0__9, %else__1 - call void @__quantum__rt__result_update_reference_count(%Result* %94, i64 -1) - call void @__quantum__rt__result_update_reference_count(%Result* %101, i64 -1) - br label %continue__8 - -continue__8: ; preds = %continue__9, %then0__8 - call void @__quantum__rt__qubit_release_array(%Array* %targets) - call void @__quantum__rt__qubit_release_array(%Array* %ctls) - call void @__quantum__rt__array_update_alias_count(%Array* %targets, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %ctls, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %targets, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %ctls, i64 -1) - call void @__quantum__rt__result_update_reference_count(%Result* %49, i64 -1) - call void @__quantum__rt__result_update_reference_count(%Result* %56, i64 -1) - %106 = load i64, i64* %res - %107 = icmp ne i64 %106, 0 - br i1 %107, label %then0__10, label %continue__10 - -then0__10: ; preds = %continue__8 - %108 = add i64 70, %106 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %30, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %30, i64 -1) - ret i64 %108 - -continue__10: ; preds = %continue__8 - %qs = call %Array* @__quantum__rt__qubit_allocate_array(i64 3) - call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 1) - %109 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 0) - %110 = bitcast i8* %109 to %Qubit** - %qb__inline__9 = load %Qubit*, %Qubit** %110 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__9) - %111 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 2) - %112 = bitcast i8* %111 to %Qubit** - %qb__inline__10 = load %Qubit*, %Qubit** %112 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__10) - %bases__inline__11 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 3) - %113 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__11, i64 0) - %114 = bitcast i8* %113 to i2* - %115 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__11, i64 1) - %116 = bitcast i8* %115 to i2* - %117 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %bases__inline__11, i64 2) - %118 = bitcast i8* %117 to i2* - %119 = load i2, i2* @PauliX - %120 = load i2, i2* @PauliZ - %121 = load i2, i2* @PauliX - store i2 %119, i2* %114 - store i2 %120, i2* %116 - store i2 %121, i2* %118 - call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__11, i64 1) - call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 1) - %122 = call %Result* @__quantum__qis__measure__body(%Array* %bases__inline__11, %Array* %qs) - call void @__quantum__rt__array_update_alias_count(%Array* %bases__inline__11, i64 -1) - call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %bases__inline__11, i64 -1) - %123 = load %Result*, %Result** @ResultZero - %124 = call i1 @__quantum__rt__result_equal(%Result* %122, %Result* %123) - %125 = xor i1 %124, true - br i1 %125, label %then0__11, label %continue__11 - -then0__11: ; preds = %continue__10 - store i64 80, i64* %res - br label %continue__11 - -continue__11: ; preds = %then0__11, %continue__10 - %126 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 0) - %127 = bitcast i8* %126 to %Qubit** - %qb__inline__12 = load %Qubit*, %Qubit** %127 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__12) - %128 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %qs, i64 2) - %129 = bitcast i8* %128 to %Qubit** - %qb__inline__13 = load %Qubit*, %Qubit** %129 - call void @__quantum__qis__h__body(%Qubit* %qb__inline__13) - call void @__quantum__rt__qubit_release_array(%Array* %qs) - call void @__quantum__rt__array_update_alias_count(%Array* %qs, i64 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %qs, i64 -1) - call void @__quantum__rt__result_update_reference_count(%Result* %122, i64 -1) - %130 = load i64, i64* %res - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %0, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %0, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %3, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %3, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %7, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %7, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %11, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %11, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %19, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %19, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %30, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %30, i64 -1) - ret i64 %130 -} - -define void @Microsoft__Quantum__Intrinsic__X__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__X__body(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__X__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__X__adj(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__X__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__X__ctl(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__X__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__X__ctladj(%Array* %3, %Qubit* %4) - ret void -} - -declare %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]*, [2 x void (%Tuple*, i64)*]*, %Tuple*) - -define void @Microsoft__Quantum__Intrinsic__Y__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__Y__body(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Y__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__Y__adj(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Y__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__Y__ctl(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Y__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__Y__ctladj(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Z__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__Z__body(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Z__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__Z__adj(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Z__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__Z__ctl(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__Z__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__Z__ctladj(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__H__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__H__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__H__adj(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__H__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__H__ctl(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__H__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__H__ctladj(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__S__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__S__body(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__S__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__S__adj(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__S__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__S__ctl(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__S__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__S__ctladj(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__T__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__T__body(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__T__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %1 = getelementptr { %Qubit* }, { %Qubit* }* %0, i64 0, i32 0 - %2 = load %Qubit*, %Qubit** %1 - call void @Microsoft__Quantum__Intrinsic__T__adj(%Qubit* %2) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__T__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__T__ctl(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__T__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - call void @Microsoft__Quantum__Intrinsic__T__ctladj(%Array* %3, %Qubit* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__R__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { i2, double, %Qubit* }* - %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 - %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 - %4 = load i2, i2* %1 - %5 = load double, double* %2 - %6 = load %Qubit*, %Qubit** %3 - call void @Microsoft__Quantum__Intrinsic__R__body(i2 %4, double %5, %Qubit* %6) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__R__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { i2, double, %Qubit* }* - %1 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 1 - %3 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %0, i64 0, i32 2 - %4 = load i2, i2* %1 - %5 = load double, double* %2 - %6 = load %Qubit*, %Qubit** %3 - call void @Microsoft__Quantum__Intrinsic__R__adj(i2 %4, double %5, %Qubit* %6) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__R__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, { i2, double, %Qubit* }* }* - %1 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load { i2, double, %Qubit* }*, { i2, double, %Qubit* }** %2 - call void @Microsoft__Quantum__Intrinsic__R__ctl(%Array* %3, { i2, double, %Qubit* }* %4) - ret void -} - -define void @Microsoft__Quantum__Intrinsic__R__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, { i2, double, %Qubit* }* }* - %1 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load { i2, double, %Qubit* }*, { i2, double, %Qubit* }** %2 - call void @Microsoft__Quantum__Intrinsic__R__ctladj(%Array* %3, { i2, double, %Qubit* }* %4) - ret void -} - -define void @Lifted__PartialApplication__1__body__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* - %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 1 - %2 = load i2, i2* %1 - %3 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 2 - %4 = load double, double* %3 - %5 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %6 = getelementptr { %Qubit* }, { %Qubit* }* %5, i64 0, i32 0 - %7 = load %Qubit*, %Qubit** %6 - %8 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) - %9 = bitcast %Tuple* %8 to { i2, double, %Qubit* }* - %10 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 0 - %11 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 1 - %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 2 - store i2 %2, i2* %10 - store double %4, double* %11 - store %Qubit* %7, %Qubit** %12 - %13 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 - %14 = load %Callable*, %Callable** %13 - call void @__quantum__rt__callable_invoke(%Callable* %14, %Tuple* %8, %Tuple* %result-tuple) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %8, i64 -1) - ret void -} - -define void @Lifted__PartialApplication__1__adj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* - %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 1 - %2 = load i2, i2* %1 - %3 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 2 - %4 = load double, double* %3 - %5 = bitcast %Tuple* %arg-tuple to { %Qubit* }* - %6 = getelementptr { %Qubit* }, { %Qubit* }* %5, i64 0, i32 0 - %7 = load %Qubit*, %Qubit** %6 - %8 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) - %9 = bitcast %Tuple* %8 to { i2, double, %Qubit* }* - %10 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 0 - %11 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 1 - %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %9, i64 0, i32 2 - store i2 %2, i2* %10 - store double %4, double* %11 - store %Qubit* %7, %Qubit** %12 - %13 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 - %14 = load %Callable*, %Callable** %13 - %15 = call %Callable* @__quantum__rt__callable_copy(%Callable* %14, i1 false) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 1) - call void @__quantum__rt__callable_make_adjoint(%Callable* %15) - call void @__quantum__rt__callable_invoke(%Callable* %15, %Tuple* %8, %Tuple* %result-tuple) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %8, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %15, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %15, i64 -1) - ret void -} - -define void @Lifted__PartialApplication__1__ctl__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - %5 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* - %6 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 1 - %7 = load i2, i2* %6 - %8 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 2 - %9 = load double, double* %8 - %10 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) - %11 = bitcast %Tuple* %10 to { i2, double, %Qubit* }* - %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 0 - %13 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 1 - %14 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 2 - store i2 %7, i2* %12 - store double %9, double* %13 - store %Qubit* %4, %Qubit** %14 - %15 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) - %16 = bitcast %Tuple* %15 to { %Array*, { i2, double, %Qubit* }* }* - %17 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 0 - %18 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 1 - store %Array* %3, %Array** %17 - store { i2, double, %Qubit* }* %11, { i2, double, %Qubit* }** %18 - %19 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 0 - %20 = load %Callable*, %Callable** %19 - %21 = call %Callable* @__quantum__rt__callable_copy(%Callable* %20, i1 false) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 1) - call void @__quantum__rt__callable_make_controlled(%Callable* %21) - call void @__quantum__rt__callable_invoke(%Callable* %21, %Tuple* %15, %Tuple* %result-tuple) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %10, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %15, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %21, i64 -1) - ret void -} - -define void @Lifted__PartialApplication__1__ctladj__wrapper(%Tuple* %capture-tuple, %Tuple* %arg-tuple, %Tuple* %result-tuple) { -entry: - %0 = bitcast %Tuple* %arg-tuple to { %Array*, %Qubit* }* - %1 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 0 - %2 = getelementptr { %Array*, %Qubit* }, { %Array*, %Qubit* }* %0, i64 0, i32 1 - %3 = load %Array*, %Array** %1 - %4 = load %Qubit*, %Qubit** %2 - %5 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* - %6 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 1 - %7 = load i2, i2* %6 - %8 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 2 - %9 = load double, double* %8 - %10 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i2, double, %Qubit* }* getelementptr ({ i2, double, %Qubit* }, { i2, double, %Qubit* }* null, i32 1) to i64)) - %11 = bitcast %Tuple* %10 to { i2, double, %Qubit* }* - %12 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 0 - %13 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 1 - %14 = getelementptr { i2, double, %Qubit* }, { i2, double, %Qubit* }* %11, i64 0, i32 2 - store i2 %7, i2* %12 - store double %9, double* %13 - store %Qubit* %4, %Qubit** %14 - %15 = call %Tuple* @__quantum__rt__tuple_create(i64 mul nuw (i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64 2)) - %16 = bitcast %Tuple* %15 to { %Array*, { i2, double, %Qubit* }* }* - %17 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 0 - %18 = getelementptr { %Array*, { i2, double, %Qubit* }* }, { %Array*, { i2, double, %Qubit* }* }* %16, i64 0, i32 1 - store %Array* %3, %Array** %17 - store { i2, double, %Qubit* }* %11, { i2, double, %Qubit* }** %18 - %19 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %5, i64 0, i32 0 - %20 = load %Callable*, %Callable** %19 - %21 = call %Callable* @__quantum__rt__callable_copy(%Callable* %20, i1 false) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 1) - call void @__quantum__rt__callable_make_adjoint(%Callable* %21) - call void @__quantum__rt__callable_make_controlled(%Callable* %21) - call void @__quantum__rt__callable_invoke(%Callable* %21, %Tuple* %15, %Tuple* %result-tuple) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %10, i64 -1) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %15, i64 -1) - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %21, i64 -1) - call void @__quantum__rt__callable_update_reference_count(%Callable* %21, i64 -1) - ret void -} - -define void @MemoryManagement__1__RefCount(%Tuple* %capture-tuple, i64 %count-change) { -entry: - %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* - %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 - %2 = load %Callable*, %Callable** %1 - call void @__quantum__rt__callable_memory_management(i32 0, %Callable* %2, i64 %count-change) - call void @__quantum__rt__callable_update_reference_count(%Callable* %2, i64 %count-change) - call void @__quantum__rt__tuple_update_reference_count(%Tuple* %capture-tuple, i64 %count-change) - ret void -} - -define void @MemoryManagement__1__AliasCount(%Tuple* %capture-tuple, i64 %count-change) { -entry: - %0 = bitcast %Tuple* %capture-tuple to { %Callable*, i2, double }* - %1 = getelementptr { %Callable*, i2, double }, { %Callable*, i2, double }* %0, i64 0, i32 0 - %2 = load %Callable*, %Callable** %1 - call void @__quantum__rt__callable_memory_management(i32 1, %Callable* %2, i64 %count-change) - call void @__quantum__rt__callable_update_alias_count(%Callable* %2, i64 %count-change) - call void @__quantum__rt__tuple_update_alias_count(%Tuple* %capture-tuple, i64 %count-change) - ret void -} - -declare void @__quantum__rt__tuple_update_alias_count(%Tuple*, i64) - -attributes #0 = { "EntryPoint" } diff --git a/src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.qs b/src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.qs deleted file mode 100644 index b32c6bed364..00000000000 --- a/src/Qir/Tests/OpenSystemsSimulator/qir-test-opensimulator.qs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// This test uses all operations from the quantum instruction set that targets full state simulator. -// We are not validating the result, only that the test can compile and run against the simulator. -namespace Microsoft.Quantum.Testing.QIR -{ - open Microsoft.Quantum.Intrinsic; - - operation InvokeAllVariants(op : (Qubit => Unit is Adj + Ctl)) : Int - { - mutable res = 0; - using((target, ctls) = (Qubit(), Qubit[2])) - { - op(target); - Adjoint op(target); - if (M(target) != Zero) { let res = 1; } - else - { - H(ctls[0]); - H(ctls[1]); - Controlled op(ctls, target); - Adjoint Controlled op(ctls, target); - if (M(target) != Zero) { return 2; } - H(ctls[0]); - H(ctls[1]); - } - } - return res; - } - - @EntryPoint() - operation Test_Simulator_QIS() : Int - { - mutable res = 0; - let res = InvokeAllVariants(X); - if (res != 0) { return res; } - - let res = InvokeAllVariants(Y); - if (res != 0) { return 10 + res; } - - let res = InvokeAllVariants(Z); - if (res != 0) { return 20 + res; } - - let res = InvokeAllVariants(H); - if (res != 0) { return 30 + res; } - - let res = InvokeAllVariants(S); - if (res != 0) { return 40 + res; } - - let res = InvokeAllVariants(T); - if (res != 0) { return 50 + res; } - - let res = InvokeAllVariants(R(PauliX, 0.42, _)); - if (res != 0) { return 60 + res; } - - using((targets, ctls) = (Qubit[2], Qubit[2])) - { - let theta = 0.42; - Exp([PauliX, PauliY], theta, targets); - Adjoint Exp([PauliX, PauliY], theta, targets); - if (M(target) != Zero) { let res = 1; } - - H(ctls[0]); - H(ctls[1]); - Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); - Adjoint Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); - H(ctls[0]); - H(ctls[1]); - if (M(target) != Zero) { let res = 70 + 2; } - } - if (res != 0) { return 70 + res; } - - using (qs = Qubit[3]) - { - H(qs[0]); - H(qs[2]); - if (Measure([PauliX, PauliZ, PauliX], qs) != zero) { let res = 80; } - } - return res; - } -} From 394e3a789025f0b17c5114ab2be811bc12141748 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 09:57:48 -0700 Subject: [PATCH 139/158] Generate C++ headers as well as C. --- src/Simulation/qdk_sim_rs/build.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/build.rs b/src/Simulation/qdk_sim_rs/build.rs index cc5fb1c8731..aae76e31ea3 100644 --- a/src/Simulation/qdk_sim_rs/build.rs +++ b/src/Simulation/qdk_sim_rs/build.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + extern crate cbindgen; use std::env; @@ -7,9 +10,24 @@ fn main() -> Result<(), String> { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - cbindgen::generate(crate_dir) - .map_err(|e| e.to_string())? - .write_to_file("include/qdk_sim.h"); + cbindgen::generate_with_config( + &crate_dir, + cbindgen::Config { + language: cbindgen::Language::C, + ..Default::default() + }, + ) + .map_err(|e| e.to_string())? + .write_to_file("include/qdk_sim.h"); + + cbindgen::generate_with_config( + &crate_dir, + cbindgen::Config { + ..Default::default() + }, + ) + .map_err(|e| e.to_string())? + .write_to_file("include/qdk_sim.hpp"); Ok(()) } From 7e0f4f3fdbe58738f33eb66779300e9cb457fd10 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 10:10:27 -0700 Subject: [PATCH 140/158] Fixed issue building docs locally with python disabled. --- src/Simulation/qdk_sim_rs/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 9bab0e9b352..9b5635dcac1 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -8,8 +8,10 @@ #![cfg_attr(doc, feature(extended_key_value_attributes))] #![cfg_attr(doc, cfg_attr(doc, doc = include_str!("../README.md")))] // Set linting rules for documentation. We will stop the build on missing docs, -// or docs with known broken links. -#![cfg_attr(doc, deny(rustdoc::broken_intra_doc_links))] +// or docs with known broken links. We only enable this when all relevant +// features are enabled, otherwise the docs build will fail on links to +// features that are disabled for the current build. +#![cfg_attr(all(doc, feature = "python"), deny(rustdoc::broken_intra_doc_links))] #![cfg_attr(doc, deny(missing_docs))] #![cfg_attr(doc, warn(missing_doc_code_examples))] From 8f46c5521867c0613b4f7f541702f4ccd7e5b60d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 10:11:19 -0700 Subject: [PATCH 141/158] Updated link to tracking issue for experimental sims. --- src/Simulation/qdk_sim_rs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index 6343b16123c..4954ca706ee 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -13,9 +13,9 @@ > > 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#504](https://github.com/microsoft/qsharp-runtime/issues/504) for more information. +> 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. -This **experimental** crate contains simulation functionality for the Quantum Development Kit, including: +This **experimental** crate implements simulation functionality for the Quantum Development Kit, including: - Open systems simulation - Stabilizer simulation From 3db934b80f7d5f1a8445e348eaee5672e4076a86 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 10:17:57 -0700 Subject: [PATCH 142/158] Gate `cargo clean` on CI builds. --- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 10 ++++++---- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 7 ++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 6b2358d5431..8918fc4d212 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -1,4 +1,5 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); +$IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; Push-Location $PSScriptRoot $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); @@ -17,8 +18,9 @@ Push-Location $PSScriptRoot "--html-after-content $(Resolve-Path docs-includes/after.html)" cargo +nightly doc; - # Free disk space by cleaning up. - # Note that this takes longer, but saves ~1 GB of space, which is - # exceptionally helpful in CI builds. - cargo clean; + # 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/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index 4b35b545b9b..d669f5486f6 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -1,4 +1,5 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); +$IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; $script:allOk = $true; @@ -51,7 +52,11 @@ Push-Location $PSScriptRoot # Free disk space by cleaning up. # Note that this takes longer, but saves ~1 GB of space, which is # exceptionally helpful in CI builds. - cargo clean + # 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 if (-not $script:allOk) { From 091ace11ad48a5b00e1f974051735483693e9b93 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 14:34:11 -0700 Subject: [PATCH 143/158] Addressing feedback. --- src/Simulation/qdk_sim_rs/src/instrument.rs | 1 - src/Simulation/qdk_sim_rs/src/lib.rs | 4 ++++ src/Simulation/qdk_sim_rs/src/python.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/instrument.rs b/src/Simulation/qdk_sim_rs/src/instrument.rs index 61dc265e7bc..2ef4c49f1e4 100644 --- a/src/Simulation/qdk_sim_rs/src/instrument.rs +++ b/src/Simulation/qdk_sim_rs/src/instrument.rs @@ -148,6 +148,5 @@ fn sample_effects(effects: &[Process], idx_qubits: &[usize], state: &State) -> ( } } let (idx, output_state, _) = possible_outcomes.pop().unwrap(); - drop(possible_outcomes); (idx, output_state) } diff --git a/src/Simulation/qdk_sim_rs/src/lib.rs b/src/Simulation/qdk_sim_rs/src/lib.rs index 9b5635dcac1..e451a567a3b 100644 --- a/src/Simulation/qdk_sim_rs/src/lib.rs +++ b/src/Simulation/qdk_sim_rs/src/lib.rs @@ -13,6 +13,10 @@ // features that are disabled for the current build. #![cfg_attr(all(doc, feature = "python"), deny(rustdoc::broken_intra_doc_links))] #![cfg_attr(doc, deny(missing_docs))] +// This linting rule raises a warning on any documentation comments +// that are missing an `# Example` section. Currently, that raises a lot of +// warnings when building docs, but ideally we should make sure to address +// warnings going forward by adding relevant examples. #![cfg_attr(doc, warn(missing_doc_code_examples))] #[macro_use(array, s)] diff --git a/src/Simulation/qdk_sim_rs/src/python.rs b/src/Simulation/qdk_sim_rs/src/python.rs index da12c751fbd..c23b1a62427 100644 --- a/src/Simulation/qdk_sim_rs/src/python.rs +++ b/src/Simulation/qdk_sim_rs/src/python.rs @@ -228,7 +228,7 @@ impl FromPyObject<'_> for Pauli { // We want to support either a primitive type that can extract to u8, // or a value of type qdk_sim.Pauli from the root module. Python::with_gil(|py| { - let root = PyModule::import(py, "qdk_sim").unwrap(); + let root = PyModule::import(py, "qdk_sim_experimental").unwrap(); let py_enum: &PyType = root.get("Pauli").unwrap().downcast().unwrap(); let value: u8 = match py_enum.is_instance(ob) { Ok(true) => ob.getattr("value")?, From bb3c2917de3d3bf70bf59155f8056f5dae34e578 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 14:37:49 -0700 Subject: [PATCH 144/158] Force dependency on S.T.Encodings.Web to be >= 4.7.2. --- src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj index a3dd60f7333..9baf8e3e8b6 100644 --- a/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj +++ b/src/Simulation/Simulators/Microsoft.Quantum.Simulators.csproj @@ -26,6 +26,7 @@ + From 939a4b88a79411f372040e3e383b1253a9193b41 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 14:41:08 -0700 Subject: [PATCH 145/158] Adding comment to explain serialization field. --- .../Simulators/OpenSystemsSimulator/DataModel/State.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs index 3e1406c3c98..45e98f6b254 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs @@ -38,6 +38,12 @@ public class StabilizerState : State // 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; From 54831b97336b59aec19eaddce345c9d070d8b595 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 14:41:47 -0700 Subject: [PATCH 146/158] Added README link to experimental simulators doc. --- src/Simulation/qdk_sim_rs/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Simulation/qdk_sim_rs/README.md b/src/Simulation/qdk_sim_rs/README.md index 4954ca706ee..f48c36c0bd5 100644 --- a/src/Simulation/qdk_sim_rs/README.md +++ b/src/Simulation/qdk_sim_rs/README.md @@ -15,6 +15,10 @@ > > 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 From 8847f2ab1c36e7028ea784a0699645a9c836814d Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 14:41:55 -0700 Subject: [PATCH 147/158] Fix typo. --- .../Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index 56326b710b9..2f8ae5555dd 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -227,7 +227,7 @@ void IIntrinsicZ.ControlledBody(IQArray controls, Qubit target) // TODO: pass off to decompositions for more than one control. if (controls is { Count: 0 }) { - (this as IIntrinsicY).Body(target); + (this as IIntrinsicZ).Body(target); } else if (controls is { Count: 1 }) { From 363b17ef5c526c472b2894c9eb69655a98b762c5 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 18:02:38 -0700 Subject: [PATCH 148/158] Add stabilizers to state converter. --- .../OpenSystemsSimulator/DataModel/StateConverter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs index ad0be705a46..14ecee5f548 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs @@ -80,7 +80,8 @@ public override void Write(Utf8JsonWriter writer, State value, JsonSerializerOpt { PureState _ => "Pure", MixedState _ => "Mixed", - _ => throw new JsonException() + StabilizerState _ => "Stabilizer", + _ => throw new JsonException($"Unknown state type {value.GetType()}.") } ); From ce0fde5a162c8406df3ea56b1922a8014493f5b7 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 7 Jun 2021 18:10:53 -0700 Subject: [PATCH 149/158] Move format/linting checks to build from test. --- src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 | 17 ++++++++++++++++- src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 | 17 +---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 8918fc4d212..4ab192620d6 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -1,7 +1,22 @@ & (Join-Path $PSScriptRoot ".." ".." ".." "build" "set-env.ps1"); $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; -Push-Location $PSScriptRoot +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) diff --git a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 index d669f5486f6..441e76e10ae 100644 --- a/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/test-qdk-sim-rs.ps1 @@ -4,24 +4,9 @@ $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; $script:allOk = $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; - # If running in CI, use cargo2junit to expose unit tests to the # PublishTestResults task. - if ("$Env:TF_BUILD" -ne "") { + if ($IsCI) { cargo install cargo2junit $testJson = cargo +nightly test -- -Z unstable-options --format json; $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; From 9433e45249423cd09a232a9f7bdc5034851bd2cb Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 01:00:56 -0700 Subject: [PATCH 150/158] Fix stabilizer noise model serialization, added new test. --- .../NoiseModelSerializationTests.cs | 14 ++++++++++++++ .../OpenSystemsSimulator/DataModel/Instrument.cs | 3 +++ .../OpenSystemsSimulator/DataModel/NoiseModel.cs | 2 +- .../OpenSystemsSimulator/DataModel/State.cs | 4 +++- .../DataModel/StateConverter.cs | 8 +++++++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs index a1f008cd4d7..f4f86c9b8ef 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs @@ -40,5 +40,19 @@ public void IdealNoiseModelRoundTrips() idealJson.AssertJsonIsEqualTo(roundtripJson); } + + [Fact] + public void IdealStabilizerNoiseModelRoundTrips() + { + var idealStabilizerJson = JsonSerializer.Serialize( + NoiseModel.TryGetByName("ideal_stabilizer", out var model) + ? model + : throw new Exception("Could not get noise model by name.") + ); + var idealStabilizerNoiseModel = JsonSerializer.Deserialize(idealStabilizerJson); + var roundtripJson = JsonSerializer.Serialize(idealStabilizerNoiseModel); + + idealStabilizerJson.AssertJsonIsEqualTo(roundtripJson); + } } } diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs index b43b3c93606..f99737d0a54 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Instrument.cs @@ -53,7 +53,10 @@ public override void Write(Utf8JsonWriter writer, Instrument value, JsonSerializ break; case ZMeasurementInstrument zInstrument: + writer.WriteStartObject(); + writer.WritePropertyName("ZMeasurement"); JsonSerializer.Serialize(writer, zInstrument); + writer.WriteEndObject(); break; default: diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs index 55b9f4c55dc..79c122a102d 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/NoiseModel.cs @@ -63,7 +63,7 @@ public static bool TryGetByName(string name, [NotNullWhen(true)] out NoiseModel? model = NativeInterface.GetNoiseModelByName(name); return true; } - catch + catch (SimulationException) { model = null; return false; diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs index 45e98f6b254..439e3570b81 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/State.cs @@ -53,6 +53,7 @@ public class TableArray [JsonPropertyName("data")] public List? Data { get; set; } + [JsonIgnore] public NDArray? AsArray => Dimensions == null || Data == null ? null @@ -66,7 +67,8 @@ public class TableArray [JsonPropertyName("table")] public TableArray? Table { get; set; } - public NDArray Data => Table?.AsArray; + [JsonIgnore] + public NDArray? Data => Table?.AsArray; } public abstract class ArrayState : State diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs index 14ecee5f548..22c1fa3867c 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/StateConverter.cs @@ -57,6 +57,7 @@ public override State Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe "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; } @@ -96,7 +97,12 @@ public override void Write(Utf8JsonWriter writer, State value, JsonSerializerOpt Data = stabilizerState.Data.flat.ToArray().ToList(), Dimensions = stabilizerState.Data.Shape.Dimensions.ToList() }; - JsonSerializer.Serialize(writer, array); + writer.WriteStartObject(); + writer.WritePropertyName("n_qubits"); + writer.WriteNumberValue(stabilizerState.NQubits); + writer.WritePropertyName("table"); + JsonSerializer.Serialize(writer, array); + writer.WriteEndObject(); } writer.WriteEndObject(); writer.WriteEndObject(); From 55e0a7cb339ea6ecb9d3cef1431d96c6d7e08561 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 01:01:38 -0700 Subject: [PATCH 151/158] Work on docs. --- .../experimental-simulators-from-python.ipynb | 597 ++++++++++++++++++ documentation/experimental-simulators.md | 39 ++ 2 files changed, 636 insertions(+) create mode 100644 documentation/examples/experimental-simulators-from-python.ipynb create mode 100644 documentation/experimental-simulators.md diff --git a/documentation/examples/experimental-simulators-from-python.ipynb b/documentation/examples/experimental-simulators-from-python.ipynb new file mode 100644 index 00000000000..5274b9cb4eb --- /dev/null +++ b/documentation/examples/experimental-simulators-from-python.ipynb @@ -0,0 +1,597 @@ +{ + "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": "7cc8011b", + "metadata": {}, + "outputs": [], + "source": [ + "dump_plus = qsharp.compile(\"\"\"\n", + " operation DumpPlus() : Unit {\n", + " use q = Qubit();\n", + " H(q);\n", + " Microsoft.Quantum.Diagnostics.DumpMachine();\n", + " X(q);\n", + " Reset(q);\n", + " }\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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Mixed state
# of qubits3
State data\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", + " " + ], + "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": [ + "dump_plus.simulate_noise()" + ] + }, + { + "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 `opensim.nQubits` configuration setting:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "865fa045", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.config['opensim.nQubits'] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Mixed state
# of qubits1
State data\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", + " " + ], + "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": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dump_plus.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`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4cfc0e13", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.experimental.set_noise_model_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": 9, + "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": 10, + "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": 10, + "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": 11, + "id": "b0a01d12", + "metadata": {}, + "outputs": [], + "source": [ + "I, X, Y, Z = [P.as_qobj() for P in qsharp.Pauli]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "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": 13, + "id": "3add5aa9", + "metadata": {}, + "outputs": [], + "source": [ + "noise_model['h'] = depolarizing_noise(0.99) * qt.to_super(qt.qip.operations.hadamard_transform())" + ] + }, + { + "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": 14, + "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": 15, + "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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Mixed state
# of qubits1
State data\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", + " " + ], + "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": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dump_plus.simulate_noise()" + ] + }, + { + "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": 16, + "id": "a02721e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'initial_state': {'n_qubits': 1,\n", + " 'data': {'Stabilizer': array([[ True, False, False],\n", + " [False, True, False]])}},\n", + " 'cnot': {'n_qubits': 1, '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': {'pr_readout_error': 0}}" + ] + }, + "execution_count": 16, + "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": 20, + "id": "9f7791f6", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: not yet implemented for stabilizer models.\n", + "# qsharp.experimental.set_noise_model(noise_model)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7794ba38", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.experimental.set_noise_model_by_name('ideal_stabilizer')" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "fe0142d8", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.config['opensim.representation'] = 'stabilizer'" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "4db703f8", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Could not set noise model from JSON: Exception in native open systems simulator runtime: Data / schema error deserializing noise model at line 1, column 160.\n", + "JSON data:\n", + "{\"initial_state\":{\"n_qubits\":1,\"data\":{\"Stabilizer\":{\"v\":1,\"dim\":[2,3],\"data\":[true,false,false,false,true,false],\"AsArray\":[true,false,false,false,true,false]}}},\"cnot\":{\"n_qubits\":1,\"data\":{\"ChpDecomposition\":[{\"Cnot\":[0,1]}]}},\"i\":{\"n_qubits\":1,\"data\":{\"Sequence\":[]}},\"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\"},\"h\":{\"n_qubits\":1,\"data\":{\"ChpDecomposition\":[{\"Hadamard\":0}]}},\"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}]}},\"z_meas\":{\"pr_readout_error\":0}}\r\n" + ] + } + ], + "source": [ + "dump_plus.simulate_noise()" + ] + }, + { + "cell_type": "markdown", + "id": "a64c1a27", + "metadata": {}, + "source": [ + "## Epilogue" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56168b7e", + "metadata": {}, + "outputs": [], + "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..1336b286761 --- /dev/null +++ b/documentation/experimental-simulators.md @@ -0,0 +1,39 @@ +# 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 + +## 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. + +> **Known limitation**: Currently, error channels for stabilizer simulation must be specified manually by their JSON serialization. + From e50dccf7c89c79bd6419736c5415a6671b46ea9a Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 09:46:33 -0700 Subject: [PATCH 152/158] Fixed some slight issues with n_qubits handling. --- src/Simulation/qdk_sim_rs/src/noise_model.rs | 2 +- src/Simulation/qdk_sim_rs/src/processes/apply.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Simulation/qdk_sim_rs/src/noise_model.rs b/src/Simulation/qdk_sim_rs/src/noise_model.rs index b8852736233..50fdd79e12a 100644 --- a/src/Simulation/qdk_sim_rs/src/noise_model.rs +++ b/src/Simulation/qdk_sim_rs/src/noise_model.rs @@ -230,7 +230,7 @@ impl NoiseModel { data: ProcessData::Unsupported, }, cnot: Process { - n_qubits: 1, + n_qubits: 2, data: ProcessData::ChpDecomposition(vec![ChpOperation::Cnot(0, 1)]), }, z_meas: Instrument::ZMeasurement { diff --git a/src/Simulation/qdk_sim_rs/src/processes/apply.rs b/src/Simulation/qdk_sim_rs/src/processes/apply.rs index c65cd321d92..21de1e77060 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/apply.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/apply.rs @@ -59,8 +59,8 @@ impl Process { // Fail if there's not enough qubits. if state.n_qubits < self.n_qubits { return log_as_err(format!( - "Channel acts on {} qubits, but a state on only {} qubits was given.", - self.n_qubits, state.n_qubits + "Channel acts on {} qubits, but a state on only {} qubits was given.\n\nChannel:\n{:?}\n\nState:\n{:?}", + self.n_qubits, state.n_qubits, self, state )); } @@ -101,7 +101,7 @@ impl Process { // tune to find crossover point. if let ChpDecomposition(operations) = &self.data { if let Stabilizer(tableau) = &state.data { - return apply_chp_decomposition_to(operations, self.n_qubits, idx_qubits, tableau); + return apply_chp_decomposition_to(operations, state.n_qubits, idx_qubits, tableau); } } From e4bf2948b36b70673d42e42147e0f896c16967f1 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 10:06:44 -0700 Subject: [PATCH 153/158] Update documentation notebook. --- .../experimental-simulators-from-python.ipynb | 444 +++++++++++++++++- 1 file changed, 422 insertions(+), 22 deletions(-) diff --git a/documentation/examples/experimental-simulators-from-python.ipynb b/documentation/examples/experimental-simulators-from-python.ipynb index 5274b9cb4eb..600044943ef 100644 --- a/documentation/examples/experimental-simulators-from-python.ipynb +++ b/documentation/examples/experimental-simulators-from-python.ipynb @@ -172,7 +172,7 @@ "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 `opensim.nQubits` configuration setting:" + "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:" ] }, { @@ -182,7 +182,7 @@ "metadata": {}, "outputs": [], "source": [ - "qsharp.config['opensim.nQubits'] = 1" + "qsharp.config['experimental.simulators.nQubits'] = 1" ] }, { @@ -346,9 +346,29 @@ "execution_count": 13, "id": "3add5aa9", "metadata": {}, - "outputs": [], + "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": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "noise_model['h'] = depolarizing_noise(0.99) * qt.to_super(qt.qip.operations.hadamard_transform())" + "noise_model['h'] = depolarizing_noise(0.99) * qt.to_super(qt.qip.operations.hadamard_transform())\n", + "noise_model['h']" ] }, { @@ -459,9 +479,10 @@ "data": { "text/plain": [ "{'initial_state': {'n_qubits': 1,\n", - " 'data': {'Stabilizer': array([[ True, False, False],\n", - " [False, True, False]])}},\n", - " 'cnot': {'n_qubits': 1, 'data': {'ChpDecomposition': [{'Cnot': [0, 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", @@ -482,7 +503,7 @@ " {'Phase': 0}]}},\n", " 'z': {'n_qubits': 1,\n", " 'data': {'ChpDecomposition': [{'Phase': 0}, {'Phase': 0}]}},\n", - " 'z_meas': {'pr_readout_error': 0}}" + " 'z_meas': {'ZMeasurement': {'pr_readout_error': 0}}}" ] }, "execution_count": 16, @@ -497,7 +518,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 17, "id": "9f7791f6", "metadata": {}, "outputs": [], @@ -508,7 +529,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 18, "id": "7794ba38", "metadata": {}, "outputs": [], @@ -516,36 +537,401 @@ "qsharp.experimental.set_noise_model_by_name('ideal_stabilizer')" ] }, + { + "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": 24, + "execution_count": 19, "id": "fe0142d8", "metadata": {}, "outputs": [], "source": [ - "qsharp.config['opensim.representation'] = 'stabilizer'" + "qsharp.config['experimental.simulators.representation'] = 'stabilizer'" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 20, "id": "4db703f8", "metadata": {}, "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "Could not set noise model from JSON: Exception in native open systems simulator runtime: Data / schema error deserializing noise model at line 1, column 160.\n", - "JSON data:\n", - "{\"initial_state\":{\"n_qubits\":1,\"data\":{\"Stabilizer\":{\"v\":1,\"dim\":[2,3],\"data\":[true,false,false,false,true,false],\"AsArray\":[true,false,false,false,true,false]}}},\"cnot\":{\"n_qubits\":1,\"data\":{\"ChpDecomposition\":[{\"Cnot\":[0,1]}]}},\"i\":{\"n_qubits\":1,\"data\":{\"Sequence\":[]}},\"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\"},\"h\":{\"n_qubits\":1,\"data\":{\"ChpDecomposition\":[{\"Hadamard\":0}]}},\"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}]}},\"z_meas\":{\"pr_readout_error\":0}}\r\n" - ] + "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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Stabilizer state
# of qubits1
State data$$\\left(\\begin{array}{c|c|c}0 & 1 & 0\\\\\n", + "\\hline\n", + "1 & 0 & 0\\end{array}\\right)$$
\r\n", + " " + ], + "text/plain": [ + "Microsoft.Quantum.Experimental.StabilizerState" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ "dump_plus.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": 21, + "id": "5f24b886", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.config['experimental.simulators.nQubits'] = 40" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "5f4d45b8", + "metadata": {}, + "outputs": [ + { + "data": { + "application/json": "{\"Table\":{\"SchemaVersion\":1,\"Dimensions\":[80,81],\"Data\":[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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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],\"AsArray\":[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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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]},\"Data\":[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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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],\"NQubits\":40}", + "text/html": [ + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Stabilizer state
# of qubits40
State data$$\\left(\\begin{array}{cccccccccccccccccccccccccccccccccccccccc|cccccccccccccccccccccccccccccccccccccccc|c}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 & 1 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "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 & 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\\\\\n", + "0 & 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 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "\\hline\n", + "1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 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\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 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 & 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 & 1 & 0 & 0 & 0 & 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 & 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 & 1 & 0 & 0 & 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 & 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 & 1 & 0 & 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 & 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 & 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 & 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 & 1 & 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 & 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 & 1 & 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 & 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 & 1 & 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 & 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 & 1 & 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 & 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 & 1 & 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 & 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 & 1 & 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 & 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 & 1 & 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 & 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 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0 & 0\\\\\n", + "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 & 1 & 0 & 0\\\\\n", + "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 & 1 & 0\\end{array}\\right)$$
\r\n", + " " + ], + "text/plain": [ + "Microsoft.Quantum.Experimental.StabilizerState" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dump_plus.simulate_noise()" + ] + }, + { + "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": 23, + "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": 24, + "id": "2204f4ae", + "metadata": {}, + "outputs": [], + "source": [ + "dump_bell = qsharp.compile(\"\"\"\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", + " }\n", + "\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "70908857", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.config['experimental.simulators.stabilizerStateStyle'] = 'matrixWithoutDestabilizers'" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Stabilizer state
# of qubits4
State data$$\\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", + " " + ], + "text/plain": [ + "Microsoft.Quantum.Experimental.StabilizerState" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dump_bell.simulate_noise()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "40431ed1", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.config['experimental.simulators.stabilizerStateStyle'] = 'denseGroupPresentation'" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Stabilizer state
# of qubits4
State data$$\\left\\langle XX𝟙𝟙, ZZ𝟙𝟙, 𝟙𝟙Z𝟙, 𝟙𝟙𝟙Z \\right\\rangle$$
\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": [ + "dump_bell.simulate_noise()" + ] + }, + { + "cell_type": "markdown", + "id": "4a4f833c", + "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": 29, + "id": "f897b131", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: not yet implemented for stabilizer models.\n", + "# qsharp.experimental.set_noise_model(noise_model)" + ] + }, { "cell_type": "markdown", "id": "a64c1a27", @@ -556,10 +942,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "id": "56168b7e", "metadata": {}, - "outputs": [], + "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')}" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "qsharp.component_versions()" ] From 682d4401b6a1fb5bc04ecc898050b517dabcd625 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 13:10:45 -0700 Subject: [PATCH 154/158] Regenerate notebook from latest build. --- .../examples/experimental-simulators-from-python.ipynb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/documentation/examples/experimental-simulators-from-python.ipynb b/documentation/examples/experimental-simulators-from-python.ipynb index 600044943ef..061f0eae54d 100644 --- a/documentation/examples/experimental-simulators-from-python.ipynb +++ b/documentation/examples/experimental-simulators-from-python.ipynb @@ -44,7 +44,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Preparing Q# environment...\n" + "Preparing Q# environment...\n", + "." ] } ], @@ -915,7 +916,7 @@ }, { "cell_type": "markdown", - "id": "4a4f833c", + "id": "3ef8c86f", "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?" @@ -924,7 +925,7 @@ { "cell_type": "code", "execution_count": 29, - "id": "f897b131", + "id": "0c1c19de", "metadata": {}, "outputs": [], "source": [ From 069ba53fa6dcb820931838b81666ecb434e65971 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 16:09:44 -0700 Subject: [PATCH 155/158] Fixed some serialization issues. --- .../OpenSystemsSimulator/DataModel/Process.cs | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs index 7c1cdfbb9af..0ce895adc26 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs @@ -17,6 +17,38 @@ 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((Pauli)reader.GetInt64()); + } + 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); @@ -31,7 +63,7 @@ public override Process Read(ref Utf8JsonReader reader, Type typeToConvert, Json "KrausDecomposition" => arrayConverter.Read(ref reader, typeof(NDArray), options).Bind( (int nQubits, NDArray data) => new KrausDecompositionProcess(nQubits, data) ), - "MixedPauli" => JsonSerializer.Deserialize)>>(ref reader).Bind( + "MixedPauli" => ReadMixedPauliData(ref reader).Bind( (int nQubits, IList<(double, IList)> data) => new MixedPauliProcess(nQubits, data) ), "Sequence" => JsonSerializer.Deserialize>(ref reader).Bind( @@ -81,7 +113,20 @@ public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerO } else if (value is MixedPauliProcess mixedPauliProcess) { - JsonSerializer.Serialize(writer, mixedPauliProcess.Operators); + writer.WriteStartArray(); + foreach (var op in mixedPauliProcess.Operators) + { + writer.WriteStartArray(); + writer.WriteNumberValue(op.Item1); + writer.WriteStartArray(); + foreach (var p in op.Item2) + { + writer.WriteNumberValue((int)p); + } + writer.WriteEndArray(); + writer.WriteEndArray(); + } + writer.WriteEndArray(); } else if (value is ChpDecompositionProcess chpDecomposition) { @@ -130,7 +175,7 @@ internal MixedPauliProcess(int nQubits, IList<(double, IList)> operators) } } - public class ChpDecompositionProcess : Process + public class ChpDecompositionProcess : Process { public IList Operations; From 4c2485101dd6faf8624f4159c3d8515597bf44c6 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 16:11:06 -0700 Subject: [PATCH 156/158] Fixed some serialization tests. --- .../NoiseModelSerializationTests.cs | 19 ++++---- .../ProcessSerializationTests.cs | 47 +++++++++++++++++-- .../SerializationExtensions.cs | 8 ++++ .../StateSerializationTests.cs | 6 +-- 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs index f4f86c9b8ef..57af996e521 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/NoiseModelSerializationTests.cs @@ -14,7 +14,7 @@ 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],[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]]}}}]}}"; + 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() @@ -35,24 +35,23 @@ public void IdealNoiseModelDeserializes() [Fact] public void IdealNoiseModelRoundTrips() { - var idealNoiseModel = JsonSerializer.Deserialize(idealJson); - var roundtripJson = JsonSerializer.Serialize(idealNoiseModel); - - idealJson.AssertJsonIsEqualTo(roundtripJson); + 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 idealStabilizerJson = JsonSerializer.Serialize( + var idealStabilizerModel = ( NoiseModel.TryGetByName("ideal_stabilizer", out var model) ? model : throw new Exception("Could not get noise model by name.") ); - var idealStabilizerNoiseModel = JsonSerializer.Deserialize(idealStabilizerJson); - var roundtripJson = JsonSerializer.Serialize(idealStabilizerNoiseModel); - - idealStabilizerJson.AssertJsonIsEqualTo(roundtripJson); + idealStabilizerModel.AssertSerializationRoundTrips(); } } } diff --git a/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs index 662e17171d5..1c5f37f0f47 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs @@ -15,6 +15,45 @@ 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, [0]], + [0.1, [1]] + ] + } + }".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() { @@ -23,7 +62,7 @@ public void CnotSerializesCorrectly() IdxControl = 0, IdxTarget = 1 }; - var json = JsonSerializer.Serialize(cnot); + var json = JsonSerializer.Serialize(cnot); var expectedJson = @"{ ""Cnot"": [0, 1] }"; @@ -38,7 +77,7 @@ public void HadamardSerializesCorrectly() { IdxTarget = 1 }; - var json = JsonSerializer.Serialize(h); + var json = JsonSerializer.Serialize(h); var expectedJson = @"{ ""Hadamard"": 1 }"; @@ -53,7 +92,7 @@ public void PhaseSerializesCorrectly() { IdxTarget = 1 }; - var json = JsonSerializer.Serialize(s); + var json = JsonSerializer.Serialize(s); var expectedJson = @"{ ""Phase"": 1 }"; @@ -68,7 +107,7 @@ public void AdjointPhaseSerializesCorrectly() { IdxTarget = 1 }; - var json = JsonSerializer.Serialize(sAdj); + var json = JsonSerializer.Serialize(sAdj); var expectedJson = @"{ ""AdjointPhase"": 1 }"; diff --git a/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs b/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs index 4777142dadc..8194aa7b0b8 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/SerializationExtensions.cs @@ -31,9 +31,17 @@ 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 diff --git a/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs index e5d20be2987..e90fce0465b 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/StateSerializationTests.cs @@ -30,12 +30,12 @@ public void StabilizerStateSerializesCorrectly() Dimensions = new List { 2, 3 } } }; - var json = JsonSerializer.Serialize(state); + var json = JsonSerializer.Serialize(state); var expectedJson = @"{ - ""n_qubits"": 1, + ""n_qubits"": 2, ""data"": { ""Stabilizer"": { - ""n_qubits"": 1, + ""n_qubits"": 2, ""table"": { ""v"": 1, ""dim"": [2, 3], From 141536ac605450c82c8c76ea2665a823a6ae2076 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 8 Jun 2021 18:32:07 -0700 Subject: [PATCH 157/158] Expose build info and fix mixed pauli channels. --- .../ProcessSerializationTests.cs | 4 ++-- .../OpenSystemsSimulator/DataModel/Process.cs | 18 ++++++++++++++++-- .../OpenSystemsSimulator/NativeInterface.cs | 2 +- .../OpenSystemsSimulator.cs | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs index 1c5f37f0f47..1e518bff509 100644 --- a/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs +++ b/src/Simulation/Simulators.Tests/SerializationTests/ProcessSerializationTests.cs @@ -31,8 +31,8 @@ public void MixedPauliSerializesCorrectly() ""n_qubits"": 1, ""data"": { ""MixedPauli"": [ - [0.9, [0]], - [0.1, [1]] + [0.9, [""I""]], + [0.1, [""X""]] ] } }".AssertJsonIsEqualTo(actualJson); diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs index 0ce895adc26..cde27687137 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/DataModel/Process.cs @@ -40,7 +40,14 @@ public class ProcessConverter : JsonConverter break; } - ops.Add((Pauli)reader.GetInt64()); + 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); @@ -121,7 +128,14 @@ public override void Write(Utf8JsonWriter writer, Process value, JsonSerializerO writer.WriteStartArray(); foreach (var p in op.Item2) { - writer.WriteNumberValue((int)p); + 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(); diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs index 9d1e2473c3c..84db6154488 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/NativeInterface.cs @@ -61,7 +61,7 @@ private static void CheckCall(Int64 errorCode) [DllImport(DLL_NAME, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl, EntryPoint="get_simulator_info")] private static extern string _GetSimulatorInfo(); - private static readonly JToken SimulatorInfo; + internal static readonly JToken SimulatorInfo; static NativeInterface() { diff --git a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs index 2f8ae5555dd..dfe8e965f94 100644 --- a/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs +++ b/src/Simulation/Simulators/OpenSystemsSimulator/OpenSystemsSimulator.cs @@ -11,6 +11,7 @@ using Microsoft.Quantum.Intrinsic.Interfaces; using System.Collections.Generic; using System.Diagnostics; +using Newtonsoft.Json.Linq; namespace Microsoft.Quantum.Experimental { @@ -19,6 +20,8 @@ namespace Microsoft.Quantum.Experimental // 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; From 1b9aa59508c6e06b65d665820420842b25fac549 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Wed, 9 Jun 2021 09:29:49 -0700 Subject: [PATCH 158/158] Updated documentation notebook from latest build. --- .../experimental-simulators-from-python.ipynb | 263 +++++++++++++----- 1 file changed, 196 insertions(+), 67 deletions(-) diff --git a/documentation/examples/experimental-simulators-from-python.ipynb b/documentation/examples/experimental-simulators-from-python.ipynb index 061f0eae54d..164612e243b 100644 --- a/documentation/examples/experimental-simulators-from-python.ipynb +++ b/documentation/examples/experimental-simulators-from-python.ipynb @@ -44,8 +44,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Preparing Q# environment...\n", - "." + "Preparing Q# environment...\n" ] } ], @@ -83,19 +82,19 @@ { "cell_type": "code", "execution_count": 4, - "id": "7cc8011b", + "id": "a97eefa7", "metadata": {}, "outputs": [], "source": [ - "dump_plus = qsharp.compile(\"\"\"\n", - " operation DumpPlus() : Unit {\n", - " use q = Qubit();\n", - " H(q);\n", - " Microsoft.Quantum.Diagnostics.DumpMachine();\n", - " X(q);\n", - " Reset(q);\n", - " }\n", - "\"\"\")" + "%%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", + "}" ] }, { @@ -157,7 +156,7 @@ } ], "source": [ - "dump_plus.simulate_noise()" + "DumpPlus.simulate_noise()" ] }, { @@ -239,7 +238,7 @@ } ], "source": [ - "dump_plus.simulate_noise()" + "DumpPlus.simulate_noise()" ] }, { @@ -247,7 +246,7 @@ "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`:" + "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:" ] }, { @@ -257,7 +256,8 @@ "metadata": {}, "outputs": [], "source": [ - "qsharp.experimental.set_noise_model_by_name('ideal')" + "qsharp.experimental.set_noise_model_by_name('ideal')\n", + "%noise_model --set-by-name ideal" ] }, { @@ -451,7 +451,7 @@ } ], "source": [ - "dump_plus.simulate_noise()" + "DumpPlus.simulate_noise()" ] }, { @@ -524,18 +524,7 @@ "metadata": {}, "outputs": [], "source": [ - "# TODO: not yet implemented for stabilizer models.\n", - "# qsharp.experimental.set_noise_model(noise_model)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "7794ba38", - "metadata": {}, - "outputs": [], - "source": [ - "qsharp.experimental.set_noise_model_by_name('ideal_stabilizer')" + "qsharp.experimental.set_noise_model(noise_model)" ] }, { @@ -548,7 +537,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 18, "id": "fe0142d8", "metadata": {}, "outputs": [], @@ -558,7 +547,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "id": "4db703f8", "metadata": {}, "outputs": [ @@ -596,13 +585,13 @@ "()" ] }, - "execution_count": 20, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "dump_plus.simulate_noise()" + "DumpPlus.simulate_noise()" ] }, { @@ -615,7 +604,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 20, "id": "5f24b886", "metadata": {}, "outputs": [], @@ -625,7 +614,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 21, "id": "5f4d45b8", "metadata": {}, "outputs": [ @@ -741,13 +730,13 @@ "()" ] }, - "execution_count": 22, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "dump_plus.simulate_noise()" + "DumpPlus.simulate_noise()" ] }, { @@ -760,7 +749,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "id": "f3587f4c", "metadata": {}, "outputs": [], @@ -778,28 +767,28 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "id": "2204f4ae", "metadata": {}, "outputs": [], "source": [ - "dump_bell = qsharp.compile(\"\"\"\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", + "%%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": 25, + "execution_count": 24, "id": "70908857", "metadata": {}, "outputs": [], @@ -809,7 +798,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "id": "89b437a8", "metadata": {}, "outputs": [ @@ -848,18 +837,18 @@ "()" ] }, - "execution_count": 26, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "dump_bell.simulate_noise()" + "DumpBellPair.simulate_noise()" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "id": "40431ed1", "metadata": {}, "outputs": [], @@ -869,7 +858,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "id": "1cc366e4", "metadata": {}, "outputs": [ @@ -887,7 +876,64 @@ "\r\n", " \r\n", " State data\r\n", - " $$\\left\\langle XX𝟙𝟙, ZZ𝟙𝟙, 𝟙𝟙Z𝟙, 𝟙𝟙𝟙Z \\right\\rangle$$\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": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "DumpBellPair.simulate_noise()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "07439931", + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.config['experimental.simulators.stabilizerStateStyle'] = 'sparseGroupPresentation'" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", " \r\n", "
Stabilizer state
# of qubits4
State data$$\\left\\langle X_{0}X_{1}, Z_{0}Z_{1}, Z_{2}, -Z_{3} \\right\\rangle$$
\r\n", " " @@ -905,18 +951,18 @@ "()" ] }, - "execution_count": 28, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "dump_bell.simulate_noise()" + "DumpBellPair.simulate_noise()" ] }, { "cell_type": "markdown", - "id": "3ef8c86f", + "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?" @@ -924,13 +970,91 @@ }, { "cell_type": "code", - "execution_count": 29, - "id": "0c1c19de", + "execution_count": 30, + "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": 30, + "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": 31, + "id": "94c8b146", "metadata": {}, "outputs": [], "source": [ - "# TODO: not yet implemented for stabilizer models.\n", - "# qsharp.experimental.set_noise_model(noise_model)" + "qsharp.experimental.set_noise_model(noise_model)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "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,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", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "\r\n", + " \r\n", + " \r\n", + " \r\n", + " \r\n", + "
Stabilizer state
# of qubits4
State data$$\\left\\langle X_{0}X_{1}, Z_{0}Z_{1}, Z_{2}, -Z_{3} \\right\\rangle$$
\r\n", + " " + ], + "text/plain": [ + "Microsoft.Quantum.Experimental.StabilizerState" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "DumpBellPair.simulate_noise()" ] }, { @@ -943,7 +1067,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 33, "id": "56168b7e", "metadata": {}, "outputs": [ @@ -953,10 +1077,15 @@ "{'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')}" + " '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": 30, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" }