From 5a4fb4f34b367484090558bfe688e16c26be372f Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 16 Sep 2022 11:43:00 -0700 Subject: [PATCH] Replace C++ QIR Runtime with Rust QIR stdlib (#1087) * Add parallel Rust QIR Runtime * Remove old dependencies * Use --all-targets clippy, corresponding fixes * Add conditionals instrinc support, result_to_string * Add QIR simulator support * Share C++ tests across Rust impl * Add to build scripts * Fix build ordering * Fix CI cleanup * More build fixes * Fix build bug * Minor error message fix * Move result related api into sim * refactor qir_runtime, split out qir_sim * Use BigUint for sparse sim indexing * Type updates, sparse nearly_zero fix * Consolidation and optimization of sparse sim * Rust qubit/result to string fixes * Fullstate sim as QIR backend, staticlib runtime * rename runtime to stdlib * rename sim to backend * remove conditionals support * Fix public module ref * Update cmdline tool to use simulator QIR backend * Use whole-archive linking on non-Windows * Use thread_local for simulator init * Use all_load on MacOS * Skip QIR libs,include for runtime tool * Use force_load on MacOs * Try alternate MacOS linking strategy * Use full path in MacOs linking * Try explicit lib name * Clean up and comments * Remove QIR C++ runtime, rename folders * Use environment variables in native sim build * Put back Tools tests * Remove tracer test from solution * Remove unused qir_backend * Include full state sim lib in nuget package * Update array unit tests * Update bigint unit tests * Update range unit tests * Update strings unit tests * __quantum__rt__fail will print string before panic * Fix broken readme links * Fix formatting in arrays.rs * Clarifying comments, minor fixes from initial review * Fix formatting * Add `__quantum__rt__message_record_output` * Clean up file encoding for .cargo/config.toml * Updating script patterns * Updates from PR feedback * Fix double free typo in tests * More updates from PR feedback * Remove leftover debug line --- .gitignore | 1 + Cargo.toml | 2 + README.md | 2 - Simulation.sln | 22 - bootstrap.ps1 | 18 +- build/build.ps1 | 6 +- build/manifest.ps1 | 6 +- build/pack.ps1 | 3 + build/set-env.ps1 | 10 + build/steps-codecheck.yml | 14 +- build/steps-init.yml | 7 +- build/test.ps1 | 6 +- .../rust-toolchain => rust-toolchain | 0 src/Qir/CommandLineTool/Program.cs | 8 +- src/Qir/Common/Include/SimulatorStub.hpp | 137 --- .../Include/qsharp__foundation_internal.hpp | 25 - src/Qir/Common/cmake/unit_test_include.cmake | 7 +- src/Qir/Runtime/.cargo/config.toml | 2 + src/Qir/Runtime/CMakeLists.txt | 33 - src/Qir/Runtime/README.md | 197 --- src/Qir/Runtime/build-qir-runtime.ps1 | 33 - src/Qir/Runtime/build-qir-stdlib.ps1 | 61 + src/Qir/Runtime/lib/CMakeLists.txt | 5 - .../Runtime/lib/QIR/BasicRuntimeDriver.cpp | 75 -- src/Qir/Runtime/lib/QIR/CMakeLists.txt | 60 - src/Qir/Runtime/lib/QIR/Output.cpp | 26 - src/Qir/Runtime/lib/QIR/OutputStream.cpp | 46 - src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp | 87 -- src/Qir/Runtime/lib/QIR/QubitManager.cpp | 483 -------- src/Qir/Runtime/lib/QIR/README.md | 59 - .../Runtime/lib/QIR/allocationsTracker.cpp | 83 -- .../Runtime/lib/QIR/allocationsTracker.hpp | 28 - src/Qir/Runtime/lib/QIR/arrays.cpp | 683 ----------- src/Qir/Runtime/lib/QIR/callables.cpp | 471 -------- src/Qir/Runtime/lib/QIR/context.cpp | 131 -- src/Qir/Runtime/lib/QIR/delegated.cpp | 157 --- src/Qir/Runtime/lib/QIR/strings.cpp | 187 --- src/Qir/Runtime/lib/QIR/utils.cpp | 36 - src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 44 - src/Qir/Runtime/lib/QSharpCore/README.md | 26 - src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp | 192 --- .../Runtime/lib/QSharpCore/intrinsicsDump.cpp | 23 - .../lib/QSharpCore/qsharp__core__qis.hpp | 63 - .../QSharpFoundation/AssertMeasurement.cpp | 60 - .../lib/QSharpFoundation/CMakeLists.txt | 49 - .../Runtime/lib/QSharpFoundation/README.md | 37 - .../lib/QSharpFoundation/conditionals.cpp | 44 - .../lib/QSharpFoundation/intrinsicsMath.cpp | 185 --- .../qsharp__foundation__qis.hpp | 65 - src/Qir/Runtime/lib/README.md | 62 - src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 21 - .../lib/Simulators/FullstateSimulator.cpp | 654 ---------- src/Qir/Runtime/lib/Simulators/README.md | 51 - .../lib/Simulators/ToffoliSimulator.cpp | 257 ---- src/Qir/Runtime/lib/Tracer/CMakeLists.txt | 41 - src/Qir/Runtime/lib/Tracer/README.md | 218 ---- src/Qir/Runtime/lib/Tracer/TracerInternal.hpp | 18 - .../Runtime/lib/Tracer/layering_example.png | Bin 26209 -> 0 bytes src/Qir/Runtime/lib/Tracer/tracer-qis.cpp | 72 -- src/Qir/Runtime/lib/Tracer/tracer-qis.hpp | 31 - src/Qir/Runtime/lib/Tracer/tracer.cpp | 382 ------ src/Qir/Runtime/lib/Tracer/tracer.hpp | 220 ---- src/Qir/Runtime/prerequisites.ps1 | 26 +- .../public/BasicRuntimeDriverFactory.h | 21 - src/Qir/Runtime/public/CoreDefines.h | 14 - src/Qir/Runtime/public/CoreTypes.hpp | 49 - src/Qir/Runtime/public/OutputStream.hpp | 41 - src/Qir/Runtime/public/QSharpSimApi_I.hpp | 92 -- src/Qir/Runtime/public/QirContext.h | 18 - src/Qir/Runtime/public/QirContext.hpp | 69 -- src/Qir/Runtime/public/QirOutputHandling.hpp | 88 -- src/Qir/Runtime/public/QirRuntime.hpp | 317 ----- src/Qir/Runtime/public/QirRuntimeApi_I.hpp | 59 - src/Qir/Runtime/public/QirTypes.hpp | 204 ---- src/Qir/Runtime/public/QubitManager.hpp | 255 ---- src/Qir/Runtime/public/README.md | 57 - src/Qir/Runtime/public/SimFactory.h | 21 - src/Qir/Runtime/public/SimFactory.hpp | 23 - src/Qir/Runtime/public/TracerTypes.hpp | 20 - src/Qir/Runtime/qir.png | Bin 37446 -> 0 bytes src/Qir/Runtime/stdlib/Cargo.toml | 15 + src/Qir/Runtime/stdlib/build.rs | 59 + src/Qir/Runtime/stdlib/include/qir_stdlib.def | 82 ++ src/Qir/Runtime/stdlib/include/qir_stdlib.h | 243 ++++ src/Qir/Runtime/stdlib/src/arrays.rs | 303 +++++ src/Qir/Runtime/stdlib/src/bigints.rs | 269 +++++ .../{lib/QIR => stdlib/src}/bridge-rt.ll | 81 +- src/Qir/Runtime/stdlib/src/callables.rs | 170 +++ src/Qir/Runtime/stdlib/src/lib.rs | 123 ++ src/Qir/Runtime/stdlib/src/math.rs | 118 ++ .../Runtime/stdlib/src/output_recording.rs | 52 + src/Qir/Runtime/stdlib/src/range_support.rs | 199 +++ src/Qir/Runtime/stdlib/src/strings.rs | 383 ++++++ src/Qir/Runtime/stdlib/src/tuples.rs | 129 ++ src/Qir/Runtime/test-qir-runtime.ps1 | 13 - src/Qir/Runtime/test-qir-stdlib.ps1 | 26 + src/Qir/Runtime/unittests/CMakeLists.txt | 32 - .../unittests/QirOutputHandlingTests.cpp | 238 ---- src/Qir/Runtime/unittests/QirRuntimeTests.cpp | 1075 ----------------- .../Runtime/unittests/QubitManagerTests.cpp | 341 ------ src/Qir/Runtime/unittests/ToffoliTests.cpp | 130 -- src/Qir/Runtime/unittests/TracerTests.cpp | 482 -------- src/Qir/Runtime/unittests/driver.cpp | 7 - src/Qir/Samples/CMakeLists.txt | 8 +- .../StandaloneInputReference/CMakeLists.txt | 14 +- .../StandaloneInputReference/qir-driver.cpp | 52 +- src/Qir/Tests/.clang-tidy | 3 - src/Qir/Tests/CMakeLists.txt | 15 +- .../Tests/FullstateSimulator/CMakeLists.txt | 10 +- .../FullstateSimulatorTests.cpp | 415 ------- src/Qir/Tests/QIR-dynamic/CMakeLists.txt | 15 +- src/Qir/Tests/QIR-dynamic/qir-driver.cpp | 151 --- src/Qir/Tests/QIR-static/CMakeLists.txt | 11 +- .../QIR-static}/FloatUtils.hpp | 0 src/Qir/Tests/QIR-static/qir-driver.cpp | 255 +--- .../QIR-static/qir-test-conditionals.cpp | 163 --- src/Qir/Tests/QIR-static/qir-test-math.cpp | 158 --- src/Qir/Tests/QIR-static/qir-test-noqsharp.ll | 48 - src/Qir/Tests/QIR-static/qir-test-other.cpp | 14 +- src/Qir/Tests/QIR-static/qir-test-ouput.cpp | 29 - .../qsharp/qir-test-conditionals.qs | 66 - src/Qir/Tests/QIR-tracer/CMakeLists.txt | 30 - src/Qir/Tests/QIR-tracer/generate.py | 46 - .../Tests/QIR-tracer/qir-tracer-driver.cpp | 49 - .../QIR-tracer/qsharp/tracer-conditionals.qs | 26 - .../Tests/QIR-tracer/qsharp/tracer-core.qs | 30 - .../QIR-tracer/qsharp/tracer-intrinsics.qs | 64 - .../Tests/QIR-tracer/qsharp/tracer-qir.csproj | 11 - .../Tests/QIR-tracer/qsharp/tracer-target.qs | 280 ----- src/Qir/Tests/QIR-tracer/tracer-config.cpp | 18 - src/Qir/Tests/QIR-tracer/tracer-config.hpp | 20 - src/Qir/Tests/TestUtils.cpp | 15 - src/Qir/Tests/TestUtils.hpp | 11 - .../Tests/Tools/QirDriverGeneratorTests.cs | 41 +- .../FullStateDriverGenerator/UseBoolArg.cpp | 33 +- .../UseBoolArrayArg.cpp | 33 +- .../FullStateDriverGenerator/UseDoubleArg.cpp | 33 +- .../UseDoubleArrayArg.cpp | 33 +- .../UseIntegerArg.cpp | 33 +- .../UseIntegerArrayArg.cpp | 33 +- .../FullStateDriverGenerator/UseMiscArgs.cpp | 33 +- .../FullStateDriverGenerator/UseNoArgs.cpp | 33 +- .../UseNoArgsDebug.cpp | 67 - .../FullStateDriverGenerator/UsePauliArg.cpp | 54 +- .../UsePauliArrayArg.cpp | 54 +- .../FullStateDriverGenerator/UseRangeArg.cpp | 33 +- .../UseRangeArrayArg.cpp | 33 +- .../FullStateDriverGenerator/UseResultArg.cpp | 33 +- .../UseResultArrayArg.cpp | 33 +- .../FullStateDriverGenerator/UseStringArg.cpp | 33 +- ...Microsoft.Quantum.Qir.Runtime.Tools.csproj | 3 - src/Qir/Tests/build-qir-tests.ps1 | 1 - src/Qir/Tools/Driver/QirCppDriver.cs | 41 +- src/Qir/Tools/Driver/QirCppDriver.tt | 42 +- .../QirCppFullStateSimulatorInitializer.cs | 303 ----- .../QirCppFullStateSimulatorInitializer.tt | 3 - .../QirCppFullStateSimulatorInitializerEx.cs | 9 - src/Qir/Tools/Driver/QirCppInterop.cs | 6 +- .../Driver/QirFullStateDriverGenerator.cs | 4 +- .../QirFullStateSimulatorInitializer.cs | 10 +- .../Executable/QirFullStateExecutable.cs | 13 +- ...Microsoft.Quantum.Qir.Runtime.Tools.csproj | 19 - src/Qir/Tools/QirTools.cs | 6 +- src/Qir/build_all.ps1 | 1 - src/Qir/test_all.ps1 | 3 - src/README.md | 5 - src/Simulation/Common/Simulators.Dev.props | 1 + .../Native/build-native-simulator.ps1 | 4 +- src/Simulation/Native/src/CMakeLists.txt | 41 +- src/Simulation/Native/src/simulator/capi.cpp | 5 + src/Simulation/Native/src/simulator/capi.hpp | 1 + src/Simulation/Native/src/simulator/qir.cpp | 499 ++++++++ src/Simulation/Native/src/simulator/qir.hpp | 123 ++ .../src/simulator/simulatorinterface.hpp | 1 + .../qdk_sim_rs/build-qdk-sim-rs.ps1 | 2 +- src/Simulation/qdk_sim_rs/prerequisites.ps1 | 4 +- .../src/linalg/decompositions/eig.rs | 7 +- src/Simulation/qdk_sim_rs/src/math/sp_func.rs | 2 +- .../src/processes/generators/mod.rs | 22 +- .../qdk_sim_rs/tests/serialization_tests.rs | 2 +- 180 files changed, 3172 insertions(+), 12659 deletions(-) rename src/Simulation/qdk_sim_rs/rust-toolchain => rust-toolchain (100%) delete mode 100644 src/Qir/Common/Include/SimulatorStub.hpp delete mode 100644 src/Qir/Common/Include/qsharp__foundation_internal.hpp create mode 100644 src/Qir/Runtime/.cargo/config.toml delete mode 100644 src/Qir/Runtime/CMakeLists.txt delete mode 100644 src/Qir/Runtime/README.md delete mode 100644 src/Qir/Runtime/build-qir-runtime.ps1 create mode 100644 src/Qir/Runtime/build-qir-stdlib.ps1 delete mode 100644 src/Qir/Runtime/lib/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QIR/Output.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/OutputStream.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/QubitManager.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/README.md delete mode 100644 src/Qir/Runtime/lib/QIR/allocationsTracker.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/allocationsTracker.hpp delete mode 100644 src/Qir/Runtime/lib/QIR/arrays.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/callables.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/context.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/delegated.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/strings.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/utils.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QSharpCore/README.md delete mode 100644 src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/README.md delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp delete mode 100644 src/Qir/Runtime/lib/README.md delete mode 100644 src/Qir/Runtime/lib/Simulators/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp delete mode 100644 src/Qir/Runtime/lib/Simulators/README.md delete mode 100644 src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp delete mode 100644 src/Qir/Runtime/lib/Tracer/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/Tracer/README.md delete mode 100644 src/Qir/Runtime/lib/Tracer/TracerInternal.hpp delete mode 100644 src/Qir/Runtime/lib/Tracer/layering_example.png delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer-qis.cpp delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer-qis.hpp delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer.cpp delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer.hpp delete mode 100644 src/Qir/Runtime/public/BasicRuntimeDriverFactory.h delete mode 100644 src/Qir/Runtime/public/CoreDefines.h delete mode 100644 src/Qir/Runtime/public/CoreTypes.hpp delete mode 100644 src/Qir/Runtime/public/OutputStream.hpp delete mode 100644 src/Qir/Runtime/public/QSharpSimApi_I.hpp delete mode 100644 src/Qir/Runtime/public/QirContext.h delete mode 100644 src/Qir/Runtime/public/QirContext.hpp delete mode 100644 src/Qir/Runtime/public/QirOutputHandling.hpp delete mode 100644 src/Qir/Runtime/public/QirRuntime.hpp delete mode 100644 src/Qir/Runtime/public/QirRuntimeApi_I.hpp delete mode 100644 src/Qir/Runtime/public/QirTypes.hpp delete mode 100644 src/Qir/Runtime/public/QubitManager.hpp delete mode 100644 src/Qir/Runtime/public/README.md delete mode 100644 src/Qir/Runtime/public/SimFactory.h delete mode 100644 src/Qir/Runtime/public/SimFactory.hpp delete mode 100644 src/Qir/Runtime/public/TracerTypes.hpp delete mode 100644 src/Qir/Runtime/qir.png create mode 100644 src/Qir/Runtime/stdlib/Cargo.toml create mode 100644 src/Qir/Runtime/stdlib/build.rs create mode 100644 src/Qir/Runtime/stdlib/include/qir_stdlib.def create mode 100644 src/Qir/Runtime/stdlib/include/qir_stdlib.h create mode 100644 src/Qir/Runtime/stdlib/src/arrays.rs create mode 100644 src/Qir/Runtime/stdlib/src/bigints.rs rename src/Qir/Runtime/{lib/QIR => stdlib/src}/bridge-rt.ll (71%) create mode 100644 src/Qir/Runtime/stdlib/src/callables.rs create mode 100644 src/Qir/Runtime/stdlib/src/lib.rs create mode 100644 src/Qir/Runtime/stdlib/src/math.rs create mode 100644 src/Qir/Runtime/stdlib/src/output_recording.rs create mode 100644 src/Qir/Runtime/stdlib/src/range_support.rs create mode 100644 src/Qir/Runtime/stdlib/src/strings.rs create mode 100644 src/Qir/Runtime/stdlib/src/tuples.rs delete mode 100644 src/Qir/Runtime/test-qir-runtime.ps1 create mode 100644 src/Qir/Runtime/test-qir-stdlib.ps1 delete mode 100644 src/Qir/Runtime/unittests/CMakeLists.txt delete mode 100644 src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp delete mode 100644 src/Qir/Runtime/unittests/QirRuntimeTests.cpp delete mode 100644 src/Qir/Runtime/unittests/QubitManagerTests.cpp delete mode 100644 src/Qir/Runtime/unittests/ToffoliTests.cpp delete mode 100644 src/Qir/Runtime/unittests/TracerTests.cpp delete mode 100644 src/Qir/Runtime/unittests/driver.cpp delete mode 100644 src/Qir/Tests/.clang-tidy rename src/Qir/{Common/Include => Tests/QIR-static}/FloatUtils.hpp (100%) delete mode 100644 src/Qir/Tests/QIR-static/qir-test-conditionals.cpp delete mode 100644 src/Qir/Tests/QIR-static/qir-test-noqsharp.ll delete mode 100644 src/Qir/Tests/QIR-static/qir-test-ouput.cpp delete mode 100644 src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs delete mode 100644 src/Qir/Tests/QIR-tracer/CMakeLists.txt delete mode 100644 src/Qir/Tests/QIR-tracer/generate.py delete mode 100644 src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs delete mode 100644 src/Qir/Tests/QIR-tracer/tracer-config.cpp delete mode 100644 src/Qir/Tests/QIR-tracer/tracer-config.hpp delete mode 100644 src/Qir/Tests/TestUtils.cpp delete mode 100644 src/Qir/Tests/TestUtils.hpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp delete mode 100644 src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs delete mode 100644 src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt delete mode 100644 src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs delete mode 100644 src/Qir/build_all.ps1 delete mode 100644 src/Qir/test_all.ps1 create mode 100644 src/Simulation/Native/src/simulator/qir.cpp create mode 100644 src/Simulation/Native/src/simulator/qir.hpp diff --git a/.gitignore b/.gitignore index a012f955a9c..4d84dbe7f05 100644 --- a/.gitignore +++ b/.gitignore @@ -355,6 +355,7 @@ out/ # Ignore drops from building native simulators. xplat src/Simulation/Native/win10/Microsoft.Quantum.Simulator.Runtime.dll +src/Simulation/Native/win10/Microsoft.Quantum.Simulator.Runtime.lib src/Simulation/Native/linux/libMicrosoft.Quantum.Simulator.Runtime.so src/Simulation/Native/osx/libMicrosoft.Quantum.Simulator.Runtime.dylib src/Simulation/Native/win10/Microsoft.Quantum.SparseSimulator.Runtime.dll diff --git a/Cargo.toml b/Cargo.toml index 2a73b23a1ad..fccdcff7ac1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,11 @@ members = [ "src/Simulation/qdk_sim_rs", + "src/Qir/Runtime/stdlib", ] [profile.release] +debug = true codegen-units = 1 # Reduce number of codegen units to increase optimizations. opt-level = 3 panic = 'unwind' diff --git a/README.md b/README.md index 3978cceb071..2ca038490c5 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ You may also visit our [Quantum](https://github.com/microsoft/quantum) repositor Note that when building from source, this repository is configured so that .NET will automatically look at the [Quantum Development Kit prerelease feed](https://dev.azure.com/ms-quantum-public/Microsoft%20Quantum%20(public)/_packaging?_a=feed&feed=alpha) in addition to any other feeds you may have configured. -Building **QIR Runtime** isn't enabled by default yet. Please see [its readme](./src/Qir/Runtime/README.md) for details. - ### All platforms ### 1. Install the pre-reqs: diff --git a/Simulation.sln b/Simulation.sln index 0259b6f8eff..eac73692ab4 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -93,10 +93,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-static", "QIR-static", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-gen", "src\Qir\Tests\QIR-static\qsharp\qir-gen.csproj", "{33ED37AB-61B1-4A49-9952-58D19AA8EF30}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-tracer", "QIR-tracer", "{522EAA31-6317-42D5-831F-C39313DF03A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tracer-qir", "src\Qir\Tests\QIR-tracer\qsharp\tracer-qir.csproj", "{002174F4-BFA8-4675-908D-0E9C32ED951A}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{AAFB81D3-BC87-404D-BA64-AF40B2D2E45A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandaloneInputReference", "StandaloneInputReference", "{A7DB7367-9FD6-4164-8263-A05077BE54AB}" @@ -772,22 +768,6 @@ Global {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|x64.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|x64.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|Any CPU.Build.0 = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|x64.ActiveCfg = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|x64.Build.0 = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1039,8 +1019,6 @@ Global {5DBF6402-D9CD-4470-A309-3755CFDA42CF} = {3DFACF7F-D5C2-455B-AB8A-26908DEF7E2D} {54000816-122C-4AA0-9FE9-B0ABB9547232} = {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} {33ED37AB-61B1-4A49-9952-58D19AA8EF30} = {54000816-122C-4AA0-9FE9-B0ABB9547232} - {522EAA31-6317-42D5-831F-C39313DF03A0} = {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} - {002174F4-BFA8-4675-908D-0E9C32ED951A} = {522EAA31-6317-42D5-831F-C39313DF03A0} {AAFB81D3-BC87-404D-BA64-AF40B2D2E45A} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {A7DB7367-9FD6-4164-8263-A05077BE54AB} = {AAFB81D3-BC87-404D-BA64-AF40B2D2E45A} {D7D34736-A719-4B45-A33F-2723F59EC29D} = {A7DB7367-9FD6-4164-8263-A05077BE54AB} diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 302106c3be3..472344c779f 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + $ErrorActionPreference = 'Stop' Push-Location (Join-Path $PSScriptRoot "build") @@ -13,6 +15,14 @@ Push-Location (Join-Path $PSScriptRoot "./src/Simulation/qdk_sim_rs") Pop-Location if (-not (Test-Path Env:/AGENT_OS)) { # If not CI build, i.e. local build (if AGENT_OS envvar is not defined) + if ($Env:ENABLE_QIRRUNTIME -ne "false") { + Write-Host "Build release flavor of the QIR standard library" + $Env:BUILD_CONFIGURATION = "Release" + Push-Location (Join-Path $PSScriptRoot "src/Qir/Runtime") + .\build-qir-stdlib.ps1 + Pop-Location + $Env:BUILD_CONFIGURATION = $null + } if ($Env:ENABLE_NATIVE -ne "false") { $Env:BUILD_CONFIGURATION = "Release" Write-Host "Build release flavor of the full state simulator" @@ -35,14 +45,6 @@ if (-not (Test-Path Env:/AGENT_OS)) { # If no Pop-Location $Env:BUILD_CONFIGURATION = $null } - if ($Env:ENABLE_QIRRUNTIME -ne "false") { - Write-Host "Build release flavor of the QIR Runtime" - $Env:BUILD_CONFIGURATION = "Release" - Push-Location (Join-Path $PSScriptRoot "src/Qir/Runtime") - .\build-qir-runtime.ps1 - Pop-Location - $Env:BUILD_CONFIGURATION = $null - } Write-Host "Build simulation solution" dotnet build Simulation.sln diff --git a/build/build.ps1 b/build/build.ps1 index ab02fd81e7a..29df268aa83 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -1,14 +1,16 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + $ErrorActionPreference = 'Stop' & "$PSScriptRoot/set-env.ps1" $all_ok = $True if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirRuntime/build-qir-runtime.ps1" + $qirStdLibPath = (Join-Path $PSScriptRoot .. src Qir Runtime build-qir-stdlib.ps1) + & $qirStdLibPath if ($LastExitCode -ne 0) { $script:all_ok = $False } diff --git a/build/manifest.ps1 b/build/manifest.ps1 index c36ac237180..8e1b368e8f0 100644 --- a/build/manifest.ps1 +++ b/build/manifest.ps1 @@ -54,11 +54,7 @@ $artifacts = @{ Native = @( ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Simulator.Runtime.dll", - ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.SparseSimulator.Runtime.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.QSharp.Core.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.QSharp.Foundation.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.Runtime.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.Tracer.dll" + ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.SparseSimulator.Runtime.dll" ) | ForEach-Object { Join-Path $PSScriptRoot (Join-Path ".." $_) }; } diff --git a/build/pack.ps1 b/build/pack.ps1 index cabc7be4dfc..61edd997681 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -25,6 +25,9 @@ Push-Location (Join-Path $PSScriptRoot ../src/Simulation/Native) If (Test-Path "$DROP/Microsoft.Quantum.Simulator.Runtime.dll") { Copy-Item -Verbose "$DROP/Microsoft.Quantum.Simulator.Runtime.dll" "win10/Microsoft.Quantum.Simulator.Runtime.dll" } + If (Test-Path "$DROP/Microsoft.Quantum.Simulator.Runtime.lib") { + Copy-Item -Verbose "$DROP/Microsoft.Quantum.Simulator.Runtime.lib" "win10/Microsoft.Quantum.Simulator.Runtime.lib" + } $DROP = "$Env:DROP_NATIVE/src/Simulation/NativeSparseSimulator/build" Write-Host "##[info]Copying NativeSparseSimulator files from $DROP..."; diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 76cd12dd28b..fe92c6c23b8 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + $ErrorActionPreference = 'Stop' Write-Host "Setting up build environment variables" @@ -39,10 +41,18 @@ 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) } +$env:BUILD_PLATFORM = "win-x64" +if ($IsLinux) { + $env:BUILD_PLATFORM = "linux-x64" +} elseif ($IsMacOS) { + $env:BUILD_PLATFORM = "osx-x64" +} + Get-ChildItem -Path Env:/* -Include @( "BUILD_BUILDNUMBER", "BUILD_CONFIGURATION", "BUILD_VERBOSITY", + "BUILD_PLATFORM", "ASSEMBLY_VERSION", "PYTHON_VERSION", "NUGET_VERSION", diff --git a/build/steps-codecheck.yml b/build/steps-codecheck.yml index d1bc37cc4f7..f85d8dce325 100644 --- a/build/steps-codecheck.yml +++ b/build/steps-codecheck.yml @@ -14,13 +14,13 @@ steps: packageType: sdk version: '6.0.x' - # QIR Runtime: + # QIR stdlib: - pwsh: src/Qir/Runtime/prerequisites.ps1 - displayName: "Install QIR Runtime Prerequisites" + displayName: "Install QIR stdlib Prerequisites" workingDirectory: $(System.DefaultWorkingDirectory) -- pwsh: src/Qir/Runtime/build-qir-runtime.ps1 - displayName: "Build QIR Runtime" +- pwsh: src/Qir/Runtime/build-qir-stdlib.ps1 + displayName: "Build QIR stdlib" workingDirectory: $(System.DefaultWorkingDirectory) # Native Simulator (needed to build and run the QIR tests): @@ -33,9 +33,9 @@ steps: displayName: "Build Native Simulator" workingDirectory: $(System.DefaultWorkingDirectory)/src/Simulation/Native - # QIR Runtime Tests: -- pwsh: src/Qir/Runtime/test-qir-runtime.ps1 - displayName: "Test QIR Runtime" + # QIR stdlib Tests: +- pwsh: src/Qir/Runtime/test-qir-stdlib.ps1 + displayName: "Test QIR stdlib" workingDirectory: $(System.DefaultWorkingDirectory) # QIR Tests: diff --git a/build/steps-init.yml b/build/steps-init.yml index feb3e4cc351..044e9170344 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -26,10 +26,13 @@ steps: displayName: Windows install rust condition: eq( variables['Agent.OS'], 'Windows_NT' ) +# Installs Rust nightly toolchain and components. +# Note: the llvm-tools-preview component can be removed once QIR range support is simplified. +# See https://github.com/microsoft/qsharp-language/issues/108 - script: | rustup install nightly - rustup component add rustfmt clippy - rustup component add rustfmt clippy --toolchain nightly + rustup component add rustfmt clippy llvm-tools-preview + rustup component add rustfmt clippy llvm-tools-preview --toolchain nightly displayName: Enable Rust formatting and nightly options. ## diff --git a/build/test.ps1 b/build/test.ps1 index 09b08eb316d..955ba9c23c5 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + & "$PSScriptRoot/set-env.ps1" $all_ok = $True @@ -43,8 +45,8 @@ function Test-One { Test-One '../Simulation.sln' if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirRuntime/test-qir-runtime.ps1" + $qirStdLibPath = (Join-Path $PSScriptRoot .. src Qir Runtime test-qir-stdlib.ps1) + & $qirStdLibPath if ($LastExitCode -ne 0) { $script:all_ok = $False } diff --git a/src/Simulation/qdk_sim_rs/rust-toolchain b/rust-toolchain similarity index 100% rename from src/Simulation/qdk_sim_rs/rust-toolchain rename to rust-toolchain diff --git a/src/Qir/CommandLineTool/Program.cs b/src/Qir/CommandLineTool/Program.cs index 85a09c8cb59..7d329ad9b19 100644 --- a/src/Qir/CommandLineTool/Program.cs +++ b/src/Qir/CommandLineTool/Program.cs @@ -46,8 +46,7 @@ private static Command CreateBuildCommand() settings.QSharpDll, settings.LibraryDirectories, settings.IncludeDirectories, - settings.ExecutablesDirectory, - settings.Debug)), + settings.ExecutablesDirectory)), TreatUnmatchedTokensAsErrors = true }; @@ -153,11 +152,6 @@ public sealed class BuildOptions /// The path to the output directory where the created executables will be placed. /// public DirectoryInfo ExecutablesDirectory { get; set; } - - /// - /// Enable additional debugging checks at runtime. - /// - public bool Debug { get; set; } } } } diff --git a/src/Qir/Common/Include/SimulatorStub.hpp b/src/Qir/Common/Include/SimulatorStub.hpp deleted file mode 100644 index b6f705345ab..00000000000 --- a/src/Qir/Common/Include/SimulatorStub.hpp +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct SimulatorStub - : public IRuntimeDriver - , public IQuantumGateSet - { - QubitIdType AllocateQubit() override - { - throw std::logic_error("not_implemented: AllocateQubit"); - } - void ReleaseQubit(QubitIdType /* qubit */) override - { - throw std::logic_error("not_implemented: ReleaseQubit"); - } - virtual std::string QubitToString(QubitIdType /* qubit */) override - { - throw std::logic_error("not_implemented: QubitToString"); - } - void X(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: X"); - } - void Y(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: Y"); - } - void Z(QubitIdType /* target */) override - { - throw std::logic_error("not_implemented: Z"); - } - void H(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: H"); - } - void S(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: S"); - } - void T(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: T"); - } - void R(PauliId /* axis */, QubitIdType /* target */, double /* theta */) override - { - throw std::logic_error("not_implemented: R"); - } - void Exp(long /* numTargets */, PauliId* /* paulis */, QubitIdType* /* targets */, double /* theta */) override - { - throw std::logic_error("not_implemented: Exp"); - } - void ControlledX(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledX"); - } - void ControlledY(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledY"); - } - void ControlledZ(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledZ"); - } - void ControlledH(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledH"); - } - void ControlledS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledS"); - } - void ControlledT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledT"); - } - void ControlledR(long /*numControls*/, QubitIdType* /*controls*/, PauliId /*axis*/, QubitIdType /*target*/, - double /*theta*/) override - { - throw std::logic_error("not_implemented: ControlledR"); - } - void ControlledExp(long /*numControls*/, QubitIdType* /*controls*/, long /*numTargets*/, PauliId* /*paulis*/, - QubitIdType* /*targets*/, double /*theta*/) override - { - throw std::logic_error("not_implemented: ControlledExp"); - } - void AdjointS(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: AdjointS"); - } - void AdjointT(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: AdjointT"); - } - void ControlledAdjointS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledAdjointS"); - } - void ControlledAdjointT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledAdjointT"); - } - Result Measure(long /*numBases*/, PauliId* /*bases*/, long /*numTargets*/, QubitIdType* /*targets*/) override - { - throw std::logic_error("not_implemented: Measure"); - } - void ReleaseResult(Result /*result*/) override - { - throw std::logic_error("not_implemented: ReleaseResult"); - } - bool AreEqualResults(Result /*r1*/, Result /*r2*/) override - { - throw std::logic_error("not_implemented: AreEqualResults"); - } - ResultValue GetResultValue(Result /*result*/) override - { - throw std::logic_error("not_implemented: GetResultValue"); - } - Result UseZero() override - { - throw std::logic_error("not_implemented: UseZero"); - } - Result UseOne() override - { - throw std::logic_error("not_implemented: UseOne"); - } - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Common/Include/qsharp__foundation_internal.hpp b/src/Qir/Common/Include/qsharp__foundation_internal.hpp deleted file mode 100644 index df2e844e5a1..00000000000 --- a/src/Qir/Common/Include/qsharp__foundation_internal.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// To be included by the QIS implementation and QIS tests only. -// Not to be included by parties outside QIS. - -#include "CoreTypes.hpp" - -// For test purposes only: -namespace Quantum // Replace with `namespace Quantum::Qis::Internal` after migration to C++17. -{ -namespace Qis -{ - namespace Internal - { - QIR_SHARED_API extern char const excStrDrawRandomVal[]; - - QIR_SHARED_API void RandomizeSeed(bool randomize); - QIR_SHARED_API int64_t GetLastGeneratedRandomI64(); - QIR_SHARED_API double GetLastGeneratedRandomDouble(); - } // namespace Internal -} // namespace Qis -} // namespace Quantum diff --git a/src/Qir/Common/cmake/unit_test_include.cmake b/src/Qir/Common/cmake/unit_test_include.cmake index 93701f563b0..ed8256eb1d2 100644 --- a/src/Qir/Common/cmake/unit_test_include.cmake +++ b/src/Qir/Common/cmake/unit_test_include.cmake @@ -14,9 +14,10 @@ macro(add_unit_test target) set(TEST_DEPS2 "${CMAKE_BINARY_DIR}/bin") set(TEST_DEPS3 "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") + set(TEST_DEPS4 "${PROJECT_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") set_property(TEST ${target} PROPERTY ENVIRONMENT - "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${LD_LIBRARY_PATH}" - "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${TEST_DEPS3}\;${PATH}" - "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${DYLD_LIBRARY_PATH}" + "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${TEST_DEPS4}:${LD_LIBRARY_PATH}" + "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${TEST_DEPS3}\;${TEST_DEPS4}\;${PATH}" + "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${TEST_DEPS4}:${DYLD_LIBRARY_PATH}" ) endmacro(add_unit_test) diff --git a/src/Qir/Runtime/.cargo/config.toml b/src/Qir/Runtime/.cargo/config.toml new file mode 100644 index 00000000000..cb0dc23f903 --- /dev/null +++ b/src/Qir/Runtime/.cargo/config.toml @@ -0,0 +1,2 @@ +[profile.release] +panic = 'abort' diff --git a/src/Qir/Runtime/CMakeLists.txt b/src/Qir/Runtime/CMakeLists.txt deleted file mode 100644 index d5379352dd3..00000000000 --- a/src/Qir/Runtime/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -cmake_minimum_required(VERSION 3.20 FATAL_ERROR) - -message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") - -# Load common utils and configure cmake policies -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../Common/cmake") -include(secure_dependencies) -set_msvc_static_runtime_policy() - -# set the project name and version -project(qirruntime) - -# specify the C++ standard, compiler and other tools -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -locate_win32_spectre_static_runtime() -configure_security_flags() - -# feel free to customize these flags for your local builds (don't check in) -set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-inline") - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") - -set(public_includes "${PROJECT_SOURCE_DIR}/public") -set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") - -include(qir_cmake_include) - -add_subdirectory(lib) -add_subdirectory(unittests) diff --git a/src/Qir/Runtime/README.md b/src/Qir/Runtime/README.md deleted file mode 100644 index 93b36db8d79..00000000000 --- a/src/Qir/Runtime/README.md +++ /dev/null @@ -1,197 +0,0 @@ -# The Native QIR Runtime - -This folder contains the Quantum Intermediate Representation (QIR) Runtime project. The QIR is a subset of the [LLVM](https://llvm.org/) Intermediate Representation. -The QIR runtime includes an implementation of the - [QIR specification](https://github.com/qir-alliance/qir-spec) and the bridge to - run QIR against the native full state simulator. - -- `public` folder contains the public headers -- `lib` folder contains the implementation of the runtime and the simulators. -- `unittests` folder contains tests for the runtime -- `Externals` folder contains external dependencies. We'll strive to keep those minimal. - -## Build - -### Prerequisites - -The QirRuntime project is using CMake (3.17) + Ninja(1.10.0) + Clang++(14). Other versions of the tools might work - but haven't been tested. Only x64 architecture is supported. -For running the PowerShell scripts below use -[PowerShell Core or PowerShell 7+ (`pwsh`)](https://github.com/PowerShell/PowerShell), not the inbox PowerShell. - -To install prerequisite tools for building the QIR runtime, you can set the `ENABLE_QIRRUNTIME` environment variable to the string `"true"` -and run [`prerequisites.ps1`](prerequisites.ps1), or manually install pre-reqs with the steps listed below. -Note that on Windows, this script relies on the [Chocolatey package manager](https://chocolatey.org/), -while on macOS, `prerequisites.ps1` relies on the [`brew` package manager](https://brew.sh). - -#### Windows pre-reqs - -1. Install Clang 14, Ninja and CMake from the public distros. -1. Add all three to your/system `%PATH%`. -1. Install VS 2019 and enable "Desktop development with C++" component (Clang uses MSVC's standard library on Windows). -1. Install clang-tidy and clang-format if your Clang/LLVM packages didn't include the tools. -1. Install the same version of dotnet as specified by qsharp-runtime [README](../../../README.md) - -*Building from Visual Studio and VS Code is **not** supported. -Running cmake from the editors will likely default to MSVC or clang-cl and fail.* - -#### Linux via WSL pre-reqs - -1. On the host Windows machine [enable WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and install - Ubuntu 20.04 LTS. -1. In the Ubuntu's terminal: - 1. `$ sudo apt install cmake` (`$ cmake --version` should return 3.16.3) - 1. `$ sudo apt-get install ninja-build` (`$ ninja --version` should return 1.10.0) - 1. `$ sudo apt install clang-14` (`$ clang++-14 --version` should return 14.0.0 or newer) - 1. Set Clang as the preferred C/C++ compiler: - - $ export CC=/usr/bin/clang-14 - - $ export CXX=/usr/bin/clang++-14 - 1. `$ sudo apt install clang-tidy-14` (`$ clang-tidy-14 --version` should return 'LLVM version 14.0.0' or newer) - 1. Install the same version of dotnet as specified by qsharp-runtime [README](../../../README.md) - -See [https://code.visualstudio.com/docs/remote/wsl] on how to use VS Code with WSL. - -#### Other Prerequisites - -The build depends on `Microsoft.Quantum.Simulator.Runtime` dynamic library built at a higher level of the directory tree. -To build that library follow the instructions in [`qsharp-runtime/README.md`](../../../README.md#building-from-source) -(up to and including the step `Simulation.sln`). - - -### Build Commands - -To build QirRuntime you can run [`build-qir-runtime.ps1`](build-qir-runtime.ps1) script from QirRuntime folder: -```batch -pwsh build-qir-runtime.ps1 -``` - -The script will create the `build/{Debug|Release}` folder and place the build artifacts in it. The configuration `Debug|Release` -is specified with the `BUILD_CONFIGURATION` environment variable. -If the variable is not set then the default is specified in [`set-env.ps1`](../../../build/set-env.ps1). - -## Tests - -The tests in the `unittests` folder are those that are compiled directly against the object libraries -from the runtime, and verify behavior of the runtime using more than the public API. For tests that -verify behavior of the public API surface using compiled QIR from Q# projects, see the `src/Qir/Tests` folder. - -### Running All Tests - -```powershell -# Navigate to QirRuntime folder. - -pwsh test-qir-runtime.ps1 -``` - -### Running Test Binaries Individually - -` -help` provides details on how to run a subset of the tests and other options. For example, you can - filter tests from the "[skip]" category out by ` ~[skip]`. - -For tests that depend on the native simulator and qdk shared libraries, you might need to modify the corresponding - dynamic libraries lookup path environment variable: - -- (Windows) PATH -- (Unix) LD_LIBRARY_PATH -- (Darwin) DYLD_LIBRARY_PATH - -## QIR Bridge and Runtime - -This project contains an implementation of the QIR runtime per the - [QIR specifications](https://github.com/qir-alliance/qir-spec) and the translation - layer between the QIR and the IR, generated by Clang from the native code. Translation layer is called the "QIR Bridge". - -![QIR Bridge architecture diagram](qir.png?raw=true "QIR Bridge architecture diagram") - -This project also provides an implementation of the quantum instruction set, used by Q# for simulation against the full -state simulator: - -```llvm -operation Exp (paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Adj + Ctl -void @__quantum__qis__exp__body(%Array*, double, %Array*) -void @__quantum__qis__exp__adj(%Array*, double, %Array*) -void @__quantum__qis__exp__ctl(%Array*, { %Array*, double, %Array* }*) -void @__quantum__qis__exp__ctladj(%Array*, { %Array*, double, %Array* }*) -void @__quantum__qis__h__body(%Qubit*) -void @__quantum__qis__h__ctl(%Array*, %Qubit*) -%Result* @__quantum__qis__measure__body(%Array*, %Array*) -void @__quantum__qis__r__body(i2, double, %Qubit*) -void @__quantum__qis__r__adj(i2, double, %Qubit*) -void @__quantum__qis__r__ctl(%Array*, { i2, double, %Qubit* }*) -void @__quantum__qis__r__ctladj(%Array*, { i2, double, %Qubit* }*) -void @__quantum__qis__s__body(%Qubit*) -void @__quantum__qis__s__adj(%Qubit*) -void @__quantum__qis__s__ctl(%Array*, %Qubit*) -void @__quantum__qis__s__ctladj(%Array*, %Qubit*) -void @__quantum__qis__t__body(%Qubit*) -void @__quantum__qis__t__adj(%Qubit*) -void @__quantum__qis__t__ctl(%Array*, %Qubit*) -void @__quantum__qis__t__ctladj(%Array*, %Qubit*) -void @__quantum__qis__x__body(%Qubit*) -void @__quantum__qis__x__ctl(%Array*, %Qubit*) -void @__quantum__qis__y__body(%Qubit*) -void @__quantum__qis__y__ctl(%Array*, %Qubit*) -void @__quantum__qis__z__body(%Qubit*) -void @__quantum__qis__z__ctl(%Array*, %Qubit*) -``` - -There are two ways to compile and run the QIR files against the runtime. - -1. Link against the runtime libraries *statically*. For the example of this approach see `test/QIR-static` tests. It - allows the client to access the target simulator directly, if so desired. -1. Link against the *shared qdk* library. The example of this approach can be found in `test/QIR-dynamic` folder. In the - future we'll provide fully self-contained packages of the runtime to enable this workflow completely outside of the - current project. For now, this way of consuming QIR only supports running against the native full state simulator. - -QIR's architecture assumes a single target, whether that be hardware or a particular simulator. As a result, there is no - provision in the QIR specifications to choose a target dynamically. To connect QIR to the simulators from this runtime, - we provide `QirExecutionContext::Init()` (earlier `InitializeQirContext`) - and `QirExecutionContext::Deinit()` (earlier `ReleaseQirContext`) methods. - Switching contexts while executing QIR isn't supported and would yield undefined behavior. - -### Building from IR files - -CMake doesn't support using LLVM's IR files as input so instead we invoke Clang directly from custom commands to create - utility libs that can be linked into other targets using their absolute paths. - -*NB*: Compiling from IR has fewer checks than compiling from C++. For example, IR doesn't support overloading so - declarations and definitions of functions are matched by name, without taking into account the arguments. This means - that a build might succeed with mismatched signatures between caller/callee which will likely lead to crashes and other - bugs at runtime. - -**The QIR runtime is work in progress. Current known limitations are as follows:** - -1. All functionality related to BigInt type (including `__quantum__rt__bigint_to_string`) NYI. -1. QIR is assumed to be __single threaded__. No effort was made to make the bridge and runtime thread safe. -1. Strings are implemented as a thin wrapper over std::string with virtually no optimizations. -1. Variadic functions (e.g. `__quantum__rt__array_create`) require platform specific bridges. The currently implemented - bridge is for Windows. -1. Qubit borrowing NYI (needs both bridge and simulator's support). - -## Coding style and conventions - -If during compilation you see an error like this - -``` -##vso[task.logissue type=error;]Formatting check failed. The following files need to be formatted before compiling: -``` - -then this means that the edits you made violate the -[coding style](https://github.com/microsoft/qsharp-runtime/blob/main/src/Qir/.clang-format) -enforced by clang-format utility. To format the file install the -Clang-Format extension to your editor ([example for VSCode](https://clang.llvm.org/docs/ClangFormat.html#visual-studio-code-integration)), -open the file, press the corresponding formatting hot keys (for VSCode it is \), and save the file. -See more links in [.clang-format](https://github.com/microsoft/qsharp-runtime/blob/main/src/Qir/.clang-format) file. - -Most of our coding style and conventions are enforced via clang-tidy. The project is currently set up to treat - clang-tidy warnings as build errors and we'd like to keep it this way. If you absolutely need to violate the style, - mark the problematic line with `// NOLINT`. To suppress style checks in a whole folder, add .clang-tidy file into the - folder with checks reduced to `Checks: '-*,bugprone-*'`. - -Clang-tidy checks reference: [https://clang.llvm.org/extra/clang-tidy/checks/list.html] - -Conventions not covered by .clang-format and .clang-tidy: - -- fields of a class/struct must be placed at the top of the class/struct definition; -- must use `this` to access class and struct members: `this->fooBar`; -- Interface declarations should be placed in separate header files with "_I" suffix.: `FooBar_I.hpp`. diff --git a/src/Qir/Runtime/build-qir-runtime.ps1 b/src/Qir/Runtime/build-qir-runtime.ps1 deleted file mode 100644 index 947dacf5b55..00000000000 --- a/src/Qir/Runtime/build-qir-runtime.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -. (Join-Path $PSScriptRoot .. qir-utils.ps1) - -Write-Host "##[info]Compile QIR Runtime" - -& (Join-Path $PSScriptRoot ".." check-sources-formatted.ps1) -Path $PSScriptRoot -& (Join-Path $PSScriptRoot ".." check-sources-formatted.ps1) -Path (Join-Path $PSScriptRoot ".." Common) - -if (-not (Build-CMakeProject $PSScriptRoot "QIR Runtime")) { - throw "At least one project failed to compile. Check the logs." -} - -# Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so -# they can be included in pipeline artifacts. -$osDir = "win-x64" -if ($IsLinux) { - $osDir = "linux-x64" -} elseif ($IsMacOS) { - $osDir = "osx-x64" -} -$qirDropsBin = (Join-Path $Env:QIR_DROPS bin $osDir native) -$qirDropsInclude = (Join-Path $Env:QIR_DROPS include) -if (-not (Test-Path $Env:QIR_DROPS)) { - New-Item -Path $Env:QIR_DROPS -ItemType "directory" - New-Item -Path $qirDropsBin -ItemType "directory" - New-Item -Path $qirDropsInclude -ItemType "directory" -} -$qirBinaries = (Join-Path $PSScriptRoot bin $Env:BUILD_CONFIGURATION bin *) -$qirIncludes = (Join-Path $PSScriptRoot public *) -Copy-Item $qirBinaries $qirDropsBin -Exclude "*unittests*","*.Tracer.*" -Copy-Item $qirIncludes $qirDropsInclude -Exclude "*.md","Tracer*" \ No newline at end of file diff --git a/src/Qir/Runtime/build-qir-stdlib.ps1 b/src/Qir/Runtime/build-qir-stdlib.ps1 new file mode 100644 index 00000000000..90bf98d2f34 --- /dev/null +++ b/src/Qir/Runtime/build-qir-stdlib.ps1 @@ -0,0 +1,61 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +#Requires -PSEdition Core + +& (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) + +$IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; + +Push-Location (Join-Path $PSScriptRoot stdlib) +try { + # Start with the quick check first and make sure that Rust sources + # meet formatting and style guide rules. + cargo fmt -- --check + if ($LASTEXITCODE -ne 0) { throw "Failed cargo fmt check on QIR stdlib." } + + # 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 --all-targets -- -D warnings + if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR stdlib." } + + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # Actually run the build. + cargo build @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR stdlib." } + + # Copy the results of compilation and the corresponding headers to the QIR drops folder so + # they can be included in pipeline artifacts. + $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) + $qirDropsInclude = (Join-Path $Env:QIR_DROPS include) + if (-not (Test-Path $Env:QIR_DROPS)) { + New-Item -Path $Env:QIR_DROPS -ItemType "directory" + } + if (-not (Test-Path $qirDropsBin)) { + New-Item -Path $qirDropsBin -ItemType "directory" + } + if (-not (Test-Path $qirDropsInclude)) { + New-Item -Path $qirDropsInclude -ItemType "directory" + } + $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) + Copy-Item $qirBinaries $qirDropsBin -Include "*qir_stdlib*" -Exclude "*.rlib","*.d","*.exp" + + # Copy the C API header and def file + Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.h) (Join-Path $Env:QIR_DROPS include) + Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.def) (Join-Path $Env:QIR_DROPS include) + + # 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; + } +} +finally { + Pop-Location +} diff --git a/src/Qir/Runtime/lib/CMakeLists.txt b/src/Qir/Runtime/lib/CMakeLists.txt deleted file mode 100644 index 343d7162ebc..00000000000 --- a/src/Qir/Runtime/lib/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_subdirectory(QIR) -add_subdirectory(QSharpFoundation) -add_subdirectory(QSharpCore) -add_subdirectory(Simulators) -add_subdirectory(Tracer) diff --git a/src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp b/src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp deleted file mode 100644 index f84399ae30c..00000000000 --- a/src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include "QirRuntimeApi_I.hpp" -#include "QubitManager.hpp" -#include "BasicRuntimeDriverFactory.h" - -namespace Microsoft -{ -namespace Quantum -{ - class CBasicRuntimeDriver : public IRuntimeDriver - { - std::unique_ptr qubitManager; - - public: - CBasicRuntimeDriver() - { - qubitManager = std::make_unique(); - } - - ~CBasicRuntimeDriver() override - { - } - - std::string QubitToString(QubitIdType q) override - { - return std::to_string(q); - } - - QubitIdType AllocateQubit() override - { - return qubitManager->Allocate(); - } - - void ReleaseQubit(QubitIdType q) override - { - qubitManager->Release(q); - } - - void ReleaseResult(Result /* result */) override - { - } - - bool AreEqualResults(Result r1, Result r2) override - { - return r1 == r2; - } - - ResultValue GetResultValue(Result r) override - { - return (r == UseZero()) ? Result_Zero : Result_One; - } - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } - }; - - extern "C" void* CreateBasicRuntimeDriver() - { - return (IRuntimeDriver*)new CBasicRuntimeDriver(); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/CMakeLists.txt b/src/Qir/Runtime/lib/QIR/CMakeLists.txt deleted file mode 100644 index 4dd5b41054e..00000000000 --- a/src/Qir/Runtime/lib/QIR/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -# The downstream consumers but must pick up both the native support lib and the utility -# lib, produced from ll bridge files when linking against either qir-rt or qir-qis. - -#+++++++++++++++++++++++++++++++++++++ -# qir-rt -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# create qir-rt-support lib from the C++ sources -# -set(rt_sup_source_files - QirOutputHandling.cpp - OutputStream.cpp - Output.cpp - allocationsTracker.cpp - arrays.cpp - callables.cpp - context.cpp - delegated.cpp - strings.cpp - utils.cpp - QubitManager.cpp - BasicRuntimeDriver.cpp -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(qir-rt-support-obj OBJECT ${rt_sup_source_files}) -target_source_from_qir(qir-rt-support-obj bridge-rt.ll) -target_include_directories(qir-rt-support-obj PUBLIC - ${public_includes} - ${common_includes} -) -set_property(TARGET qir-rt-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(qir-rt-support-obj PRIVATE EXPORT_QIR_API) - -#+++++++++++++++++++++++++++++++++++++ -# qir-qis -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.Runtime dynamic library -# -add_library(Microsoft.Quantum.Qir.Runtime SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.Runtime - ${CMAKE_DL_LIBS} - qir-rt-support-obj - ${SPECTRE_LIBS} -) - -target_include_directories(Microsoft.Quantum.Qir.Runtime PUBLIC ${public_includes}) -target_compile_definitions(Microsoft.Quantum.Qir.Runtime PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.Runtime PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.Runtime - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) diff --git a/src/Qir/Runtime/lib/QIR/Output.cpp b/src/Qir/Runtime/lib/QIR/Output.cpp deleted file mode 100644 index b07cd3da325..00000000000 --- a/src/Qir/Runtime/lib/QIR/Output.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" // QIR_SHARED_API for quantum__rt__message. -#include "OutputStream.hpp" -#include "QirOutputHandling.hpp" - -void WriteToCurrentStream(QirString* qstr); - -// Public API: -extern "C" -{ - void __quantum__rt__message(QirString* qstr) // NOLINT - { - WriteToCurrentStream(qstr); - } -} // extern "C" - - -void WriteToCurrentStream(QirString* qstr) -{ - std::ostream& currentOutputStream = Microsoft::Quantum::OutputStream::Get(); - currentOutputStream << qstr->str << QOH_REC_DELIMITER; - currentOutputStream.flush(); -} diff --git a/src/Qir/Runtime/lib/QIR/OutputStream.cpp b/src/Qir/Runtime/lib/QIR/OutputStream.cpp deleted file mode 100644 index f2d91f190c6..00000000000 --- a/src/Qir/Runtime/lib/QIR/OutputStream.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// https://stackoverflow.com/a/5419388/6362941 redirect std::cout to a string -// Discussion/history and some more info about the output redirection: -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 - -#include -#include "QirRuntime.hpp" -#include "OutputStream.hpp" - -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - std::ostream* OutputStream::currentOutputStream = &std::cout; // Output to std::cout by default. - - std::ostream& OutputStream::Get() - { - return *currentOutputStream; - } - - std::ostream& OutputStream::Set(std::ostream& newOStream) - { - std::ostream& oldOStream = *currentOutputStream; - currentOutputStream = &newOStream; - return oldOStream; - } - - OutputStream::ScopedRedirector::ScopedRedirector(std::ostream& newOstream) : old(OutputStream::Set(newOstream)) - { - } - - OutputStream::ScopedRedirector::~ScopedRedirector() - { - OutputStream::Set(old); - } - - std::ostream& SetOutputStream(std::ostream& newOStream) - { - return OutputStream::Set(newOStream); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp b/src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp deleted file mode 100644 index d1d695643e3..00000000000 --- a/src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include "QirOutputHandling.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -extern void WriteToCurrentStream(QirString*); - -static void PrintCStr(const char* cStr) -{ - QirString msg(cStr); - WriteToCurrentStream(&msg); -} - - -extern "C" -{ - // Tuple Type Records - - void __quantum__rt__tuple_start_record_output() // NOLINT - { - PrintCStr(QOH_REC_TUPLE_START); - } - - void __quantum__rt__tuple_end_record_output() // NOLINT - { - PrintCStr(QOH_REC_TUPLE_END); - } - - - // Array Type Records - - void __quantum__rt__array_start_record_output() // NOLINT - { - PrintCStr(QOH_REC_ARRAY_START); - } - - void __quantum__rt__array_end_record_output() // NOLINT - { - PrintCStr(QOH_REC_ARRAY_END); - } - - - // Primitive Result Records - - void __quantum__rt__result_record_output(Result res) // NOLINT - { - if (__quantum__rt__result_equal(res, __quantum__rt__result_get_zero())) - { - PrintCStr(QOH_REC_RESULT_ZERO); - } - else - { - PrintCStr(QOH_REC_RESULT_ONE); - } - } - - void __quantum__rt__bool_record_output(bool isTrue) // NOLINT - { - if (!isTrue) - { - PrintCStr(QOH_REC_FALSE); - } - else - { - PrintCStr(QOH_REC_TRUE); - } - } - - void __quantum__rt__integer_record_output(int64_t i64Val) // NOLINT - { - std::stringstream strStream; - strStream << QOH_REC_PREFIX << i64Val; - PrintCStr(strStream.str().c_str()); - } - - void __quantum__rt__double_record_output(double doubleVal) // NOLINT - { - std::stringstream strStream; - strStream << QOH_REC_PREFIX << doubleVal; - PrintCStr(strStream.str().c_str()); - } - -} // extern "C" diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp deleted file mode 100644 index 8a506827e79..00000000000 --- a/src/Qir/Runtime/lib/QIR/QubitManager.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// 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* sharedQbitStatusArray) - : firstElement(startId) - , lastElement(endId) - { - FailIf(startId > endId || startId < 0 || endId == MaximumQubitCapacity, - "Incorrect boundaries in the linked list initialization."); - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - - for (QubitIdType i = startId; i < endId; i++) - { - sharedQbitStatusArray[i] = i + 1; // Current element points to the next element. - } - sharedQbitStatusArray[endId] = NoneMarker; // Last element ends the chain. - } - - bool CQubitManager::QubitListInSharedArray::IsEmpty() const - { - return firstElement == NoneMarker; - } - - void CQubitManager::QubitListInSharedArray::AddQubit(QubitIdType id, QubitIdType* sharedQbitStatusArray) - { - FailIf(id == NoneMarker, "Incorrect qubit id, cannot add it to the list."); - FailIf(sharedQbitStatusArray == 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; - sharedQbitStatusArray[id] = NoneMarker; // List with a single elemenet in the chain. - return; - } - - sharedQbitStatusArray[id] = firstElement; // The new element will point to the former first element. - firstElement = id; // The new element is now the first in the chain. - } - - QubitIdType CQubitManager::QubitListInSharedArray::TakeQubitFromFront(QubitIdType* sharedQbitStatusArray) - { - FailIf(sharedQbitStatusArray == 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 = sharedQbitStatusArray[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) - { - sharedQbitStatusArray[result] = AllocatedMarker; - } - - return result; - } - - void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, - QubitIdType* sharedQbitStatusArray) - { - FailIf(sharedQbitStatusArray == 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. - sharedQbitStatusArray[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 mayExtendCap) - : mayExtendCapacity(mayExtendCap) - , qubitCapacity(initialQubitCapacity) - { - FailIf(qubitCapacity <= 0, "Qubit capacity must be positive."); - sharedQubitStatusArray = new QubitIdType[(size_t)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() - { - FailIf(freeQubitsInAreas.Count() <= 0, "Internal error! No reuse areas."); - RestrictedReuseArea areaAboutToBegin; - RestrictedReuseArea& currentArea = freeQubitsInAreas.PeekBack(); - if (currentArea.FreeQubitsReuseAllowed.IsEmpty()) - { - areaAboutToBegin.prevAreaWithFreeQubits = currentArea.prevAreaWithFreeQubits; - } - else - { - areaAboutToBegin.prevAreaWithFreeQubits = freeQubitsInAreas.Count() - 1; - } - freeQubitsInAreas.PushToBack(areaAboutToBegin); - } - - 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(); - if (areaAboutToEnd.prevAreaWithFreeQubits < containingArea.prevAreaWithFreeQubits) - { - containingArea.prevAreaWithFreeQubits = areaAboutToEnd.prevAreaWithFreeQubits; - } - // 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); - } - - QubitIdType CQubitManager::Allocate() - { - QubitIdType newQubitId = AllocateQubitId(); - FailIf(newQubitId == NoneMarker, "Not enough qubits."); - return newQubitId; - } - - void CQubitManager::Allocate(QubitIdType* 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 (QubitIdType k = 0; k < i; k++) - { - Release(qubitsToAllocate[k]); - } - FailNow("Not enough qubits."); - } - qubitsToAllocate[i] = newQubitId; - } - } - - void CQubitManager::Release(QubitIdType qubit) - { - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - ReleaseQubitId(qubit); - } - - void CQubitManager::Release(QubitIdType* qubitsToRelease, int32_t qubitCountToRelease) - { - if (qubitCountToRelease == 0) - { - return; - } - FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); - - for (int32_t i = 0; i < qubitCountToRelease; i++) - { - Release(qubitsToRelease[i]); - qubitsToRelease[i] = NoneMarker; - } - } - - QubitIdType CQubitManager::Borrow() - { - // We don't support true borrowing/returning at the moment. - return Allocate(); - } - - void CQubitManager::Borrow(QubitIdType* qubitsToBorrow, int32_t qubitCountToBorrow) - { - // We don't support true borrowing/returning at the moment. - return Allocate(qubitsToBorrow, qubitCountToBorrow); - } - - void CQubitManager::Return(QubitIdType qubit) - { - // We don't support true borrowing/returning at the moment. - Release(qubit); - } - - void CQubitManager::Return(QubitIdType* qubitsToReturn, int32_t qubitCountToReturn) - { - // We don't support true borrowing/returning at the moment. - Release(qubitsToReturn, qubitCountToReturn); - } - - void CQubitManager::Disable(QubitIdType qubit) - { - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - - // We can only disable explicitly allocated qubits that were not borrowed. - FailIf(!IsExplicitlyAllocatedId(qubit), "Cannot disable qubit that is not explicitly allocated."); - sharedQubitStatusArray[qubit] = DisabledMarker; - - disabledQubitCount++; - FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); - } - - void CQubitManager::Disable(QubitIdType* qubitsToDisable, int32_t qubitCountToDisable) - { - if (qubitCountToDisable == 0) - { - return; - } - 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(QubitIdType qubit) const - { - return IsValidId(qubit); - } - - bool CQubitManager::IsDisabledQubit(QubitIdType qubit) const - { - return IsValidQubit(qubit) && IsDisabledId(qubit); - } - - bool CQubitManager::IsExplicitlyAllocatedQubit(QubitIdType qubit) const - { - return IsValidQubit(qubit) && IsExplicitlyAllocatedId(qubit); - } - - bool CQubitManager::IsFreeQubitId(QubitIdType id) const - { - return IsValidId(id) && IsFreeId(id); - } - - 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 - auto* newStatusArray = new QubitIdType[(size_t)requestedCapacity]; - memcpy(newStatusArray, sharedQubitStatusArray, (size_t)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); - } - - QubitIdType CQubitManager::TakeFreeQubitId() - { - // First we try to take qubit from the current (innermost) area. - QubitIdType id = freeQubitsInAreas.PeekBack().FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); - - // Then, if no free qubits available for reuse, we scan outer areas (if they exist). - if (id == NoneMarker && freeQubitsInAreas.Count() >= 2) - { - int32_t areaIndex = freeQubitsInAreas.Count() - 1; - do - { - areaIndex = freeQubitsInAreas[(size_t)areaIndex].prevAreaWithFreeQubits; - id = freeQubitsInAreas[(size_t)areaIndex].FreeQubitsReuseAllowed.TakeQubitFromFront( - sharedQubitStatusArray); - } while ((areaIndex != 0) && (id == NoneMarker)); - - // We remember previous area where a free qubit was found or 0 if none were found. - freeQubitsInAreas.PeekBack().prevAreaWithFreeQubits = areaIndex; - } - - 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; - } - - 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."); - - // 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, sharedQubitStatusArray); - - freeQubitCount++; - FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/README.md b/src/Qir/Runtime/lib/QIR/README.md deleted file mode 100644 index 600d8c22d1c..00000000000 --- a/src/Qir/Runtime/lib/QIR/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Directory - -**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**public\QirRuntime.hpp** Declares `__quantum__rt__*()`. Depends on the listed earlier ones. - - -## Level 1 - -**allocationsTracker.hpp** Defines `Microsoft::Quantum::AllocationsTracker` that tracks the allocations and detects the mem leaks. - Does not depend on anything of our code. - -**utils.cpp** Implements `__quantum__rt__fail()`, `__quantum__rt__memory_allocate()`, `__quantum__rt__heap_{alloc,free}()`. - `__quantum__rt__heap_alloc()` calls **strings.cpp**'s `__quantum__rt__string_create()` - cyclical dependency. - -**strings.cpp** Implements `QirString`, `__quantum__rt__string_*()`, `__quantum__rt___to_string()` (except `qubit_to_string` and `result_to_string`). - Depends on **utils.cpp**'s `__quantum__rt__fail()` - cyclical dependency. - - -## Level 2 - -**allocationsTracker.cpp** Implements the internals of `Microsoft::Quantum::AllocationsTracker`. - Depends on `__quantum__rt__fail()`, `__quantum__rt__string_create()` - -**context.cpp** Implements the internals of `Microsoft::Quantum::QirExecutionContext`, - Depends on **allocationsTracker.hpp**'s `Microsoft::Quantum::AllocationsTracker`. - Gets/returns `IRuntimeDriver *`. - -## Level 3 - -**delegated.cpp** Implements `__quantum__rt__result_*()`, `__quantum__rt__qubit_{allocate,release,to_string}()`. - Each API depends on `Microsoft::Quantum::GlobalContext()[->GetDriver()]`, - `__quantum__rt__qubit_to_string()` also depends on strings.cpp's `__quantum__rt__string_create()`. - `__quantum__rt__result_to_string()` also depends on strings.cpp's `__quantum__rt__string_create()`. - -**arrays.cpp** Implements {the internals of `QirArray`} and `__quantum__rt__*array*`. - Depends on `Microsoft::Quantum::GlobalContext()`, `__quantum__rt__fail()`, `__quantum__rt__string_create()`, - **delegated.cpp**'s `__quantum__rt__qubit_allocate()` - -## Level 4 - -**callables.cpp** Defines the {internals of `QirTupleHeader`, `QirCallable`}, `__quantum__rt__tuple_*()`, `__quantum__rt__callable_*()` - Depends on `QirArray`, `Microsoft::Quantum::GlobalContext()`, `__quantum__rt__fail()`, `__quantum__rt__string_create()`, `TupleWithControls`, - Consider breaking up into **Tuples.cpp** and **Callables.cpp**. diff --git a/src/Qir/Runtime/lib/QIR/allocationsTracker.cpp b/src/Qir/Runtime/lib/QIR/allocationsTracker.cpp deleted file mode 100644 index e9b516c2646..00000000000 --- a/src/Qir/Runtime/lib/QIR/allocationsTracker.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "allocationsTracker.hpp" - -#include "QirRuntime.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - void AllocationsTracker::OnAllocate(void* object) - { - auto inserted = this->allocatedObjects.insert(std::make_pair(object, 1)); - if (inserted.second) - { - // first time we are allocating an object at this address, nothing to do - } - else - { - if (inserted.first->second > 0) - { - __quantum__rt__fail_cstr("Allocating an object over an existing object!"); - } - else - { - inserted.first->second = 1; - } - } - } - - void AllocationsTracker::OnAddRef(void* object) - { - auto tracked = this->allocatedObjects.find(object); - if (tracked == this->allocatedObjects.end()) - { - __quantum__rt__fail_cstr("Attempting to addref an object that isn't tracked!"); - } - else - { - if (tracked->second <= 0) - { - __quantum__rt__fail_cstr("Attempting to ressurect a previously released object!"); - } - else - { - tracked->second += 1; - } - } - } - - void AllocationsTracker::OnRelease(void* object) - { - auto tracked = this->allocatedObjects.find(object); - if (tracked == this->allocatedObjects.end()) - { - __quantum__rt__fail_cstr("Attempting to release an object that isn't tracked!"); - } - else - { - if (tracked->second <= 0) - { - __quantum__rt__fail_cstr("Attempting to release a previously released object!"); - } - else - { - tracked->second -= 1; - } - } - } - - void AllocationsTracker::CheckForLeaks() const - { - for (auto& tracked : this->allocatedObjects) - { - if (tracked.second > 0) - { - __quantum__rt__fail_cstr("Found a potentially leaked object!"); - } - } - } -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp b/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp deleted file mode 100644 index a76d772e75f..00000000000 --- a/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace Microsoft -{ -namespace Quantum -{ - // The tracker keeps a list of pointers to all qir objects that have been allocated during the lifetime of an - // execution context and their reference counts, which allows us to check for double-releases and leaks when the - // actual objects have been released. - struct AllocationsTracker - { - void OnAllocate(void* object); - void OnAddRef(void* object); - void OnRelease(void* object); - - void CheckForLeaks() const; - - private: - std::unordered_map allocatedObjects; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/arrays.cpp b/src/Qir/Runtime/lib/QIR/arrays.cpp deleted file mode 100644 index 5c980a9fe26..00000000000 --- a/src/Qir/Runtime/lib/QIR/arrays.cpp +++ /dev/null @@ -1,683 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include // for memcpy -#include -#include -#include -#include - -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -using namespace Microsoft::Quantum; - -int QirArray::AddRef() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAddRef(this); - } - - assert(this->refCount != 0 && "Cannot resurrect released array!"); - return ++this->refCount; -} - -// NB: Release doesn't trigger destruction of the QirArray itself to allow for it -// being used both on the stack and on the heap. The creator of the array -// should delete it, if allocated from the heap. -int QirArray::Release() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnRelease(this); - } - - assert(this->refCount != 0 && "Cannot release already released array!"); - const int rc = --this->refCount; - if (rc == 0) - { - if (ownsQubits) - { - delete[](reinterpret_cast(this->buffer)); - } - else - { - delete[] this->buffer; - } - this->buffer = nullptr; - } - return rc; -} - -QirArray::QirArray(TItemCount qubitsCount) - : count(qubitsCount) - , itemSizeInBytes((TItemSize)sizeof(void*)) - , ownsQubits(true) - , refCount(1) -{ - if (this->count > 0) - { - QubitIdType* qbuffer = new QubitIdType[count]; - for (TItemCount i = 0; i < count; i++) - { - qbuffer[i] = reinterpret_cast(__quantum__rt__qubit_allocate()); - } - this->buffer = reinterpret_cast(qbuffer); - } - else - { - this->buffer = nullptr; - } - this->dimensionSizes.push_back(this->count); - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } -} - -QirArray::QirArray(TItemCount countItems, TItemSize itemSizeBytes, TDimCount dimCount, TDimContainer&& dimSizes) - : count(countItems) - - // Each array item needs to be properly aligned. Let's align them by correcting the `itemSizeInBytes`. - , itemSizeInBytes( - ((itemSizeBytes == 1) || (itemSizeBytes == 2) || (itemSizeBytes == 4) || - ((itemSizeBytes % sizeof(size_t)) == 0) // For built-in types or multiples of architecture alignment - ) - ? itemSizeBytes // leave their natural alignment. - // Other types align on the architecture boundary `sizeof(size_t)`: - // 4 bytes on 32-bit arch, 8 on 64-bit arch. - : itemSizeBytes + sizeof(size_t) - (itemSizeBytes % sizeof(size_t))) - - , dimensions(dimCount) - , dimensionSizes(std::move(dimSizes)) - , ownsQubits(false) - , refCount(1) -{ - assert(itemSizeBytes != 0); - assert(dimCount > 0); - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } - - if (dimCount == 1) - { - assert(this->dimensionSizes.empty() || this->dimensionSizes[0] == this->count); - if (this->dimensionSizes.empty()) - { - this->dimensionSizes.push_back(countItems); - } - } - - assert(this->count * (TBufSize)itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize bufferSize = (TBufSize)this->count * (TBufSize)itemSizeInBytes; - if (bufferSize > 0) - { - this->buffer = new char[bufferSize]; - assert(bufferSize <= std::numeric_limits::max()); - memset(this->buffer, 0, (size_t)bufferSize); - } - else - { - this->buffer = nullptr; - } -} - -QirArray::QirArray(const QirArray& other) - : count(other.count) - , itemSizeInBytes(other.itemSizeInBytes) - , dimensions(other.dimensions) - , dimensionSizes(other.dimensionSizes) - , ownsQubits(false) - , refCount(1) -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } - - assert((TBufSize)(this->count) * this->itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize size = (TBufSize)this->count * (TBufSize)this->itemSizeInBytes; - if (this->count > 0) - { - this->buffer = new char[size]; - assert(size <= std::numeric_limits::max()); - memcpy(this->buffer, other.buffer, (size_t)size); - } - else - { - this->buffer = nullptr; - } -} - -QirArray::~QirArray() -{ - assert(this->buffer == nullptr); -} - -char* QirArray::GetItemPointer(TItemCount index) const -{ - assert(index < this->count); - return &this->buffer[static_cast(index * this->itemSizeInBytes)]; -} - -void QirArray::Append(const QirArray* other) -{ - assert(!this->ownsQubits); // cannot take ownership of the appended qubits, as they might be owned by somebody else - assert(this->itemSizeInBytes == other->itemSizeInBytes); - assert(this->dimensions == 1 && other->dimensions == 1); - - assert((TBufSize)(other->count) * other->itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize otherSize = (TBufSize)other->count * (TBufSize)other->itemSizeInBytes; - - if (otherSize == 0) - { - return; - } - - assert((TBufSize)(this->count) * this->itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize thisSize = (TBufSize)this->count * (TBufSize)this->itemSizeInBytes; - - char* newBuffer = new char[thisSize + otherSize]; - if (thisSize) - { - memcpy(newBuffer, this->buffer, thisSize); - } - memcpy(&newBuffer[thisSize], other->buffer, otherSize); - - delete[] this->buffer; - this->buffer = newBuffer; - this->count += other->count; - this->dimensionSizes[0] = this->count; -} - -// We are using "row-major" layout so the linearized index into the multi-dimensional array where indexes into -// each dimension (dim_0, ... , dim_(n-1)) are i_0, i_1, ..., i_(n-1) as: -// i_0*dim_1*...*dim_(n-1) + i_1*dim_2*...*dim_(n-1) + ... + i_(n-2)*dim_(n-1) + i_(n-1) -// -// For example, 3D array with dims [5, 3, 4]: -// 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] -// 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] -// ... [24 - 35] -// ... [36 - 47] -// 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] -// index[112] ~ linear index 18 = 1*3*4 + 1*4 + 2 -static QirArray::TItemCount GetLinearIndex(const QirArray::TDimContainer& dimensionSizes, - const QirArray::TDimContainer& indexes) -{ - const size_t dimensions = dimensionSizes.size(); - QirArray::TItemCount linearIndex = 0; - QirArray::TItemCount layerSize = 1; - for (size_t i = dimensions; i > 0;) - { - --i; - linearIndex += indexes[i] * layerSize; - layerSize *= dimensionSizes[i]; - } - return linearIndex; -} - -// Calculate the length of the linear run, where the index in given `dimension` doesn't change. -// It's equal to the product of the dimension sizes in higher dimensions. -static QirArray::TItemCount RunCount(const QirArray::TDimContainer& dimensionSizes, QirArray::TDimCount dimension) -{ - assert((0 <= dimension) && ((size_t)dimension < dimensionSizes.size())); - return std::accumulate(dimensionSizes.begin() + dimension + 1, dimensionSizes.end(), (QirArray::TDimCount)1, - std::multiplies()); -} - -/*============================================================================== - Implementation of __quantum__rt__* methods for arrays -==============================================================================*/ -extern "C" -{ - QirArray* __quantum__rt__qubit_allocate_array(int64_t count) // TODO: Use `QirArray::TItemCount count` - { // (breaking change). - return new QirArray((QirArray::TItemCount)count); - } - - QirArray* __quantum__rt__qubit_borrow_array(int64_t count) - { - // Currently we implement borrowing as allocation. - return __quantum__rt__qubit_allocate_array(count); - } - - void __quantum__rt__qubit_release_array(QirArray* qa) - { - if (qa == nullptr) - { - return; - } - - assert(qa->ownsQubits); - if (qa->ownsQubits) - { - QubitIdType* qubits = reinterpret_cast(qa->buffer); - for (QirArray::TItemCount i = 0; i < qa->count; i++) - { - __quantum__rt__qubit_release(reinterpret_cast(qubits[i])); - } - } - - __quantum__rt__array_update_reference_count(qa, -1); - } - - void __quantum__rt__qubit_return_array(QirArray* qa) - { - // Currently we implement borrowing as allocation. - __quantum__rt__qubit_release_array(qa); - } - - QirArray* __quantum__rt__array_create_1d(int32_t itemSizeInBytes, int64_t countItems) - { - assert(itemSizeInBytes > 0); - return new QirArray((QirArray::TItemCount)countItems, (QirArray::TItemSize)itemSizeInBytes); - } - - // Bucketing of addref/release is non-standard so for now we'll keep the more traditional addref/release semantics - // in the native types. Should reconsider, if the perf of the loops becomes an issue. - void __quantum__rt__array_update_reference_count(QirArray* array, int32_t increment) - { - if (array == nullptr || increment == 0) - { - return; - } - else if (increment > 0) - { - for (int i = 0; i < increment; i++) - { - array->AddRef(); - } - } - else - { - for (int i = increment; i < 0; i++) - { - const long refCount = array->Release(); - if (refCount == 0) - { - delete array; - assert(i == -1 && "Attempting to decrement reference count below zero!"); - break; - } - } - } - } - - void __quantum__rt__array_update_alias_count(QirArray* array, int32_t increment) - { - if (array == nullptr || increment == 0) - { - return; - } - array->aliasCount += increment; - if (array->aliasCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Alias count cannot be negative!")); - } - } - - // TODO: Use `QirArray::TItemCount index` (breaking change): - char* __quantum__rt__array_get_element_ptr_1d(QirArray* array, int64_t index) - { - assert(array != nullptr); - return array->GetItemPointer((QirArray::TItemCount)index); - } - - // Returns the number of dimensions in the array. - int32_t __quantum__rt__array_get_dim(QirArray* array) // TODO: Return `QirArray::TDimCount` (breaking change). - { - assert(array != nullptr); - return array->dimensions; - } - - // TODO: Use `QirArray::TDimCount dim`, return `QirArray::TItemCount` (breaking change): - int64_t __quantum__rt__array_get_size(QirArray* array, int32_t dim) - { - assert(array != nullptr); - assert(dim < array->dimensions); - - return array->dimensionSizes[(size_t)dim]; - } - - int64_t __quantum__rt__array_get_size_1d(QirArray* array) - { - return __quantum__rt__array_get_size(array, 0); - } - - QirArray* __quantum__rt__array_copy(QirArray* array, bool forceNewInstance) - { - if (array == nullptr) - { - return nullptr; - } - if (forceNewInstance || array->aliasCount > 0) - { - return new QirArray(*array); - } - (void)array->AddRef(); - return array; - } - - QirArray* __quantum__rt__array_concatenate(QirArray* head, QirArray* tail) - { - assert(head != nullptr && tail != nullptr); - assert(head->dimensions == 1 && tail->dimensions == 1); - - QirArray* concatenated = new QirArray(*head); - concatenated->Append(tail); - return concatenated; - } - - // Creates a new array. The first int is the size of each element in bytes. The second int is the dimension count. - // The variable arguments should be a sequence of int64_ts contains the length of each dimension. The bytes of the - // new array should be set to zero. - // TODO: Use unsigned types (breaking change): - QirArray* __quantum__rt__array_create_nonvariadic(int itemSizeInBytes, int countDimensions, va_list dims) - { - QirArray::TDimContainer dimSizes; - dimSizes.reserve((size_t)countDimensions); - QirArray::TItemCount totalCount = 1; - - for (int i = 0; i < countDimensions; i++) - { - const QirArray::TItemCount dimSize = (QirArray::TItemCount)va_arg(dims, int64_t); - // TODO: Use `va_arg(dims, QirArray::TItemCount)`. - dimSizes.push_back(dimSize); - totalCount *= dimSize; - } - - assert(countDimensions < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler in case `countDimensions` becomes - // `QirArray::TDimCount`. - return new QirArray(totalCount, (QirArray::TItemSize)itemSizeInBytes, (QirArray::TDimCount)countDimensions, - std::move(dimSizes)); - } - - QirArray* __quantum__rt__array_create(int itemSizeInBytes, int countDimensions, ...) // NOLINT - { - va_list args; - va_start(args, countDimensions); - QirArray* array = __quantum__rt__array_create_nonvariadic(itemSizeInBytes, countDimensions, args); - va_end(args); - - return array; - } - - char* __quantum__rt__array_get_element_ptr_nonvariadic(QirArray* array, va_list args) // NOLINT - { - assert(array != nullptr); - - QirArray::TDimContainer indexes; - indexes.reserve(array->dimensions); - - for (QirArray::TDimCount i = 0; i < array->dimensions; i++) - { - indexes.push_back((QirArray::TItemCount)va_arg(args, int64_t)); - // TODO: Use `va_arg(args, QirArray::TItemCount)`. - assert(indexes.back() < array->dimensionSizes[i]); - } - - const QirArray::TItemCount linearIndex = GetLinearIndex(array->dimensionSizes, indexes); - return array->GetItemPointer(linearIndex); - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wvarargs" - // Returns a pointer to the indicated element of the array. The variable arguments should be a sequence of int64_ts - // that are the indices for each dimension. - char* __quantum__rt__array_get_element_ptr(QirArray* array, ...) // NOLINT - { - assert(array != nullptr); - - va_list args; - va_start(args, array->dimensions); // TODO: (Bug or hack?) Replace `array->dimensions` with `array`. - char* ptr = __quantum__rt__array_get_element_ptr_nonvariadic(array, args); - va_end(args); - - return ptr; - } -#pragma GCC diagnostic pop - - struct CheckedRange - { - int64_t start; // inclusive - int64_t step; // cannot be zero - int64_t end; // EXclusive (as opposed to `QirRange`) - int64_t width; // number of items in the range - - CheckedRange(const QirRange& r, int64_t upperBound /*exclusive*/) // lower bound assumed to be 0 (inclusive) - { - this->start = r.start; - this->step = r.step; - - if (r.step == 0) - { - throw std::runtime_error("invalid range"); - } - else if ((r.step > 0 && r.end < r.start) // Positive step and negative range. - || (r.step < 0 && r.start < r.end)) // Negative step and positive range. - { - // the QirRange generates empty sequence, normalize it - this->start = 0; - this->step = 1; - this->end = 0; - this->width = 0; - } - else if (r.step > 0) // Positive step and non-negative range. - { - this->width = (r.end - r.start + 1) / r.step // Number of full periods. - + ((r.end - r.start + 1) % r.step != 0 ? 1 : 0); // Every item is in the beginning of its - // period (also true for the last item in - // incomplete period at the end). - assert(this->width > 0); - - const int64_t lastSequenceItem = r.start + (this->width - 1) * r.step; - if (lastSequenceItem >= upperBound || r.start < 0) - { - throw std::runtime_error("range out of bounds"); - } - - this->end = lastSequenceItem + r.step; // `this->end` is EXclusive (as opposed to `QirRange`). - // `this->end` can also be `lastSequenceItem + 1`. - } - else // Negative step and non-positive range. - { // Range{10, -3, 1} == { 10, 7, 4, 1 } - // (B) Range{1, -5 , 0} = { 1 } - // (C) Range{4, -2, 0} = {4, 2, 0} - this->width = (r.end - r.start - 1) / r.step // (1 - 10 - 1) / (-3) == (-10) / (-3) == 3. - // (B) (0 - 1 - 1) / (-5) == -2 / -5 == 0. - + ((r.end - r.start - 1) % r.step != 0 ? 1 : 0); // (-10) % (-3) == -1; (-1) ? 1 : 0 == 1. - // (B) -2 % -5 = -2; -2 ? 1 : 0 == 1. - // Total: 4. - // (B) Total: 1. - assert(this->width > 0); - - const int64_t lastSequenceItem = - r.start + (this->width - 1) * r.step; // 10 + (4 - 1) * (-3) = 1. - // (B) 1 + (1 - 1)*(-5) = 1 + 0*5 = 1. - if (lastSequenceItem < 0 || r.start >= upperBound) - { - throw std::runtime_error("range out of bounds"); - } - - this->end = lastSequenceItem + r.step; // (B) 1 + (-5) = -4. - // `this->end` is EXclusive (as opposed to `QirRange`). - // `this->end` can also be `lastSequenceItem - 1`. - } - - // normalize the range of width 1, as the step doesn't matter for it - if (this->width == 1) - { - this->step = 1; - this->end = this->start + 1; - } - } - - bool IsEmpty() const - { - return this->width == 0; - } - }; - - // Creates and returns an array that is a slice of an existing array. The int indicates which dimension the slice is - // on. The %Range specifies the slice. Both ends of the range are inclusive. Negative step means the the order of - // elements should be reversed. - // TODO: Use `QirArray::TDimCount dim` (breaking change): - QirArray* quantum__rt__array_slice( // NOLINT - QirArray* array, int32_t dim, const QirRange& qirRange, - bool /*ignored: forceNewInstance*/) // https://github.com/microsoft/qsharp-language/issues/102 - // https://github.com/microsoft/qsharp-runtime/pull/830#issuecomment-925435170 - { - assert(array != nullptr); - assert(dim >= 0 && dim < array->dimensions); - - const QirArray::TItemSize itemSizeInBytes = array->itemSizeInBytes; - const QirArray::TDimCount dimensions = array->dimensions; - - const CheckedRange range(qirRange, array->dimensionSizes[(size_t)dim]); - - // If the range is empty, return an empty array of the same type but 0 items in dim dimension. - if (range.IsEmpty()) - { - QirArray::TDimContainer dims = array->dimensionSizes; - dims[(size_t)dim] = 0; - return new QirArray(0, itemSizeInBytes, dimensions, std::move(dims)); - } - - // When range covers the whole dimension, can return a copy of the array without doing any math. - if (range.step == 1 && range.start == 0 && range.end == array->dimensionSizes[(size_t)dim]) - { - return __quantum__rt__array_copy(array, true /*force*/); - } - - // Create slice array of appropriate size. - QirArray::TDimContainer sliceDims = array->dimensionSizes; - sliceDims[(size_t)dim] = (QirArray::TItemCount)(range.width); - const QirArray::TItemCount sliceItemsCount = std::accumulate( - sliceDims.begin(), sliceDims.end(), (QirArray::TItemCount)1, std::multiplies()); - QirArray* slice = new QirArray(sliceItemsCount, itemSizeInBytes, dimensions, std::move(sliceDims)); - if (nullptr == slice->buffer) - { - return slice; - } - const QirArray::TItemCount singleIndexRunCount = RunCount(array->dimensionSizes, (QirArray::TDimCount)dim); - const QirArray::TItemCount rowCount = singleIndexRunCount * array->dimensionSizes[(size_t)dim]; - - // When range is continuous, can copy data in larger chunks. For example, if the slice is on dim = 0, - // we will copy exactly once. - if (range.step == 1) - { - const QirArray::TItemCount rangeRunCount = - (QirArray::TItemCount)(singleIndexRunCount * (range.end - range.start)); - - assert((QirArray::TBufSize)rangeRunCount * itemSizeInBytes < - std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const QirArray::TBufSize rangeChunkSize = - (QirArray::TBufSize)rangeRunCount * (QirArray::TBufSize)itemSizeInBytes; - - QirArray::TItemCount dst = 0; - QirArray::TItemCount src = (QirArray::TItemCount)(singleIndexRunCount * range.start); - while (src < array->count) - { - assert(dst < slice->count); - memcpy(&slice->buffer[static_cast(dst * itemSizeInBytes)], - &array->buffer[static_cast(src * itemSizeInBytes)], rangeChunkSize); - src += rowCount; - dst += rangeRunCount; - } - return slice; - } - - // In case of disconnected or reversed range have to copy the data one run at a time. - assert((QirArray::TBufSize)singleIndexRunCount * itemSizeInBytes < - std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const QirArray::TBufSize chunkSize = - (QirArray::TBufSize)singleIndexRunCount * (QirArray::TBufSize)itemSizeInBytes; - QirArray::TItemCount dst = 0; - QirArray::TItemCount src = (QirArray::TItemCount)(singleIndexRunCount * range.start); - while (src < array->count) - { - assert(dst < slice->count); - - int64_t srcInner = src; // The `srcInner` can go negative in the end of the last iteration. - for (int64_t index = range.start; index != range.end; index += range.step) - { - assert(((QirArray::TItemSize)dst * itemSizeInBytes + (QirArray::TItemSize)chunkSize) <= - (QirArray::TItemSize)slice->count * slice->itemSizeInBytes); - assert((srcInner * (int64_t)itemSizeInBytes + (int64_t)chunkSize) <= - ((int64_t)array->count * (int64_t)array->itemSizeInBytes)); - assert(srcInner >= 0); - - memcpy(&slice->buffer[static_cast(dst * itemSizeInBytes)], - &array->buffer[static_cast(srcInner * itemSizeInBytes)], chunkSize); - srcInner += (singleIndexRunCount * range.step); - dst += singleIndexRunCount; - } - src += rowCount; - } - return slice; - } - - // Creates and returns an array that is a projection of an existing array. The int indicates which dimension the - // projection is on, and the int64_t specifies the specific index value to project. The returned Array* will have - // one fewer dimension than the existing array. - // TODO: Use `QirArray::TDimCount dim, QirArray::TItemCount index` (breaking change): - QirArray* __quantum__rt__array_project(QirArray* array, int dim, int64_t index) // NOLINT - { - assert(array != nullptr); - assert(dim >= 0 && dim < array->dimensions); - assert(array->dimensions > 1); // cannot project from 1D array into an array - assert(index >= 0 && index < array->dimensionSizes[(size_t)dim]); - - const QirArray::TItemSize itemSizeInBytes = array->itemSizeInBytes; - const QirArray::TDimCount dimensions = array->dimensions; - - // Create projected array of appropriate size. - QirArray::TDimContainer projectDims = array->dimensionSizes; - projectDims.erase(projectDims.begin() + dim); - - const QirArray::TItemCount projectItemsCount = std::accumulate( - projectDims.begin(), projectDims.end(), (QirArray::TItemCount)1, std::multiplies()); - QirArray* project = new QirArray(projectItemsCount, itemSizeInBytes, dimensions - 1, std::move(projectDims)); - if (nullptr == project->buffer) - { - return project; - } - - const QirArray::TItemCount singleIndexRunCount = RunCount(array->dimensionSizes, (QirArray::TDimCount)dim); - const QirArray::TItemCount rowCount = singleIndexRunCount * array->dimensionSizes[(size_t)dim]; - - assert((QirArray::TBufSize)singleIndexRunCount * itemSizeInBytes < - std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - - const QirArray::TBufSize chunkSize = - (QirArray::TBufSize)singleIndexRunCount * (QirArray::TBufSize)itemSizeInBytes; - - QirArray::TItemCount dst = 0; - QirArray::TItemCount src = (QirArray::TItemCount)(singleIndexRunCount * index); - while (src < array->count) - { - assert(dst < project->count); - memcpy(&project->buffer[static_cast(dst * itemSizeInBytes)], - &array->buffer[static_cast(src * itemSizeInBytes)], chunkSize); - src += rowCount; - dst += singleIndexRunCount; - } - return project; - } -} diff --git a/src/Qir/Runtime/lib/QIR/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp deleted file mode 100644 index 2108d574e54..00000000000 --- a/src/Qir/Runtime/lib/QIR/callables.cpp +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include // for memcpy -#include -#include -#include -#include - -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -// Exposed to tests only: -QirTupleHeader* FlattenControlArrays(QirTupleHeader* tuple, int depth); - -using namespace Microsoft::Quantum; - -/*============================================================================== - Implementation of __quantum__rt__tuple_* and __quantum__rt__callable_* -==============================================================================*/ -extern "C" -{ - PTuple __quantum__rt__tuple_create(int64_t size) // TODO: Use unsigned integer type for param (breaking change). - { - assert((uint64_t)size < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 64-bit arch. - return QirTupleHeader::Create(static_cast(size))->AsTuple(); - } - - void __quantum__rt__tuple_update_reference_count(PTuple tuple, int32_t increment) - { - if (tuple == nullptr || increment == 0) - { - return; - } - - QirTupleHeader* th = QirTupleHeader::GetHeader(tuple); - if (increment > 0) - { - for (int i = 0; i < increment; i++) - { - (void)th->AddRef(); - } - } - else - { - for (int i = increment; i < 0; i++) - { - (void)th->Release(); - } - } - } - - void __quantum__rt__tuple_update_alias_count(PTuple tuple, int32_t increment) - { - if (tuple == nullptr || increment == 0) - { - return; - } - QirTupleHeader* th = QirTupleHeader::GetHeader(tuple); - th->aliasCount += increment; - - if (th->aliasCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Alias count cannot be negative")); - } - } - - PTuple __quantum__rt__tuple_copy(PTuple tuple, bool forceNewInstance) - { - if (tuple == nullptr) - { - return nullptr; - } - - QirTupleHeader* th = QirTupleHeader::GetHeader(tuple); - if (forceNewInstance || th->aliasCount > 0) - { - return QirTupleHeader::CreateWithCopiedData(th)->AsTuple(); - } - - th->AddRef(); - return tuple; - } - - void __quantum__rt__callable_update_reference_count(QirCallable* callable, int32_t increment) - { - if (callable == nullptr || increment == 0) - { - return; - } - else if (increment > 0) - { - for (int i = 0; i < increment; i++) - { - (void)callable->AddRef(); - } - } - else - { - for (int i = increment; i < 0; i++) - { - if (0 == callable->Release()) - { - assert(-1 == i && "Attempting to decrement reference count below zero!"); - break; - } - } - } - } - - void __quantum__rt__callable_update_alias_count(QirCallable* callable, int32_t increment) - { - if (callable == nullptr || increment == 0) - { - return; - } - callable->UpdateAliasCount(increment); - } - - QirCallable* __quantum__rt__callable_create(t_CallableEntry* entries, t_CaptureCallback* captureCallbacks, - PTuple capture) - { - assert(entries != nullptr); - return new QirCallable(entries, captureCallbacks, capture); - } - - void __quantum__rt__callable_invoke(QirCallable* callable, PTuple args, PTuple result) - { - assert(callable != nullptr); - callable->Invoke(args, result); - } - - QirCallable* __quantum__rt__callable_copy(QirCallable* other, bool forceNewInstance) - { - if (other == nullptr) - { - return nullptr; - } - if (forceNewInstance) - { - return new QirCallable(*other); - } - return other->CloneIfShared(); - } - - void __quantum__rt__callable_make_adjoint(QirCallable* callable) - { - assert(callable != nullptr); - callable->ApplyFunctor(QirCallable::Adjoint); - } - - void __quantum__rt__callable_make_controlled(QirCallable* callable) - { - assert(callable != nullptr); - callable->ApplyFunctor(QirCallable::Controlled); - } - - void __quantum__rt__capture_update_reference_count(QirCallable* callable, int32_t parameter) - { - callable->InvokeCaptureCallback(0, parameter); - } - - void __quantum__rt__capture_update_alias_count(QirCallable* callable, int32_t parameter) - { - callable->InvokeCaptureCallback(1, parameter); - } -} - -/*============================================================================== - Implementation of QirTupleHeader -==============================================================================*/ -int QirTupleHeader::AddRef() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAddRef(this); - } - - assert(this->refCount > 0); - return ++this->refCount; -} - -int QirTupleHeader::Release() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnRelease(this); - } - - - assert(this->refCount > 0); // doesn't guarantee we catch double releases but better than nothing - int retVal = --this->refCount; - if (this->refCount == 0) - { - PTuplePointedType* buffer = reinterpret_cast(this); - delete[] buffer; - } - return retVal; -} - -QirTupleHeader* QirTupleHeader::Create(TBufSize size) -{ - PTuplePointedType* buffer = new PTuplePointedType[sizeof(QirTupleHeader) + size]; - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(buffer); - } - - // at the beginning of the buffer place QirTupleHeader, leave the buffer uninitialized - QirTupleHeader* th = reinterpret_cast(buffer); - th->refCount = 1; - th->aliasCount = 0; - th->tupleSize = size; - - return th; -} - -QirTupleHeader* QirTupleHeader::CreateWithCopiedData(QirTupleHeader* other) -{ - const TBufSize size = other->tupleSize; - PTuplePointedType* buffer = new PTuplePointedType[sizeof(QirTupleHeader) + size]; - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(buffer); - } - - // at the beginning of the buffer place QirTupleHeader - QirTupleHeader* th = reinterpret_cast(buffer); - th->refCount = 1; - th->aliasCount = 0; - th->tupleSize = size; - - // copy the contents of the other tuple - memcpy(th->AsTuple(), other->AsTuple(), size); - - return th; -} - -/*============================================================================== - Implementation of QirCallable -==============================================================================*/ -QirCallable::~QirCallable() -{ - assert(refCount == 0); -} - -QirCallable::QirCallable(const t_CallableEntry* ftEntries, const t_CaptureCallback* callbacks, PTuple capt) - : refCount(1) - , capture(capt) - , appliedFunctor(0) - , controlledDepth(0) -{ - memcpy(this->functionTable, ftEntries, sizeof(this->functionTable)); - assert(this->functionTable[0] != nullptr); // base must be always defined - - if (callbacks != nullptr) - { - memcpy(this->captureCallbacks, callbacks, sizeof(this->captureCallbacks)); - } - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } -} - -QirCallable::QirCallable(const QirCallable& other) - : refCount(1) - , capture(other.capture) - , appliedFunctor(other.appliedFunctor) - , controlledDepth(other.controlledDepth) -{ - memcpy(this->functionTable, other.functionTable, sizeof(this->functionTable)); - memcpy(this->captureCallbacks, other.captureCallbacks, sizeof(this->captureCallbacks)); - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } -} - -QirCallable* QirCallable::CloneIfShared() -{ - if (this->aliasCount == 0) - { - this->AddRef(); - return this; - } - return new QirCallable(*this); -} - -int QirCallable::AddRef() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAddRef(this); - } - - int rc = ++this->refCount; - assert(rc != 1); // not allowed to resurrect! - return rc; -} - -int QirCallable::Release() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnRelease(this); - } - assert(this->refCount > 0); - - int rc = --this->refCount; - if (rc == 0) - { - delete this; - } - return rc; -} - -void QirCallable::UpdateAliasCount(int increment) -{ - this->aliasCount += increment; - if (this->aliasCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Alias count cannot be negative")); - } -} - -// The function _assumes_ a particular structure of the passed in tuple (see -// https://github.com/microsoft/qsharp-language/blob/main/Specifications/QIR/Callables.md) and recurses into it upto -// the given depth to create a new tuple with a combined array of controls. -// -// For example, `src` tuple might point to a tuple with the following structure (depth = 2): -// { %Array*, { %Array*, { i64, %Qubit* }* }* } -// or it might point to a tuple where the inner type isn't a tuple but a built-in type (depth = 2): -// { %Array*, { %Array*, %Qubit* }* } -// The function will create a new tuple, where the array contains elements of all nested arrays, respectively for the -// two cases will get: -// { %Array*, { i64, %Qubit* }* } -// { %Array*, %Qubit* } -// The caller is responsible for releasing both the returned tuple and the array it contains. -// The order of the elements in the array is unspecified. -QirTupleHeader* FlattenControlArrays(QirTupleHeader* tuple, int depth) -{ - assert(depth > 1); // no need to unpack at depth 1, and should avoid allocating unnecessary tuples - - const QirArray::TItemSize qubitSize = - sizeof(/*Qubit*/ void*); // Compiler complains for `sizeof(Qubit)`: - // warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate - // [bugprone-sizeof-expression]. To be fixed when the `Qubit` is made fixed-size type. - - TupleWithControls* outer = TupleWithControls::FromTupleHeader(tuple); - - // Discover, how many controls there are in total so can allocate a correctly sized array for them. - QirArray::TItemCount cControls = 0; - TupleWithControls* current = outer; - for (int i = 0; i < depth; i++) - { - assert(i == depth - 1 || current->GetHeader()->tupleSize == sizeof(TupleWithControls)); - QirArray* controls = current->controls; - assert(controls->itemSizeInBytes == qubitSize); - cControls += controls->count; - current = current->innerTuple; - } - - // Copy the controls into the new array. This array doesn't own the qubits so must use the generic constructor. - QirArray* combinedControls = new QirArray(cControls, qubitSize); - char* dst = combinedControls->buffer; - [[maybe_unused]] const char* dstEnd = dst + static_cast(qubitSize * cControls); - current = outer; - QirTupleHeader* last = nullptr; - for (int i = 0; i < depth; i++) - { - if (i == depth - 1) - { - last = current->GetHeader(); - } - - QirArray* controls = current->controls; - - const QirArray::TBufSize blockSize = (QirArray::TBufSize)qubitSize * (QirArray::TBufSize)controls->count; - // Make sure we don't overflow `TBufSize` on 32-bit arch: - assert((blockSize >= qubitSize) && (blockSize >= controls->count)); - - assert(dst + blockSize <= dstEnd); - memcpy(dst, controls->buffer, blockSize); - dst += blockSize; - // in the last iteration the innerTuple isn't valid, but we are not going to use it - current = current->innerTuple; - } - - // Create the new tuple with the flattened controls array and args from the `last` tuple. - QirTupleHeader* flatTuple = QirTupleHeader::CreateWithCopiedData(last); - QirArray** arr = reinterpret_cast(flatTuple->AsTuple()); - *arr = combinedControls; - - return flatTuple; -} - -void QirCallable::Invoke(PTuple args, PTuple result) -{ - assert(this->appliedFunctor < QirCallable::TableSize); - if (this->controlledDepth < 2) - { - // For uncontrolled or singly-controlled callables, the `args` tuple is "flat" and can be passed directly to the - // implementing function. - this->functionTable[this->appliedFunctor](capture, args, result); - } - else - { - // Must unpack the `args` tuple into a new tuple with flattened controls. - QirTupleHeader* flat = FlattenControlArrays(QirTupleHeader::GetHeader(args), this->controlledDepth); - this->functionTable[this->appliedFunctor](capture, flat->AsTuple(), result); - - QirArray* controls = *reinterpret_cast(flat->AsTuple()); - __quantum__rt__array_update_reference_count(controls, -1); - flat->Release(); - } -} - -void QirCallable::Invoke() -{ - assert((this->appliedFunctor & QirCallable::Controlled) == 0 && "Cannot invoke controlled callable without args"); - PTuple args = __quantum__rt__tuple_create(0); - this->Invoke(args, nullptr); - __quantum__rt__tuple_update_reference_count(args, -1); -} - -// A + A = I; A + C = C + A = CA; C + C = C; CA + A = C; CA + C = CA -void QirCallable::ApplyFunctor(int functor) -{ - assert(functor == QirCallable::Adjoint || functor == QirCallable::Controlled); - - if (functor == QirCallable::Adjoint) - { - this->appliedFunctor ^= QirCallable::Adjoint; - if (this->functionTable[this->appliedFunctor] == nullptr) - { - this->appliedFunctor ^= QirCallable::Adjoint; - __quantum__rt__fail(__quantum__rt__string_create("The callable doesn't provide adjoint operation")); - } - } - else if (functor == QirCallable::Controlled) - { - this->appliedFunctor |= QirCallable::Controlled; - if (this->functionTable[this->appliedFunctor] == nullptr) - { - if (this->controlledDepth == 0) - { - this->appliedFunctor ^= QirCallable::Controlled; - } - __quantum__rt__fail(__quantum__rt__string_create("The callable doesn't provide controlled operation")); - } - this->controlledDepth++; - } -} - -void QirCallable::InvokeCaptureCallback(int32_t index, int32_t parameter) -{ - assert(index >= 0 && index < QirCallable::CaptureCallbacksTableSize && "Capture callback index out of range"); - - if (this->captureCallbacks[index] != nullptr) - { - this->captureCallbacks[index](this->capture, parameter); - } -} diff --git a/src/Qir/Runtime/lib/QIR/context.cpp b/src/Qir/Runtime/lib/QIR/context.cpp deleted file mode 100644 index d19d886604b..00000000000 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include "QirContext.hpp" -#include "QirContext.h" - -#include "CoreTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "allocationsTracker.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - std::unique_ptr g_context = nullptr; - std::unique_ptr& GlobalContext() - { - return g_context; - } - - void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects) - { - assert(g_context == nullptr); - g_context = std::make_unique(driver, trackAllocatedObjects); - } - - extern "C" void InitializeQirContext(void* driver, bool trackAllocatedObjects) - { - InitializeQirContext((IRuntimeDriver*)driver, trackAllocatedObjects); - } - - void ReleaseQirContext() - { - assert(g_context != nullptr); - - if (g_context->trackAllocatedObjects) - { - g_context->allocationsTracker->CheckForLeaks(); - } - - g_context.reset(nullptr); - } - - QirExecutionContext::QirExecutionContext(IRuntimeDriver* drv, bool trackAllocatedObj) - : driver(drv) - , trackAllocatedObjects(trackAllocatedObj) - { - if (this->trackAllocatedObjects) - { - this->allocationsTracker = std::make_unique(); - } - } - - // If we just remove this user-declared-and-defined dtor - // then it will be automatically defined together with the class definition, - // which will require the `AllocationsTracker` to be a complete type - // everywhere where `public/QirContext.hpp` is included - // (we'll have to move `allocationsTracker.hpp` to `public/`). - QirExecutionContext::~QirExecutionContext() = default; - - void QirExecutionContext::Init(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) - { - assert(GlobalContext() == nullptr); - GlobalContext() = std::make_unique(driver, trackAllocatedObjects); - } - - void QirExecutionContext::Deinit() - { - assert(GlobalContext() != nullptr); - - if (GlobalContext()->trackAllocatedObjects) - { - GlobalContext()->allocationsTracker->CheckForLeaks(); - } - - GlobalContext().reset(nullptr); - } - - void QirExecutionContext::OnAddRef(void* object) - { - if (trackAllocatedObjects) - { - this->allocationsTracker->OnAddRef(object); - } - } - - void QirExecutionContext::OnRelease(void* object) - { - if (this->trackAllocatedObjects) - { - this->allocationsTracker->OnRelease(object); - } - } - - void QirExecutionContext::OnAllocate(void* object) - { - if (this->trackAllocatedObjects) - { - this->allocationsTracker->OnAllocate(object); - } - } - - IRuntimeDriver* QirExecutionContext::GetDriver() const - { - return this->driver; - } - - QirExecutionContext::Scoped::Scoped(IRuntimeDriver* drv, bool trackAllocatedObj /*= false*/) - { - QirExecutionContext::Init(drv, trackAllocatedObj); - } - - QirExecutionContext::Scoped::~Scoped() - { - QirExecutionContext::Deinit(); - } - - QirContextScope::QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObj /*= false*/) - { - InitializeQirContext(driver, trackAllocatedObj); - } - - QirContextScope::~QirContextScope() - { - ReleaseQirContext(); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/delegated.cpp b/src/Qir/Runtime/lib/QIR/delegated.cpp deleted file mode 100644 index 30c41fe19f8..00000000000 --- a/src/Qir/Runtime/lib/QIR/delegated.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include "QirRuntime.hpp" - -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" - -/*============================================================================= - Note: QIR assumes a single global execution context! -=============================================================================*/ - -// QIR specification requires the Result type to be reference counted, even though Results are created by the target and -// qubits, created by the same target, aren't reference counted. To minimize the implementation burden on the target, -// the runtime will track the reference counts for results. The trade-off is the performance penalty of such external -// tracking. The design should be evaluated against real user code when we have it. -static std::unordered_map& AllocatedResults() -{ - static std::unordered_map allocatedResults; - return allocatedResults; -} - -static Microsoft::Quantum::IRestrictedAreaManagement* RestrictedAreaManagement() -{ - return dynamic_cast( - Microsoft::Quantum::GlobalContext()->GetDriver()); -} - -extern "C" -{ - Result __quantum__rt__result_get_zero() - { - return Microsoft::Quantum::GlobalContext()->GetDriver()->UseZero(); - } - - Result __quantum__rt__result_get_one() - { - return Microsoft::Quantum::GlobalContext()->GetDriver()->UseOne(); - } - - QUBIT* __quantum__rt__qubit_allocate() - { - return reinterpret_cast(Microsoft::Quantum::GlobalContext()->GetDriver()->AllocateQubit()); - } - - void __quantum__rt__qubit_release(QUBIT* qubit) - { - Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseQubit(reinterpret_cast(qubit)); - } - - QUBIT* __quantum__rt__qubit_borrow() - { - // Currently we implement borrowing as allocation. - return __quantum__rt__qubit_allocate(); - } - - void __quantum__rt__qubit_return(QUBIT* qubit) - { - // Currently we implement borrowing as allocation. - __quantum__rt__qubit_release(qubit); - } - - void __quantum__rt__qubit_restricted_reuse_area_start() - { - RestrictedAreaManagement()->StartArea(); - } - - void __quantum__rt__qubit_restricted_reuse_segment_next() - { - RestrictedAreaManagement()->NextSegment(); - } - - void __quantum__rt__qubit_restricted_reuse_area_end() - { - RestrictedAreaManagement()->EndArea(); - } - - void __quantum__rt__result_update_reference_count(RESULT* r, int32_t increment) - { - if (increment == 0) - { - return; // Inefficient QIR? But no harm. - } - else if (increment > 0) - { - // If we don't have the result in our map, assume it has been allocated by a measurement with refcount = 1, - // and this is the first attempt to share it. - std::unordered_map& trackedResults = AllocatedResults(); - auto rit = trackedResults.find(r); - if (rit == trackedResults.end()) - { - trackedResults[r] = 1 + increment; - } - else - { - rit->second += increment; - } - } - else - { - // If we don't have the result in our map, assume it has been never shared, so it's reference count is 1. - std::unordered_map& trackedResults = AllocatedResults(); - auto rit = trackedResults.find(r); - if (rit == trackedResults.end()) - { - assert(increment == -1); - Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseResult(r); - } - else - { - const int newRefcount = rit->second + increment; - assert(newRefcount >= 0); - if (newRefcount == 0) - { - trackedResults.erase(rit); - Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseResult(r); - } - else - { - rit->second = newRefcount; - } - } - } - } - - bool __quantum__rt__result_equal(RESULT* r1, RESULT* r2) // NOLINT - { - if (r1 == r2) - { - return true; - } - return Microsoft::Quantum::GlobalContext()->GetDriver()->AreEqualResults(r1, r2); - } - - // Returns a string representation of the result. - QirString* __quantum__rt__result_to_string(RESULT* result) // NOLINT - { - ResultValue rv = Microsoft::Quantum::GlobalContext()->GetDriver()->GetResultValue(result); - assert(rv != Result_Pending); - - return (rv == Result_Zero) ? __quantum__rt__string_create("Zero") : __quantum__rt__string_create("One"); - } - - // Returns a string representation of the qubit. - QirString* __quantum__rt__qubit_to_string(QUBIT* qubit) // NOLINT - { - return __quantum__rt__string_create(Microsoft::Quantum::GlobalContext() - ->GetDriver() - ->QubitToString(reinterpret_cast(qubit)) - .c_str()); - } -} diff --git a/src/Qir/Runtime/lib/QIR/strings.cpp b/src/Qir/Runtime/lib/QIR/strings.cpp deleted file mode 100644 index 7fc034bce7c..00000000000 --- a/src/Qir/Runtime/lib/QIR/strings.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -// Exposed to tests only: -std::unordered_map& AllocatedStrings(); - - -std::unordered_map& AllocatedStrings() // Cannot be static, is called by tests. -{ - static std::unordered_map allocatedStrings; - return allocatedStrings; -} - -static QirString* CreateOrReuseAlreadyAllocated(std::string&& str) -{ - QirString* qstr = nullptr; - auto alreadyAllocated = AllocatedStrings().find(str); - if (alreadyAllocated != AllocatedStrings().end()) - { - qstr = alreadyAllocated->second; - assert(qstr->str == str); - qstr->refCount++; - } - else - { - qstr = new QirString(std::move(str)); - AllocatedStrings()[qstr->str] = qstr; - } - return qstr; -} - -QirString::QirString(std::string&& otherStr) - // clang-format off - : str(std::move(otherStr)) -// clang-format on -{ -} - -QirString::QirString(const char* cstr) - // clang-format off - : str(cstr) -// clang-format on -{ -} - - -extern "C" -{ - // Creates a string from an array of UTF-8 bytes. - QirString* __quantum__rt__string_create(const char* bytes) // NOLINT - { - return CreateOrReuseAlreadyAllocated(std::string(bytes)); - } - - void __quantum__rt__string_update_reference_count(QirString* qstr, int32_t increment) // NOLINT - { - if (qstr == nullptr || increment == 0) - { - return; - } - - assert(qstr->refCount > 0 && "The string has been already released!"); - qstr->refCount += increment; - - if (qstr->refCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Attempting to decrement reference count below zero!")); - } - else if (qstr->refCount == 0) - { - auto allocated = AllocatedStrings().find(qstr->str); - assert(allocated != AllocatedStrings().end()); - // TODO: consider amortizing map cleanup across multiple iterations - AllocatedStrings().erase(allocated); - delete qstr; - } - } - - // Creates a new string that is the concatenation of the two argument strings. - QirString* __quantum__rt__string_concatenate(QirString* left, QirString* right) // NOLINT - { - return CreateOrReuseAlreadyAllocated(left->str + right->str); - } - - // Returns true if the two strings are equal, false otherwise. - bool __quantum__rt__string_equal(QirString* left, QirString* right) // NOLINT - { - assert((left == right) == (left->str == right->str)); - return left == right; - } - - // Returns a string representation of the integer. - QirString* __quantum__rt__int_to_string(int64_t value) // NOLINT - { - return CreateOrReuseAlreadyAllocated(std::to_string(value)); - } - - // Returns a string representation of the double. - QirString* __quantum__rt__double_to_string(double value) // NOLINT - { - std::ostringstream oss; - oss.precision(std::numeric_limits::max_digits10); - oss << std::fixed << value; - std::string str = oss.str(); - - // Remove padding zeros from the decimal part (relies on the fact that the output for integers always contains - // period). - std::size_t pos1 = str.find_last_not_of('0'); - if (pos1 != std::string::npos) - { - str.erase(pos1 + 1); - } - // For readability don't end with "." -- always have at least one digit in the decimal part. - if (str[str.size() - 1] == '.') - { - str.append("0"); - } - - return CreateOrReuseAlreadyAllocated(std::move(str)); - } - - // Returns a string representation of the Boolean. - QirString* __quantum__rt__bool_to_string(bool value) // NOLINT - { - std::string str = value ? "true" : "false"; - return CreateOrReuseAlreadyAllocated(std::move(str)); - } - - // Returns a string representation of the Pauli. - QirString* __quantum__rt__pauli_to_string(PauliId pauli) // NOLINT - { - switch (pauli) - { - case PauliId_I: - return __quantum__rt__string_create("PauliI"); - case PauliId_X: - return __quantum__rt__string_create("PauliX"); - case PauliId_Y: - return __quantum__rt__string_create("PauliY"); - case PauliId_Z: - return __quantum__rt__string_create("PauliZ"); - default: - break; - } - return __quantum__rt__string_create(""); - } - - // Returns a string representation of the range. - QirString* quantum__rt__range_to_string(const QirRange& range) // NOLINT - { - std::ostringstream oss; - oss << range.start << ".."; - if (range.step != 1) - { - oss << range.step << ".."; - } - oss << range.end; - - return __quantum__rt__string_create(oss.str().c_str()); - } - - const char* __quantum__rt__string_get_data(QirString* str) // NOLINT - { - return str->str.c_str(); - } - - uint32_t __quantum__rt__string_get_length(QirString* str) // NOLINT - { - return (uint32_t)(str->str.size()); - } - - // Implemented in delegated.cpp: - // QirString* __quantum__rt__qubit_to_string(QUBIT* qubit); // NOLINT - - // Returns a string representation of the big integer. - // TODO QirString* __quantum__rt__bigint_to_string(QirBigInt*); // NOLINT -} diff --git a/src/Qir/Runtime/lib/QIR/utils.cpp b/src/Qir/Runtime/lib/QIR/utils.cpp deleted file mode 100644 index 82b18700108..00000000000 --- a/src/Qir/Runtime/lib/QIR/utils.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include -#include - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" -#include "OutputStream.hpp" - - -extern "C" -{ - char* __quantum__rt__memory_allocate(uint64_t size) - { - return (char*)malloc((size_t)size); - } - - // Fail the computation with the given error message. - void __quantum__rt__fail(QirString* msg) // NOLINT - { - __quantum__rt__fail_cstr(msg->str.c_str()); - } - - void __quantum__rt__fail_cstr(const char* cstr) - { - Microsoft::Quantum::OutputStream::Get() << cstr << std::endl; - Microsoft::Quantum::OutputStream::Get().flush(); - throw std::runtime_error(cstr); - } -} diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt deleted file mode 100644 index 7bd86a74662..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -#+++++++++++++++++++++++++++++++++++++ -# qsharp-core-qis -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# create qsharp-core-qis-support lib from the C++ sources -# -set(qsharp_core_sup_source_files - intrinsicsDump.cpp - intrinsics.cpp -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(qsharp-core-qis-support-obj OBJECT ${qsharp_core_sup_source_files}) -target_include_directories(qsharp-core-qis-support-obj PUBLIC ${public_includes}) -set_property(TARGET qsharp-core-qis-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(qsharp-core-qis-support-obj PUBLIC EXPORT_QIR_API) - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.QSharp.Core dynamic library -# -add_library(Microsoft.Quantum.Qir.QSharp.Core SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.QSharp.Core - ${CMAKE_DL_LIBS} - qsharp-core-qis-support-obj - simulators-obj - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - ${SPECTRE_LIBS} -) -add_dependencies(Microsoft.Quantum.Qir.QSharp.Core Microsoft.Quantum.Qir.Runtime) - -target_include_directories(Microsoft.Quantum.Qir.QSharp.Core PUBLIC ${public_includes}) -target_compile_definitions(Microsoft.Quantum.Qir.QSharp.Core PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.QSharp.Core PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.QSharp.Core - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) - diff --git a/src/Qir/Runtime/lib/QSharpCore/README.md b/src/Qir/Runtime/lib/QSharpCore/README.md deleted file mode 100644 index 1a80606d70a..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Directory - -**public\CoreTypes.hpp** (QUBIT, PauliId, RESULT)[, `public\QirTypes.hpp` (QirArray)]. -**public\QirContext.hpp** Declares `GlobalContext()`. -**public\QSharpSimApi_I.hpp** - Defines `IQuantumGateSet`. - -## Level 1 - -**qsharp__core__qis.hpp** Declares `__quantum__qis__*()` gate set implementations. - Depends on `public\CoreTypes.hpp` (QUBIT, PauliId, RESULT) - Uses `QirArray *` from `public\QirTypes.hpp`. - -**intrinsics.cpp** Defines `__quantum__qis__*()` gate set implementation. - Each API depends on `GlobalContext()`, `IQuantumGateSet`. diff --git a/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp b/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp deleted file mode 100644 index b9fdeb2e6e0..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/*============================================================================= - QIR assumes a single global execution context. - To support the dispatch over the qir-bridge, the clients must register their - Microsoft::Quantum::IRuntimeDriver* first. -=============================================================================*/ -#include -#include - -#include "qsharp__core__qis.hpp" - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" - -// Pauli consts are {i2} in QIR, likely stored as {i8} in arrays, but we are using the standard C++ enum type based on -// {i32} so cannot pass through the buffer and have to allocate a new one instead and copy. -static std::vector ExtractPauliIds(QirArray* paulis) -{ - const QirArray::TItemCount count = paulis->count; - std::vector pauliIds; - pauliIds.reserve(count); - for (QirArray::TItemCount i = 0; i < count; i++) - { - pauliIds.push_back(static_cast(*paulis->GetItemPointer(i))); - } - return pauliIds; -} - -static Microsoft::Quantum::IQuantumGateSet* GateSet() -{ - return dynamic_cast(Microsoft::Quantum::GlobalContext()->GetDriver()); -} - -extern "C" -{ - void __quantum__qis__exp__body(QirArray* paulis, double angle, QirArray* qubits) - { - assert(paulis->count == qubits->count); - - std::vector pauliIds = ExtractPauliIds(paulis); - GateSet()->Exp((long)(paulis->count), reinterpret_cast(pauliIds.data()), - reinterpret_cast(qubits->buffer), angle); - } - - void __quantum__qis__exp__adj(QirArray* paulis, double angle, QirArray* qubits) - { - __quantum__qis__exp__body(paulis, -angle, qubits); - } - - void __quantum__qis__exp__ctl(QirArray* ctls, QirExpTuple* args) - { - assert(args->paulis->count == args->targets->count); - - std::vector pauliIds = ExtractPauliIds(args->paulis); - GateSet()->ControlledExp((long)(ctls->count), reinterpret_cast(ctls->buffer), - (long)(args->paulis->count), reinterpret_cast(pauliIds.data()), - reinterpret_cast(args->targets->buffer), args->angle); - } - - void __quantum__qis__exp__ctladj(QirArray* ctls, QirExpTuple* args) - { - assert(args->paulis->count == args->targets->count); - - QirExpTuple updatedArgs = {args->paulis, -(args->angle), args->targets}; - - __quantum__qis__exp__ctl(ctls, &updatedArgs); - } - - void __quantum__qis__h__body(QUBIT* qubit) - { - GateSet()->H(reinterpret_cast(qubit)); - } - - void __quantum__qis__h__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledH((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - Result __quantum__qis__measure__body(QirArray* paulis, QirArray* qubits) - { - const QirArray::TItemCount count = qubits->count; - assert(count == paulis->count); - - std::vector pauliIds = ExtractPauliIds(paulis); - return GateSet()->Measure((long)(count), reinterpret_cast(pauliIds.data()), (long)(count), - reinterpret_cast(qubits->buffer)); - } - - void __quantum__qis__r__body(PauliId axis, double angle, QUBIT* qubit) - { - GateSet()->R(axis, reinterpret_cast(qubit), angle); - } - - void __quantum__qis__r__adj(PauliId axis, double angle, QUBIT* qubit) - { - __quantum__qis__r__body(axis, -angle, qubit); - } - - void __quantum__qis__r__ctl(QirArray* ctls, QirRTuple* args) - { - GateSet()->ControlledR((long)(ctls->count), reinterpret_cast(ctls->buffer), args->pauli, - reinterpret_cast(args->target), args->angle); - } - - void __quantum__qis__r__ctladj(QirArray* ctls, QirRTuple* args) - { - GateSet()->ControlledR((long)(ctls->count), reinterpret_cast(ctls->buffer), args->pauli, - reinterpret_cast(args->target), -(args->angle)); - } - - void __quantum__qis__s__body(QUBIT* qubit) - { - GateSet()->S(reinterpret_cast(qubit)); - } - - void __quantum__qis__s__adj(QUBIT* qubit) - { - GateSet()->AdjointS(reinterpret_cast(qubit)); - } - - void __quantum__qis__s__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledS((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__s__ctladj(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledAdjointS((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__t__body(QUBIT* qubit) - { - GateSet()->T(reinterpret_cast(qubit)); - } - - void __quantum__qis__t__adj(QUBIT* qubit) - { - GateSet()->AdjointT(reinterpret_cast(qubit)); - } - - void __quantum__qis__t__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledT((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__t__ctladj(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledAdjointT((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__x__body(QUBIT* qubit) - { - GateSet()->X(reinterpret_cast(qubit)); - } - - void __quantum__qis__x__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledX((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__y__body(QUBIT* qubit) - { - GateSet()->Y(reinterpret_cast(qubit)); - } - - void __quantum__qis__y__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledY((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__z__body(QUBIT* qubit) - { - GateSet()->Z(reinterpret_cast(qubit)); - } - - void __quantum__qis__z__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledZ((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } -} diff --git a/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp b/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp deleted file mode 100644 index 4d6dd714c5e..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "QirContext.hpp" -#include "QSharpSimApi_I.hpp" -#include "QirRuntimeApi_I.hpp" -#include "qsharp__core__qis.hpp" - -static Microsoft::Quantum::IDiagnostics* GetDiagnostics() -{ - return dynamic_cast(Microsoft::Quantum::GlobalContext()->GetDriver()); -} - -// Implementation: -extern "C" -{ - void __quantum__qis__dumpmachine__body(uint8_t* location) - { - GetDiagnostics()->DumpMachine(location); - } - - void __quantum__qis__dumpregister__body(uint8_t* location, const QirArray* qubits) - { - GetDiagnostics()->DumpRegister(location, qubits); - } -} // extern "C" diff --git a/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp b/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp deleted file mode 100644 index ebbd1a5987c..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "CoreTypes.hpp" // QUBIT, PauliId, RESULT - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -struct QirArray; - -struct QirRTuple -{ - PauliId pauli; - double angle; - QUBIT* target; -}; - -struct QirExpTuple -{ - QirArray* paulis; - double angle; - QirArray* targets; -}; - -/* - Methods from __quantum__qis namespace are specific to the target. When QIR is generated it might limit or extend - the set of intrinsics, supported by the target (known to QIR generator at compile time). This provides the - implementation of the QSharp.Core intrinsics that redirect to IQuantumGateSet. -*/ -extern "C" -{ - // Q# Gate Set - QIR_SHARED_API void __quantum__qis__exp__body(QirArray*, double, QirArray*); // NOLINT - QIR_SHARED_API void __quantum__qis__exp__adj(QirArray*, double, QirArray*); // NOLINT - QIR_SHARED_API void __quantum__qis__exp__ctl(QirArray*, QirExpTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__exp__ctladj(QirArray*, QirExpTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__h__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__h__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API RESULT* __quantum__qis__measure__body(QirArray*, QirArray*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__body(PauliId, double, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__adj(PauliId, double, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__ctl(QirArray*, QirRTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__ctladj(QirArray*, QirRTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__adj(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__ctladj(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__adj(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__ctladj(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__x__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__x__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__y__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__y__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__z__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__z__ctl(QirArray*, QUBIT*); // NOLINT - - // Q# Dump: - // Note: The param `location` must be `const void*`, - // but it is called from .ll, where `const void*` is not supported. - QIR_SHARED_API void __quantum__qis__dumpmachine__body(uint8_t* location); // NOLINT - QIR_SHARED_API void __quantum__qis__dumpregister__body(uint8_t* location, const QirArray* qubits); // NOLINT -} diff --git a/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp b/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp deleted file mode 100644 index e82b578e80e..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QirRuntime.hpp" -#include "QSharpSimApi_I.hpp" -#include "QirRuntimeApi_I.hpp" -#include "QirContext.hpp" - -#include "qsharp__foundation__qis.hpp" - -using namespace Microsoft::Quantum; - -static IDiagnostics* GetDiagnostics() -{ - return dynamic_cast(GlobalContext()->GetDriver()); -} - -// Implementation: -extern "C" -{ - void __quantum__qis__assertmeasurementprobability__body(QirArray* bases, QirArray* qubits, RESULT* result, - double prob, QirString* msg, double tol) - { - if (bases->count != qubits->count) - { - __quantum__rt__fail_cstr("Both input arrays - bases, qubits - for AssertMeasurementProbability(), " - "must be of same size."); - } - - IRuntimeDriver* driver = GlobalContext()->GetDriver(); - if (driver->AreEqualResults(result, driver->UseOne())) - { - prob = 1.0 - prob; - } - - // Convert paulis from sequence of bytes to sequence of PauliId: - std::vector paulis(bases->count); - for (QirArray::TItemCount i = 0; i < bases->count; ++i) - { - paulis[i] = (PauliId) * (bases->GetItemPointer(i)); - } - - if (!GetDiagnostics()->AssertProbability((long)qubits->count, paulis.data(), - reinterpret_cast(qubits->GetItemPointer(0)), prob, tol, - nullptr)) - { - __quantum__rt__fail(msg); - } - } - - void __quantum__qis__assertmeasurementprobability__ctl(QirArray* /* ctls */, - QirAssertMeasurementProbabilityTuple* args) - { - // Controlled AssertMeasurementProbability ignores control bits. See the discussion on - // https://github.com/microsoft/qsharp-runtime/pull/450 for more details. - __quantum__qis__assertmeasurementprobability__body(args->bases, args->qubits, args->result, args->prob, - args->msg, args->tol); - } - -} // extern "C" diff --git a/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt deleted file mode 100644 index f8ca4ebe1c0..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -#+++++++++++++++++++++++++++++++++++++ -# qsharp-foundation-qis -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# create qsharp-foundation-qis-support lib from the C++ sources -# -set(qsharp_foundation_sup_source_files - AssertMeasurement.cpp - intrinsicsMath.cpp - conditionals.cpp -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(qsharp-foundation-qis-support-obj OBJECT ${qsharp_foundation_sup_source_files}) -target_include_directories(qsharp-foundation-qis-support-obj PUBLIC - ${public_includes} - ${common_includes} -) -set_property(TARGET qsharp-foundation-qis-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(qsharp-foundation-qis-support-obj PUBLIC EXPORT_QIR_API) - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.QSharp.Foundation dynamic library -# -add_library(Microsoft.Quantum.Qir.QSharp.Foundation SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.QSharp.Foundation - ${CMAKE_DL_LIBS} - qsharp-foundation-qis-support-obj - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - ${SPECTRE_LIBS} -) -add_dependencies(Microsoft.Quantum.Qir.QSharp.Foundation Microsoft.Quantum.Qir.Runtime) - -target_include_directories(Microsoft.Quantum.Qir.QSharp.Foundation PUBLIC - ${public_includes} - ${common_includes} -) -target_compile_definitions(Microsoft.Quantum.Qir.QSharp.Foundation PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.QSharp.Foundation PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.QSharp.Foundation - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) diff --git a/src/Qir/Runtime/lib/QSharpFoundation/README.md b/src/Qir/Runtime/lib/QSharpFoundation/README.md deleted file mode 100644 index bf373827db2..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Directory - -**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**public\QirRuntime.hpp** Declares `__quantum__rt__*()`. Depends on the listed earlier ones. - - -## Level 1 - -**conditionals.cpp** Defines `__quantum__qis__apply*__body()`. - Depends on QIR's `quantum__rt__result_{equal,get_zero}()`, declared in **public\QirRuntime.hpp**. - -**intrinsicsMath.cpp** Defines `__quantum__qis__*` math funcs implementations, - `Quantum::Qis::Internal::{excStrDrawRandomVal,RandomizeSeed,GetLastGeneratedRandomI64,GetLastGeneratedRandomDouble}`. - Depends on `quantum__rt__fail()`, `quantum__rt__string_create()`, declared in **public\QirRuntime.hpp**. - - -## Level 2 - -**qsharp__foundation__qis.hpp** - Declares `__quantum__qis__*()` math funcs and ApplyIf. - Depends on **public\CoreTypes.hpp**, **public\QirTypes.hpp**. diff --git a/src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp b/src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp deleted file mode 100644 index c7df89fbd6c..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include "qsharp__foundation__qis.hpp" - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -static bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) -{ - assert(rs1 != nullptr && rs2 != nullptr && rs1->count == rs2->count); - assert(rs1->itemSizeInBytes == sizeof(void*)); // the array should contain pointers to RESULT - assert(rs2->itemSizeInBytes == sizeof(void*)); // the array should contain pointers to RESULT - - RESULT** results1 = reinterpret_cast(rs1->buffer); - RESULT** results2 = reinterpret_cast(rs2->buffer); - for (QirArray::TItemCount i = 0; i < rs1->count; i++) - { - if (!__quantum__rt__result_equal(results1[i], results2[i])) - { - return false; - } - } - return true; -} - -extern "C" -{ - void __quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) - { - QirCallable* clb = __quantum__rt__result_equal(r, __quantum__rt__result_get_zero()) ? clbOnZero : clbOnOne; - clb->Invoke(); - } - - void __quantum__qis__applyconditionallyintrinsic__body(QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, - QirCallable* clbOnSomeDifferent) - { - QirCallable* clb = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; - clb->Invoke(); - } -} diff --git a/src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp b/src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp deleted file mode 100644 index 3a748101f35..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include "qsharp__foundation__qis.hpp" -#include "qsharp__foundation_internal.hpp" -#include "QirRuntime.hpp" - -// Forward declarations: -namespace // Visible in this translation unit only. -{ -extern thread_local bool randomizeSeed; -extern int64_t lastGeneratedRndI64; -extern double lastGeneratedRndDouble; -} // namespace - -// Implementation: -extern "C" -{ - - // Implementations: - double __quantum__qis__nan__body() - { - return std::sqrt(-1.0); // sqrt() -> NaN - } - - bool __quantum__qis__isnan__body(double d) - { - return std::isnan(d); // https://en.cppreference.com/w/cpp/numeric/math/isnan - } - - double __quantum__qis__infinity__body() - { - return (double)INFINITY; // https://en.cppreference.com/w/c/numeric/math/INFINITY - } - - bool __quantum__qis__isinf__body(double d) - { - return std::isinf(d) && d > 0.0; // https://en.cppreference.com/w/cpp/numeric/math/isinf - } - - bool __quantum__qis__isnegativeinfinity__body(double d) - { - return std::isinf(d) && d < 0.0; // https://en.cppreference.com/w/cpp/numeric/math/isinf - } - - double __quantum__qis__sin__body(double d) - { - return std::sin(d); - } - - double __quantum__qis__cos__body(double d) - { - return std::cos(d); - } - - double __quantum__qis__tan__body(double d) - { - return std::tan(d); - } - - double __quantum__qis__arctan2__body(double y, double x) - { - return std::atan2(y, x); // https://en.cppreference.com/w/cpp/numeric/math/atan2 - } - - double __quantum__qis__sinh__body(double theta) - { - return std::sinh(theta); - } - - double __quantum__qis__cosh__body(double theta) - { - return std::cosh(theta); - } - - double __quantum__qis__tanh__body(double theta) - { - return std::tanh(theta); - } - - double __quantum__qis__arcsin__body(double theta) - { - return std::asin(theta); // https://en.cppreference.com/w/cpp/numeric/math/asin - } - - double __quantum__qis__arccos__body(double theta) - { - return std::acos(theta); // https://en.cppreference.com/w/cpp/numeric/math/acos - } - - double __quantum__qis__arctan__body(double theta) - { - return std::atan(theta); // https://en.cppreference.com/w/cpp/numeric/math/atan - } - - double __quantum__qis__sqrt__body(double d) - { - return std::sqrt(d); - } - - double __quantum__qis__log__body(double d) - { - return std::log(d); - } - - double __quantum__qis__ieeeremainder__body(double x, double y) - { - return std::remainder(x, y); // https://en.cppreference.com/w/cpp/numeric/math/remainder - } - - int64_t __quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum) - { - if (minimum > maximum) - { - __quantum__rt__fail_cstr(Quantum::Qis::Internal::excStrDrawRandomVal); - } - - // https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution - // https://en.cppreference.com/w/cpp/numeric/random - thread_local static std::mt19937_64 gen(randomizeSeed ? std::random_device()() : // Default - 0); // For test purposes only. - - lastGeneratedRndI64 = std::uniform_int_distribution(minimum, maximum)(gen); - return lastGeneratedRndI64; - } - - double __quantum__qis__drawrandomdouble__body(double minimum, double maximum) - { - if (minimum > maximum) - { - __quantum__rt__fail_cstr(Quantum::Qis::Internal::excStrDrawRandomVal); - } - - // For testing purposes we need separate generators for Int and Double: - // https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution - // https://en.cppreference.com/w/cpp/numeric/random - thread_local static std::mt19937_64 gen(randomizeSeed ? std::random_device()() : // Default - 0); // For test purposes only. - - // https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution - lastGeneratedRndDouble = std::uniform_real_distribution( - minimum, std::nextafter(maximum, std::numeric_limits::max())) // "Notes" section. - (gen); - return lastGeneratedRndDouble; - } - -} // extern "C" - -namespace // Visible in this translation unit only. -{ -thread_local bool randomizeSeed = true; -int64_t lastGeneratedRndI64 = 0; -double lastGeneratedRndDouble = 0.0; -} // namespace - -// For test purposes only: -namespace Quantum -{ -namespace Qis -{ - namespace Internal - { - char const excStrDrawRandomVal[] = "Invalid Argument: minimum > maximum"; - - void RandomizeSeed(bool randomize) - { - randomizeSeed = randomize; - } - - int64_t GetLastGeneratedRandomI64() - { - return lastGeneratedRndI64; - } - - double GetLastGeneratedRandomDouble() - { - return lastGeneratedRndDouble; - } - - } // namespace Internal -} // namespace Qis -} // namespace Quantum diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp b/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp deleted file mode 100644 index 9148ff5222d..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -struct QirArray; -struct QirCallable; - -struct QirAssertMeasurementProbabilityTuple -{ - QirArray* bases; - QirArray* qubits; - RESULT* result; - double prob; - QirString* msg; - double tol; -}; - -/* - Methods from __quantum__qis namespace are specific to the target. When QIR is generated it might limit or extend - the set of intrinsics, supported by the target (known to QIR generator at compile time). As part of the runtime - we provide _optional_ implementation of the common intrinsics that redirects to IQuantumGateSet. -*/ -extern "C" -{ - // Q# Math: - QIR_SHARED_API double __quantum__qis__nan__body(); // NOLINT - QIR_SHARED_API bool __quantum__qis__isnan__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__infinity__body(); // NOLINT - QIR_SHARED_API bool __quantum__qis__isinf__body(double d); // NOLINT - QIR_SHARED_API bool __quantum__qis__isnegativeinfinity__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__sin__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__cos__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__tan__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__arctan2__body(double y, double x); // NOLINT - QIR_SHARED_API double __quantum__qis__sinh__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__cosh__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__tanh__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__arcsin__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__arccos__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__arctan__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__sqrt__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__log__body(double d); // NOLINT - - QIR_SHARED_API double __quantum__qis__ieeeremainder__body(double x, double y); // NOLINT - QIR_SHARED_API int64_t __quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT - QIR_SHARED_API double __quantum__qis__drawrandomdouble__body(double minimum, double maximum); // NOLINT - - // Q# ApplyIf: - QIR_SHARED_API void __quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT - QIR_SHARED_API void __quantum__qis__applyconditionallyintrinsic__body( // NOLINT - QirArray*, QirArray*, QirCallable*, QirCallable*); - - // Q# Assert Measurement: - QIR_SHARED_API void __quantum__qis__assertmeasurementprobability__body( // NOLINT - QirArray* bases, QirArray* qubits, RESULT* result, double prob, QirString* msg, double tol); - QIR_SHARED_API void __quantum__qis__assertmeasurementprobability__ctl( // NOLINT - QirArray* ctls, QirAssertMeasurementProbabilityTuple* args); - -} // extern "C" diff --git a/src/Qir/Runtime/lib/README.md b/src/Qir/Runtime/lib/README.md deleted file mode 100644 index c29f64860de..00000000000 --- a/src/Qir/Runtime/lib/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Directory Structure - -[../../../Simulation/README.md](../../../Simulation/README.md) - -This directory structure mirrors how the Q# language runtime itself is implemented, -namely that the QSharpFoundation is code that is specific to the concepts and patterns inherent in Q# -while QSharpCore is the default quantum instruction target package that is part of the QDK. -This was introduced as part of the target package feature work, specifically the -[PR #476](https://github.com/microsoft/qsharp-runtime/pull/476). - - -## QSharpFoundation -Is a project defined [here](../../../Simulation/QSharpFoundation). - -## QSharpCore -Is a project defined [here](../../../Simulation/QSharpCore). - -## QIR -Anything that is required by the [QIR specs](https://github.com/qir-alliance/qir-spec), -which in particular includes the ["methods that delegate to the simulators"](QIR/bridge-rt.ll#46), should live in the QIR folder. -They require support from the backend, but are not language-specific. -Both the Q# Core and the Q# Foundation are Q#-specific in that these are the target instructions that the Q# libraries are built on. - -Qubit allocation (`@quantum__rt__qubit_allocate()`, `@quantum__rt__qubit_release()`), as a specific example, -is not part of one target package or another, but rather part of the QSharp Foundation package, -which is why it is defined there in the QIR as well. - - -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Solution - -**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** - Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. - -## Level 1 - -**Simulators** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`, `FullstateSimulator`, `CreateFullstateSimulator()`. - `FullstateSimulator` depends on the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** - -## Level 2 - -**QIR** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). - Provides the access to simulators (through the `IRuntimeDriver *` returned by `GlobalContext()->GetDriver()`). - -## Level 3 - -**QSharpFoundation** Defines `@__quantum__qis__*()` math funcs and ApplyIf entry points. - Depends on QIR (`quantum__rt__result_{equal,get_zero}()`, `quantum__rt__fail()`, `quantum__rt__string_create()`). - -**QSharpCore** Defines `@__quantum__qis__*()` quantum gate set entry points. - Each API depends on `GlobalContext()`, `IQuantumGateSet`. - Uses `QirArray *` from `public\QirTypes.hpp`. diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt deleted file mode 100644 index 4277958afbb..00000000000 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(source_files - "FullstateSimulator.cpp" - "ToffoliSimulator.cpp" -) - -set(includes - "${public_includes}" - "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src" - "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src/simulator" -) - -#=============================================================================== -# Produce object lib we'll use to create a shared lib (so/dll) later on - -add_library(simulators-obj OBJECT ${source_files}) -target_include_directories(simulators-obj PUBLIC - ${includes} - ${common_includes} -) -set_property(TARGET simulators-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(simulators-obj PRIVATE EXPORT_QIR_API) diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp deleted file mode 100644 index 2116c534eda..00000000000 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ /dev/null @@ -1,654 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "capi.hpp" - - -#include "FloatUtils.hpp" -#include "QirTypes.hpp" // TODO: Consider removing dependency on this file. -#include "QirRuntime.hpp" -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" -#include "SimFactory.h" -#include "OutputStream.hpp" -#include "QubitManager.hpp" - -#ifdef _WIN32 -#include -typedef HMODULE QUANTUM_SIMULATOR; -#else // not _WIN32 -#include -typedef void* QUANTUM_SIMULATOR; -#endif - -namespace -{ -const char* FULLSTATESIMULATORLIB = "Microsoft.Quantum.Simulator.Runtime.dll"; -#if defined(__APPLE__) -const char* XPLATFULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.dylib"; -#elif !defined(_WIN32) -const char* XPLATFULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.so"; -#endif - -QUANTUM_SIMULATOR LoadQuantumSimulator() -{ - QUANTUM_SIMULATOR handle = nullptr; -#ifdef _WIN32 - handle = ::LoadLibraryA(FULLSTATESIMULATORLIB); - if (handle == nullptr) - { - throw std::runtime_error(std::string("Failed to load ") + FULLSTATESIMULATORLIB + - " (error code: " + std::to_string(GetLastError()) + ")"); - } -#else - handle = ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); - if (handle == nullptr) - { - handle = ::dlopen(XPLATFULLSTATESIMULATORLIB, RTLD_LAZY); - if (handle == nullptr) - { - throw std::runtime_error(std::string("Failed to load ") + XPLATFULLSTATESIMULATORLIB + " (" + ::dlerror() + - ")"); - } - } -#endif - return handle; -} - - -void* LoadProc(QUANTUM_SIMULATOR handle, const char* procName) -{ -#ifdef _WIN32 - return reinterpret_cast(::GetProcAddress(handle, procName)); -#else // not _WIN32 - return ::dlsym(handle, procName); -#endif -} -} // namespace - -namespace Microsoft -{ -namespace Quantum -{ - // TODO: is it OK to load/unload the dll for each simulator instance? - class CFullstateSimulator - : public IRuntimeDriver - , public IRestrictedAreaManagement - , public IQuantumGateSet - , public IDiagnostics - { - typedef void (*TSingleQubitGate)(unsigned /*simulator id*/, unsigned /*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); - } - - std::vector GetBases(long num, PauliId* paulis) - { - std::vector convertedBases; - convertedBases.reserve((size_t)num); - for (auto i = 0; i < num; i++) - { - convertedBases.push_back(GetBasis(paulis[i])); - } - return convertedBases; - } - - const QUANTUM_SIMULATOR handle = nullptr; - - using TSimulatorId = unsigned; // TODO: Use `void*` or a fixed-size integer, - // starting in native simulator (breaking change). - static constexpr TSimulatorId NULL_SIMULATORID = UINT_MAX; - // Should be `= std::numeric_limits::max()` but the Clang 12.0.0 complains. - - TSimulatorId simulatorId = NULL_SIMULATORID; - - // the QuantumSimulator expects contiguous ids, starting from 0 - std::unique_ptr qubitManager; - - unsigned GetQubitId(QubitIdType qubit) const - { - // Qubit manager uses unsigned range of intptr_t for qubit ids. - return static_cast(qubit); - } - - std::vector GetQubitIds(long num, QubitIdType* qubits) const - { - std::vector ids; - ids.reserve((size_t)num); - for (long i = 0; i < num; i++) - { - ids.push_back(GetQubitId(qubits[i])); - } - return ids; - } - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void DumpState() - { - std::cout << "*********************" << std::endl; - this->GetState( - [](const char* idx, double re, double im) - { - if (!Close(re, 0.0) || !Close(im, 0.0)) - { - std::cout << "|" << idx << ">: " << re << "+" << im << "i" << std::endl; - } - return true; - }); - std::cout << "*********************" << std::endl; - } - - void* GetProc(const char* name) - { - void* proc = LoadProc(this->handle, name); - if (proc == nullptr) - { - throw std::runtime_error(std::string("Failed to find '") + name + "' proc in " + FULLSTATESIMULATORLIB); - } - return proc; - } - - void UnmarkAsMeasuredSingleQubit(QubitIdType q) - { - isMeasured[GetQubitId(q)] = false; - } - - void UnmarkAsMeasuredQubitList(long num, QubitIdType* qubit) - { - for (const auto& id : GetQubitIds(num, qubit)) - { - isMeasured[id] = false; - } - } - - public: - CFullstateSimulator(uint32_t userProvidedSeed = 0) : handle(LoadQuantumSimulator()) - { - typedef unsigned (*TInit)(); - static TInit initSimulatorInstance = reinterpret_cast(this->GetProc("init")); - - qubitManager = std::make_unique(); - this->simulatorId = initSimulatorInstance(); - - typedef void (*TSeed)(unsigned, unsigned); - static TSeed setSimulatorSeed = reinterpret_cast(this->GetProc("seed")); - setSimulatorSeed(this->simulatorId, - (userProvidedSeed == 0) - ? (unsigned)std::chrono::system_clock::now().time_since_epoch().count() - : (unsigned)userProvidedSeed); - } - ~CFullstateSimulator() override - { - if (this->simulatorId != NULL_SIMULATORID) - { - typedef void (*TDestroy)(unsigned); - static TDestroy destroySimulatorInstance = - reinterpret_cast(LoadProc(this->handle, "destroy")); - assert(destroySimulatorInstance); - destroySimulatorInstance(this->simulatorId); - - // TODO: It seems that simulator might still be doing something on background threads so attempting to - // unload it might crash. - // UnloadQuantumSimulator(this->handle); - } - } - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void GetState(TGetStateCallback callback) override - { - typedef void (*TDump)(unsigned, TGetStateCallback); - static TDump dump = reinterpret_cast(this->GetProc("Dump")); - dump(this->simulatorId, callback); - } - - virtual std::string QubitToString(QubitIdType q) override - { - return std::to_string(q); - } - - void DumpMachine(const void* location) override; - void DumpRegister(const void* location, const QirArray* qubits) override; - - QubitIdType AllocateQubit() override - { - typedef void (*TAllocateQubit)(unsigned, unsigned); - static TAllocateQubit allocateQubit = reinterpret_cast(this->GetProc("allocateQubit")); - - QubitIdType q = qubitManager->Allocate(); // Allocate qubit in qubit manager. - unsigned id = GetQubitId(q); // Get its id. - allocateQubit(this->simulatorId, id); // Allocate it in the simulator. - if (isMeasured.size() < id + 1) - { - isMeasured.resize(id + 1, false); - } - return q; - } - - void ReleaseQubit(QubitIdType q) override - { - typedef bool (*TReleaseQubit)(unsigned, unsigned); - static TReleaseQubit releaseQubit = reinterpret_cast(this->GetProc("release")); - - // Release qubit in the simulator, checking to make sure that release was valid. - auto id = GetQubitId(q); - if (!releaseQubit(this->simulatorId, id) && !isMeasured[id]) - { - // We reject the release of a qubit that is not in the ground state (releaseQubit returns false), - // and was not recently measured (ie: the last operation was not measurement). This means the - // state is not well known, and therefore the safety of release is not guaranteed. - __quantum__rt__fail_cstr("Released qubit neither measured nor in ground state."); - } - qubitManager->Release(q); // Release it in the qubit manager. - } - - // IRestrictedAreaManagement - - virtual void StartArea() override - { - qubitManager->StartRestrictedReuseArea(); - } - - virtual void NextSegment() override - { - qubitManager->NextRestrictedReuseSegment(); - } - virtual void EndArea() override - { - qubitManager->EndRestrictedReuseArea(); - } - - Result Measure(long numBases, PauliId bases[], long numTargets, QubitIdType targets[]) override - { - assert(numBases == numTargets); - typedef unsigned (*TMeasure)(unsigned, unsigned, unsigned*, unsigned*); - static TMeasure m = reinterpret_cast(this->GetProc("Measure")); - std::vector ids = GetQubitIds(numTargets, targets); - if (ids.size() == 1) - { - // If measuring exactly one qubit, mark it as measured for tracking. - isMeasured[ids[0]] = true; - } - std::vector convertedBases = GetBases(numBases, bases); - - return reinterpret_cast( - m(this->simulatorId, (unsigned)numBases, convertedBases.data(), ids.data())); - } - - 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(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("X")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledX(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCX")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void Y(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("Y")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledY(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCY")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void Z(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("Z")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledZ(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCZ")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void H(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("H")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledH(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCH")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void S(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("S")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledS(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCS")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void AdjointS(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("AdjS")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledAdjointS(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = - reinterpret_cast(this->GetProc("MCAdjS")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void T(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("T")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledT(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCT")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void AdjointT(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("AdjT")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledAdjointT(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = - reinterpret_cast(this->GetProc("MCAdjT")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void R(PauliId axis, QubitIdType target, double theta) override - { - typedef void (*TR)(unsigned, unsigned, double, unsigned); - static TR r = reinterpret_cast(this->GetProc("R")); - - r(this->simulatorId, GetBasis(axis), theta, GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - } - - void ControlledR(long numControls, QubitIdType controls[], PauliId axis, QubitIdType target, - double theta) override - { - typedef void (*TMCR)(unsigned, unsigned, double, unsigned, unsigned*, unsigned); - static TMCR cr = reinterpret_cast(this->GetProc("MCR")); - - std::vector ids = GetQubitIds(numControls, controls); - cr(this->simulatorId, GetBasis(axis), theta, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void Exp(long numTargets, PauliId paulis[], QubitIdType targets[], double theta) override - { - typedef void (*TExp)(unsigned, unsigned, unsigned*, double, unsigned*); - static TExp exp = reinterpret_cast(this->GetProc("Exp")); - std::vector ids = GetQubitIds(numTargets, targets); - std::vector convertedBases = GetBases(numTargets, paulis); - exp(this->simulatorId, (unsigned)numTargets, convertedBases.data(), theta, ids.data()); - UnmarkAsMeasuredQubitList(numTargets, targets); - } - - void ControlledExp(long numControls, QubitIdType controls[], long numTargets, PauliId paulis[], - QubitIdType targets[], double theta) override - { - typedef void (*TMCExp)(unsigned, unsigned, unsigned*, double, unsigned, unsigned*, unsigned*); - static TMCExp cexp = reinterpret_cast(this->GetProc("MCExp")); - std::vector idsTargets = GetQubitIds(numTargets, targets); - std::vector idsControls = GetQubitIds(numControls, controls); - std::vector convertedBases = GetBases(numTargets, paulis); - cexp(this->simulatorId, (unsigned)numTargets, convertedBases.data(), theta, (unsigned)numControls, - idsControls.data(), idsTargets.data()); - UnmarkAsMeasuredQubitList(numTargets, targets); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - bool Assert(long numTargets, PauliId* bases, QubitIdType* targets, Result result, - const char* failureMessage) override - { - const double probabilityOfZero = AreEqualResults(result, UseZero()) ? 1.0 : 0.0; - return AssertProbability(numTargets, bases, targets, probabilityOfZero, 1e-10, failureMessage); - } - - bool AssertProbability(long numTargets, PauliId bases[], QubitIdType targets[], double probabilityOfZero, - double precision, const char* /*failureMessage*/) override - { - typedef double (*TOp)(unsigned id, unsigned n, int* b, unsigned* q); - static TOp jointEnsembleProbability = reinterpret_cast(this->GetProc("JointEnsembleProbability")); - - std::vector ids = GetQubitIds(numTargets, targets); - std::vector convertedBases = GetBases(numTargets, bases); - double actualProbability = - 1.0 - jointEnsembleProbability(this->simulatorId, (unsigned)numTargets, - reinterpret_cast(convertedBases.data()), ids.data()); - - return (std::abs(actualProbability - probabilityOfZero) < precision); - } - - private: - std::ostream& GetOutStream(const void* location, std::ofstream& outFileStream); - void DumpMachineImpl(std::ostream& outStream); - void DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits); - void GetStateTo(TDumpLocation location, TDumpToLocationCallback callback); - bool GetRegisterTo(TDumpLocation location, TDumpToLocationCallback callback, const QirArray* qubits); - - // This bit std::vector tracks whether the last operation on a given qubit was Measure. - // Note that `std::vector` is already specialized to use an underlying bitfied to save space. - // See: https://www.cplusplus.com/reference/vector/vector-bool/ - std::vector isMeasured; - - private: - TDumpToLocationCallback const dumpToLocationCallback = [](size_t idx, double re, double im, - TDumpLocation location) -> bool - { - std::ostream& outStream = *reinterpret_cast(location); - - if (!Close(re, 0.0) || !Close(im, 0.0)) - { - outStream << "|" << std::bitset<8>(idx) << ">: " << re << "+" << im << "i" << std::endl; - } - return true; - }; - }; // class CFullstateSimulator - - void CFullstateSimulator::DumpMachineImpl(std::ostream& outStream) - { - outStream << "# wave function for qubits (least to most significant qubit ids):" << std::endl; - this->GetStateTo((TDumpLocation)&outStream, dumpToLocationCallback); - outStream.flush(); - } - - void CFullstateSimulator::DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits) - { - outStream << "# wave function for qubits with ids (least to most significant): "; - for (QirArray::TItemCount idx = 0; idx < qubits->count; ++idx) - { - if (idx != 0) - { - outStream << "; "; - } - outStream << (uintptr_t)((reinterpret_cast(qubits->GetItemPointer(0)))[idx]); - } - outStream << ':' << std::endl; - - if (!this->GetRegisterTo((TDumpLocation)&outStream, dumpToLocationCallback, qubits)) - { - outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" - << std::endl; - } - outStream.flush(); - } - - bool CFullstateSimulator::GetRegisterTo(TDumpLocation location, TDumpToLocationCallback callback, - const QirArray* qubits) - { - std::vector ids = - GetQubitIds((long)(qubits->count), reinterpret_cast(qubits->GetItemPointer(0))); - static TDumpQubitsToLocationAPI dumpQubitsToLocation = - reinterpret_cast(this->GetProc("DumpQubitsToLocation")); - return dumpQubitsToLocation(this->simulatorId, (unsigned)(qubits->count), ids.data(), callback, location); - } - - void CFullstateSimulator::GetStateTo(TDumpLocation location, TDumpToLocationCallback callback) - { - static TDumpToLocationAPI dumpTo = reinterpret_cast(this->GetProc("DumpToLocation")); - - dumpTo(this->simulatorId, callback, location); - } - - std::ostream& CFullstateSimulator::GetOutStream(const void* location, std::ofstream& outFileStream) - { - // If the location is not nullptr and not empty string then dump to a file: - if ((location != nullptr) && (((static_cast(location))->str) != "")) - { - // Open the file for appending: - const std::string& filePath = (static_cast(location))->str; - - bool openException = false; - try - { - outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); - } - catch (const std::ofstream::failure& e) - { - openException = true; - std::cerr << "Exception caught: \"" << e.what() << "\".\n"; - } - - if (((outFileStream.rdstate() & std::ofstream::failbit) != 0) || openException) - { - std::cerr << "Failed to open dump file \"" + filePath + "\".\n"; - return OutputStream::Get(); // Dump to std::cout. - } - - return outFileStream; - } - - // Otherwise dump to std::cout: - return OutputStream::Get(); - } - - void CFullstateSimulator::DumpMachine(const void* location) - { - std::ofstream outFileStream; - std::ostream& outStream = GetOutStream(location, outFileStream); - DumpMachineImpl(outStream); - } - - void CFullstateSimulator::DumpRegister(const void* location, const QirArray* qubits) - { - std::ofstream outFileStream; - std::ostream& outStream = GetOutStream(location, outFileStream); - DumpRegisterImpl(outStream, qubits); - } - - std::unique_ptr CreateFullstateSimulator(uint32_t userProvidedSeed /*= 0*/) - { - return std::make_unique(userProvidedSeed); - } - - extern "C" void* CreateFullstateSimulatorC(uint32_t userProvidedSeed) - { - return (IRuntimeDriver*)new CFullstateSimulator(userProvidedSeed); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/Simulators/README.md b/src/Qir/Runtime/lib/Simulators/README.md deleted file mode 100644 index 6decc7a6b3d..00000000000 --- a/src/Qir/Runtime/lib/Simulators/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Solution - -**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** - Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. - -**src\Simulation\Native\src\simulator\capi.hpp** - Declares the APIs exposed by **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** . - - -## Level 1. External To This Directory - -**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. - -**public\QirRuntimeApi_I.hpp** - Defines `IRuntimeDriver`. - Depends on **public\CoreTypes.hpp**. - -**public\SimFactory.hpp** Declares `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. - Depends on `IRuntimeDriver`. - -**public\QSharpSimApi_I.hpp** - Defines `IQuantumGateSet`, `IDiagnostics`. - Depends on **public\CoreTypes.hpp**, `QirArray`. - - -## Level 2 - -**ToffoliSimulator.cpp** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`. - Depends on `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`, **public\CoreTypes.hpp** - -**FullstateSimulator.cpp** Defines the `FullstateSimulator` - QIR wrapper around the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, `CreateFullstateSimulator()`. - Depends on **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, **src\Simulation\Native\src\simulator\capi.hpp**, - `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`. - Consider breaking up into **FullstateSimulator.hpp** and **FullstateSimulator.cpp**. diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp deleted file mode 100644 index 0ad70b48973..00000000000 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include -#include - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - /*============================================================================== - CToffoliSimulator - Simulator for reversible classical logic. - ==============================================================================*/ - class CToffoliSimulator final - : public IRuntimeDriver - , public IQuantumGateSet - , public IDiagnostics - { - QubitIdType nextQubitId = 0; - - // State of a qubit is represented by a bit in states indexed by qubit's id, - // bits 0 and 1 correspond to |0> and |1> states respectively. - std::vector states; - - // The clients should never attempt to derefenece the Result, so we'll use fake - // pointers to avoid allocation and deallocation. - Result zero = reinterpret_cast(0xface0000); - Result one = reinterpret_cast(0xface1000); - - public: - CToffoliSimulator() = default; - ~CToffoliSimulator() override = default; - - /// - /// Implementation of IRuntimeDriver - /// - void ReleaseResult(Result /* result */) override - { - } - - bool AreEqualResults(Result r1, Result r2) override - { - return (r1 == r2); - } - - ResultValue GetResultValue(Result result) override - { - return (result == one) ? Result_One : Result_Zero; - } - - Result UseZero() override - { - return zero; - } - Result UseOne() override - { - return one; - } - - QubitIdType AllocateQubit() override - { - QubitIdType retVal = this->nextQubitId; - ++(this->nextQubitId); - assert(this->nextQubitId < std::numeric_limits::max()); // Check aginast the risk of overflow. - this->states.emplace_back(false); - return retVal; - } - - void ReleaseQubit([[maybe_unused]] QubitIdType qubit) override - { - assert((qubit + 1) == this->nextQubitId); - assert(!this->states.at(static_cast(qubit))); - --(this->nextQubitId); - this->states.pop_back(); - } - - std::string QubitToString(QubitIdType qubit) override - { - return std::to_string(qubit) + ":" + (this->states.at(static_cast(qubit)) ? "1" : "0"); - } - - /// - /// Implementation of IDiagnostics - /// - bool Assert(long numTargets, PauliId* bases, QubitIdType* targets, Result result, - const char* /* failureMessage */) override - { - // Measurements in Toffoli simulator don't change the state. - // TODO: log failureMessage? - return AreEqualResults(result, Measure(numTargets, bases, numTargets, targets)); - } - - bool AssertProbability(long numTargets, PauliId bases[], QubitIdType targets[], double probabilityOfZero, - double precision, const char* /* failureMessage */) override - { - assert(precision >= 0); - - // Measurements in Toffoli simulator don't change the state, and the result is deterministic. - const double actualZeroProbability = (Measure(numTargets, bases, numTargets, targets) == zero) ? 1.0 : 0.0; - return std::abs(actualZeroProbability - probabilityOfZero) < precision; - } - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void GetState(TGetStateCallback /* callback */) override - { - throw std::logic_error("operation_not_supported"); - } - - void DumpMachine(const void* /* location */) override - { - std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 - } - - void DumpRegister(const void* /* location */, const QirArray* /* qubits */) override - { - std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 - } - - - /// - /// Implementation of IQuantumGateSet - /// - void X(QubitIdType qubit) override - { - this->states.at(static_cast(qubit)).flip(); - } - - void ControlledX(long numControls, QubitIdType* const controls, QubitIdType qubit) override - { - bool allControlsSet = true; - for (long i = 0; i < numControls; i++) - { - if (!this->states.at(static_cast(controls[i]))) - { - allControlsSet = false; - break; - } - } - - if (allControlsSet) - { - this->states.at(static_cast(qubit)).flip(); - } - } - - - Result Measure(long numBases, PauliId bases[], long /* numTargets */, QubitIdType targets[]) override - { - bool odd = false; - for (long i = 0; i < numBases; i++) - { - if (bases[i] == PauliId_X || bases[i] == PauliId_Y) - { - throw std::runtime_error("Toffoli simulator only supports measurements in Z basis"); - } - if (bases[i] == PauliId_Z) - { - odd ^= (this->states.at(static_cast(targets[i]))); - } - } - return odd ? one : zero; - } - - - // - // The rest of the gate set Toffoli simulator doesn't support - // - void Y(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void Z(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void H(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void S(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void T(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void R(PauliId /* axis */, QubitIdType /*target*/, double /* theta */) override - { - throw std::logic_error("operation_not_supported"); - } - void Exp(long /* numTargets */, PauliId* /* paulis */, QubitIdType* /*targets*/, double /* theta */) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledY(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledZ(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledH(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledR(long /*numControls*/, QubitIdType* /*controls*/, PauliId /*axis*/, QubitIdType /*target*/, - double /*theta*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledExp(long /*numControls*/, QubitIdType* /*controls*/, long /*numTargets*/, PauliId* /*paulis*/, - QubitIdType* /*targets*/, double /* theta */) override - { - throw std::logic_error("operation_not_supported"); - } - void AdjointS(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void AdjointT(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledAdjointS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledAdjointT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - }; - - std::unique_ptr CreateToffoliSimulator() - { - return std::make_unique(); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/Tracer/CMakeLists.txt b/src/Qir/Runtime/lib/Tracer/CMakeLists.txt deleted file mode 100644 index a28bd52a0d3..00000000000 --- a/src/Qir/Runtime/lib/Tracer/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# build the native part of the tracer -set(source_files - "tracer-qis.cpp" - "tracer.cpp" -) - -set(includes - "${public_includes}" - "${PROJECT_SOURCE_DIR}/lib/QIR" -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(tracer-obj OBJECT ${source_files}) -target_include_directories(tracer-obj PUBLIC ${includes}) -set_property(TARGET tracer-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(tracer-obj PRIVATE EXPORT_QIR_API) - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.Tracer dynamic library -# -add_library(Microsoft.Quantum.Qir.Tracer SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.Tracer - ${CMAKE_DL_LIBS} - tracer-obj - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - ${SPECTRE_LIBS} -) -add_dependencies(Microsoft.Quantum.Qir.QSharp.Foundation Microsoft.Quantum.Qir.Runtime) - -target_include_directories(Microsoft.Quantum.Qir.Tracer PUBLIC ${includes}) -target_compile_definitions(Microsoft.Quantum.Qir.Tracer PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.Tracer PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.Tracer - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) diff --git a/src/Qir/Runtime/lib/Tracer/README.md b/src/Qir/Runtime/lib/Tracer/README.md deleted file mode 100644 index 9e41cab56d6..00000000000 --- a/src/Qir/Runtime/lib/Tracer/README.md +++ /dev/null @@ -1,218 +0,0 @@ -# Resource Tracer Design Document # - -The purpose of the Resource Tracer is to provide efficient and flexible way to estimate resources of a quantum program - in QIR representation. The estimates are calculated by simulating execution of the program (as opposed to the static - analysis). Please see [Resource Estimator](https://docs.microsoft.com/azure/quantum/machines/resources-estimator) - for more background on resource estimation for quantum programs. - -To run against the tracer, the quantum program should comply with the - [QIR specifications](https://github.com/qir-alliance/qir-spec) as well as: - -1. convert _each_ used intrinsic operation into one of the Quantum Instruction Set (_qis_) operations supported by the - tracer (see the last section of this readme); -1. (_optional_) provide callbacks for handling of conditional branches on a measurement (if not provided, the estimates - would cover only one branch of the execution); -1. (_optional_) provide callbacks for start/end of quantum operations (if not provided, all operations will be treated - as inlined as if the whole program consisted of a single operation); -1. (_optional_) provide callbacks for global barriers; -1. (_optional_) provide description of mapping for frame tracking; -1. (_optional_) provide names of operations for output (in the form of `tracer-config.hpp|cpp` files). - -The Resource Tracer will consist of: - -1. the bridge for the `__quantum__qis__*` methods listed below; -2. the native implementation to back the `__quantum__qis__*` methods; -3. the logic for partitioning gates into layers; -4. the logic for frame tracking; -5. output of the collected statistics; -6. (_lower priority_) the scheduling component to optimize depth and/or width of the circuit. - -## Layering ## - -One of the goals of the tracer is to compute which of the quantum operations can be executed in parallel. Further in - this section we provide the defintions of used concepts and the description of how we group the operations into - _layers_, however, we hope that the following example of layering is intuitively clear. - -### Example of layering ### - -The diagram below shows an example of how a sequential program, represented by the left circuit, could be layered. The gates in light gray are of duration zero, the preferrred layer duration is 1, and the barrier, - represented by a vertical squiggle, is set to have duration 0. - -![layering example](layering_example.png?raw=true "Layering example diagram") - -Notice, that gate 9 is dropped because it cannot cross the barrier to be added into L(2,1). - -### Definitions ### - -Each quantum operation in a program can be assigned an integer value, which we'll call its ___start time___. Some - operations might have non-zero duration, so they will also have ___end time___. For each qubit, there are also times - when the qubit is allocated and released. Start time of a gate cannot be less than allocation time of any of the qubits - the gate is using. If two gates or measurements use the same qubit, one of the gates must have start time greater than - or equal to the end time of the other. We'll call a particular assignment of times across a program its ___time function___. - -A sequentially executed quantum program can be assigned a trivial time function, when all quantum operations have - duration of 1 and unique start times, ordered to match the flow of the program. Layering compresses the timeline by - assuming that some operations might be executed simultaneously while allowing for different operations to have various - durations. - -Provided a valid _time_ function for the program a ___layer of duration N at time T, denoted as L(T,N),___ - is a subset of operations in the program such that all of these operations have start time greater or equal _T_ and - finish time less than _T + N_. The program is ___layered___ if all gates in it are partitioned into layers, that don't - overlap in time. The union of all qubits that are involved in operations of a given layer, will be denoted _Qubits(T,N)_. - -A sequential program can be trivially layered such that each layer contains exactly one operation. Notice, that the - definition of layer doesn't require the gates to be executed _in parallel_. For example, all gates in a fully sequential - program can be also placed into a single layer L(0, infinity). Some gates might be considered to be very cheap and take - zero time to execute, those gates can be added to a layer even if they act on the same qubit another gate in this layer - is acting on and have to be executed sequentially within the layer. - -### The Resource Tracer's Layering Algorithm ### - -As the tracer is executing a sequential quantum program, it will compute a time function and corresponding layering - using the _conceptual_ algorithm, described below (aka "tetris algorithm"). The actual implementation of layering might - be done differently, as long as the resulting layering is the same as if running the conceptual algorithm. - -A layer _L(T,N)_ acts as a ___fence___ if it does _not_ accepts any new operations, even if these operations don't - involve qubits from _Qubits(T,N)_. - -__Conditional execution on measurement results__: The Tracer will execute LLVM IR's branching structures "as is", - depending on the values of the corresponding variables at runtime. To enable estimation of branches that depend on a - measurement result, the source Q# program must be authored in such a way that the Q# compiler will translate the - conditionals into corresponding callbacks to the tracer (`__quantum__qis__apply_conditionally`). The tracer will - execute _both branches_ of the conditional statement to compute the upper bound estimate. The conditional callbacks - will mark the layers that contain measurements that produced the results used in conditionals as _fences_ for the - duration of the conditional callback. - -A user can create special layers that act as permanent _fences_ by calling `__quantum__qis__inject_barrier` function. The - user can choose duration of a barrier which would affect start time of the following layers but no operations will be - added to a barrier, independent of its duration. _Terminology note_: 'fence' is a role of layer, which might be assigned - to a layer temporarily or permanently; 'barrier' is a special layer the user can inject that has the role of a permanent - fence and contains no operations. - -__TODO__: figure out which operations should or should _not_ be supported inside conditional callbacks. For example: - -- nested conditional callbacks; -- measurements; -- opening and closing operations of tracked frames (if tracking is set up). - -__Caching__ (lower priority): It might be a huge perf win if the Resource Tracer could cache statistics for repeated - computations. The Tracer will have an option to cache layering results per quantum module if the boundaries of modules - are treated as barriers. - -#### The conceptual algorithm #### - -Note: The tracer assumes that the preferred layer duration is _P_. - -1. The first encountered operation of duration _N_, where either _N > 0_ or the operation involves multiple qubits, is - added into layer _L(0, max(P,N))_. The value of _conditional fence_ variable on the tracer is set to 0. -1. When conditional callback is encountered, the layer _L(t,N)_ of the measurement that produced the result used in the - conditional callback, is looked up and the _conditional fence_ is set to _t + N_. At the end of the conditional callback - _conditional fence_ is reset to 0. (Effectively, no operations, conditioned on the result of a measurement, can happen - before or in the same layer as the measurement, even if they don't involve the measured qubits.) -1. Suppose, there are already layers _L(0,N0), ... , L(k,Nk)_ and the operation being executed is a single-qubit _op_ of - duration __0__ (controlled and multi-qubit operations of duration 0 are treated the same as non-zero operations). - - - Scan from [boundaries included] _L(k,Nk)_ to _L(conditional fence,Nf)_ until find a layer _L(t,Nt)_ - such that _Qubits(t,Nt)_ contains the qubit of _op_. - - Add _op_ into this layer. - - If no such layer is found, add _op_ to the list of pending operations on the qubit. - - At the end of the program still pending operations will be ignored. - -1. Suppose, there are already layers _L(0,N0), ... , L(k,Nk)_ and the operation being executed is _op_ of duration _N > 0_ - or it involves more than one qubit. - - - Scan from [boundaries included] _L(k,Nk)_ to _L(conditional fence,Nf)_ until find a layer _L(w,Nw)_ - such that _Qubits(w,Nw)_ contain some of _op_'s qubits. - - If _L(w,Nw)_ is found and _op_ can be added into it without increasing the layer's duration, add _op_ into - _L(w,Nw)_, otherwise set _L(w,Nw) = L(conditional fence,Nf)_. - - If _op_ hasn't been added to a layer, scan from [boundaries included] _L(w,Nw)_ to _L(k,Nk)_ until find - a layer _L(t,Nt)_ such that _N <= Nt_ (notice, that this layer cannot contain any qubits from _op_). - - If _L(t,Nt)_ is found, add _op_ into this layer. - - If _op_ hasn't been added to a layer, add _op_ into a new layer _L(k+Nk, max(P, N))_. - - Add the pending operations of all _op_'s qubits into the same layer and clear the pending lists of these qubits. - -## Special handling of SWAP ## - -The tracer will provide a way to handle SWAP as, effectively, renaming of the involved qubits. The users will have the - choice of using the special handling versus treating the gate as a standard counted intrinsic. - -## Frame tracking ## - -A user might want to count differently operations that are applied in a different state. For example, if Hadamard gate - is applied to a qubit and then Rz gate, a user might want to count it as if Rz were executed instead. - The frame is closed when the state of the qubit is reset (in Hadamard's case, another Hadamard operator is applied to - the qubit). The user will be able to register the required frame tracking with the tracer via a C++ registration - callback. - -The descriptor of the frame will contain the following information and will be provided to the Tracer when initializing - it in C++. - -- openingOp: the operation id that opens the frame on the qubits this operation is applied to -- closingOp: the operation id that closes the frame on the qubits this operation is applied to -- vector of: { bitmask_ctls, bitmask_targets, operationIdOriginal, operationIdMapped } - -The closing operation will be ignored if the frame on the qubit hasn't been open. The bitmasks define which of the qubits - should be in an open frame to trigger the mapping. For non-controlled operations the first mask will be ignored. To - begin with, the tracer will support frame mapping for up to 8 control/target qubits. - -__TBD__: C++ definitions of the structure above + the interface to register frame tracking with the Tracer. - -## Output format ## - -The tracer will have options to output the estimates into command line or into a file, specified by the user. In both - cases the output will be in the same format: - -- column separator is configurable (the regex expressions below use comma as separator) -- the first column specifies the time _t_ of a layer _L(t, n)_ or of a barrier -- the second column contains the optional name of the layer or the barrier -- the remaining columns contain counts per operation in the layer (all zeros in case of a barrier) - -- The first row is a header row: `layer_id,name(,[0-9a-zA-Z]+)*`. The fragment `(,[0-9a-zA-Z]+)*` lists operation - names or their ids if the names weren't provided by the user. -- The following rows contain statistics per layer: `[0-9]+,[a-zA-Z]*(,([0-9]*))*`. -- The rows are sorted in order of increasing layer time. -- Zero counts for the statistics _can_ be replaced with empty string. - -The map of operation ids to names can be passed to the tracer's constructor as `std::unordered_map`. - The mapping can be partial, ids will be used in the ouput for unnamed operations. - -Example of valid output: - -```csv -layer_id,name,Y,Z,5 -0,,0,1,0 -1,,0,0,1 -2,b,0,0,0 -4,,0,1,0 -8,,1,0,0 -``` - -## Depth vs width optimizations ## - -TBD but lower priority. - -## List of `__quantum__qis__*` methods, supported by the Tracer ## - -| Signature | Description | -| :---------------------------------------------------- | :----------------------------------------------------------- | -| `void __quantum__qis__inject_barrier(i32 %id, i32 %duration)` | Function to insert a barrier. The first argument is the id of the barrier that can be used to map it to a user-friendly name in the output and the second argument specifies the duration of the barrier. See [Layering](#layering) section for details. | -| `void __quantum__qis__on_module_start(i64 %id)` | Function to identify the start of a quantum module. The argument is a unique _id_ of the module. The tracer will have an option to treat module boundaries as barriers between layers and (_lower priority_) option to cache estimates for a module, executed multiple times. For example, a call to the function might be inserted into QIR, generated by the Q# compiler, immediately before the body code of a Q# `operation`. | -| `void __quantum__qis__on_module_end(i64 %id)` | Function to identify the end of a quantum module. The argument is a unique _id_ of the module and must match the _id_ supplied on start of the module. For example, a call to the function might be inserted into QIR, generated by the Q# compiler, immediately after the body code of a Q# `operation`. | -| `void __quantum__qis__single_qubit_op(i32 %id, i32 %duration, %Qubit* %q)` | Function for counting operations that involve a single qubit. The first argument is the id of the operation. Multiple intrinsics can be assigned the same id, in which case they will be counted together. The second argument is duration to be assigned to the particular invocation of the operation. | -| `void __quantum__qis__multi_qubit_op(i32 %id, i32 %duration, %Array* %qs)` | Function for counting operations that involve multiple qubits.| -| `void __quantum__qis__single_qubit_op__ctl(i32 %id, i32 %duration, %Array* %ctls, %Qubit* %q)` | Function for counting controlled operations with single target qubit and `%ctls` array of controls. | -| `void __quantum__qis__multi_qubit_op__ctl(i32 %id, i32 %duration, %Array* %ctls, %Array* %qs)` | Function for counting controlled operations with multiple target qubits and `%ctls` array of controls. | -| `%Result* @__quantum__qis__single_qubit_measure(i32 %id, i32 %duration, %Qubit* %q)` | Function for counting measurements of a single qubit. The user can assign different operation ids for different measurement bases. | -| `%Result* @__quantum__qis__joint_measure(i32 %id, i32 %duration, %Array* %qs)` | Function for counting joint-measurements of qubits. The user can assign different operation ids for different measurement bases. | -| `void __quantum__qis__swap(%Qubit* %q1, %Qubit* %q2)` | See [Special handling of SWAP](#special-handling-of-swap) for details. | -| `void __quantum__qis__apply_conditionally(%Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different)` | The first two arguments contain arrays of results to be compared pairwise. The third argument is a callable that represents the branch that would be executed if all results compared equal and the forth argument is a callable that represents the branch that would be executed if any of the results compared different. The tracer executes _both_ branches.| - -_Note on operation ids_: The user is responsible for using operation ids in a consistent manner. Operations with the - same id will be counted by the tracer as the _same_ operation, even accross invocations with different number of target - qubits or when different functors are applied. - -_Note on mapping Q# intrinsics to the methods above_: Q# compiler will support Tracer as a special target and will let - the user to either choose some default mapping or specify their custom mapping. For example, see QIR-tracer tests in - this project (`tracer-target.qs` specifies the mapping). - -The Resource Tracer will reuse qir-rt library while implementing the qis methods specified above. diff --git a/src/Qir/Runtime/lib/Tracer/TracerInternal.hpp b/src/Qir/Runtime/lib/Tracer/TracerInternal.hpp deleted file mode 100644 index dbd1390c4f0..00000000000 --- a/src/Qir/Runtime/lib/Tracer/TracerInternal.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#ifndef TRACERINTERNAL_HPP -#define TRACERINTERNAL_HPP - -#include -#include "tracer.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - extern thread_local std::shared_ptr tracer; - -} // namespace Quantum -} // namespace Microsoft - -#endif // #ifndef TRACERINTERNAL_HPP diff --git a/src/Qir/Runtime/lib/Tracer/layering_example.png b/src/Qir/Runtime/lib/Tracer/layering_example.png deleted file mode 100644 index 5713843d8598169f80720dffc2b9fb714ab61527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26209 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 - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" -#include "tracer.hpp" -#include "tracer-qis.hpp" -#include "TracerInternal.hpp" - - -using namespace Microsoft::Quantum; -extern "C" -{ - void __quantum__qis__on_operation_start(int64_t /* id */) // NOLINT - { - } - void __quantum__qis__on_operation_end(int64_t /* id */) // NOLINT - { - } - - void __quantum__qis__swap(QUBIT* /*q1*/, QUBIT* /*q2*/) // NOLINT - { - } - - void __quantum__qis__single_qubit_op(int32_t id, int32_t duration, QUBIT* target) // NOLINT - { - (void)tracer->TraceSingleQubitOp(id, duration, reinterpret_cast(target)); - } - void __quantum__qis__single_qubit_op_ctl(int32_t id, int32_t duration, QirArray* ctls, QUBIT* target) // NOLINT - { - (void)tracer->TraceMultiQubitOp(id, duration, (long)(ctls->count), reinterpret_cast(ctls->buffer), - 1, reinterpret_cast(&target)); - } - void __quantum__qis__multi_qubit_op(int32_t id, int32_t duration, QirArray* targets) // NOLINT - { - (void)tracer->TraceMultiQubitOp(id, duration, 0, nullptr, (long)(targets->count), - reinterpret_cast(targets->buffer)); - } - void __quantum__qis__multi_qubit_op_ctl(int32_t id, int32_t duration, QirArray* ctls, QirArray* targets) // NOLINT - { - (void)tracer->TraceMultiQubitOp(id, duration, (long)(ctls->count), reinterpret_cast(ctls->buffer), - (long)(targets->count), reinterpret_cast(targets->buffer)); - } - - void __quantum__qis__inject_barrier(int32_t id, int32_t duration) // NOLINT - { - (void)tracer->InjectGlobalBarrier(id, duration); - } - - RESULT* __quantum__qis__single_qubit_measure(int32_t id, int32_t duration, QUBIT* q) // NOLINT - { - return tracer->TraceSingleQubitMeasurement(id, duration, reinterpret_cast(q)); - } - - RESULT* __quantum__qis__joint_measure(int32_t id, int32_t duration, QirArray* qs) // NOLINT - { - return tracer->TraceMultiQubitMeasurement(id, duration, (long)(qs->count), - reinterpret_cast(qs->buffer)); - } - - void __quantum__qis__apply_conditionally( // NOLINT - QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, QirCallable* clbOnSomeDifferent) - { - CTracer::FenceScope sf(tracer.get(), (long)(rs1->count), reinterpret_cast(rs1->buffer), - (long)(rs2->count), reinterpret_cast(rs2->buffer)); - - clbOnAllEqual->Invoke(); - clbOnSomeDifferent->Invoke(); - } -} diff --git a/src/Qir/Runtime/lib/Tracer/tracer-qis.hpp b/src/Qir/Runtime/lib/Tracer/tracer-qis.hpp deleted file mode 100644 index 44918fe00c8..00000000000 --- a/src/Qir/Runtime/lib/Tracer/tracer-qis.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - - -#include - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -extern "C" -{ - QIR_SHARED_API void __quantum__qis__on_operation_start(int64_t /* id */); // NOLINT - QIR_SHARED_API void __quantum__qis__on_operation_end(int64_t /* id */); // NOLINT - QIR_SHARED_API void __quantum__qis__swap(QUBIT* /*q1*/, QUBIT* /*q2*/); // NOLINT - - QIR_SHARED_API void __quantum__qis__single_qubit_op(int32_t id, int32_t duration, QUBIT* target); // NOLINT - QIR_SHARED_API void __quantum__qis__single_qubit_op_ctl( // NOLINT - int32_t id, int32_t duration, QirArray* ctls, QUBIT* target); - QIR_SHARED_API void __quantum__qis__multi_qubit_op(int32_t id, int32_t duration, QirArray* targets); // NOLINT - QIR_SHARED_API void __quantum__qis__multi_qubit_op_ctl( // NOLINT - int32_t id, int32_t duration, QirArray* ctls, QirArray* targets); - - QIR_SHARED_API void __quantum__qis__inject_barrier(int32_t id, int32_t duration); // NOLINT - QIR_SHARED_API RESULT* __quantum__qis__single_qubit_measure(int32_t id, int32_t duration, QUBIT* q); // NOLINT - - QIR_SHARED_API RESULT* __quantum__qis__joint_measure(int32_t id, int32_t duration, QirArray* qs); // NOLINT - - QIR_SHARED_API void __quantum__qis__apply_conditionally( // NOLINT - QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, QirCallable* clbOnSomeDifferent); - -} // extern "C" diff --git a/src/Qir/Runtime/lib/Tracer/tracer.cpp b/src/Qir/Runtime/lib/Tracer/tracer.cpp deleted file mode 100644 index 4e9f9510611..00000000000 --- a/src/Qir/Runtime/lib/Tracer/tracer.cpp +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include - -#include "tracer.hpp" -#include "TracerInternal.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - thread_local std::shared_ptr tracer = nullptr; - std::shared_ptr CreateTracer(int preferredLayerDuration) - { - tracer = std::make_shared(preferredLayerDuration); - return tracer; - } - std::shared_ptr CreateTracer(int preferredLayerDuration, - const std::unordered_map& opNames) - { - tracer = std::make_shared(preferredLayerDuration, opNames); - return tracer; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::LaterLayerOf - //------------------------------------------------------------------------------------------------------------------ - /*static*/ LayerId CTracer::LaterLayerOf(LayerId l1, LayerId l2) - { - return std::max(l1, l2); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer's IRuntimeDriver implementation - //------------------------------------------------------------------------------------------------------------------ - QubitIdType CTracer::AllocateQubit() - { - size_t qubit = qubits.size(); - qubits.emplace_back(QubitState{}); - return static_cast(qubit); - } - - void CTracer::ReleaseQubit(QubitIdType /*qubit*/) - { - // nothing for now - } - - // TODO: what would be meaningful information we could printout for a qubit? - std::string CTracer::QubitToString(QubitIdType q) - { - size_t qubitIndex = static_cast(q); - const QubitState& qstate = this->UseQubit(q); - - std::stringstream str(std::to_string(qubitIndex)); - str << " last used in layer " << qstate.layer << "(pending zero ops: " << qstate.pendingZeroDurationOps.size() - << ")"; - return str.str(); - } - - void CTracer::ReleaseResult(Result /*result*/) - { - // nothing to do, we don't allocate results on measurement [yet] - } - - // Although the tracer should never compare results or get their values, it still has to implement UseZero and - // UseOne methods as they are invoked by the QIR initialization. - Result CTracer::UseZero() - { - return reinterpret_cast(INVALID); - } - - Result CTracer::UseOne() - { - return reinterpret_cast(INVALID); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::CreateNewLayer - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::CreateNewLayer(Duration minRequiredDuration) - { - // Create a new layer for the operation. - Time layerStartTime = 0; - if (!this->metricsByLayer.empty()) - { - const Layer& lastLayer = this->metricsByLayer.back(); - layerStartTime = lastLayer.startTime + lastLayer.duration; - } - this->metricsByLayer.emplace_back( - Layer{layerStartTime, std::max(this->preferredLayerDuration, minRequiredDuration)}); - - return (LayerId)(this->metricsByLayer.size() - 1); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::GetEffectiveFence - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::GetEffectiveFence() const - { - return CTracer::LaterLayerOf(this->globalBarrier, this->latestConditionalFence); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::FindLayerToInsertOperationInto - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::FindLayerToInsertOperationInto(QubitIdType q, Duration opDuration) const - { - const QubitState& qstate = this->UseQubit(q); - - LayerId layerToInsertInto = REQUESTNEW; - - const LayerId barrier = this->GetEffectiveFence(); - const LayerId firstLayerAfterBarrier = - (barrier == INVALID ? (this->metricsByLayer.empty() ? REQUESTNEW : 0) - : (((size_t)(barrier + 1) == this->metricsByLayer.size()) ? REQUESTNEW : barrier + 1)); - - LayerId candidate = CTracer::LaterLayerOf(qstate.layer, firstLayerAfterBarrier); - assert(candidate != INVALID); - - if (candidate != REQUESTNEW) - { - // Find the earliest layer that the operation fits in by duration - const Layer& candidateLayer = this->metricsByLayer[(size_t)candidate]; - const Time lastUsedTime = std::max(qstate.lastUsedTime, candidateLayer.startTime); - if (lastUsedTime + opDuration <= candidateLayer.startTime + candidateLayer.duration) - { - layerToInsertInto = candidate; - } - else - { - for (candidate += 1; (size_t)candidate < this->metricsByLayer.size(); ++candidate) - { - if (opDuration <= this->metricsByLayer[(size_t)candidate].duration) - { - layerToInsertInto = candidate; - break; - } - } - } - } - - return layerToInsertInto; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::AddOperationToLayer - //------------------------------------------------------------------------------------------------------------------ - void CTracer::AddOperationToLayer(OpId id, LayerId layer) - { - assert((size_t)layer < this->metricsByLayer.size()); - assert(this->metricsByLayer[(size_t)layer].barrierId == -1 && "Should not add operations to barriers"); - - this->metricsByLayer[(size_t)layer].operations[id] += 1; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::UpdateQubitState - //------------------------------------------------------------------------------------------------------------------ - void CTracer::UpdateQubitState(QubitIdType q, LayerId layer, Duration opDuration) - { - QubitState& qstate = this->UseQubit(q); - for (OpId idPending : qstate.pendingZeroDurationOps) - { - this->AddOperationToLayer(idPending, layer); - } - - // Update the qubit state. - qstate.layer = layer; - const Time layerStart = this->metricsByLayer[(size_t)layer].startTime; - qstate.lastUsedTime = std::max(layerStart, qstate.lastUsedTime) + opDuration; - qstate.pendingZeroDurationOps.clear(); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceSingleQubitOp - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::TraceSingleQubitOp(OpId id, Duration opDuration, QubitIdType target) - { - this->seenOps.insert(id); - - QubitState& qstate = this->UseQubit(target); - const LayerId barrier = this->GetEffectiveFence(); - if (opDuration == 0 && (qstate.layer == INVALID || (barrier != INVALID && qstate.layer < barrier))) - { - qstate.pendingZeroDurationOps.push_back(id); - return INVALID; - } - - // Figure out the layer this operation should go into. - LayerId layerToInsertInto = this->FindLayerToInsertOperationInto(target, opDuration); - if (layerToInsertInto == REQUESTNEW) - { - layerToInsertInto = this->CreateNewLayer(opDuration); - } - - // Add the operation and the pending zero-duration ones into the layer. - this->AddOperationToLayer(id, layerToInsertInto); - this->UpdateQubitState(target, layerToInsertInto, opDuration); - - return layerToInsertInto; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceMultiQubitOp - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::TraceMultiQubitOp(OpId id, Duration opDuration, long nFirstGroup, QubitIdType* firstGroup, - long nSecondGroup, QubitIdType* secondGroup) - { - assert(nFirstGroup >= 0); - assert(nSecondGroup > 0); - - // Special-casing operations of duration zero enables potentially better reuse of qubits, when we'll start - // optimizing for circuit width. However, tracking _the same_ pending operation across _multiple_ qubits is - // tricky and not worth the effort, so we only do single qubit case. - if (opDuration == 0 && nFirstGroup == 0 && nSecondGroup == 1) - { - return this->TraceSingleQubitOp(id, opDuration, secondGroup[0]); - } - - this->seenOps.insert(id); - - // Figure out the layer this operation should go into. - LayerId layerToInsertInto = this->FindLayerToInsertOperationInto(secondGroup[0], opDuration); - for (long i = 1; i < nSecondGroup && layerToInsertInto != REQUESTNEW; i++) - { - layerToInsertInto = - std::max(layerToInsertInto, this->FindLayerToInsertOperationInto(secondGroup[i], opDuration)); - } - for (long i = 0; i < nFirstGroup && layerToInsertInto != REQUESTNEW; i++) - { - layerToInsertInto = - std::max(layerToInsertInto, this->FindLayerToInsertOperationInto(firstGroup[i], opDuration)); - } - if (layerToInsertInto == REQUESTNEW) - { - layerToInsertInto = this->CreateNewLayer(opDuration); - } - - // Add the operation into the layer. - this->AddOperationToLayer(id, layerToInsertInto); - - // Update the state of the involved qubits. - for (long i = 0; i < nFirstGroup; i++) - { - this->UpdateQubitState(firstGroup[i], layerToInsertInto, opDuration); - } - for (long i = 0; i < nSecondGroup; i++) - { - this->UpdateQubitState(secondGroup[i], layerToInsertInto, opDuration); - } - - return layerToInsertInto; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::InjectGlobalBarrier - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::InjectGlobalBarrier(OpId id, Duration duration) - { - LayerId layer = this->CreateNewLayer(duration); - this->metricsByLayer[(size_t)layer].barrierId = id; - this->globalBarrier = layer; - return layer; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceSingleQubitMeasurement - //------------------------------------------------------------------------------------------------------------------ - Result CTracer::TraceSingleQubitMeasurement(OpId id, Duration duration, QubitIdType target) - { - LayerId layerId = this->TraceSingleQubitOp(id, duration, target); - return reinterpret_cast(layerId); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceMultiQubitMeasurement - //------------------------------------------------------------------------------------------------------------------ - Result CTracer::TraceMultiQubitMeasurement(OpId id, Duration duration, long nTargets, QubitIdType* targets) - { - LayerId layerId = this->TraceMultiQubitOp(id, duration, 0, nullptr, nTargets, targets); - return reinterpret_cast(layerId); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::FindLatestMeasurementLayer - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::FindLatestMeasurementLayer(long count, Result* results) - { - LayerId latest = INVALID; - for (long i = 0; i < count; i++) - { - const LayerId id = this->GetLayerIdOfSourceMeasurement(results[i]); - latest = CTracer::LaterLayerOf(latest, id); - } - return latest; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::FenceScope - //------------------------------------------------------------------------------------------------------------------ - CTracer::FenceScope::FenceScope(CTracer* trc, long count1, Result* rs1, long count2, Result* rs2) : tracer(trc) - { - const LayerId fence1 = - (rs1 != nullptr && count1 > 0) ? this->tracer->FindLatestMeasurementLayer(count1, rs1) : INVALID; - const LayerId fence2 = - (rs2 != nullptr && count2 > 0) ? this->tracer->FindLatestMeasurementLayer(count2, rs2) : INVALID; - - this->fence = CTracer::LaterLayerOf(fence1, fence2); - if (this->fence == INVALID) - { - return; - } - assert((size_t)(this->fence) < this->tracer->metricsByLayer.size()); - - this->tracer->conditionalFences.push_back(this->fence); - this->tracer->latestConditionalFence = CTracer::LaterLayerOf(this->tracer->latestConditionalFence, this->fence); - } - CTracer::FenceScope::~FenceScope() - { - if (this->fence == INVALID) - { - return; - } - - std::vector& fences = this->tracer->conditionalFences; - assert(!fences.empty()); - this->tracer->conditionalFences.pop_back(); - - // Update the latest layer (we expect the stack of fences to be shallow so a linear search through it - // should be OK). - this->tracer->latestConditionalFence = - fences.empty() ? INVALID : *std::max_element(fences.begin(), fences.end()); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::PrintLayerMetrics - //------------------------------------------------------------------------------------------------------------------ - static std::string GetOperationName(OpId opId, const std::unordered_map& opNames) - { - if (opId < 0) - { - return ""; - } - - auto nameIt = opNames.find(opId); - return nameIt == opNames.end() ? std::to_string(opId) : nameIt->second; - } - void CTracer::PrintLayerMetrics(std::ostream& out, const std::string& separator, bool printZeroMetrics) const - { - // Sort the operations by id so the output is deterministic. - std::set seenOpsOrderedById(this->seenOps.begin(), this->seenOps.end()); - - // header row - out << "layer_id" << separator << "name"; - for (OpId opId : seenOpsOrderedById) - { - out << separator << GetOperationName(opId, this->opNames); - } - out << std::endl; - - // data rows - const std::string zeroString = printZeroMetrics ? "0" : ""; - for (const Layer& layer : this->metricsByLayer) - { - out << layer.startTime; - out << separator << GetOperationName(layer.barrierId, this->opNames); - - for (OpId opId : seenOpsOrderedById) - { - auto foundInLayer = layer.operations.find(opId); - out << separator - << ((foundInLayer == layer.operations.end()) ? zeroString : std::to_string(foundInLayer->second)); - } - out << std::endl; - } - } -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/Tracer/tracer.hpp b/src/Qir/Runtime/lib/Tracer/tracer.hpp deleted file mode 100644 index fcaccb96762..00000000000 --- a/src/Qir/Runtime/lib/Tracer/tracer.hpp +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once - -#include -#include -#include -#include -#include - -#include "CoreTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "TracerTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - /*================================================================================================================== - Layer - ==================================================================================================================*/ - struct QIR_SHARED_API Layer - { - // Start time of the layer. - const Time startTime; - - // Width of the layer on the time axis. - const Duration duration; - - // Quantum operations, assigned to this layer. - std::unordered_map operations; - - // Optional id, if the layer represents a global barrier. - OpId barrierId = -1; - - Layer(Time startTm, Duration dur) - // clang-format off - : startTime(startTm) - , duration(dur) - // clang-format on - { - } - - Layer(Layer&&) = default; // NOLINT(bugprone-exception-escape) // TODO(rokuzmin): Clang-tidy 14.0.6 complains - // that an exception can escape from the implicit move constructor. Failed to figure out within a reasonable - // time how to make the move constructor `noexecpt`. - }; - - /*================================================================================================================== - QubitState - ==================================================================================================================*/ - struct QIR_SHARED_API QubitState - { - // The last layer this qubit was used in, `INVALID` means the qubit haven't been used yet in any - // operations of non-zero duration. - LayerId layer = INVALID; - - // `lastUsedTime` stores the end time of the last operation, the qubit participated in. It might not match the - // end time of a layer, if the duration of the last operation is less than duration of the layer. Tracking this - // time allows us to possibly fit multiple short operations on the same qubit into a single layer. - Time lastUsedTime = 0; - - std::vector pendingZeroDurationOps; - }; - - /*================================================================================================================== - The tracer implements resource estimation. See readme in this folder for details. - ==================================================================================================================*/ - class QIR_SHARED_API CTracer : public IRuntimeDriver - { - // For now the tracer assumes no reuse of qubits. - std::vector qubits; - - // The preferred duration of a layer. An operation with longer duration will make the containing layer longer. - const int preferredLayerDuration = 0; - - // The index into the vector is treated as implicit id of the layer. - std::vector metricsByLayer; - - // The last barrier, injected by the user. No new operations can be added to the barrier or to any of the - // layer that preceeded it, even if the new operations involve completely new qubits. Thus, the barriers act - // as permanent fences, that are activated at the moment the tracer executes the corresponding user code and are - // never removed. - LayerId globalBarrier = INVALID; - - // The conditional fences are layers that contain measurements for results used to guard conditional branches. - // The set of fences is a stack (for nested conditionals) but we use vector to store them so we can recalculate - // the latest (by time) fence when the stack is popped. - std::vector conditionalFences; - - // We don't expect the stack of conditional fences to be deep, so it's OK to recalculate the latest layer when - // the stack is modified. - LayerId latestConditionalFence = INVALID; - - // Mapping of operation ids to user-chosen names, for operations that user didn't name, the output will use - // operation ids. - std::unordered_map opNames; - - // Operations we've seen so far (to be able to trim output to include only those that were encounted). - std::unordered_set seenOps; - - private: - QubitState& UseQubit(QubitIdType q) - { - size_t qubitIndex = static_cast(q); - assert(qubitIndex < this->qubits.size()); - return this->qubits[qubitIndex]; - } - const QubitState& UseQubit(QubitIdType q) const - { - size_t qubitIndex = static_cast(q); - assert(qubitIndex < this->qubits.size()); - return this->qubits[qubitIndex]; - } - - // If no appropriate layer found, returns `REQUESTNEW`. - LayerId FindLayerToInsertOperationInto(QubitIdType q, Duration opDuration) const; - - // Returns the index of the created layer. - LayerId CreateNewLayer(Duration minRequiredDuration); - - // Adds operation with given id into the given layer. Assumes that duration contraints have been satisfied. - void AddOperationToLayer(OpId id, LayerId layer); - - // Update the qubit state with the new layer information. - void UpdateQubitState(QubitIdType q, LayerId layer, Duration opDuration); - - // Considers global barriers and conditional fences to find the fence currently in effect. - LayerId GetEffectiveFence() const; - - // For the given results finds the latest layer of the measurements that produced the results. - LayerId FindLatestMeasurementLayer(long count, Result* results); - - public: - // Returns the later layer of the two. INVALID LayerId is treated as -Infinity, and REQUESTNEW -- as +Infinity. - static LayerId LaterLayerOf(LayerId l1, LayerId l2); - - explicit CTracer(int preferredLayerDur) - // clang-format off - : preferredLayerDuration(preferredLayerDur) - // clang-format on - { - } - - CTracer(int preferredLayerDur, const std::unordered_map& operNames) - : preferredLayerDuration(preferredLayerDur) - , opNames(operNames) - { - } - - // ------------------------------------------------------------------------------------------------------------- - // IRuntimeDriver interface - // ------------------------------------------------------------------------------------------------------------- - QubitIdType AllocateQubit() override; - void ReleaseQubit(QubitIdType qubit) override; - std::string QubitToString(QubitIdType qubit) override; - void ReleaseResult(Result result) override; - - bool AreEqualResults(Result /*r1*/, Result /*r2*/) override - { - throw std::logic_error("Cannot compare results while tracing!"); - } - ResultValue GetResultValue(Result /*result*/) override - { - throw std::logic_error("Result values aren't available while tracing!"); - } - Result UseZero() override; - Result UseOne() override; - - // ------------------------------------------------------------------------------------------------------------- - // Instead of implementing IQuantumGateSet, the tracer provides 'tracing-by-id' methods. The QIR generation - // should translate all intrinsics to invoke these methods. - // The tracer doesn't differentiate between control and target qubits. However, While it could provide a single - // generic tracing method for and array of qubits, that would require the clients to copy control and target - // qubits into the same array. To avoid the copy, the tracer provides a method that takes two groups of qubits, - // where the first one can be empty or can be viewed as the set of controls. - // ------------------------------------------------------------------------------------------------------------- - LayerId TraceSingleQubitOp(OpId id, Duration duration, QubitIdType target); - LayerId TraceMultiQubitOp(OpId id, Duration duration, long nFirstGroup, QubitIdType* firstGroup, - long nSecondGroup, QubitIdType* secondGroup); - - Result TraceSingleQubitMeasurement(OpId id, Duration duration, QubitIdType target); - Result TraceMultiQubitMeasurement(OpId id, Duration duration, long nTargets, QubitIdType* targets); - LayerId GetLayerIdOfSourceMeasurement(Result r) const - { - return reinterpret_cast(r); - } - - // ------------------------------------------------------------------------------------------------------------- - // Backing of the rest of the bridge methods. - // ------------------------------------------------------------------------------------------------------------- - LayerId InjectGlobalBarrier(OpId id, Duration duration); - - struct FenceScope - { - CTracer* tracer = nullptr; - LayerId fence = INVALID; - explicit FenceScope(CTracer* tracer, long count1, Result* results1, long count2, Result* results2); - ~FenceScope(); - }; - - // ------------------------------------------------------------------------------------------------------------- - // Configuring the tracer and getting data back from it. - // ------------------------------------------------------------------------------------------------------------- - // Temporary method for initial testing - // TODO: replace with a safer accessor - const std::vector& UseLayers() - { - return this->metricsByLayer; - } - - void PrintLayerMetrics(std::ostream& out, const std::string& separator, bool printZeroMetrics) const; - }; - - QIR_SHARED_API std::shared_ptr CreateTracer(int preferredLayerDuration); - QIR_SHARED_API std::shared_ptr CreateTracer(int preferredLayerDuration, - const std::unordered_map& opNames); - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index 217123cd44e..84447e6483f 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -50,5 +50,29 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { apt-get install -y clang-14 clang-tidy-14 clang-format-14 } } -} + + 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 toolchain install nightly + rustup component add rustfmt clippy llvm-tools-preview + rustup component add rustfmt clippy llvm-tools-preview --toolchain nightly + } diff --git a/src/Qir/Runtime/public/BasicRuntimeDriverFactory.h b/src/Qir/Runtime/public/BasicRuntimeDriverFactory.h deleted file mode 100644 index ca33e2b609b..00000000000 --- a/src/Qir/Runtime/public/BasicRuntimeDriverFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef BASICRUNTIMEDRIVERFACTORY_H -#define BASICRUNTIMEDRIVERFACTORY_H - -#include -#include "CoreDefines.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - QIR_SHARED_API void* CreateBasicRuntimeDriver(); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef BASICRUNTIMEDRIVERFACTORY_H diff --git a/src/Qir/Runtime/public/CoreDefines.h b/src/Qir/Runtime/public/CoreDefines.h deleted file mode 100644 index c98f5f64500..00000000000 --- a/src/Qir/Runtime/public/CoreDefines.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef COREDEFINES_H -#define COREDEFINES_H - -#ifdef _WIN32 -#ifdef EXPORT_QIR_API -#define QIR_SHARED_API __declspec(dllexport) -#else -#define QIR_SHARED_API __declspec(dllimport) -#endif -#else -#define QIR_SHARED_API -#endif - -#endif // #ifndef COREDEFINES_H diff --git a/src/Qir/Runtime/public/CoreTypes.hpp b/src/Qir/Runtime/public/CoreTypes.hpp deleted file mode 100644 index 2491ac5306e..00000000000 --- a/src/Qir/Runtime/public/CoreTypes.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include "CoreDefines.h" - -// The core types will be exposed in the C-interfaces for interop, thus no -// namespaces or scoped enums can be used to define them. - -/*============================================================================== - Qubit & Result - - These two types are opaque to the clients: clients cannot directly create, delete, - copy or check state of qubits and results. QUBIT* and RESULT* should never be - dereferenced in client's code. -==============================================================================*/ - -// Although QIR uses an opaque pointer to the type "QUBIT", it never points to an actual memory -// and is never intended to be dereferenced anywhere - in the client code or in our runtime. -// Runtime always operates in terms of qubit ids, which are integers. Qubit ids are casted -// to this pointer type and stored as pointer values. This is done to ensure that qubit type -// is a unique type in the QIR. - -class QUBIT; -typedef intptr_t QubitIdType; - -class RESULT; -typedef RESULT* Result; // TODO(rokuzmin): Replace with `typedef uintXX_t Result`, where XX is 8|16|32|64. - // Remove all the `RESULT`. - -enum ResultValue -{ - Result_Zero = 0, - Result_One = 1, - Result_Pending, // indicates that this is a deferred result -}; - -/*============================================================================== - PauliId matrices -==============================================================================*/ -enum PauliId : int8_t -{ - PauliId_I = 0, - PauliId_X = 1, - PauliId_Z = 2, - PauliId_Y = 3, -}; diff --git a/src/Qir/Runtime/public/OutputStream.hpp b/src/Qir/Runtime/public/OutputStream.hpp deleted file mode 100644 index 8bfba57e4fa..00000000000 --- a/src/Qir/Runtime/public/OutputStream.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef OUTPUTSTREAM_HPP -#define OUTPUTSTREAM_HPP - -#include -#include "CoreTypes.hpp" // QIR_SHARED_API - -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - struct QIR_SHARED_API OutputStream - { - struct QIR_SHARED_API ScopedRedirector - { - explicit ScopedRedirector(std::ostream& newOstream); - ~ScopedRedirector(); - - private: - ScopedRedirector(const ScopedRedirector&) = delete; - ScopedRedirector& operator=(const ScopedRedirector&) = delete; - ScopedRedirector(ScopedRedirector&&) = delete; - ScopedRedirector& operator=(ScopedRedirector&&) = delete; - - private: - std::ostream& old; - }; - - static std::ostream& Get(); - static std::ostream& Set(std::ostream& newOStream); - - private: - static std::ostream* currentOutputStream; - }; - -} // namespace Quantum -} // namespace Microsoft - -#endif // #ifndef OUTPUTSTREAM_HPP diff --git a/src/Qir/Runtime/public/QSharpSimApi_I.hpp b/src/Qir/Runtime/public/QSharpSimApi_I.hpp deleted file mode 100644 index 11d0473cde3..00000000000 --- a/src/Qir/Runtime/public/QSharpSimApi_I.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct QIR_SHARED_API IQuantumGateSet - { - virtual ~IQuantumGateSet() - { - } - IQuantumGateSet() = default; - - // Elementary operatons - virtual void X(QubitIdType target) = 0; - virtual void Y(QubitIdType target) = 0; - virtual void Z(QubitIdType target) = 0; - virtual void H(QubitIdType target) = 0; - virtual void S(QubitIdType target) = 0; - virtual void T(QubitIdType target) = 0; - virtual void R(PauliId axis, QubitIdType target, double theta) = 0; - virtual void Exp(long numTargets, PauliId paulis[], QubitIdType targets[], double theta) = 0; - - // Multicontrolled operations - virtual void ControlledX(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledY(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledZ(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledH(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledS(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledT(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledR(long numControls, QubitIdType controls[], PauliId axis, QubitIdType target, - double theta) = 0; - virtual void ControlledExp(long numControls, QubitIdType controls[], long numTargets, PauliId paulis[], - QubitIdType targets[], double theta) = 0; - - // Adjoint operations - virtual void AdjointS(QubitIdType target) = 0; - virtual void AdjointT(QubitIdType target) = 0; - virtual void ControlledAdjointS(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledAdjointT(long numControls, QubitIdType controls[], QubitIdType target) = 0; - - // Results - virtual Result Measure(long numBases, PauliId bases[], long numTargets, QubitIdType targets[]) = 0; - - private: - IQuantumGateSet& operator=(const IQuantumGateSet&) = delete; - IQuantumGateSet(const IQuantumGateSet&) = delete; - }; - - struct QIR_SHARED_API IDiagnostics - { - virtual ~IDiagnostics() - { - } - IDiagnostics() = default; - - // The callback should be invoked on each basis vector (in the standard computational basis) in little-endian - // order until it returns `false` or the state is fully dumped. - typedef bool (*TGetStateCallback)(const char* /*basis vector*/, double /* amplitude Re*/, - double /* amplitude Im*/); - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - virtual void GetState(TGetStateCallback callback) = 0; - - virtual void DumpMachine(const void* location) = 0; - virtual void DumpRegister(const void* location, const QirArray* qubits) = 0; - - // Both Assert methods return `true`, if the assert holds, `false` otherwise. - virtual bool Assert(long numTargets, PauliId bases[], QubitIdType targets[], Result result, - const char* failureMessage) = 0; // TODO: The `failureMessage` is not used, consider - // removing. The `bool` is returned. - - virtual bool AssertProbability(long numTargets, PauliId bases[], QubitIdType targets[], - double probabilityOfZero, double precision, - const char* failureMessage) = 0; // TODO: The `failureMessage` is not used, - // consider removing. The `bool` is returned. - - private: - IDiagnostics& operator=(const IDiagnostics&) = delete; - IDiagnostics(const IDiagnostics&) = delete; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirContext.h b/src/Qir/Runtime/public/QirContext.h deleted file mode 100644 index 01f140b07a8..00000000000 --- a/src/Qir/Runtime/public/QirContext.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef QIRCONTEXT_H -#define QIRCONTEXT_H - -#include -#include "CoreDefines.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - QIR_SHARED_API void InitializeQirContext(void* driver, bool trackAllocatedObjects); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef QIRCONTEXT_H diff --git a/src/Qir/Runtime/public/QirContext.hpp b/src/Qir/Runtime/public/QirContext.hpp deleted file mode 100644 index ec298db64a0..00000000000 --- a/src/Qir/Runtime/public/QirContext.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -#include "CoreTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct IRuntimeDriver; - struct AllocationsTracker; - - // Deprecated: Use `QirExecutionContext::Init()` instead. - QIR_SHARED_API void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - // Deprecated: Use `QirExecutionContext::Deinit()` instead. - QIR_SHARED_API void ReleaseQirContext(); - - struct QIR_SHARED_API QirExecutionContext - { - // Direct access from outside of `QirExecutionContext` is deprecated: The variables are to become `private`. - // { - // Use `Microsoft::Quantum::GlobalContext()->GetDriver()` instead: - IRuntimeDriver* driver = nullptr; - // Use `QirExecutionContext::{OnAddRef(), OnRelease(), OnAllocate()}`instead of direct access: - bool trackAllocatedObjects = false; - std::unique_ptr allocationsTracker; - // } - - static void Init(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - static void Deinit(); - - QirExecutionContext(IRuntimeDriver* driver, bool trackAllocatedObjects); - ~QirExecutionContext(); - - void OnAddRef(void* object); - void OnRelease(void* object); - void OnAllocate(void* object); - - IRuntimeDriver* GetDriver() const; - - struct QIR_SHARED_API Scoped - { - Scoped(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - ~Scoped(); - - private: - Scoped& operator=(const Scoped&) = delete; - }; - }; - - // Direct access is deprecated, use GlobalContext() instead. - extern std::unique_ptr g_context; - extern QIR_SHARED_API std::unique_ptr& GlobalContext(); - - // Deprecated, use `QirExecutionContext::Scoped` instead. - struct QIR_SHARED_API QirContextScope - { - QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - ~QirContextScope(); - - private: - QirContextScope& operator=(const QirContextScope&) = delete; - }; -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirOutputHandling.hpp b/src/Qir/Runtime/public/QirOutputHandling.hpp deleted file mode 100644 index b964b649368..00000000000 --- a/src/Qir/Runtime/public/QirOutputHandling.hpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef QIROUTPUTHANDLING_HPP -#define QIROUTPUTHANDLING_HPP - -// QOH QIR Output Handling. -// REC Record. - -#ifdef __cplusplus -#include -#else -#include -#endif - -#include "CoreDefines.h" -#include "CoreTypes.hpp" - -// clang-format off -#define QOH_REC_TYPE "RESULT" -#define QOH_REC_COLUMN_DELIMITER "\t" -#define QOH_REC_PREFIX QOH_REC_TYPE QOH_REC_COLUMN_DELIMITER /* "RESULT" "\t" (== "RESULT\t") */ -#define QOH_REC_DELIMITER "\n" - -#define QOH_REC_VAL_TUPLE_START "TUPLE_START" -#define QOH_REC_VAL_TUPLE_END "TUPLE_END" -#define QOH_REC_VAL_ARRAY_START "ARRAY_START" -#define QOH_REC_VAL_ARRAY_END "ARRAY_END" -#define QOH_REC_VAL_RESULT_ZERO "0" -#define QOH_REC_VAL_RESULT_ONE "1" -#define QOH_REC_VAL_FALSE "false" -#define QOH_REC_VAL_TRUE "true" - -#define QOH_REC_TUPLE_START QOH_REC_PREFIX QOH_REC_VAL_TUPLE_START /* "RESULT" "\t" "TUPLE_START" (== "RESULT\tTUPLE_START") */ -#define QOH_REC_TUPLE_END QOH_REC_PREFIX QOH_REC_VAL_TUPLE_END -#define QOH_REC_ARRAY_START QOH_REC_PREFIX QOH_REC_VAL_ARRAY_START -#define QOH_REC_ARRAY_END QOH_REC_PREFIX QOH_REC_VAL_ARRAY_END -#define QOH_REC_RESULT_ZERO QOH_REC_PREFIX QOH_REC_VAL_RESULT_ZERO -#define QOH_REC_RESULT_ONE QOH_REC_PREFIX QOH_REC_VAL_RESULT_ONE -#define QOH_REC_FALSE QOH_REC_PREFIX QOH_REC_VAL_FALSE -#define QOH_REC_TRUE QOH_REC_PREFIX QOH_REC_VAL_TRUE -// clang-format on - - -#ifdef __cplusplus -extern "C" -{ -#endif - - // Primitive Result Records - - /// Produces output records of exactly "RESULT\\t0" or "RESULT\\t1" - QIR_SHARED_API void __quantum__rt__result_record_output(Result); // NOLINT - - /// Produces output records of exactly "RESULT\\tfalse" or "RESULT\\ttrue" - QIR_SHARED_API void __quantum__rt__bool_record_output(bool); // NOLINT - - /// Produces output records of the format "RESULT\\tn" where n is the string representation of the integer value, - /// such as "RESULT\\t42" - QIR_SHARED_API void __quantum__rt__integer_record_output(int64_t); // NOLINT - - /// Produces output records of the format "RESULT\\td" where d is the string representation of the double value, - /// such as "RESULT\\t3.14" - QIR_SHARED_API void __quantum__rt__double_record_output(double); // NOLINT - - - // Tuple Type Records - - /// Produces output records of exactly "RESULT\\tTUPLE_START" - QIR_SHARED_API void __quantum__rt__tuple_start_record_output(); // NOLINT - - /// Produces output records of exactly "RESULT\\tTUPLE_END" - QIR_SHARED_API void __quantum__rt__tuple_end_record_output(); // NOLINT - - - // Array Type Records - - /// Produces output records of exactly "RESULT\\tARRAY_START" - QIR_SHARED_API void __quantum__rt__array_start_record_output(); // NOLINT - - /// Produces output records of exactly "RESULT\\tARRAY_END" - QIR_SHARED_API void __quantum__rt__array_end_record_output(); // NOLINT - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef QIROUTPUTHANDLING_HPP diff --git a/src/Qir/Runtime/public/QirRuntime.hpp b/src/Qir/Runtime/public/QirRuntime.hpp deleted file mode 100644 index 93b0da45eac..00000000000 --- a/src/Qir/Runtime/public/QirRuntime.hpp +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include // for va_list - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -extern "C" -{ - // ------------------------------------------------------------------------ - // Qubit Management. - // ------------------------------------------------------------------------ - - // Allocate one qubit. Qubit is guaranteed to be in |0> state. - // Qubit needs to be released via __quantum__rt__qubit_release. - QIR_SHARED_API QUBIT* __quantum__rt__qubit_allocate(); // NOLINT - - // Allocate 'count' qubits, allocate and return an array that owns these qubits. - // Array and qubits in the array need to be released via __quantum__rt__qubit_release_array. - QIR_SHARED_API QirArray* __quantum__rt__qubit_allocate_array(int64_t count); // NOLINT - - // Release one qubit. - QIR_SHARED_API void __quantum__rt__qubit_release(QUBIT*); // NOLINT - - // Release qubits, owned by the array and the array itself. - QIR_SHARED_API void __quantum__rt__qubit_release_array(QirArray*); // NOLINT - - // Borrow one qubit. Qubit is not guaranteed to be in |0> state. - // Qubit needs to be returned via __quantum__rt__qubit_return in the same state in which it was borrowed. - QIR_SHARED_API QUBIT* __quantum__rt__qubit_borrow(); // NOLINT - - // Borrow 'count' qubits, allocate and return an array that owns these qubits. - // Array and qubits in the array need to be returned via __quantum__rt__qubit_return_array. - QIR_SHARED_API QirArray* __quantum__rt__qubit_borrow_array(int64_t count); // NOLINT - - // Return one borrowed qubit. Qubit must be in the same state in which it was borrowed. - QIR_SHARED_API void __quantum__rt__qubit_return(QUBIT*); // NOLINT - - // Return borrowed qubits owned by the array. Release array itself. - QIR_SHARED_API void __quantum__rt__qubit_return_array(QirArray*); // NOLINT - - // ------------------------------------------------------------------------ - // Qubit Management Restricted Reuse Control. - // ------------------------------------------------------------------------ - - // Start restricted reuse area. - // Qubits released within one segment of an area cannot be reused in other segments of the same area. - QIR_SHARED_API void __quantum__rt__qubit_restricted_reuse_area_start(); // NOLINT - - // End current restricted reuse segment and start the next one within the current area. - QIR_SHARED_API void __quantum__rt__qubit_restricted_reuse_segment_next(); // NOLINT - - // End current restricted reuse area. - QIR_SHARED_API void __quantum__rt__qubit_restricted_reuse_area_end(); // NOLINT - - // ------------------------------------------------------------------------ - // Utils - // ------------------------------------------------------------------------ - - // Returns a pointer to the malloc-allocated block. - QIR_SHARED_API char* __quantum__rt__memory_allocate(uint64_t size); // NOLINT - - // Fail the computation with the given error message. - [[noreturn]] QIR_SHARED_API void __quantum__rt__fail(QirString* msg); // NOLINT - [[noreturn]] QIR_SHARED_API void __quantum__rt__fail_cstr(const char* msg); // NOLINT - - // Include the given message in the computation's execution log or equivalent. - QIR_SHARED_API void __quantum__rt__message(QirString* msg); // NOLINT - - // ------------------------------------------------------------------------ - // Results - // ------------------------------------------------------------------------ - - // Returns true if the two results are the same, and false if they are different. - QIR_SHARED_API bool __quantum__rt__result_equal(RESULT*, RESULT*); // NOLINT - - // Adds the given integer value to the reference count for the result. Deallocates the result if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__result_update_reference_count(RESULT*, int32_t); // NOLINT - - QIR_SHARED_API RESULT* __quantum__rt__result_get_one(); // NOLINT - QIR_SHARED_API RESULT* __quantum__rt__result_get_zero(); // NOLINT - - // ------------------------------------------------------------------------ - // Tuples - // ------------------------------------------------------------------------ - - // Allocates space for a tuple requiring the given number of bytes and sets the reference count to 1. - QIR_SHARED_API PTuple __quantum__rt__tuple_create(int64_t); // NOLINT - - // Adds the given integer value to the reference count for the tuple. Deallocates the tuple if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__tuple_update_reference_count(PTuple, int32_t); // NOLINT - - // Adds the given integer value to the alias count for the tuple. Fails if the count becomes negative. - QIR_SHARED_API void __quantum__rt__tuple_update_alias_count(PTuple, int32_t); // NOLINT - - // Creates a shallow copy of the tuple if the user count is larger than 0 or the second argument is `true`. - QIR_SHARED_API PTuple __quantum__rt__tuple_copy(PTuple, bool force); // NOLINT - - // ------------------------------------------------------------------------ - // Arrrays - // ------------------------------------------------------------------------ - - // Creates a new 1-dimensional array. The int is the size of each element in bytes. The int64_t is the length - // of the array. The bytes of the new array should be set to zero. - QIR_SHARED_API QirArray* __quantum__rt__array_create_1d(int32_t, int64_t); // NOLINT - - // Adds the given integer value to the reference count for the array. Deallocates the array if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__array_update_reference_count(QirArray*, int32_t); // NOLINT - - // Adds the given integer value to the alias count for the array. Fails if the count becomes negative. - QIR_SHARED_API void __quantum__rt__array_update_alias_count(QirArray*, int32_t); // NOLINT - - // Creates a shallow copy of the array if the user count is larger than 0 or the second argument is `true`. - QIR_SHARED_API QirArray* __quantum__rt__array_copy(QirArray*, bool); // NOLINT - - // Returns a new array which is the concatenation of the two passed-in arrays. - QIR_SHARED_API QirArray* __quantum__rt__array_concatenate(QirArray*, QirArray*); // NOLINT - - // Returns the length of a dimension of the array. The int is the zero-based dimension to return the length of; it - // must be 0 for a 1-dimensional array. - QIR_SHARED_API int64_t __quantum__rt__array_get_size(QirArray*, int32_t); // NOLINT - QIR_SHARED_API int64_t __quantum__rt__array_get_size_1d(QirArray*); // NOLINT - - // Returns a pointer to the element of the array at the zero-based index given by the int64_t. - QIR_SHARED_API char* __quantum__rt__array_get_element_ptr_1d(QirArray*, int64_t); // NOLINT - - // Creates a new array. The first int is the size of each element in bytes. The second int is the dimension count. - // The variable arguments should be a sequence of int64_ts contains the length of each dimension. The bytes of the - // new array should be set to zero. - QIR_SHARED_API QirArray* __quantum__rt__array_create(int, int, ...); // NOLINT - QIR_SHARED_API QirArray* __quantum__rt__array_create_nonvariadic( // NOLINT - int itemSizeInBytes, int countDimensions, va_list dims); - - // Returns the number of dimensions in the array. - QIR_SHARED_API int32_t __quantum__rt__array_get_dim(QirArray*); // NOLINT - - // Returns a pointer to the indicated element of the array. The variable arguments should be a sequence of int64_ts - // that are the indices for each dimension. - QIR_SHARED_API char* __quantum__rt__array_get_element_ptr(QirArray*, ...); // NOLINT - QIR_SHARED_API char* __quantum__rt__array_get_element_ptr_nonvariadic(QirArray*, va_list dims); // NOLINT - - // Creates and returns an array that is a slice of an existing array. The int indicates which dimension - // the slice is on. The %Range specifies the slice. - QIR_SHARED_API QirArray* quantum__rt__array_slice(QirArray*, int32_t, const QirRange&, // NOLINT - bool /*ignored: forceNewInstance*/); - - // Creates and returns an array that is a projection of an existing array. The int indicates which dimension the - // projection is on, and the int64_t specifies the specific index value to project. The returned Array* will have - // one fewer dimension than the existing array. - QIR_SHARED_API QirArray* __quantum__rt__array_project(QirArray*, int32_t, int64_t); // NOLINT - - // ------------------------------------------------------------------------ - // Callables - // ------------------------------------------------------------------------ - - // Initializes the callable with the provided function table and capture tuple. The capture tuple pointer - // should be null if there is no capture. - QIR_SHARED_API QirCallable* __quantum__rt__callable_create(t_CallableEntry*, t_CaptureCallback*, PTuple); // NOLINT - - // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference - // count becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__callable_update_reference_count(QirCallable*, int32_t); // NOLINT - - // Adds the given integer value to the alias count for the callable. Fails if the count becomes negative. - QIR_SHARED_API void __quantum__rt__callable_update_alias_count(QirCallable*, int32_t); // NOLINT - - // Creates a shallow copy of the callable if the alias count is larger than 0 or the second argument is `true`. - // Returns the given callable pointer otherwise, after increasing its reference count by 1. - QIR_SHARED_API QirCallable* __quantum__rt__callable_copy(QirCallable*, bool); // NOLINT - - // Invokes the callable with the provided argument tuple and fills in the result tuple. - QIR_SHARED_API void __quantum__rt__callable_invoke(QirCallable*, PTuple, PTuple); // NOLINT - - // Updates the callable by applying the Adjoint functor. - QIR_SHARED_API void __quantum__rt__callable_make_adjoint(QirCallable*); // NOLINT - - // Updates the callable by applying the Controlled functor. - QIR_SHARED_API void __quantum__rt__callable_make_controlled(QirCallable*); // NOLINT - - // Invokes the function in the corresponding index in the memory management table of the callable with the capture - // tuple and the given 32-bit integer. Does nothing if the memory management table pointer or the function pointer - // at that index is null. - QIR_SHARED_API void __quantum__rt__capture_update_reference_count(QirCallable*, int32_t); // NOLINT - QIR_SHARED_API void __quantum__rt__capture_update_alias_count(QirCallable*, int32_t); // NOLINT - - // ------------------------------------------------------------------------ - // Strings - // ------------------------------------------------------------------------ - - // Creates a string from an array of UTF-8 bytes. - // TODO the provided constructor doesn't match the spec! - // QIR_SHARED_API QirString* __quantum__rt__string_create(int, char*); // NOLINT - QIR_SHARED_API QirString* __quantum__rt__string_create(const char*); // NOLINT - - // Adds the given integer value to the reference count for the string. Deallocates the string if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__string_update_reference_count(QirString*, int32_t); // NOLINT - - // Creates a new string that is the concatenation of the two argument strings. - QIR_SHARED_API QirString* __quantum__rt__string_concatenate(QirString*, QirString*); // NOLINT - - // Returns true if the two strings are equal, false otherwise. - QIR_SHARED_API bool __quantum__rt__string_equal(QirString*, QirString*); // NOLINT - - // Returns a string representation of the integer. - QIR_SHARED_API QirString* __quantum__rt__int_to_string(int64_t); // NOLINT - - // Returns a string representation of the double. - QIR_SHARED_API QirString* __quantum__rt__double_to_string(double); // NOLINT - - // Returns a string representation of the Boolean. - QIR_SHARED_API QirString* __quantum__rt__bool_to_string(bool); // NOLINT - - // Returns a string representation of the result. - QIR_SHARED_API QirString* __quantum__rt__result_to_string(RESULT*); // NOLINT - - // Returns a string representation of the Pauli. - QIR_SHARED_API QirString* __quantum__rt__pauli_to_string(PauliId); // NOLINT - - // Returns a string representation of the qubit. - QIR_SHARED_API QirString* __quantum__rt__qubit_to_string(QUBIT*); // NOLINT - - // Returns a string representation of the range. - QIR_SHARED_API QirString* quantum__rt__range_to_string(const QirRange&); // NOLINT - - // Returns a pointer to an array that contains a null-terminated sequence of characters - // (i.e., a C-string) representing the current value of the string object. - QIR_SHARED_API const char* __quantum__rt__string_get_data(QirString* str); // NOLINT - - // Returns the length of the string, in terms of bytes. - // http://www.cplusplus.com/reference/string/string/size/ - QIR_SHARED_API uint32_t __quantum__rt__string_get_length(QirString* str); // NOLINT - - // Returns a string representation of the big integer. - // TODO QIR_SHARED_API QirString* __quantum__rt__bigint_to_string(QirBigInt*); // NOLINT - - // ------------------------------------------------------------------------ - // BigInts - // ------------------------------------------------------------------------ - - // Creates a big integer with the specified initial value. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_int64_t(int64_t); // NOLINT - - // Creates a big integer with the initial value specified by the i8 array. The 0-th element of the array is the - // highest-order byte, followed by the first element, etc. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_array(int, char*); // NOLINT - - // Adds the given integer value to the reference count for the big integer. Deallocates the big integer if the - // reference count becomes 0. The behavior is undefined if the reference count becomes negative. - // TODO QIR_SHARED_API void __quantum__rt__bigint_update_reference_count(QirBigInt*, int32_t); // NOLINT - - // Returns the negative of the big integer. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_negate(QirBigInt*); // NOLINT - - // Adds two big integers and returns their sum. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_add(QirBigInt*, QirBigInt*); // NOLINT - - // Subtracts the second big integer from the first and returns their difference. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_subtract(QirBigInt*, QirBigInt*); // NOLINT - - // Multiplies two big integers and returns their product. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_multiply(QirBigInt*, QirBigInt*); // NOLINT - - // Divides the first big integer by the second and returns their quotient. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_divide(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the first big integer modulo the second. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_modulus(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the big integer raised to the integer power. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_power(QirBigInt*, int); // NOLINT - - // Returns the bitwise-AND of two big integers. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitand(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the bitwise-OR of two big integers. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitor(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the bitwise-XOR of two big integers. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitxor(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the bitwise complement of the big integer. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitnot(QirBigInt*); // NOLINT - - // Returns the big integer arithmetically shifted left by the integer amount of bits. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftleft(QirBigInt*, int64_t); // NOLINT - - // Returns the big integer arithmetically shifted right by the integer amount of bits. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftright(QirBigInt*, int64_t); // NOLINT - - // Returns true if the two big integers are equal, false otherwise. - // TODO QIR_SHARED_API bool __quantum__rt__bigint_equal(QirBigInt*, QirBigInt*); // NOLINT - - // Returns true if the first big integer is greater than the second, false otherwise. - // TODO QIR_SHARED_API bool __quantum__rt__bigint_greater(QirBigInt*, QirBigInt*); // NOLINT - - // Returns true if the first big integer is greater than or equal to the second, false otherwise. - // TODO QIR_SHARED_API bool __quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT -} - -// TODO(rokuzmin): Consider separating the `extern "C"` exports and C++ exports. -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - // Deprecated, use `Microsoft::Quantum::OutputStream::ScopedRedirector` or `Microsoft::Quantum::OutputStream::Set()` - // instead. - QIR_SHARED_API std::ostream& SetOutputStream(std::ostream& newOStream); -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirRuntimeApi_I.hpp b/src/Qir/Runtime/public/QirRuntimeApi_I.hpp deleted file mode 100644 index 318e1f29fc8..00000000000 --- a/src/Qir/Runtime/public/QirRuntimeApi_I.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -#include "CoreTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct QIR_SHARED_API IRuntimeDriver - { - virtual ~IRuntimeDriver() - { - } - IRuntimeDriver() = default; - - // Doesn't necessarily provide insight into the state of the qubit (for that look at IDiagnostics) - virtual std::string QubitToString(QubitIdType qubit) = 0; - - // Qubit management - virtual QubitIdType AllocateQubit() = 0; - virtual void ReleaseQubit(QubitIdType qubit) = 0; - - virtual void ReleaseResult(Result result) = 0; - virtual bool AreEqualResults(Result r1, Result r2) = 0; - virtual ResultValue GetResultValue(Result result) = 0; - // The caller *should not* release results obtained via these two methods. The - // results are guaranteed to be finalized to the corresponding ResultValue, but - // it's not required from the runtime to return same Result on subsequent calls. - virtual Result UseZero() = 0; - virtual Result UseOne() = 0; - - private: - IRuntimeDriver& operator=(const IRuntimeDriver&) = delete; - IRuntimeDriver(const IRuntimeDriver&) = delete; - }; - - struct QIR_SHARED_API IRestrictedAreaManagement - { - virtual ~IRestrictedAreaManagement() - { - } - IRestrictedAreaManagement() = default; - - virtual void StartArea() = 0; - virtual void NextSegment() = 0; - virtual void EndArea() = 0; - - private: - IRestrictedAreaManagement& operator=(const IRestrictedAreaManagement&) = delete; - IRestrictedAreaManagement(const IRestrictedAreaManagement&) = delete; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirTypes.hpp b/src/Qir/Runtime/public/QirTypes.hpp deleted file mode 100644 index 4dba8aa976a..00000000000 --- a/src/Qir/Runtime/public/QirTypes.hpp +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include -#include - -#include "CoreTypes.hpp" - -/*====================================================================================================================== - QirArray -======================================================================================================================*/ -struct QIR_SHARED_API QirArray -{ - using TItemCount = uint32_t; // Data type of number of items (potentially can be increased to `uint64_t`). - using TItemSize = uint32_t; // Data type of item size. - using TBufSize = size_t; // Size of the buffer pointed to by `buffer` - // (32 bit on 32-bit arch, 64 bit on 64-bit arch). - using TDimCount = uint8_t; // Data type for number of dimensions (3 for 3D array). - using TDimContainer = std::vector; // Data type for container of dimensions - // (for array 2x3x5 3 items: 2, 3, 5). - - // The product of all dimensions (2x3x5 = 30) should be equal to the overall number of items - `count`, - // i.e. the product must fit in `TItemCount`. That is why `TItemCount` should be sufficient to store each dimension. - - TItemCount count = 0; // Overall number of elements in the array across all dimensions - const TItemSize itemSizeInBytes = 0; - - const TDimCount dimensions = 1; - TDimContainer dimensionSizes; // not set for 1D arrays, as `count` is sufficient - - char* buffer = nullptr; - - bool ownsQubits = false; - int refCount = 1; - int aliasCount = 0; // used to enable copy elision, see the QIR specifications for details - - // NB: Release doesn't trigger destruction of the Array itself (only of its data buffer) to allow for it being used - // both on the stack and on the heap. The creator of the array should delete it, if allocated from the heap. - int AddRef(); - int Release(); - - explicit QirArray(TItemCount cQubits); - QirArray(TItemCount cItems, TItemSize itemSizeInBytes, TDimCount dimCount = 1, TDimContainer&& dimSizes = {}); - QirArray(const QirArray& other); - - ~QirArray(); - - [[nodiscard]] char* GetItemPointer(TItemCount index) const; - void Append(const QirArray* other); -}; - -/*====================================================================================================================== - QirString is just a wrapper around std::string -======================================================================================================================*/ -struct QIR_SHARED_API QirString -{ - long refCount = 1; - std::string str; - - explicit QirString(std::string&& str); - explicit QirString(const char* cstr); -}; - -/*====================================================================================================================== - Tuples are opaque to the runtime and the type of the data contained in them isn't (generally) known, thus, we use - char* to represent the tuples QIR operates with. However, we need to manage tuples' lifetime and in case of nested - controlled callables we also need to peek into the tuple's content. To do this we associate with each tuple's buffer - a header that contains the relevant data. The header immediately precedes the tuple's buffer in memory when the - tuple is created. -======================================================================================================================*/ -// TODO (rokuzmin): Move these types to inside of `QirTupleHeader`. -using PTuplePointedType = uint8_t; -using PTuple = PTuplePointedType*; // TODO(rokuzmin): consider replacing `uint8_t*` with `void*` in order to block - // the accidental {dereferencing and pointer arithmetic}. - // Much pointer arithmetic in tests. GetHeader() uses the pointer arithmetic. -struct QIR_SHARED_API QirTupleHeader -{ - using TBufSize = size_t; // Type of the buffer size. - - int refCount = 0; - int32_t aliasCount = 0; // used to enable copy elision, see the QIR specifications for details - TBufSize tupleSize = 0; // when creating the tuple, must be set to the size of the tuple's data buffer (in bytes) - - // flexible array member, must be last in the struct - PTuplePointedType data[]; - - PTuple AsTuple() - { - return (PTuple)data; - } - - int AddRef(); - int Release(); - - static QirTupleHeader* Create(TBufSize size); - static QirTupleHeader* CreateWithCopiedData(QirTupleHeader* other); - - static QirTupleHeader* GetHeader(PTuple tuple) - { - return reinterpret_cast(tuple - offsetof(QirTupleHeader, data)); - } -}; - -/*====================================================================================================================== - A helper type for unpacking tuples used by multi-level controlled callables -======================================================================================================================*/ -struct QIR_SHARED_API TupleWithControls -{ - QirArray* controls; - TupleWithControls* innerTuple; - - PTuple AsTuple() - { - return reinterpret_cast(this); - } - - static TupleWithControls* FromTuple(PTuple tuple) - { - return reinterpret_cast(tuple); - } - - static TupleWithControls* FromTupleHeader(QirTupleHeader* th) - { - return FromTuple(th->AsTuple()); - } - - QirTupleHeader* GetHeader() - { - return QirTupleHeader::GetHeader(this->AsTuple()); - } -}; -static_assert(sizeof(TupleWithControls) == 2 * sizeof(void*), - L"TupleWithControls must be tightly packed for FlattenControlArrays to be correct"); - -/*====================================================================================================================== - QirCallable -======================================================================================================================*/ -typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); // TODO(rokuzmin): Move to `QirCallable::t_CallableEntry`. -typedef void (*t_CaptureCallback)(PTuple, int32_t); // TODO(rokuzmin): Move to `QirCallable::t_CaptureCallback`. -struct QIR_SHARED_API QirCallable -{ - static int constexpr Adjoint = 1; - static int constexpr Controlled = 1u << 1; - - private: - static int constexpr TableSize = 4; - static_assert(QirCallable::Adjoint + QirCallable::Controlled < QirCallable::TableSize, - L"functor kind is used as index into the functionTable"); - - int refCount = 1; - int aliasCount = 0; - - // If the callable doesn't support Adjoint or Controlled functors, the corresponding entries in the table should be - // set to nullptr. - t_CallableEntry functionTable[QirCallable::TableSize] = {nullptr, nullptr, nullptr, nullptr}; - - static int constexpr CaptureCallbacksTableSize = 2; - t_CaptureCallback captureCallbacks[QirCallable::CaptureCallbacksTableSize] = {nullptr, nullptr}; - - // The callable stores the capture, it's given at creation, and passes it to the functions from the function table, - // but the runtime doesn't have any knowledge about what the tuple actually is. - PTuple const capture = nullptr; - - // By default the callable is neither adjoint nor controlled. - int appliedFunctor = 0; - - // Per https://github.com/microsoft/qsharp-language/blob/main/Specifications/QIR/Callables.md, the callable must - // unpack the nested controls from the input tuples. Because the tuples aren't typed, the callable will assume - // that its input tuples are formed in a particular way and will extract the controls to match its tracked depth. - int controlledDepth = 0; - - // Prevent stack allocations. - ~QirCallable(); - - public: - QirCallable(const t_CallableEntry* ftEntries, const t_CaptureCallback* captureCallbacks, PTuple capture); - QirCallable(const QirCallable& other); - QirCallable* CloneIfShared(); - - int AddRef(); - int Release(); - void UpdateAliasCount(int increment); - - void Invoke(PTuple args, PTuple result); - void Invoke(); // a shortcut to invoke a callable with no arguments and Unit result - void ApplyFunctor(int functor); - - void InvokeCaptureCallback(int32_t index, int32_t parameter); -}; - -extern "C" -{ - // https://docs.microsoft.com/azure/quantum/user-guide/language/expressions/valueliterals#range-literals - struct QirRange - { - int64_t start; // Inclusive. - int64_t step; - int64_t end; // Inclusive. - }; -} diff --git a/src/Qir/Runtime/public/QubitManager.hpp b/src/Qir/Runtime/public/QubitManager.hpp deleted file mode 100644 index 3f2794e9fcb..00000000000 --- a/src/Qir/Runtime/public/QubitManager.hpp +++ /dev/null @@ -1,255 +0,0 @@ -// 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: - // We want status array to be reasonably large. - constexpr static QubitIdType DefaultQubitCapacity = 8; - - // Indexes in the status array can potentially be in range 0 .. QubitId.MaxValue-1. - // This gives maximum capacity as QubitId.MaxValue. Actual configured capacity may be less than this. - // Index equal to QubitId.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); - - // No complex scenarios for now. Don't need to support copying/moving. - CQubitManager(const CQubitManager&) = delete; - CQubitManager& operator=(const CQubitManager&) = delete; - ~CQubitManager(); // If this dtor is made _virtual_ then the QIR RT tests crash (at least in Debug config) - // if the native simulator is compiled with Clang (as opposed to GCC). Nothing wrong found in - // the code, probably is the compiler bug. - - // 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) worst case, O(1) amortized cost. - QubitIdType 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(QubitIdType* qubitsToAllocate, int32_t qubitCountToAllocate); - - // Releases a given qubit. - void Release(QubitIdType 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(QubitIdType* qubitsToRelease, int32_t qubitCountToRelease); - - // Borrow (We treat borrowing as allocation currently) - QubitIdType Borrow(); - void Borrow(QubitIdType* qubitsToBorrow, int32_t qubitCountToBorrow); - // Return (We treat returning as release currently) - void Return(QubitIdType qubit); - void Return(QubitIdType* qubitsToReturn, int32_t qubitCountToReturn); - - // Disables a given qubit. - // Once a qubit is disabled it can never be "enabled" or reallocated. - void Disable(QubitIdType qubit); - // Disables a set of given qubits. - // Once a qubit is disabled it can never be "enabled" or reallocated. - void Disable(QubitIdType* qubitsToDisable, int32_t qubitCountToDisable); - - bool IsValidQubit(QubitIdType qubit) const; - bool IsDisabledQubit(QubitIdType qubit) const; - bool IsExplicitlyAllocatedQubit(QubitIdType qubit) const; - bool IsFreeQubitId(QubitIdType id) 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. - QubitIdType 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. - QubitIdType 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. - QubitIdType GetFreeQubitCount() const - { - return freeQubitCount; - } - - // Total number of qubits that are currently tracked by this qubit manager. - QubitIdType GetQubitCapacity() const - { - return qubitCapacity; - } - bool GetMayExtendCapacity() const - { - return mayExtendCapacity; - } - - 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, 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; - // When we are looking for free qubits we skip areas that are known not - // to have them (to achieve amortized cost O(1)). It is guaranteed that - // there're no free qubits available in areas between this one and the - // one pointed to by prevAreaWithFreeQubits (bounds non-inclusinve). - // This invariant is maintained in all operations. It is NOT guaranteed - // that the area pointed to by prevAreaWithFreeQubits actually has - // free qubits available, and the search may need to continue. - int32_t prevAreaWithFreeQubits = 0; - - RestrictedReuseArea() = default; - explicit 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(CRestrictedReuseAreaStack&&) = delete; - CRestrictedReuseAreaStack& operator=(CRestrictedReuseAreaStack&&) = delete; - ~CRestrictedReuseAreaStack() = default; - - void PushToBack(RestrictedReuseArea area); - RestrictedReuseArea PopFromBack(); - RestrictedReuseArea& PeekBack(); - [[nodiscard]] int32_t Count() const; - }; - - void EnsureCapacity(QubitIdType requestedCapacity); - - // Take free qubit id from a free list without extending capacity. - // Free list to take from is found by amortized O(1) algorithm. - 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); - - [[nodiscard]] bool IsValidId(QubitIdType id) const; - [[nodiscard]] bool IsDisabledId(QubitIdType id) const; - [[nodiscard]] bool IsFreeId(QubitIdType id) const; - [[nodiscard]] bool IsExplicitlyAllocatedId(QubitIdType id) const; - - private: - // Configuration Properties: - bool mayExtendCapacity = 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: - QubitIdType disabledQubitCount = 0; - QubitIdType allocatedQubitCount = 0; - QubitIdType freeQubitCount = 0; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/README.md b/src/Qir/Runtime/public/README.md deleted file mode 100644 index 658f8716347..00000000000 --- a/src/Qir/Runtime/public/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -TODO: Consider moving `public\` one level up, or all the rest one level down the directory tree. - -## Level 0. External To This Directory - -**..\..\Common\Include\qsharp__foundation_internal.hpp** - Depends on `QIR_SHARED_API` - consider moving below `public\`. - -**..\..\Common\Include\SimulatorStub.hpp** - Depends on `public\` - consider moving below `public\`. - - -## Level 1 - -**TracerTypes.hpp** Defines types `OpId`, `Time`, `Duration`, `LayerId`; constants `INVALID`, `REQUESTNEW`. - Does not depend on anything of our code. - Only used by Tracer and Tracer Test. Consider moving from `public` to a location still visible for tests. - -**CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. - - -## Level 2 - -**OutputStream.hpp** Defines `OutputStream`, `ScopedRedirector` - the means to redirect the output stream from `std::cout` to string, file, etc. - Used by the tests that verify the output (e.g. `Message()`). - Depends on `QIR_SHARED_API`. - -**QirRuntimeApi_I.hpp** Defines `IRuntimeDriver` that provides the access to the quantum machine simulator. - Depends on `QIR_SHARED_API`, `Qubit`, `Result`. - -**QirContext.hpp** Defines `QirExecutionContext`, `?Scoped`. - Uses `IRuntimeDriver *`, {`AllocationsTracker *` defined in `QIR` - reverse dependency}. - Depends on `QIR_SHARED_API` - -**QSharpSimApi_I.hpp** Defines `IQuantumGateSet`, `IDiagnostics`. - Depends on `QIR_SHARED_API`, `Qubit`, `PauliId`, `Result`, `QirArray`. - -## Level 3 - -**SimFactory.hpp** Defines `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. - Depends on `QIR_SHARED_API`, `IRuntimeDriver` diff --git a/src/Qir/Runtime/public/SimFactory.h b/src/Qir/Runtime/public/SimFactory.h deleted file mode 100644 index 6b057b62e6f..00000000000 --- a/src/Qir/Runtime/public/SimFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef SIMFACTORY_H -#define SIMFACTORY_H - -#include -#include "CoreDefines.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - QIR_SHARED_API void* CreateFullstateSimulatorC(uint32_t userProvidedSeed); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef SIMFACTORY_H diff --git a/src/Qir/Runtime/public/SimFactory.hpp b/src/Qir/Runtime/public/SimFactory.hpp deleted file mode 100644 index 36390e2b496..00000000000 --- a/src/Qir/Runtime/public/SimFactory.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include - -#include "QirRuntimeApi_I.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - // Toffoli Simulator - QIR_SHARED_API std::unique_ptr CreateToffoliSimulator(); - - // Full State Simulator - QIR_SHARED_API std::unique_ptr CreateFullstateSimulator(uint32_t userProvidedSeed = 0); - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/TracerTypes.hpp b/src/Qir/Runtime/public/TracerTypes.hpp deleted file mode 100644 index 9b1f119c3b3..00000000000 --- a/src/Qir/Runtime/public/TracerTypes.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace Microsoft -{ -namespace Quantum -{ - using OpId = int; - using Time = int; - using Duration = int; - using LayerId = int64_t; // TODO: Use unsigned type. - - constexpr LayerId INVALID = std::numeric_limits::min(); - constexpr LayerId REQUESTNEW = std::numeric_limits::max(); -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/qir.png b/src/Qir/Runtime/qir.png deleted file mode 100644 index 588193ffa2179a11806e8cb8ffee3e4516c1febf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37446 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 Result<(), String> { + // Compile the LLVM IR bridge file. Requires the llvm-tools-preview component. + // This is only needed for range support, and this entire build.rs can be dropped when that functionality is + // no longer needed. + let out_dir = env::var_os("OUT_DIR") + .map(PathBuf::from) + .ok_or_else(|| "Environment variable OUT_DIR not defined.".to_string())?; + + let llvm_tools = LlvmTools::new().map_err(|err| { + format!( + "Failed to locate llvm tools: {:?}. Is the llvm-tools-preview component installed? Try using `rustup component add llvm-tools-preview`.", + err + ) + })?; + + let llc_path = llvm_tools + .tool(llvm_tools::exe("llc").to_string().as_str()) + .ok_or_else(|| "Failed to find llc.".to_string())?; + let llvm_ar_path = llvm_tools + .tool(llvm_tools::exe("llvm-ar").to_string().as_str()) + .ok_or_else(|| "Failed to find llvm-ar.".to_string())?; + let lib_name = if cfg!(target_os = "windows") { + "bridge-rt.lib" + } else { + "libbridge-rt.a" + }; + + Command::new(llc_path) + .args(&[ + "--filetype=obj", + "./src/bridge-rt.ll", + "-o", + &format!("{}/bridge-rt.o", out_dir.display()), + ]) + .status() + .map_err(|err| format!("llc failed: {}.", err))?; + Command::new(llvm_ar_path) + .args(&[ + "-r", + &format!("{}/{}", out_dir.display(), lib_name), + &format!("{}/bridge-rt.o", out_dir.display()), + ]) + .status() + .map_err(|err| format!("llvm-ar failed: {}.", err))?; + + println!("cargo:rustc-link-lib=static=bridge-rt"); + println!("cargo:rustc-link-search=native={}", out_dir.display()); + + Ok(()) +} diff --git a/src/Qir/Runtime/stdlib/include/qir_stdlib.def b/src/Qir/Runtime/stdlib/include/qir_stdlib.def new file mode 100644 index 00000000000..af0236512b5 --- /dev/null +++ b/src/Qir/Runtime/stdlib/include/qir_stdlib.def @@ -0,0 +1,82 @@ +; Copyright (c) Microsoft Corporation. +; Licensed under the MIT License. + +; This file lists the APIs defined by the qir_stdlib so that they can be re-exported on Windows platforms. +; See https://docs.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-def-files + +EXPORTS + __quantum__rt__memory_allocate + __quantum__rt__fail + __quantum__rt__message + __quantum__rt__array_create_1d + __quantum__rt__array_update_reference_count + __quantum__rt__array_update_alias_count + __quantum__rt__array_copy + __quantum__rt__array_concatenate + __quantum__rt__array_get_size_1d + __quantum__rt__array_get_element_ptr_1d + __quantum__rt__array_slice_1d + __quantum__rt__callable_create + __quantum__rt__callable_update_reference_count + __quantum__rt__callable_update_alias_count + __quantum__rt__callable_copy + __quantum__rt__callable_invoke + __quantum__rt__callable_make_adjoint + __quantum__rt__callable_make_controlled + __quantum__rt__capture_update_reference_count + __quantum__rt__capture_update_alias_count + __quantum__rt__string_create + __quantum__rt__string_update_reference_count + __quantum__rt__string_concatenate + __quantum__rt__string_equal + __quantum__rt__int_to_string + __quantum__rt__double_to_string + __quantum__rt__bool_to_string + __quantum__rt__pauli_to_string + __quantum__rt__range_to_string + __quantum__rt__string_get_data + __quantum__rt__string_get_length + __quantum__rt__tuple_create + __quantum__rt__tuple_update_reference_count + __quantum__rt__tuple_update_alias_count + __quantum__rt__tuple_copy + __quantum__rt__bigint_to_string + __quantum__rt__bigint_create_i64 + __quantum__rt__bigint_create_array + __quantum__rt__bigint_update_reference_count + __quantum__rt__bigint_negate + __quantum__rt__bigint_add + __quantum__rt__bigint_subtract + __quantum__rt__bigint_multiply + __quantum__rt__bigint_divide + __quantum__rt__bigint_modulus + __quantum__rt__bigint_power + __quantum__rt__bigint_bitand + __quantum__rt__bigint_bitor + __quantum__rt__bigint_bitxor + __quantum__rt__bigint_bitnot + __quantum__rt__bigint_shiftleft + __quantum__rt__bigint_shiftright + __quantum__rt__bigint_equal + __quantum__rt__bigint_greater + __quantum__rt__bigint_greater_eq + __quantum__qis__nan__body + __quantum__qis__isnan__body + __quantum__qis__infinity__body + __quantum__qis__isinf__body + __quantum__qis__isnegativeinfinity__body + __quantum__qis__sin__body + __quantum__qis__cos__body + __quantum__qis__tan__body + __quantum__qis__arctan2__body + __quantum__qis__sinh__body + __quantum__qis__cosh__body + __quantum__qis__tanh__body + __quantum__qis__arcsin__body + __quantum__qis__arccos__body + __quantum__qis__arctan__body + __quantum__qis__sqrt__body + __quantum__qis__log__body + __quantum__qis__ieeeremainder__body + __quantum__qis__drawrandomint__body + __quantum__qis__drawrandomdouble__body \ No newline at end of file diff --git a/src/Qir/Runtime/stdlib/include/qir_stdlib.h b/src/Qir/Runtime/stdlib/include/qir_stdlib.h new file mode 100644 index 00000000000..213ffa90d5a --- /dev/null +++ b/src/Qir/Runtime/stdlib/include/qir_stdlib.h @@ -0,0 +1,243 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once +#ifndef QIR_STDLIB_H +#define QIR_STDLIB_H + +#include + +#ifdef _WIN32 +#define QIR_SHARED_API __declspec(dllexport) +#else +#define QIR_SHARED_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + struct QirString; + struct QirArray; + struct QirCallable; + struct QirTuple; + struct QirBigInt; + + // Type aliases with the same name are added for ease of use in C code. + typedef struct QirString QirString; + typedef struct QirArray QirArray; + typedef struct QirCallable QirCallable; + typedef struct QirTuple QirTuple; + typedef struct QirBigInt QirBigInt; + + enum PauliId : int8_t + { + PauliId_I = 0, + PauliId_X = 1, + PauliId_Z = 2, + PauliId_Y = 3, + }; + + typedef void (*t_CallableEntry)(QirTuple*, QirTuple*, QirTuple*); + typedef void (*t_CaptureCallback)(QirTuple*, int32_t); + + // Returns a pointer to the malloc-allocated block. + QIR_SHARED_API void* __quantum__rt__memory_allocate(uint64_t size); // NOLINT + + // Fail the computation with the given error message. + [[noreturn]] QIR_SHARED_API void __quantum__rt__fail(QirString* msg); // NOLINT + + // Include the given message in the computation's execution log or equivalent. + QIR_SHARED_API void __quantum__rt__message(QirString* msg); // NOLINT + + // Creates a new 1-dimensional array. The int is the size of each element in bytes. The int64_t is the length + // of the array. The bytes of the new array should be set to zero. + QIR_SHARED_API QirArray* __quantum__rt__array_create_1d(int32_t, int64_t); // NOLINT + + // Adds the given integer value to the reference count for the array. Deallocates the array if the reference count + // becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__array_update_reference_count(QirArray*, int32_t); // NOLINT + + // Adds the given integer value to the alias count for the array. Fails if the count becomes negative. + QIR_SHARED_API void __quantum__rt__array_update_alias_count(QirArray*, int32_t); // NOLINT + + // Creates a shallow copy of the array if the user count is larger than 0 or the second argument is `true`. + QIR_SHARED_API QirArray* __quantum__rt__array_copy(QirArray*, bool); // NOLINT + + // Returns a new array which is the concatenation of the two passed-in arrays. + QIR_SHARED_API QirArray* __quantum__rt__array_concatenate(QirArray*, QirArray*); // NOLINT + + // Returns the length of a dimension of the array. The int is the zero-based dimension to return the length of; it + // must be 0 for a 1-dimensional array. + QIR_SHARED_API int64_t __quantum__rt__array_get_size_1d(QirArray*); // NOLINT + + // Returns a pointer to the element of the array at the zero-based index given by the int64_t. + QIR_SHARED_API char* __quantum__rt__array_get_element_ptr_1d(QirArray*, int64_t); // NOLINT + + // Initializes the callable with the provided function table and capture tuple. The capture tuple pointer + // should be null if there is no capture. + QIR_SHARED_API QirCallable* __quantum__rt__callable_create( // NOLINT + t_CallableEntry*, t_CaptureCallback*, QirTuple*); + + // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference + // count becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__callable_update_reference_count(QirCallable*, int32_t); // NOLINT + + // Adds the given integer value to the alias count for the callable. Fails if the count becomes negative. + QIR_SHARED_API void __quantum__rt__callable_update_alias_count(QirCallable*, int32_t); // NOLINT + + // Creates a shallow copy of the callable if the alias count is larger than 0 or the second argument is `true`. + // Returns the given callable pointer otherwise, after increasing its reference count by 1. + QIR_SHARED_API QirCallable* __quantum__rt__callable_copy(QirCallable*, bool); // NOLINT + + // Invokes the callable with the provided argument tuple and fills in the result tuple. + QIR_SHARED_API void __quantum__rt__callable_invoke(QirCallable*, QirTuple*, QirTuple*); // NOLINT + + // Updates the callable by applying the Adjoint functor. + QIR_SHARED_API void __quantum__rt__callable_make_adjoint(QirCallable*); // NOLINT + + // Updates the callable by applying the Controlled functor. + QIR_SHARED_API void __quantum__rt__callable_make_controlled(QirCallable*); // NOLINT + + // Invokes the function in the corresponding index in the memory management table of the callable with the capture + // tuple and the given 32-bit integer. Does nothing if the memory management table pointer or the function pointer + // at that index is null. + QIR_SHARED_API void __quantum__rt__capture_update_reference_count(QirCallable*, int32_t); // NOLINT + QIR_SHARED_API void __quantum__rt__capture_update_alias_count(QirCallable*, int32_t); // NOLINT + + // Creates a string from an array of UTF-8 bytes. + QIR_SHARED_API QirString* __quantum__rt__string_create(const char*); // NOLINT + + // Adds the given integer value to the reference count for the string. Deallocates the string if the reference count + // becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__string_update_reference_count(QirString*, int32_t); // NOLINT + + // Creates a new string that is the concatenation of the two argument strings. + QIR_SHARED_API QirString* __quantum__rt__string_concatenate(QirString*, QirString*); // NOLINT + + // Returns true if the two strings are equal, false otherwise. + QIR_SHARED_API bool __quantum__rt__string_equal(QirString*, QirString*); // NOLINT + + // Returns a string representation of the integer. + QIR_SHARED_API QirString* __quantum__rt__int_to_string(int64_t); // NOLINT + + // Returns a string representation of the double. + QIR_SHARED_API QirString* __quantum__rt__double_to_string(double); // NOLINT + + // Returns a string representation of the Boolean. + QIR_SHARED_API QirString* __quantum__rt__bool_to_string(bool); // NOLINT + + // Returns a string representation of the Pauli. + QIR_SHARED_API QirString* __quantum__rt__pauli_to_string(PauliId); // NOLINT + + // Returns a pointer to an array that contains a null-terminated sequence of characters + // (i.e., a C-string) representing the current value of the string object. + QIR_SHARED_API const char* __quantum__rt__string_get_data(QirString* str); // NOLINT + + // Returns the length of the string, in terms of bytes. + // http://www.cplusplus.com/reference/string/string/size/ + QIR_SHARED_API uint32_t __quantum__rt__string_get_length(QirString* str); // NOLINT + + // Allocates space for a tuple requiring the given number of bytes and sets the reference count to 1. + QIR_SHARED_API QirTuple* __quantum__rt__tuple_create(int64_t); // NOLINT + + // Adds the given integer value to the reference count for the tuple. Deallocates the tuple if the reference count + // becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__tuple_update_reference_count(QirTuple*, int32_t); // NOLINT + + // Adds the given integer value to the alias count for the tuple. Fails if the count becomes negative. + QIR_SHARED_API void __quantum__rt__tuple_update_alias_count(QirTuple*, int32_t); // NOLINT + + // Creates a shallow copy of the tuple if the user count is larger than 0 or the second argument is `true`. + QIR_SHARED_API QirTuple* __quantum__rt__tuple_copy(QirTuple*, bool force); // NOLINT + + // Returns a string representation of the big integer. + QIR_SHARED_API QirString* __quantum__rt__bigint_to_string(QirBigInt*); // NOLINT + + // Creates a big integer with the specified initial value. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_i64(int64_t); // NOLINT + + // Creates a big integer with the initial value specified by the i8 array. The 0-th element of the array is the + // highest-order byte, followed by the first element, etc. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_array(int32_t, char*); // NOLINT + + // Adds the given integer value to the reference count for the big integer. Deallocates the big integer if the + // reference count becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__bigint_update_reference_count(QirBigInt*, int32_t); // NOLINT + + // Returns the negative of the big integer. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_negate(QirBigInt*); // NOLINT + + // Adds two big integers and returns their sum. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_add(QirBigInt*, QirBigInt*); // NOLINT + + // Subtracts the second big integer from the first and returns their difference. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_subtract(QirBigInt*, QirBigInt*); // NOLINT + + // Multiplies two big integers and returns their product. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_multiply(QirBigInt*, QirBigInt*); // NOLINT + + // Divides the first big integer by the second and returns their quotient. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_divide(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the first big integer modulo the second. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_modulus(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the big integer raised to the integer power. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_power(QirBigInt*, int32_t); // NOLINT + + // Returns the bitwise-AND of two big integers. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitand(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the bitwise-OR of two big integers. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitor(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the bitwise-XOR of two big integers. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitxor(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the bitwise complement of the big integer. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitnot(QirBigInt*); // NOLINT + + // Returns the big integer arithmetically shifted left by the (positive) integer amount of bits. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftleft(QirBigInt*, int64_t); // NOLINT + + // Returns the big integer arithmetically shifted right by the (positive) integer amount of bits. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftright(QirBigInt*, int64_t); // NOLINT + + // Returns true if the two big integers are equal, false otherwise. + QIR_SHARED_API bool __quantum__rt__bigint_equal(QirBigInt*, QirBigInt*); // NOLINT + + // Returns true if the first big integer is greater than the second, false otherwise. + QIR_SHARED_API bool __quantum__rt__bigint_greater(QirBigInt*, QirBigInt*); // NOLINT + + // Returns true if the first big integer is greater than or equal to the second, false otherwise. + QIR_SHARED_API bool __quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT + + // Q# Math: + QIR_SHARED_API double __quantum__qis__nan__body(); // NOLINT + QIR_SHARED_API bool __quantum__qis__isnan__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__infinity__body(); // NOLINT + QIR_SHARED_API bool __quantum__qis__isinf__body(double d); // NOLINT + QIR_SHARED_API bool __quantum__qis__isnegativeinfinity__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__sin__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__cos__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__tan__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__arctan2__body(double y, double x); // NOLINT + QIR_SHARED_API double __quantum__qis__sinh__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__cosh__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__tanh__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__arcsin__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__arccos__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__arctan__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__sqrt__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__log__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__ieeeremainder__body(double x, double y); // NOLINT + QIR_SHARED_API int64_t __quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT + QIR_SHARED_API double __quantum__qis__drawrandomdouble__body(double minimum, double maximum); // NOLINT + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/Qir/Runtime/stdlib/src/arrays.rs b/src/Qir/Runtime/stdlib/src/arrays.rs new file mode 100644 index 00000000000..bdd1942317f --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/arrays.rs @@ -0,0 +1,303 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::{__quantum__rt__fail, strings::convert, update_counts}; +use std::{mem::ManuallyDrop, rc::Rc, usize}; + +#[derive(Debug, Clone)] +pub struct QirArray { + pub(crate) elem_size: usize, + pub(crate) data: Vec, +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__array_create_1d(elem_size: u32, count: u64) -> *const QirArray { + let elem_size = elem_size + .try_into() + .expect("The `elem_size` argument should fit in the `usize` type for this platform."); + let count: usize = count + .try_into() + .expect("The `count` argument should fit in the `usize` type for this platform."); + let data = vec![0_u8; elem_size * count]; + Rc::into_raw(Rc::new(QirArray { elem_size, data })) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_copy( + arr: *const QirArray, + force: bool, +) -> *const QirArray { + // Wrap the array in a `ManuallyDrop` to effectively borrow it and ensure the array + // won't be dropped, refcount decremented, and cleaned up. + let rc = ManuallyDrop::new(Rc::from_raw(arr)); + if force || Rc::weak_count(&rc) > 0 { + let copy = rc.as_ref().clone(); + Rc::into_raw(Rc::new(copy)) + } else { + Rc::into_raw(Rc::clone(&rc)); + arr + } +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_concatenate( + arr1: *const QirArray, + arr2: *const QirArray, +) -> *const QirArray { + let array1 = &*arr1; + let array2 = &*arr2; + if array1.elem_size != array2.elem_size { + __quantum__rt__fail(convert(&format!( + "Cannot concatenate arrays with differing element sizes: {} vs {}", + array1.elem_size, array2.elem_size + ))); + } + + let mut new_array = QirArray { + elem_size: array1.elem_size, + data: Vec::new(), + }; + new_array.data.resize(array1.data.len(), 0_u8); + new_array.data.copy_from_slice(array1.data.as_slice()); + + let mut copy = Vec::new(); + copy.resize(array2.data.len(), 0_u8); + copy.copy_from_slice(array2.data.as_slice()); + + new_array.data.append(&mut copy); + Rc::into_raw(Rc::new(new_array)) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const QirArray) -> u64 { + let array = &*arr; + let len = array.data.len() / array.elem_size; + len.try_into() + .expect("Length of array should always fit in a 64-bit integer.") +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( + arr: *const QirArray, + index: u64, +) -> *mut i8 { + let array = &*arr; + let index: usize = index + .try_into() + .expect("Indices into an array should fit into the `usize` "); + array.data.as_ptr().add(array.elem_size * index) as *mut i8 +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_update_reference_count( + arr: *const QirArray, + update: i32, +) { + update_counts(arr, update, false); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_update_alias_count( + arr: *const QirArray, + update: i32, +) { + update_counts(arr, update, true); +} + +#[cfg(test)] +mod tests { + use std::{convert::TryInto, mem::size_of}; + + use super::*; + + #[test] + fn test_array_creation_simple() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + // Note: array reference counts must be decremented to prevent leaking array from test + // and failing address sanitized runs. + __quantum__rt__array_update_reference_count(arr, -1); + } + } + + #[test] + fn test_array_item_zero_init() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + // Array contents should always be zero initialized. + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0); + __quantum__rt__array_update_reference_count(arr, -1); + } + } + + #[test] + fn test_array_item_updates() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + // Confirm that array contents can be updated. + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + let third = __quantum__rt__array_get_element_ptr_1d(arr, 2); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0); + *third = 20; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20); + __quantum__rt__array_update_reference_count(arr, -1); + } + } + + #[test] + fn test_array_copy() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + // First array has contents [42, 31, 0], copy to second array and confirm contents. + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + // Update first array to [42, 31, 20], confirm second array is independent copy that still + // has original value. + let third = __quantum__rt__array_get_element_ptr_1d(arr, 2); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0); + *third = 20; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 2), 0); + // Decrement ref count on first array to trigger deletion, confirm the second array is + // independent copy with original values. + __quantum__rt__array_update_reference_count(arr, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + __quantum__rt__array_update_reference_count(arr2, -1); + } + } + + #[test] + fn test_array_concat() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + // First array has contents [42, 31, 0], copy to second array and confirm contents. + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + // Create third array with concatenated contents [42, 31, 0, 42, 31, 0]. + let arr3 = __quantum__rt__array_concatenate(arr, arr2); + assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + // Decrement counts on first and secon array to trigger deletion, confirm third array + // maintains contents. + __quantum__rt__array_update_reference_count(arr, -1); + __quantum__rt__array_update_reference_count(arr2, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + __quantum__rt__array_update_reference_count(arr3, -1); + } + } + + #[allow(clippy::cast_ptr_alignment)] + #[test] + fn test_array_creation_large_size() { + // Use a struct that is known to have a size greater than 1. + struct Data { + first: i64, + second: i64, + third: i64, + } + let arr = __quantum__rt__array_create_1d(size_of::().try_into().unwrap(), 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0).cast::(); + *first = Data { + first: 1, + second: 2, + third: 3, + }; + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1).cast::(); + *second = Data { + first: 10, + second: 20, + third: 30, + }; + let third = __quantum__rt__array_get_element_ptr_1d(arr, 2).cast::(); + *third = Data { + first: 100, + second: 200, + third: 300, + }; + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).first, + 1 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).second, + 2 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).third, + 3 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).first, + 10 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).second, + 20 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).third, + 30 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).first, + 100 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).second, + 200 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).third, + 300 + ); + __quantum__rt__array_update_reference_count(arr, -1); + } + } +} diff --git a/src/Qir/Runtime/stdlib/src/bigints.rs b/src/Qir/Runtime/stdlib/src/bigints.rs new file mode 100644 index 00000000000..f0f97a28337 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/bigints.rs @@ -0,0 +1,269 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::update_counts; +use num_bigint::BigInt; +use std::{mem::ManuallyDrop, rc::Rc}; + +#[no_mangle] +pub extern "C" fn __quantum__rt__bigint_create_i64(input: i64) -> *const BigInt { + Rc::into_raw(Rc::new(input.into())) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_create_array( + size: u32, + input: *const u8, +) -> *const BigInt { + Rc::into_raw(Rc::new(BigInt::from_signed_bytes_be( + std::slice::from_raw_parts(input, size as usize), + ))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_get_data(input: *const BigInt) -> *const u8 { + ManuallyDrop::new((*input).to_signed_bytes_be()).as_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_get_length(input: *const BigInt) -> u32 { + let size = (*input).to_signed_bytes_be().len(); + size.try_into() + .expect("Length of bigint representation too large for 32-bit integer.") +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_update_reference_count( + input: *const BigInt, + update: i32, +) { + update_counts(input, update, false); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_negate(input: *const BigInt) -> *const BigInt { + Rc::into_raw(Rc::new(&(*input) * -1)) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_add( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) + &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_subtract( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) - &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_multiply( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) * &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_divide( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) / &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_modulus( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) % &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_power( + base: *const BigInt, + exponent: u32, +) -> *const BigInt { + Rc::into_raw(Rc::new((*base).pow(exponent))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitand( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) & &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitor( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) | &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitxor( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) ^ &(*rhs))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitnot(input: *const BigInt) -> *const BigInt { + Rc::into_raw(Rc::new(!&(*input))) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_shiftleft( + input: *const BigInt, + amount: u64, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*input) << amount)) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_shiftright( + input: *const BigInt, + amount: u64, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*input) >> amount)) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_equal( + lhs: *const BigInt, + rhs: *const BigInt, +) -> bool { + (*lhs) == (*rhs) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_greater( + lhs: *const BigInt, + rhs: *const BigInt, +) -> bool { + (*lhs) > (*rhs) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_greater_eq( + lhs: *const BigInt, + rhs: *const BigInt, +) -> bool { + (*lhs) >= (*rhs) +} + +#[cfg(test)] +mod tests { + use std::convert::TryInto; + + use super::*; + + #[test] + fn test_bigint_create_from_int() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + unsafe { + assert_eq!(*bigint_0, (42).try_into().unwrap()); + // Note that the test must decrement the reference count on any created items to ensure + // they are cleaned up and tests can pass with address sanitizer. + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + } + } + + #[test] + fn test_bigint_create_from_array() { + let bytes = 42_i64.to_be_bytes(); + unsafe { + let bigint_1 = + __quantum__rt__bigint_create_array(bytes.len().try_into().unwrap(), bytes.as_ptr()); + assert_eq!(*bigint_1, (42).try_into().unwrap()); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + } + } + + #[test] + fn test_bigint_arithmetic() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bigint_1 = __quantum__rt__bigint_create_i64(3); + unsafe { + let bigint_2 = __quantum__rt__bigint_add(bigint_0, bigint_1); + assert_eq!(*bigint_2, (45).try_into().unwrap()); + let bigint_3 = __quantum__rt__bigint_subtract(bigint_2, bigint_1); + assert_eq!(*bigint_3, (42).try_into().unwrap()); + let bigint_4 = __quantum__rt__bigint_divide(bigint_3, bigint_1); + assert_eq!(*bigint_4, (14).try_into().unwrap()); + let bigint_5 = __quantum__rt__bigint_multiply(bigint_4, bigint_1); + assert_eq!(*bigint_5, (42).try_into().unwrap()); + let bigint_6 = __quantum__rt__bigint_modulus(bigint_5, bigint_1); + assert_eq!(*bigint_6, (0).try_into().unwrap()); + let bigint_7 = __quantum__rt__bigint_negate(bigint_5); + assert_eq!(*bigint_7, (-42).try_into().unwrap()); + let bigint_8 = __quantum__rt__bigint_power(bigint_7, 3); + assert_eq!(*bigint_8, (-74088).try_into().unwrap()); + __quantum__rt__bigint_update_reference_count(bigint_8, -1); + __quantum__rt__bigint_update_reference_count(bigint_7, -1); + __quantum__rt__bigint_update_reference_count(bigint_6, -1); + __quantum__rt__bigint_update_reference_count(bigint_5, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_3, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + } + } + + #[test] + fn test_bigint_bitops() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bigint_1 = __quantum__rt__bigint_create_i64(3); + unsafe { + let bigint_2 = __quantum__rt__bigint_bitand(bigint_0, bigint_1); + assert_eq!(*bigint_2, (2).try_into().unwrap()); + let bigint_3 = __quantum__rt__bigint_bitor(bigint_0, bigint_1); + assert_eq!(*bigint_3, (43).try_into().unwrap()); + let bigint_4 = __quantum__rt__bigint_bitxor(bigint_0, bigint_3); + assert_eq!(*bigint_4, (1).try_into().unwrap()); + let bigint_5 = __quantum__rt__bigint_bitnot(bigint_4); + assert_eq!(*bigint_5, (-2).try_into().unwrap()); + let bigint_6 = __quantum__rt__bigint_shiftleft(bigint_0, 2); + assert_eq!(*bigint_6, (168).try_into().unwrap()); + let bigint_7 = __quantum__rt__bigint_shiftright(bigint_6, 3); + assert_eq!(*bigint_7, (21).try_into().unwrap()); + __quantum__rt__bigint_update_reference_count(bigint_7, -1); + __quantum__rt__bigint_update_reference_count(bigint_6, -1); + __quantum__rt__bigint_update_reference_count(bigint_5, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_3, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + } + } + + #[test] + fn test_bigint_comparisons() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bigint_1 = __quantum__rt__bigint_create_i64(43); + let bigint_2 = __quantum__rt__bigint_create_i64(42); + unsafe { + assert!(__quantum__rt__bigint_greater(bigint_1, bigint_0)); + assert!(!__quantum__rt__bigint_greater(bigint_0, bigint_1)); + assert!(__quantum__rt__bigint_equal(bigint_0, bigint_2)); + assert!(__quantum__rt__bigint_greater_eq(bigint_0, bigint_2)); + assert!(__quantum__rt__bigint_greater_eq(bigint_1, bigint_2)); + assert!(!__quantum__rt__bigint_greater_eq(bigint_0, bigint_1)); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + } + } +} diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/stdlib/src/bridge-rt.ll similarity index 71% rename from src/Qir/Runtime/lib/QIR/bridge-rt.ll rename to src/Qir/Runtime/stdlib/src/bridge-rt.ll index 8ccd51d87bf..8db79e4bfb6 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/stdlib/src/bridge-rt.ll @@ -1,43 +1,38 @@ -; Copyright (c) Microsoft Corporation. -; Licensed under the MIT License. - -%Array = type opaque -%Range = type { i64, i64, i64 } -%String = type opaque - -%"struct.QirArray" = type opaque -%"struct.QirRange" = type { i64, i64, i64 } -%"struct.QirString" = type opaque - -declare %"struct.QirArray"* @quantum__rt__array_slice(%"struct.QirArray"*, i32, %"struct.QirRange"* dereferenceable(24), - i1 %forceNewInstance) -declare %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) - -; NOTE: These three functions can be converted to extern C once the spec and compiler are updated to pass %Range by -; pointer instead of by value (see https://github.com/microsoft/qsharp-language/issues/108). Once that -; happens, this file can be removed. - -define dllexport %Array* @__quantum__rt__array_slice(%Array* %.ar, i32 %dim, %Range %.range, i1 %forceNewInstance) { - %ar = bitcast %Array* %.ar to %"struct.QirArray"* - %.prange = alloca %Range - store %Range %.range, %Range* %.prange - %range = bitcast %Range* %.prange to %"struct.QirRange"* - %slice = call %"struct.QirArray"* @quantum__rt__array_slice( - %"struct.QirArray"* %ar, i32 %dim, %"struct.QirRange"* dereferenceable(24) %range, i1 %forceNewInstance) - %.slice = bitcast %"struct.QirArray"* %slice to %Array* - ret %Array* %.slice -} - -define dllexport %Array* @__quantum__rt__array_slice_1d(%Array* %.ar, %Range %.range, i1 %forceNewInstance) { - %.slice = call %Array* @__quantum__rt__array_slice(%Array* %.ar, i32 0, %Range %.range, i1 %forceNewInstance) - ret %Array* %.slice -} - -define dllexport %String* @__quantum__rt__range_to_string(%Range %.range) { - %.prange = alloca %Range - store %Range %.range, %Range* %.prange - %range = bitcast %Range* %.prange to %"struct.QirRange"* - %str = call %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) - %.str = bitcast %"struct.QirString"* %str to %String* - ret %String* %.str -} \ No newline at end of file +; Copyright (c) Microsoft Corporation. +; Licensed under the MIT License. + +%Array = type opaque +%Range = type { i64, i64, i64 } +%String = type opaque + +%"struct.QirArray" = type opaque +%"struct.QirRange" = type { i64, i64, i64 } +%"struct.QirString" = type opaque + +declare %"struct.QirArray"* @quantum__rt__array_slice_1d(%"struct.QirArray"*, %"struct.QirRange"* dereferenceable(24), + i1 %forceNewInstance) +declare %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) + +; NOTE: These three functions can be converted to extern C once the spec and compiler are updated to pass %Range by +; pointer instead of by value (see https://github.com/microsoft/qsharp-language/issues/108). Once that +; happens, this file can be removed. + +define dllexport %Array* @__quantum__rt__array_slice_1d(%Array* %.ar, %Range %.range, i1 %forceNewInstance) { + %ar = bitcast %Array* %.ar to %"struct.QirArray"* + %.prange = alloca %Range + store %Range %.range, %Range* %.prange + %range = bitcast %Range* %.prange to %"struct.QirRange"* + %slice = call %"struct.QirArray"* @quantum__rt__array_slice_1d( + %"struct.QirArray"* %ar, %"struct.QirRange"* dereferenceable(24) %range, i1 %forceNewInstance) + %.slice = bitcast %"struct.QirArray"* %slice to %Array* + ret %Array* %.slice +} + +define dllexport %String* @__quantum__rt__range_to_string(%Range %.range) { + %.prange = alloca %Range + store %Range %.range, %Range* %.prange + %range = bitcast %Range* %.prange to %"struct.QirRange"* + %str = call %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) + %.str = bitcast %"struct.QirString"* %str to %String* + ret %String* %.str +} \ No newline at end of file diff --git a/src/Qir/Runtime/stdlib/src/callables.rs b/src/Qir/Runtime/stdlib/src/callables.rs new file mode 100644 index 00000000000..30b8f38cf5a --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/callables.rs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::{ + arrays::{ + QirArray, __quantum__rt__array_concatenate, __quantum__rt__array_update_reference_count, + }, + tuples::{__quantum__rt__tuple_copy, __quantum__rt__tuple_update_reference_count}, + update_counts, +}; +use std::{cell::RefCell, mem::ManuallyDrop, rc::Rc}; + +#[derive(Clone)] +pub struct Callable { + func_table: *mut *mut u8, + mem_table: *mut *mut u8, + cap_tuple: *mut u8, + is_adj: RefCell, + ctls_count: RefCell, +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__callable_create( + func_table: *mut *mut u8, + mem_table: *mut *mut u8, + cap_tuple: *mut u8, +) -> *const Callable { + Rc::into_raw(Rc::new(Callable { + func_table, + mem_table, + cap_tuple, + is_adj: RefCell::new(false), + ctls_count: RefCell::new(0), + })) +} + +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn __quantum__rt__callable_invoke( + callable: *const Callable, + args_tup: *mut u8, + res_tup: *mut u8, +) { + let call = &*callable; + let index = (if *call.is_adj.borrow() { 1 } else { 0 }) + + (if *call.ctls_count.borrow() > 0 { 2 } else { 0 }); + + // Collect any nested controls into a single control list. + let mut args_copy: *mut *const Vec = std::ptr::null_mut(); + if !args_tup.is_null() { + // Copy the tuple so we can potentially edit it. + args_copy = __quantum__rt__tuple_copy(args_tup.cast::<*const Vec>(), true); + + if *call.ctls_count.borrow() > 0 { + // If there are any controls, increment the reference count on the control list. This is just + // to balance the decrement that will happen in the loop and at the end of invoking the callable + // to ensure the original, non-owned list does not get incorrectly cleaned up. + __quantum__rt__array_update_reference_count(*args_copy.cast::<*const QirArray>(), 1); + + let mut ctl_count = *call.ctls_count.borrow(); + while ctl_count > 1 { + let ctls = *args_copy.cast::<*const QirArray>(); + let inner_tuple = *args_copy + .cast::<*const QirArray>() + .wrapping_add(1) + .cast::<*mut *const Vec>(); + let inner_ctls = *inner_tuple.cast::<*const QirArray>(); + let new_ctls = __quantum__rt__array_concatenate(ctls, inner_ctls); + let new_args = __quantum__rt__tuple_copy(inner_tuple, true); + *new_args.cast::<*const QirArray>() = new_ctls; + + // Decrementing the reference count is either the extra count added above or the new + // list created when performing concatenate above. In the latter case, the concatenated + // list will get cleaned up, preventing memory from leaking. + __quantum__rt__array_update_reference_count( + *args_copy.cast::<*const QirArray>(), + -1, + ); + // Decrement the count on the copy to clean it up as well, since we created a new copy + // with the updated controls list. + __quantum__rt__tuple_update_reference_count(args_copy, -1); + args_copy = new_args; + ctl_count -= 1; + } + } + } + + (*call + .func_table + .wrapping_add(index) + .cast::())( + call.cap_tuple, + args_copy.cast::(), + res_tup, + ); + if *call.ctls_count.borrow() > 0 { + __quantum__rt__array_update_reference_count(*args_copy.cast::<*const QirArray>(), -1); + } + if !args_copy.is_null() { + __quantum__rt__tuple_update_reference_count(args_copy, -1); + } +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_copy( + callable: *const Callable, + force: bool, +) -> *const Callable { + let rc = ManuallyDrop::new(Rc::from_raw(callable)); + if force || Rc::weak_count(&rc) > 0 { + let copy = rc.as_ref().clone(); + Rc::into_raw(Rc::new(copy)) + } else { + Rc::into_raw(Rc::clone(&rc)); + callable + } +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_make_adjoint(callable: *const Callable) { + let call = &*callable; + call.is_adj.replace_with(|&mut old| !old); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_make_controlled(callable: *const Callable) { + let call = &*callable; + call.ctls_count.replace_with(|&mut old| old + 1); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_update_reference_count( + callable: *const Callable, + update: i32, +) { + update_counts(callable, update, false); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_update_alias_count( + callable: *const Callable, + update: i32, +) { + update_counts(callable, update, true); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__capture_update_reference_count( + callable: *const Callable, + update: i32, +) { + let call = &*callable; + if !call.mem_table.is_null() && !(*(call.mem_table)).is_null() { + (*call.mem_table.cast::())(call.cap_tuple, update); + } +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__capture_update_alias_count( + callable: *const Callable, + update: i32, +) { + let call = &*callable; + if !call.mem_table.is_null() && !(*(call.mem_table.wrapping_add(1))).is_null() { + (*call + .mem_table + .wrapping_add(1) + .cast::())(call.cap_tuple, update); + } +} diff --git a/src/Qir/Runtime/stdlib/src/lib.rs b/src/Qir/Runtime/stdlib/src/lib.rs new file mode 100644 index 00000000000..6cb2757a053 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/lib.rs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::missing_panics_doc)] + +//! # Rust Implementation for Quantum Intermediate Representation +//! This library implements the classical runtime functions described in the [QIR specification](https://github.com/qir-alliance/qir-spec). + +// FUTURE: We should add microbenchmarks to verify behavior of these APIs and have a baseline on how changes affect +// peformance of the APIs. + +pub mod arrays; +pub mod bigints; +pub mod callables; +pub mod math; +pub mod output_recording; +pub mod range_support; +pub mod strings; +pub mod tuples; + +use std::{ + ffi::CString, + mem::{self, ManuallyDrop}, + rc::{Rc, Weak}, +}; + +/// Utility used for managing refcounted items. +unsafe fn update_counts(raw_rc: *const T, update: i32, is_alias: bool) { + let mut remaining = update; + while remaining != 0 { + let rc = ManuallyDrop::new(Rc::from_raw(raw_rc)); + if remaining > 0 { + if is_alias { + // Create and leak new downgraded instances to increment the weak count on the contained item. + mem::forget(Rc::downgrade(&rc)); + } else { + Rc::increment_strong_count(raw_rc); + } + + remaining -= 1; + } else { + if is_alias { + // Create and drop downgraded instances to decrement the weak count on contained item. + let w = Weak::into_raw(Rc::downgrade(&rc)); + + // Need to drop two for a net decrement, since above line increments. + drop(Weak::from_raw(w)); + drop(Weak::from_raw(w)); + } else { + Rc::decrement_strong_count(raw_rc); + } + + remaining += 1; + } + } +} + +#[derive(Debug, Copy, Clone)] +#[repr(i8)] +pub enum Pauli { + I = 0, + X = 1, + Z = 2, + Y = 3, +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__memory_allocate(size: u64) -> *mut u8 { + (vec![ + 0_u8; + size.try_into() + .expect("Memory size is too large for `usize` type on this platform.") + ]) + .leak() + .as_mut_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) { + __quantum__rt__message(str); + panic!("{}", (*str).to_str().expect("Unable to convert string")); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__message(str: *const CString) { + println!("{}", (*str).to_str().expect("Unable to convert string")); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_memory_allocate() { + let size = 10; + let mem = __quantum__rt__memory_allocate(size as u64); + unsafe { + for val in Vec::from_raw_parts(mem, size, size) { + assert_eq!(val, 0); + } + } + } + + #[test] + #[should_panic(expected = "FAIL")] + fn test_fail() { + let str = CString::new("FAIL").unwrap(); + unsafe { + __quantum__rt__fail(&str); + } + } + + #[test] + fn test_message() { + let str = CString::new("Message").unwrap(); + unsafe { + __quantum__rt__message(&str); + } + // message should not consume string, so check that it's value is still correct. + assert_eq!(str.to_str().unwrap(), "Message"); + } +} diff --git a/src/Qir/Runtime/stdlib/src/math.rs b/src/Qir/Runtime/stdlib/src/math.rs new file mode 100644 index 00000000000..327dbdfbfb1 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/math.rs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// TODO: transition math functions to `__quantum__rt` once compiler support is ready (https://github.com/microsoft/qsharp-compiler/issues/1557). + +use rand::Rng; +use std::os::raw::c_double; + +use crate::{__quantum__rt__fail, strings::convert}; + +#[no_mangle] +pub extern "C" fn __quantum__qis__nan__body() -> c_double { + c_double::NAN +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__isnan__body(val: c_double) -> bool { + val.is_nan() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__infinity__body() -> c_double { + c_double::INFINITY +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__isinf__body(val: c_double) -> bool { + val.is_infinite() && val.is_sign_positive() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__isnegativeinfinity__body(val: c_double) -> bool { + val.is_infinite() && val.is_sign_negative() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__sin__body(val: c_double) -> c_double { + val.sin() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__cos__body(val: c_double) -> c_double { + val.cos() +} +#[no_mangle] +pub extern "C" fn __quantum__qis__tan__body(val: c_double) -> c_double { + val.tan() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arctan2__body(y: c_double, x: c_double) -> c_double { + y.atan2(x) +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__sinh__body(val: c_double) -> c_double { + val.sinh() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__cosh__body(val: c_double) -> c_double { + val.cosh() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__tanh__body(val: c_double) -> c_double { + val.tanh() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arcsin__body(val: c_double) -> c_double { + val.asin() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arccos__body(val: c_double) -> c_double { + val.acos() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arctan__body(val: c_double) -> c_double { + val.atan() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__sqrt__body(val: c_double) -> c_double { + val.sqrt() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__log__body(val: c_double) -> c_double { + val.ln() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__ieeeremainder__body(x: c_double, y: c_double) -> c_double { + x - y * (x / y).round() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__drawrandomint__body(min: i64, max: i64) -> i64 { + if min > max { + unsafe { + __quantum__rt__fail(convert(&"Invalid Argument: minimum > maximum".to_string())); + } + } + rand::thread_rng().gen_range(min..=max) +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__drawrandomdouble__body(min: c_double, max: c_double) -> f64 { + if min > max { + unsafe { + __quantum__rt__fail(convert(&"Invalid Argument: minimum > maximum".to_string())); + } + } + rand::thread_rng().gen_range(min..=max) +} diff --git a/src/Qir/Runtime/stdlib/src/output_recording.rs b/src/Qir/Runtime/stdlib/src/output_recording.rs new file mode 100644 index 00000000000..31fcd06f493 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/output_recording.rs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::{ffi::CString, os::raw::c_double}; + +use crate::strings::double_to_string; + +#[no_mangle] +pub extern "C" fn __quantum__rt__array_start_record_output() { + println!("RESULT\tARRAY_START"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__array_end_record_output() { + println!("RESULT\tARRAY_END"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__tuple_start_record_output() { + println!("RESULT\tTUPLE_START"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__tuple_end_record_output() { + println!("RESULT\tTUPLE_END"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__int_record_output(val: i64) { + println!("RESULT\t{}", val); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__double_record_output(val: c_double) { + println!("RESULT\t{}", double_to_string(val)); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__bool_record_output(val: bool) { + println!("RESULT\t{}", val); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__message_record_output(str: *const CString) { + println!( + "INFO\t{}", + (*str) + .to_str() + .expect("Unable to convert input string") + .escape_default() + ); +} diff --git a/src/Qir/Runtime/stdlib/src/range_support.rs b/src/Qir/Runtime/stdlib/src/range_support.rs new file mode 100644 index 00000000000..66b82252b47 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/range_support.rs @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Functionality in this file can be removed when range support is dropped from the QIR runtime. + +use crate::{arrays::QirArray, strings::convert}; +use std::{ffi::CString, rc::Rc}; + +#[repr(C)] +pub struct Range { + pub start: i64, + pub step: i64, + pub end: i64, +} + +#[no_mangle] +pub extern "C" fn quantum__rt__range_to_string(input: Range) -> *const CString { + let mut range_str = input.start.to_string() + ".."; + if input.step != 1 { + range_str += &(input.step.to_string() + ".."); + } + range_str += &input.end.to_string(); + + convert(&range_str) +} + +#[no_mangle] +pub unsafe extern "C" fn quantum__rt__array_slice_1d( + arr: *const QirArray, + range: Range, +) -> *const QirArray { + let array = &*arr; + let item_size: i64 = array + .elem_size + .try_into() + .expect("Array element size too large for `usize` type on this platform."); + let mut slice = QirArray { + elem_size: array.elem_size, + data: Vec::new(), + }; + let iter: Box> = if range.step > 0 { + Box::new(range.start * item_size..=range.end * item_size) + } else { + Box::new((range.end * item_size..=range.start * item_size).rev()) + }; + + let step: i64 = range.step.abs(); + for i in iter.step_by((step * item_size).try_into().expect( + "Range step multiplied by item size is too large for `usize` type on this platform", + )) { + let index = i + .try_into() + .expect("Item index too large for `usize` type on this platform."); + let mut copy = Vec::new(); + copy.resize(array.elem_size, 0_u8); + copy.copy_from_slice(&array.data[index..index + array.elem_size]); + slice.data.append(&mut copy); + } + + Rc::into_raw(Rc::new(slice)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + arrays::{ + __quantum__rt__array_concatenate, __quantum__rt__array_copy, + __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d, + __quantum__rt__array_get_size_1d, __quantum__rt__array_update_reference_count, + }, + strings::{__quantum__rt__string_get_data, __quantum__rt__string_update_reference_count}, + }; + use std::ffi::CStr; + + #[test] + fn test_range_to_string() { + let input4 = Range { + start: 0, + step: 1, + end: 9, + }; + let str4 = quantum__rt__range_to_string(input4); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str4)) + .to_str() + .unwrap(), + "0..9" + ); + } + let input5 = Range { + start: 0, + step: 2, + end: 12, + }; + let str5 = quantum__rt__range_to_string(input5); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str5)) + .to_str() + .unwrap(), + "0..2..12" + ); + } + unsafe { + __quantum__rt__string_update_reference_count(str4, -1); + __quantum__rt__string_update_reference_count(str5, -1); + } + } + + #[test] + fn test_array_slicing() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + let arr3 = __quantum__rt__array_concatenate(arr, arr2); + assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + // Third array crated via concatenation has contents [42, 31, 0, 42, 31, 0], create + // fourth array via slicing with step size 2, expected contents [42, 0, 31]. + let arr4 = quantum__rt__array_slice_1d( + arr3, + Range { + start: 0, + step: 2, + end: 5, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr4), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + // Create fifth array via slicing with reverse iteration, expected contents + // [31, 0, 42]. + let arr5 = quantum__rt__array_slice_1d( + arr3, + Range { + start: 4, + step: -2, + end: 0, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr5), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + // Create sixth array with range end less than range start, should succeed and create + // an empty array. + let arr6 = quantum__rt__array_slice_1d( + arr5, + Range { + start: 0, + step: 1, + end: -1, + }, + ); + // Confirm each copy, concatenation, and slice is independent of others. + assert_eq!(__quantum__rt__array_get_size_1d(arr6), 0); + __quantum__rt__array_update_reference_count(arr, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + __quantum__rt__array_update_reference_count(arr2, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + __quantum__rt__array_update_reference_count(arr3, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + __quantum__rt__array_update_reference_count(arr4, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + __quantum__rt__array_update_reference_count(arr5, -1); + __quantum__rt__array_update_reference_count(arr6, -1); + } + } +} diff --git a/src/Qir/Runtime/stdlib/src/strings.rs b/src/Qir/Runtime/stdlib/src/strings.rs new file mode 100644 index 00000000000..0b02ef92a95 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/strings.rs @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::update_counts; +use crate::Pauli; +use num_bigint::BigInt; +use std::{ + ffi::{CStr, CString}, + os::raw::{c_char, c_double}, + rc::Rc, +}; + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_create(str: *mut c_char) -> *const CString { + let cstring = CString::new(CStr::from_ptr(str).to_owned()).expect("Failed to create %String"); + Rc::into_raw(Rc::new(cstring)) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_get_data(str: *const CString) -> *const c_char { + (*str).as_bytes_with_nul().as_ptr().cast::() +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_get_length(str: *const CString) -> u32 { + (*str) + .as_bytes() + .len() + .try_into() + .expect("String length is too large for 32-bit integer.") +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_update_reference_count( + str: *const CString, + update: i32, +) { + update_counts(str, update, false); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_concatenate( + s1: *const CString, + s2: *const CString, +) -> *const CString { + let mut new_str = (*s1).clone().into_bytes(); + new_str.extend_from_slice((*s2).to_bytes()); + + Rc::into_raw(Rc::new( + CString::new(new_str).expect("Unable to convert string"), + )) +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_equal( + s1: *const CString, + s2: *const CString, +) -> bool { + *s1 == *s2 +} + +pub(crate) fn convert(input: &T) -> *const CString +where + T: ToString, +{ + unsafe { + __quantum__rt__string_create( + CString::new(input.to_string()) + .expect("Unable to allocate string for conversion.") + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ) + } +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__int_to_string(input: i64) -> *const CString { + convert(&input) +} + +pub(crate) fn double_to_string(input: c_double) -> String { + if (input.floor() - input.ceil()).abs() < c_double::EPSILON { + // The value is a whole number, which by convention is displayed with one decimal point + // to differentiate it from an integer value. + format!("{:.1}", input) + } else { + format!("{}", input) + } +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString { + convert(&double_to_string(input)) +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__bool_to_string(input: bool) -> *const CString { + convert(&input) +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__pauli_to_string(input: Pauli) -> *const CString { + match input { + Pauli::I => convert(&"PauliI"), + Pauli::X => convert(&"PauliX"), + Pauli::Y => convert(&"PauliY"), + Pauli::Z => convert(&"PauliZ"), + } +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_to_string(input: *const BigInt) -> *const CString { + convert(&*input) +} + +#[cfg(test)] +mod tests { + use std::mem::ManuallyDrop; + + use super::*; + use crate::bigints::{ + __quantum__rt__bigint_create_i64, __quantum__rt__bigint_update_reference_count, + }; + + #[test] + fn test_string_create() { + let orig_str = CString::new("Test String").unwrap(); + let str = unsafe { + __quantum__rt__string_create( + orig_str.as_bytes_with_nul().as_ptr() as *mut std::os::raw::c_char + ) + }; + // string_create should make a copy, not consume original. + assert_eq!(orig_str.to_str().unwrap(), "Test String"); + drop(orig_str); + assert!(!str.is_null()); + unsafe { + // Copy should be valid after original is dropped. + assert_eq!( + Rc::from_raw(str) + .to_str() + .expect("Unable to convert input string"), + "Test String" + ); + } + } + + #[test] + fn test_string_get_data() { + let str = unsafe { + __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8 + ) + }; + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str)) + .to_str() + .unwrap(), + "Data" + ); + } + unsafe { + __quantum__rt__string_update_reference_count(str, -1); + } + } + + #[test] + fn test_string_get_length() { + let str = unsafe { + __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8 + ) + }; + assert_eq!(unsafe { __quantum__rt__string_get_length(str) }, 4); + unsafe { + __quantum__rt__string_update_reference_count(str, -1); + } + } + + #[test] + fn test_string_update_reference_count() { + unsafe { + let str = __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let rc = ManuallyDrop::new(Rc::from_raw(str)); + assert_eq!(Rc::strong_count(&rc), 1); + __quantum__rt__string_update_reference_count(str, 2); + assert_eq!(Rc::strong_count(&rc), 3); + __quantum__rt__string_update_reference_count(str, -2); + assert_eq!(Rc::strong_count(&rc), 1); + __quantum__rt__string_update_reference_count(str, -1); + } + } + + #[test] + fn test_string_concatenate() { + unsafe { + let str1 = __quantum__rt__string_create( + CString::new("Hello").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let str2 = __quantum__rt__string_create( + CString::new(", World!") + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ); + let str3 = __quantum__rt__string_concatenate(str1, str2); + // Concatenated string should have combined value. + let rc = ManuallyDrop::new(Rc::from_raw(str3)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str3)) + .to_str() + .unwrap(), + "Hello, World!" + ); + __quantum__rt__string_update_reference_count(str3, -1); + // After decrement and drop, original strings should still be valid. + let rc = ManuallyDrop::new(Rc::from_raw(str2)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str2)) + .to_str() + .unwrap(), + ", World!" + ); + __quantum__rt__string_update_reference_count(str2, -1); + let rc = ManuallyDrop::new(Rc::from_raw(str1)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1)) + .to_str() + .unwrap(), + "Hello" + ); + __quantum__rt__string_update_reference_count(str1, -1); + } + } + + #[test] + fn test_string_equal() { + unsafe { + let str1 = __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let str2 = __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let str3 = __quantum__rt__string_create( + CString::new("Not Data") + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ); + assert!(__quantum__rt__string_equal(str1, str2)); + assert!(!__quantum__rt__string_equal(str1, str3)); + // Confirm data is still valid and not consumed by the check. + let rc = ManuallyDrop::new(Rc::from_raw(str3)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str3)) + .to_str() + .unwrap(), + "Not Data" + ); + __quantum__rt__string_update_reference_count(str3, -1); + let rc = ManuallyDrop::new(Rc::from_raw(str2)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str2)) + .to_str() + .unwrap(), + "Data" + ); + __quantum__rt__string_update_reference_count(str2, -1); + let rc = ManuallyDrop::new(Rc::from_raw(str1)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1)) + .to_str() + .unwrap(), + "Data" + ); + __quantum__rt__string_update_reference_count(str1, -1); + } + } + + #[test] + fn test_to_string() { + let input0 = 42; + let str0 = __quantum__rt__int_to_string(input0); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str0)) + .to_str() + .unwrap(), + "42" + ); + } + let input1 = 4.2; + let str1 = __quantum__rt__double_to_string(input1); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1)) + .to_str() + .unwrap(), + "4.2" + ); + } + let input1_1 = 4.0; + let str1_1 = __quantum__rt__double_to_string(input1_1); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1_1)) + .to_str() + .unwrap(), + "4.0" + ); + } + let input1_2 = 0.1; + let str1_2 = __quantum__rt__double_to_string(input1_2); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1_2)) + .to_str() + .unwrap(), + "0.1" + ); + } + let input1_3 = 0.100_000_000_01; + let str1_3 = __quantum__rt__double_to_string(input1_3); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1_3)) + .to_str() + .unwrap(), + "0.10000000001" + ); + } + let input2 = false; + let str2 = __quantum__rt__bool_to_string(input2); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str2)) + .to_str() + .unwrap(), + "false" + ); + } + let input3 = Pauli::Z; + let str3 = __quantum__rt__pauli_to_string(input3); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str3)) + .to_str() + .unwrap(), + "PauliZ" + ); + } + let input4 = __quantum__rt__bigint_create_i64(400_002); + unsafe { + let str4 = __quantum__rt__bigint_to_string(input4); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str4)) + .to_str() + .unwrap(), + "400002" + ); + + __quantum__rt__string_update_reference_count(str0, -1); + __quantum__rt__string_update_reference_count(str1, -1); + __quantum__rt__string_update_reference_count(str1_1, -1); + __quantum__rt__string_update_reference_count(str1_2, -1); + __quantum__rt__string_update_reference_count(str1_3, -1); + __quantum__rt__string_update_reference_count(str2, -1); + __quantum__rt__string_update_reference_count(str3, -1); + __quantum__rt__string_update_reference_count(str4, -1); + __quantum__rt__bigint_update_reference_count(input4, -1); + } + } +} diff --git a/src/Qir/Runtime/stdlib/src/tuples.rs b/src/Qir/Runtime/stdlib/src/tuples.rs new file mode 100644 index 00000000000..f3774f63d95 --- /dev/null +++ b/src/Qir/Runtime/stdlib/src/tuples.rs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::update_counts; +use std::{ + mem::{size_of, ManuallyDrop}, + rc::Rc, + usize, +}; + +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn __quantum__rt__tuple_create(size: u64) -> *mut *const Vec { + let mut mem = vec![ + 0_u8; + >::try_from(size) + .expect("Tuple size too large for `usize` type on this platform.") + + size_of::<*const Vec>() + ]; + + unsafe { + let header = mem.as_mut_ptr().cast::<*const Vec>(); + *header = Rc::into_raw(Rc::new(mem)); + header.wrapping_add(1) + } +} + +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__tuple_copy( + raw_tup: *mut *const Vec, + force: bool, +) -> *mut *const Vec { + let rc = ManuallyDrop::new(Rc::from_raw(*(raw_tup).wrapping_sub(1))); + if force || Rc::weak_count(&rc) > 0 { + let mut copy = rc.as_ref().clone(); + let header = copy.as_mut_ptr().cast::<*const Vec>(); + *header = Rc::into_raw(Rc::new(copy)); + header.wrapping_add(1) + } else { + Rc::into_raw(Rc::clone(&rc)); + raw_tup + } +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__tuple_update_reference_count( + raw_tup: *mut *const Vec, + update: i32, +) { + update_counts(*raw_tup.wrapping_sub(1), update, false); +} + +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__tuple_update_alias_count( + raw_tup: *mut *const Vec, + update: i32, +) { + update_counts(*raw_tup.wrapping_sub(1), update, true); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tuple_create() { + let tup = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + *tup.cast::() = 42; + __quantum__rt__tuple_update_reference_count(tup, -1); + } + } + + #[test] + fn test_tuple_update_reference_count() { + let tup = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + let rc = ManuallyDrop::new(Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1))); + assert_eq!(Rc::strong_count(&rc), 1); + __quantum__rt__tuple_update_reference_count(tup, 2); + assert_eq!(Rc::strong_count(&rc), 3); + __quantum__rt__tuple_update_reference_count(tup, -2); + assert_eq!(Rc::strong_count(&rc), 1); + __quantum__rt__tuple_update_reference_count(tup, -1); + } + } + + #[test] + fn test_tuple_update_alias_count() { + let tup = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + let rc = ManuallyDrop::new(Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1))); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!(Rc::weak_count(&rc), 0); + __quantum__rt__tuple_update_alias_count(tup, 2); + assert_eq!(Rc::weak_count(&rc), 2); + __quantum__rt__tuple_update_alias_count(tup, -2); + assert_eq!(Rc::weak_count(&rc), 0); + __quantum__rt__tuple_update_reference_count(tup, -1); + } + } + + #[test] + fn test_tuple_copy() { + let tup1 = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + *tup1.cast::() = 42; + let tup2 = __quantum__rt__tuple_copy(tup1, false); + assert_eq!(tup2, tup1); + assert_eq!(*tup2.cast::(), 42); + __quantum__rt__tuple_update_reference_count(tup2, -1); + assert_eq!(*tup1.cast::(), 42); + let tup3 = __quantum__rt__tuple_copy(tup1, true); + assert_ne!(tup3, tup1); + assert_eq!(*tup3.cast::(), 42); + __quantum__rt__tuple_update_reference_count(tup3, -1); + assert_eq!(*tup1.cast::(), 42); + __quantum__rt__tuple_update_alias_count(tup1, 1); + let tup4 = __quantum__rt__tuple_copy(tup1, false); + assert_ne!(tup4, tup1); + assert_eq!(*tup4.cast::(), 42); + __quantum__rt__tuple_update_reference_count(tup4, -1); + assert_eq!(*tup1.cast::(), 42); + __quantum__rt__tuple_update_alias_count(tup1, -1); + __quantum__rt__tuple_update_reference_count(tup1, -1); + } + } +} diff --git a/src/Qir/Runtime/test-qir-runtime.ps1 b/src/Qir/Runtime/test-qir-runtime.ps1 deleted file mode 100644 index 377ee141411..00000000000 --- a/src/Qir/Runtime/test-qir-runtime.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -. (Join-Path $PSScriptRoot .. qir-utils.ps1) - -# TODO: macOS: `ASAN_OPTIONS=detect_leaks=1` (https://clang.llvm.org/docs/AddressSanitizer.html#memory-leak-detection). -$env:ASAN_OPTIONS = "check_initialization_order=true:detect_stack_use_after_return=true:" ` - + "alloc_dealloc_mismatch=true:new_delete_type_mismatch=true:strict_init_order=true:strict_string_checks=true:" ` - + "detect_invalid_pointer_pairs=2" - -if (-not (Test-CTest (Join-Path $PSScriptRoot bin $Env:BUILD_CONFIGURATION unittests) "QIR Runtime")) { - throw "At least one project failed testing. Check the logs." -} diff --git a/src/Qir/Runtime/test-qir-stdlib.ps1 b/src/Qir/Runtime/test-qir-stdlib.ps1 new file mode 100644 index 00000000000..482b25d6a00 --- /dev/null +++ b/src/Qir/Runtime/test-qir-stdlib.ps1 @@ -0,0 +1,26 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Push-Location (Join-Path $PSScriptRoot stdlib) +try { + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) + # for interoperating Rust and C. + # NB: CFG is only supported on Windows, but the Rust flag is supported on + # all platforms; it's ignored on platforms without CFG functionality. + $Env:RUSTFLAGS = "-C control-flow-guard"; + + # Actually run the test. + cargo test @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo test on QIR stdlib." } + + # 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; + } +} +finally { + Pop-Location +} diff --git a/src/Qir/Runtime/unittests/CMakeLists.txt b/src/Qir/Runtime/unittests/CMakeLists.txt deleted file mode 100644 index dead4858740..00000000000 --- a/src/Qir/Runtime/unittests/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -include(unit_test_include) - -#============================================================================== -# produce the unit tests binary: qir-runtime-unittests.exe -# -add_executable(qir-runtime-unittests - driver.cpp - QirOutputHandlingTests.cpp - QirRuntimeTests.cpp - ToffoliTests.cpp - TracerTests.cpp - QubitManagerTests.cpp - $ - $ - $ - $ - $ -) - -target_include_directories(qir-runtime-unittests PUBLIC - "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2" - ${public_includes} - ${common_includes} - "${PROJECT_SOURCE_DIR}/lib/QIR" - "${PROJECT_SOURCE_DIR}/lib/QSharpFoundation" - "${PROJECT_SOURCE_DIR}/lib/QSharpCore" - "${PROJECT_SOURCE_DIR}/lib/Tracer" -) -target_link_libraries(qir-runtime-unittests ${CMAKE_DL_LIBS}) -target_compile_definitions(qir-runtime-unittests PRIVATE EXPORT_QIR_API) -install(TARGETS qir-runtime-unittests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-runtime-unittests) diff --git a/src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp b/src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp deleted file mode 100644 index c9203659756..00000000000 --- a/src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -#include - -#include "catch.hpp" - -#include "QirRuntime.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "QirOutputHandling.hpp" -#include "OutputStream.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -TEST_CASE("QIR: TupleOutHandle", "[qir][out_handl][tuple_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // tuple_start_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__tuple_start_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_TUPLE_START) + QOH_REC_DELIMITER)); - } - - // tuple_end_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__tuple_end_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_TUPLE_END) + QOH_REC_DELIMITER)); - } - - // start + end - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__tuple_start_record_output(); - __quantum__rt__tuple_end_record_output(); - } - REQUIRE(actualStrStream.str() == - (string(QOH_REC_TUPLE_START) + QOH_REC_DELIMITER + string(QOH_REC_TUPLE_END) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: ArrayOutHandle", "[qir][out_handl][array_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // array_start_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_start_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_ARRAY_START) + QOH_REC_DELIMITER)); - } - - // array_end_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_end_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_ARRAY_END) + QOH_REC_DELIMITER)); - } - - // start + end - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_start_record_output(); - __quantum__rt__array_end_record_output(); - } - REQUIRE(actualStrStream.str() == - (string(QOH_REC_ARRAY_START) + QOH_REC_DELIMITER + string(QOH_REC_ARRAY_END) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: ResultOutHandle", "[qir][out_handl][result_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__result_record_output(__quantum__rt__result_get_zero()); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_RESULT_ZERO) + QOH_REC_DELIMITER)); - } - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__result_record_output(__quantum__rt__result_get_one()); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_RESULT_ONE) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: BoolOutHandle", "[qir][out_handl][bool_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__bool_record_output(false); - } - - REQUIRE(actualStrStream.str() == (string(QOH_REC_FALSE) + QOH_REC_DELIMITER)); - } - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__bool_record_output(true); - } - - REQUIRE(actualStrStream.str() == (string(QOH_REC_TRUE) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: i64OutHandle", "[qir][out_handl][i64_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - auto i64Value = GENERATE(42LL, -2LL, 0x7FFFFFFFFFFFFFFFLL, 0x8000000000000000LL, 0x3333333333333333LL); - // 9223372036854775807 -9223372036854775808 3689348814741910323 - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__integer_record_output(i64Value); - } - - std::stringstream expectedStrStream; - expectedStrStream << i64Value; - - REQUIRE(actualStrStream.str() == (string(QOH_REC_PREFIX) + expectedStrStream.str() + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: doubleOutHandle", "[qir][out_handl][double_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - auto value = GENERATE(0.0, 1.0, -2.0, 3.14159, -6.28, 6.67E+23, NAN, INFINITY); - // 6.67e+23 nan inf - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__double_record_output(value); - } - - std::stringstream expectedStrStream; - expectedStrStream << value; - - REQUIRE(actualStrStream.str() == (string(QOH_REC_PREFIX) + expectedStrStream.str() + QOH_REC_DELIMITER)); - } -} - - -// Group/mixed. -TEST_CASE("QIR: MixedOutHandle", "[qir][out_handl][tuple_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // clang-format off - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_start_record_output(); - __quantum__rt__tuple_start_record_output(); - __quantum__rt__double_record_output(6.67E+23); - __quantum__rt__integer_record_output(42LL); - __quantum__rt__bool_record_output(true); - __quantum__rt__result_record_output(__quantum__rt__result_get_one()); - __quantum__rt__tuple_end_record_output(); - - __quantum__rt__tuple_start_record_output(); - __quantum__rt__double_record_output(-10.123); - __quantum__rt__integer_record_output(-2049LL); - __quantum__rt__bool_record_output(false); - __quantum__rt__result_record_output(__quantum__rt__result_get_zero()); - __quantum__rt__tuple_end_record_output(); - __quantum__rt__array_end_record_output(); - } - - std::stringstream expectedStrStream; - expectedStrStream << - QOH_REC_ARRAY_START << QOH_REC_DELIMITER << - QOH_REC_TUPLE_START << QOH_REC_DELIMITER << - QOH_REC_PREFIX << (double)6.67E+23 << QOH_REC_DELIMITER << - QOH_REC_PREFIX << 42LL << QOH_REC_DELIMITER << - QOH_REC_TRUE << QOH_REC_DELIMITER << - QOH_REC_RESULT_ONE << QOH_REC_DELIMITER << - QOH_REC_TUPLE_END << QOH_REC_DELIMITER << - - QOH_REC_TUPLE_START << QOH_REC_DELIMITER << - QOH_REC_PREFIX << (double)-10.123 << QOH_REC_DELIMITER << - QOH_REC_PREFIX << -2049LL << QOH_REC_DELIMITER << - QOH_REC_FALSE << QOH_REC_DELIMITER << - QOH_REC_RESULT_ZERO << QOH_REC_DELIMITER << - QOH_REC_TUPLE_END << QOH_REC_DELIMITER << - QOH_REC_ARRAY_END << QOH_REC_DELIMITER; - - REQUIRE(actualStrStream.str() == expectedStrStream.str()); - // actual: "RESULT\tARRAY_START\nRESULT\tTUPLE_START\nRESULT\t6.67e+23\nRESULT\t42\nRESULT\ttrue\nRESULT\t1\nRESULT\tTUPLE_END\n" - // "RESULT\tTUPLE_START\nRESULT\t-10.123\nRESULT\t-2049\nRESULT\tfalse\nRESULT\t0\nRESULT\tTUPLE_END\n" - // "RESULT\tARRAY_END\n\xcd\xcd\xcd\xcd\xcd..\xee\xfe\xee\xfe" - } - // clang-format on -} diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp deleted file mode 100644 index 6b29ac9e5cf..00000000000 --- a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp +++ /dev/null @@ -1,1075 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "catch.hpp" - -#include -#include // for memcpy -#include -#include -#include - -#include "QirTypes.hpp" -#include "qsharp__foundation__qis.hpp" -#include "qsharp__core__qis.hpp" -#include "QirRuntime.hpp" - -#include "QirContext.hpp" -#include "SimulatorStub.hpp" - -using namespace Microsoft::Quantum; - -static constexpr bool forseNewInstance = true; - -struct ResultsReferenceCountingTestQAPI : public SimulatorStub -{ - int lastId = -1; - const int maxResults; // TODO: Use unsigned type. - std::vector allocated; - - static int GetResultId(Result r) - { - return static_cast(reinterpret_cast(r)); - } - - ResultsReferenceCountingTestQAPI(int maxRes) : maxResults(maxRes), allocated((size_t)maxRes, false) - { - } - - Result Measure(long, PauliId[], long, QubitIdType[]) override - { - assert(this->lastId < this->maxResults); - this->lastId++; - this->allocated.at((size_t)(this->lastId)) = true; - return reinterpret_cast(this->lastId); - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - void ReleaseResult(Result result) override - { - const int id = GetResultId(result); - INFO(id); - REQUIRE(this->allocated.at((size_t)id)); - this->allocated.at((size_t)id).flip(); - } - bool AreEqualResults(Result r1, Result r2) override - { - return (r1 == r2); - } - - bool HaveResultsInFlight() const - { - for (const auto b : this->allocated) - { - if (b) - { - return true; - } - } - return false; - } -}; -TEST_CASE("Results: comparison and reference counting", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(3); - QirExecutionContext::Scoped qirctx(qapi.get()); - - Result r1 = qapi->Measure(0, nullptr, 0, nullptr); // we don't need real qubits for this test - Result r2 = qapi->Measure(0, nullptr, 0, nullptr); - REQUIRE(__quantum__rt__result_equal(r1, r1)); - REQUIRE(!__quantum__rt__result_equal(r1, r2)); - - // release result that has never been shared, the test QAPI will verify double release - __quantum__rt__result_update_reference_count(r2, -1); - - // share a result a few times - __quantum__rt__result_update_reference_count(r1, 2); - - Result r3 = qapi->Measure(0, nullptr, 0, nullptr); - - // release shared result, the test QAPI will verify double release - __quantum__rt__result_update_reference_count(r1, -3); // one release for shared and for the original allocation - - REQUIRE(qapi->HaveResultsInFlight()); // r3 should be still alive - __quantum__rt__result_update_reference_count(r3, -1); - - REQUIRE(!qapi->HaveResultsInFlight()); // no leaks -} - -TEST_CASE("Arrays: one dimensional", "[qir_support]") -{ - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), 5); - - memcpy(a->buffer, "Hello", 5); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(a, 4) == 'o'); - REQUIRE(__quantum__rt__array_get_dim(a) == 1); - REQUIRE(__quantum__rt__array_get_size_1d(a) == 5); - - QirArray* b = new QirArray(1, sizeof(char)); - *__quantum__rt__array_get_element_ptr_1d(b, 0) = '!'; - - QirArray* ab = __quantum__rt__array_concatenate(a, b); - REQUIRE(__quantum__rt__array_get_size_1d(ab) == 6); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(ab, 4) == 'o'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(ab, 5) == '!'); - - __quantum__rt__array_update_reference_count(a, -1); - __quantum__rt__array_update_reference_count(b, -1); - __quantum__rt__array_update_reference_count(ab, -1); -} - -TEST_CASE("Arrays: multiple dimensions", "[qir_support]") -{ - const size_t count = (size_t)(5 * 3 * 4); // 60 - QirArray* a = __quantum__rt__array_create(sizeof(int), 3, (int64_t)5, (int64_t)3, (int64_t)4); - REQUIRE(__quantum__rt__array_get_dim(a) == 3); - REQUIRE(__quantum__rt__array_get_size(a, 0) == 5); - REQUIRE(__quantum__rt__array_get_size(a, 1) == 3); - REQUIRE(__quantum__rt__array_get_size(a, 2) == 4); - - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 0, 0, 1))) == 1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 0, 1, 0))) == 4); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 4, 2, 3))) == 59); - - QirArray* b = __quantum__rt__array_copy(a, true /*force*/); - *(reinterpret_cast(__quantum__rt__array_get_element_ptr(b, 1, 2, 3))) = 42; - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 2, 3))) == 23); - - __quantum__rt__array_update_reference_count(a, -1); - __quantum__rt__array_update_reference_count(b, -1); -} - -TEST_CASE("Arrays: copy elision", "[qir_support]") -{ - QirArray* copy = __quantum__rt__array_copy(nullptr, true /*force*/); - CHECK(copy == nullptr); - - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), 5); - // the `a` array contains garbage but for this test we don't care - - // no aliases for the array, copy should be elided unless enforced - copy = __quantum__rt__array_copy(a, false /*force*/); - CHECK(a == copy); - __quantum__rt__array_update_reference_count(copy, -1); - - // single alias for the array, but copy enforced - copy = __quantum__rt__array_copy(a, true /*force*/); - CHECK(a != copy); - __quantum__rt__array_update_reference_count(copy, -1); - - // existing aliases for the array -- cannot elide copy - __quantum__rt__array_update_alias_count(a, 1); - copy = __quantum__rt__array_copy(a, false /*force*/); - CHECK(a != copy); - __quantum__rt__array_update_reference_count(copy, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: empty", "[qir_support]") -{ - QirArray* b = __quantum__rt__array_create(sizeof(int), 3, (int64_t)4, (int64_t)0, (int64_t)3); - REQUIRE(__quantum__rt__array_get_dim(b) == 3); - REQUIRE(__quantum__rt__array_get_size(b, 0) == 4); - REQUIRE(__quantum__rt__array_get_size(b, 1) == 0); - REQUIRE(__quantum__rt__array_get_size(b, 2) == 3); - REQUIRE(b->buffer == nullptr); - __quantum__rt__array_update_reference_count(b, -1); - - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), 0); - REQUIRE(__quantum__rt__array_get_dim(a) == 1); - REQUIRE(__quantum__rt__array_get_size_1d(a) == 0); - REQUIRE(a->buffer == nullptr); - - QirArray* a1 = __quantum__rt__array_copy(a, true /*force*/); - REQUIRE(__quantum__rt__array_get_dim(a1) == 1); - REQUIRE(__quantum__rt__array_get_size_1d(a1) == 0); - REQUIRE(a1->buffer == nullptr); - __quantum__rt__array_update_reference_count(a1, -1); - - QirArray* c = __quantum__rt__array_create_1d(sizeof(char), 5); - memcpy(c->buffer, "hello", 5); - QirArray* ac = __quantum__rt__array_concatenate(a, c); - REQUIRE(__quantum__rt__array_get_size_1d(ac) == 5); - QirArray* ca = __quantum__rt__array_concatenate(c, a); - REQUIRE(__quantum__rt__array_get_size_1d(ca) == 5); - - __quantum__rt__array_update_reference_count(a, -1); - __quantum__rt__array_update_reference_count(ac, -1); - __quantum__rt__array_update_reference_count(ca, -1); - __quantum__rt__array_update_reference_count(c, -1); -} - -TEST_CASE("Arrays: check the slice range", "[qir_support]") -{ - const int64_t dim0 = 5; - const int64_t dim1 = 6; - QirArray* a = __quantum__rt__array_create(sizeof(int), 2, dim0, dim1); - QirArray* slice = nullptr; - - // invalid range - CHECK_THROWS(quantum__rt__array_slice(a, 0, {0, 0, 0}, forseNewInstance)); - - // violated bounds - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 1, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 1, dim1}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 1, dim0 - 1}, forseNewInstance)); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -1, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -1, 0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -1, -1}, forseNewInstance)); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 3, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 3, dim1 + 2}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 3, dim0 - 1}, forseNewInstance)); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -3, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -3, 0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -3, -3}, forseNewInstance)); - - // empty range should produce empty array - slice = quantum__rt__array_slice(a, 0, {dim0 - 1, 1, 0}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - __quantum__rt__array_update_reference_count(slice, -1); - - slice = quantum__rt__array_slice(a, 1, {0, -1, dim0 - 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 0); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: slice of 1D array", "[qir_support]") -{ - const int64_t dim = 5; - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), dim); - memcpy(a->buffer, "01234", 5); - QirArray* slice = nullptr; - - // even if slice results in a single value, it's still an array - slice = quantum__rt__array_slice(a, 0, {1, 2 * dim, dim}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 1); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '1'); - __quantum__rt__array_update_reference_count(slice, -1); - - // if the range covers the whole array, it's effectively a copy - slice = quantum__rt__array_slice(a, 0, {0, 1, dim - 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '0'); - REQUIRE(*__quantum__rt__array_get_element_ptr(slice, 4) == '4'); - __quantum__rt__array_update_reference_count(slice, -1); - - // disconnected slice (also check that the end of range can be above bounds as long as the generated sequence is - // within them) - slice = quantum__rt__array_slice(a, 0, {0, 4, dim + 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 2); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '0'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 1) == '4'); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: reversed slice of 1D array", "[qir_support]") -{ - const int64_t dim = 5; - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), dim); - memcpy(a->buffer, "01234", 5); - QirArray* slice = nullptr; - - // even if slice results in a single value, it's still an array - slice = quantum__rt__array_slice(a, 0, {1, -dim, 0}, forseNewInstance); - // Range{1, -dim, 0} == Range{1, -5, 0} == { 1 }. - // slice == char[1] == { '1' }. - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 1); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '1'); - __quantum__rt__array_update_reference_count(slice, -1); // slice == dangling pointer. - - // reversed slices are alwayes disconnected - slice = quantum__rt__array_slice(a, 0, {dim - 1, -2, 0}, forseNewInstance); - // Range{dim - 1, -2, 0} == Range{4, -2, 0} == {4, 2, 0}. - // slice == char[3] == {'4', '2', '0'}. - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 3); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '4'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 1) == '2'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 2) == '0'); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: slice of 3D array", "[qir_support]") -{ - const int32_t dims = 3; - const int64_t dim0 = 5; - const int64_t dim1 = 3; - const int64_t dim2 = 4; - - QirArray* a = __quantum__rt__array_create(sizeof(int), dims, dim0, dim1, dim2); - QirArray* slice = nullptr; - - const size_t count = (size_t)(dim0 * dim1 * dim2); // 60 - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, dim0 - 1, dim1 - 1, dim2 - 1))) == - count - 1); - - // if the range covers the whole dimension, it's effectively a copy - slice = quantum__rt__array_slice(a, 1, {0, 1, dim1 - 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 3))) == 59); - __quantum__rt__array_update_reference_count(slice, -1); - - // if the range consists of a single point, the slice still has the same dimensions - slice = quantum__rt__array_slice(a, 1, {1, 2 * dim1, dim1}, forseNewInstance); // items with second index = 1 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 4); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 0, 3))) == 55); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on 0 dimension - slice = quantum__rt__array_slice(a, 0, {1, 1, 3}, forseNewInstance); // items with first index = 1, 2 or 3 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 3); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 2, 2, 3))) == 47); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on last dimension, expected result: - // indexes -- values - // 000 001 | 010 011 | 020 021 -- [ 1 2 | 5 6 | 9 10] - // 100 101 | 110 111 | 120 121 -- [13 14 | 17 18 | 21 22] - // ... [25 ... ] - // ... [37 ... ] - // 400 401 | 410 411 | 420 421 -- [49 50 | 53 54 | 57 58] - slice = quantum__rt__array_slice(a, 2, {1, 1, 2}, forseNewInstance); // items with last index = 1 or 2 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 2, 1))) == 10); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 1, 1))) == 18); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 1))) == 58); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on sparse range in 0 dimension (also check that the end of range can be above bounds as long as the - // generated sequence is within them) - slice = quantum__rt__array_slice(a, 0, {0, 3, dim0}, forseNewInstance); // items with first index = 0 or 3 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 2); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 0); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 2, 3))) == 47); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on sparse range in the middle dimension - slice = quantum__rt__array_slice(a, 1, {0, 2, 2}, forseNewInstance); // items with second index = 0 or 2 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 2); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 0); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 1, 3))) == 59); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on sparse range in the last dimension - // indexes -- values - // 000 001 | 010 011 | 020 021 -- [01 03 | 05 07 | 09 11] - // 100 101 | 110 111 | 120 121 -- [13 15 | 17 19 | 21 23] - // ... -- [25 ... ] - // ... -- [37 ... ] - // 400 401 | 410 411 | 420 421 -- [49 51 | 53 55 | 57 59] - slice = - quantum__rt__array_slice(a, 2, {1, 2, 3}, forseNewInstance); // items with last index = 1 or 3 (all odd numbers) - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 2, 1))) == 11); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 1, 0))) == 17); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 1))) == 59); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: reversed slice of 3D array", "[qir_support]") -{ - const int32_t dims = 3; - const int64_t dim0 = 5; - const int64_t dim1 = 3; - const int64_t dim2 = 4; - - QirArray* a = __quantum__rt__array_create(sizeof(int), dims, dim0, dim1, dim2); - QirArray* slice = nullptr; - - const size_t count = (size_t)(dim0 * dim1 * dim2); // 60 - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, dim0 - 1, dim1 - 1, dim2 - 1))) == - count - 1); - - // if the range consists of a single point, the slice still has the same dimensions - slice = quantum__rt__array_slice(a, 1, {1, -dim1, 0}, forseNewInstance); // items with second index = 1 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 4); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 0, 3))) == 55); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on dim0, expect the result to look like: - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [36 - 47] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [24 - 35] - // 200 201 202 203 | 210 211 212 213 | 220 221 222 223 -- [12 - 23] - slice = quantum__rt__array_slice(a, 0, {dim0 - 2, -1, 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 3); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 36); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 2, 2, 3))) == 23); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on last dimension, expect the result to look like: - // indexes -- values - // 000 001 | 010 011 | 020 021 -- [03 01 | 07 05 | 11 09] - // 100 101 | 110 111 | 120 121 -- [15 13 | 19 17 | 23 21] - // ... -- [27 ... ] - // ... -- [39 ... ] - // 400 401 | 410 411 | 420 421 -- [51 49 | 55 53 | 59 57] - slice = quantum__rt__array_slice(a, 2, {dim2 - 1, -2, 0}, - forseNewInstance); // items with last index 3, 1 (all odd numbers) - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 3); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 2, 1))) == 9); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 1, 0))) == 19); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 1))) == 57); - __quantum__rt__array_update_reference_count(slice, -1); - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: project of 3D array", "[qir_support]") -{ - const int32_t dims = 3; - const int64_t dim0 = 5; - const int64_t dim1 = 3; - const int64_t dim2 = 4; - - QirArray* a = __quantum__rt__array_create(sizeof(int), dims, dim0, dim1, dim2); - QirArray* project = nullptr; - - const size_t count = (size_t)(dim0 * dim1 * dim2); // 60 - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, dim0 - 1, dim1 - 1, dim2 - 1))) == - count - 1); - - // project on 0 dimension, expected result: - // indexes -- values - // 00 01 02 03 | 10 11 12 13 | 20 21 22 23 -- [12 - 23] - project = __quantum__rt__array_project(a, 0, 1); // items with first index = 1 - REQUIRE(__quantum__rt__array_get_dim(project) == dims - 1); - REQUIRE(__quantum__rt__array_get_size(project, 0) == dim1); - REQUIRE(__quantum__rt__array_get_size(project, 1) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 1, 1))) == 17); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 2, 3))) == 23); - __quantum__rt__array_update_reference_count(project, -1); - - // project on last dimension, expected result: - // indexes -- values - // 00 | 01 | 02 -- [02 06 10] - // 10 | 11 | 12 -- [14 18 22] - // ... -- [26 30 34] - // ... -- [38 42 46] - // 40 | 41 | 42 -- [50 54 58] - project = __quantum__rt__array_project(a, 2, 2); // items with last index = 2 - REQUIRE(__quantum__rt__array_get_dim(project) == dims - 1); - REQUIRE(__quantum__rt__array_get_size(project, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(project, 1) == dim1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 0, 0, 0))) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 1, 1, 2))) == 18); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 4, 2, 2))) == 58); - __quantum__rt__array_update_reference_count(project, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -std::unordered_map& AllocatedStrings(); -TEST_CASE("Strings: reuse", "[qir_support]") -{ - QirString* a = __quantum__rt__string_create("abc"); - QirString* b = __quantum__rt__string_create("abc"); - QirString* c = __quantum__rt__string_create("xyz"); - - REQUIRE(a == b); - REQUIRE(a->refCount == 2); - REQUIRE(a != c); - REQUIRE(c->refCount == 1); - - __quantum__rt__string_update_reference_count(a, -1); - REQUIRE(b->str.compare("abc") == 0); - - __quantum__rt__string_update_reference_count(b, -1); - __quantum__rt__string_update_reference_count(c, -1); - - REQUIRE(AllocatedStrings().empty()); -} - -TEST_CASE("Strings: concatenate", "[qir_support]") -{ - QirString* a = __quantum__rt__string_create("abc"); - QirString* b = __quantum__rt__string_create("xyz"); - QirString* abExpected = __quantum__rt__string_create("abcxyz"); - - QirString* ab = __quantum__rt__string_concatenate(a, b); - REQUIRE(ab == abExpected); - - QirString* aa = __quantum__rt__string_concatenate(a, a); - REQUIRE(aa->str.compare("abcabc") == 0); - - __quantum__rt__string_update_reference_count(a, -1); - __quantum__rt__string_update_reference_count(b, -1); - __quantum__rt__string_update_reference_count(abExpected, -1); - __quantum__rt__string_update_reference_count(ab, -1); - __quantum__rt__string_update_reference_count(aa, -1); - - REQUIRE(AllocatedStrings().empty()); -} - -TEST_CASE("Strings: conversions from built-in types", "[qir_support]") -{ - std::vector strings; - - strings.push_back(__quantum__rt__int_to_string(0)); - REQUIRE(strings.back()->str == std::string("0")); - - strings.push_back(__quantum__rt__int_to_string(42)); - REQUIRE(strings.back()->str == std::string("42")); - - strings.push_back(__quantum__rt__int_to_string(-42)); - REQUIRE(strings.back()->str == std::string("-42")); - - strings.push_back(__quantum__rt__double_to_string(4.2)); - REQUIRE(strings.back()->str == std::string("4.20000000000000018")); // platform dependent? - - strings.push_back(__quantum__rt__double_to_string(42.0)); - REQUIRE(strings.back()->str == std::string("42.0")); - - strings.push_back(__quantum__rt__double_to_string(1e-9)); - REQUIRE(strings.back()->str == std::string("0.000000001")); - - strings.push_back(__quantum__rt__double_to_string(0.0)); - REQUIRE(strings.back()->str == std::string("0.0")); - - strings.push_back(__quantum__rt__double_to_string(-42.0)); - REQUIRE(strings.back()->str == std::string("-42.0")); - - strings.push_back(__quantum__rt__double_to_string(-0.0)); - REQUIRE(strings.back()->str == std::string("-0.0")); - - strings.push_back(__quantum__rt__bool_to_string(false)); - REQUIRE(strings.back()->str == std::string("false")); - - strings.push_back(__quantum__rt__bool_to_string(true)); - REQUIRE(strings.back()->str == std::string("true")); - - // strings, created by conversions are reused for each type - strings.push_back(__quantum__rt__int_to_string(0)); - REQUIRE(std::count(strings.begin(), strings.end(), strings.back()) == 2); - - strings.push_back(__quantum__rt__double_to_string(42.0)); - REQUIRE(std::count(strings.begin(), strings.end(), strings.back()) == 2); - - strings.push_back(__quantum__rt__bool_to_string(1)); - REQUIRE(std::count(strings.begin(), strings.end(), strings.back()) == 2); - - for (QirString* qstr : strings) - { - __quantum__rt__string_update_reference_count(qstr, -1); - } - - REQUIRE(AllocatedStrings().empty()); -} - -TEST_CASE("Strings: conversions from custom qir types", "[qir_support]") -{ - QirString* qstr1 = quantum__rt__range_to_string({0, 1, 42}); - REQUIRE(qstr1->str == std::string("0..42")); - - QirString* qstr2 = quantum__rt__range_to_string({0, 3, 42}); - REQUIRE(qstr2->str == std::string("0..3..42")); - - __quantum__rt__string_update_reference_count(qstr1, -1); - __quantum__rt__string_update_reference_count(qstr2, -1); - - REQUIRE(AllocatedStrings().empty()); -} - -struct QubitTestQAPI : public SimulatorStub -{ - int lastId = -1; // TODO: Use unsigned type. - const int maxQubits; // TODO: Use unsigned type. - std::vector allocated; - - static QubitIdType GetQubitId(QubitIdType q) - { - return q; - } - - QubitTestQAPI(int maxQbits) : maxQubits(maxQbits), allocated((size_t)maxQbits, false) - { - } - - QubitIdType AllocateQubit() override - { - assert(this->lastId < this->maxQubits); - this->lastId++; - this->allocated.at((size_t)(this->lastId)) = true; - return static_cast(this->lastId); - } - void ReleaseQubit(QubitIdType qubit) override - { - const QubitIdType id = GetQubitId(qubit); - INFO(id); - REQUIRE(this->allocated.at(static_cast(id))); - this->allocated.at(static_cast(id)).flip(); - } - std::string QubitToString(QubitIdType qubit) override - { - const QubitIdType id = GetQubitId(qubit); - return std::to_string(id); - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - - bool HaveQubitsInFlight() const - { - for (const auto b : this->allocated) - { - if (b) - { - return true; - } - } - return false; - } -}; -TEST_CASE("Qubits: allocate, release, dump", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(4); - QirExecutionContext::Scoped qirctx(qapi.get()); - QirString* qstr = nullptr; - - QUBIT* q = __quantum__rt__qubit_allocate(); - qstr = __quantum__rt__qubit_to_string(q); - REQUIRE(qstr->str == std::string("0")); - __quantum__rt__string_update_reference_count(qstr, -1); - __quantum__rt__qubit_release(q); - REQUIRE(!qapi->HaveQubitsInFlight()); - - QirArray* qs = __quantum__rt__qubit_allocate_array(3); - REQUIRE(qs->ownsQubits); - REQUIRE(qs->count == 3); - REQUIRE(qs->itemSizeInBytes == sizeof(void*)); - - QUBIT* last = *reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qs, 2)); - qstr = __quantum__rt__qubit_to_string(last); - REQUIRE(qstr->str == std::string("3")); - __quantum__rt__string_update_reference_count(qstr, -1); - - QirArray* copy = __quantum__rt__array_copy(qs, true /*force*/); - REQUIRE(!copy->ownsQubits); - - __quantum__rt__qubit_release_array(qs); // The `qs` is a dangling pointer from now on. - REQUIRE(!qapi->HaveQubitsInFlight()); - - __quantum__rt__array_update_reference_count(copy, -1); -} - -QirTupleHeader* FlattenControlArrays(QirTupleHeader* nestedTuple, int depth); -struct ControlledCallablesTestSimulator : public SimulatorStub -{ - intptr_t lastId = -1; - QubitIdType AllocateQubit() override - { - return static_cast(++this->lastId); - } - void ReleaseQubit(QubitIdType /*qubit*/) override - { - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } -}; -TEST_CASE("Unpacking input tuples of nested callables (case2)", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - QUBIT* target = __quantum__rt__qubit_allocate(); - QirArray* controlsInner = __quantum__rt__qubit_allocate_array(3); - QirArray* controlsOuter = __quantum__rt__qubit_allocate_array(2); - - PTuple inner = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*Qubit*/ void*)); - TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); - innerWithControls->controls = controlsInner; - *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/* QirArray* */ void*)) = target; - - PTuple outer = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*QirTupleHeader*/ void*)); - TupleWithControls* outerWithControls = TupleWithControls::FromTuple(outer); - outerWithControls->controls = controlsOuter; - outerWithControls->innerTuple = innerWithControls; - - QirTupleHeader* unpacked = FlattenControlArrays(outerWithControls->GetHeader(), 2 /*depth*/); - QirArray* combined = *(reinterpret_cast(unpacked->AsTuple())); - REQUIRE(5 == combined->count); - REQUIRE(!combined->ownsQubits); - REQUIRE(target == *reinterpret_cast(unpacked->AsTuple() + sizeof(/*QirArrray*/ void*))); - - unpacked->Release(); - __quantum__rt__array_update_reference_count(combined, -1); - __quantum__rt__tuple_update_reference_count(outer, -1); - __quantum__rt__tuple_update_reference_count(inner, -1); - - // release the original resources - __quantum__rt__qubit_release_array(controlsOuter); // The `controlsOuter` is a dangling pointer from now on. - __quantum__rt__qubit_release_array(controlsInner); // The `controlsInner` is a dangling pointer from now on. - __quantum__rt__qubit_release(target); -} - -TEST_CASE("Unpacking input tuples of nested callables (case1)", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - QUBIT* target = __quantum__rt__qubit_allocate(); - QirArray* controlsInner = __quantum__rt__qubit_allocate_array(3); - QirArray* controlsOuter = __quantum__rt__qubit_allocate_array(2); - - PTuple args = __quantum__rt__tuple_create(sizeof(/*Qubit*/ void*) + sizeof(int)); - *reinterpret_cast(args) = target; - *reinterpret_cast(args + sizeof(/*Qubit*/ void*)) = 42; - - PTuple inner = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*Tuple*/ void*)); - TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); - innerWithControls->controls = controlsInner; - *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/* QirArray* */ void*)) = args; - - PTuple outer = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*QirTupleHeader*/ void*)); - TupleWithControls* outerWithControls = TupleWithControls::FromTuple(outer); - outerWithControls->controls = controlsOuter; - outerWithControls->innerTuple = innerWithControls; - - QirTupleHeader* unpacked = FlattenControlArrays(outerWithControls->GetHeader(), 2 /*depth*/); - QirArray* combined = *(reinterpret_cast(unpacked->AsTuple())); - REQUIRE(5 == combined->count); - REQUIRE(!combined->ownsQubits); - - QirTupleHeader* unpackedArgs = - QirTupleHeader::GetHeader(*reinterpret_cast(unpacked->AsTuple() + sizeof(/* QirArray* */ void*))); - REQUIRE(target == *reinterpret_cast(unpackedArgs->AsTuple())); - REQUIRE(42 == *reinterpret_cast(unpackedArgs->AsTuple() + sizeof(/*Qubit*/ void*))); - - unpacked->Release(); - __quantum__rt__array_update_reference_count(combined, -1); - __quantum__rt__tuple_update_reference_count(outer, -1); - __quantum__rt__tuple_update_reference_count(inner, -1); - __quantum__rt__tuple_update_reference_count(args, -1); - - // release the original resources - __quantum__rt__qubit_release_array(controlsOuter); // The `controlsOuter` is a dangling pointer from now on. - __quantum__rt__qubit_release_array(controlsInner); // The `controlsInner` is a dangling pointer from now on. - __quantum__rt__qubit_release(target); -} - -TEST_CASE("Allocation tracking for arrays", "[qir_support]") -{ - QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); - - QirArray* bounce = __quantum__rt__array_create_1d(1, 1); - __quantum__rt__array_update_reference_count(bounce, -1); - CHECK_THROWS(__quantum__rt__array_update_reference_count(bounce, 1)); - - QirArray* releaseTwice = __quantum__rt__array_create_1d(1, 1); - __quantum__rt__array_update_reference_count(releaseTwice, -1); - CHECK_THROWS(__quantum__rt__array_update_reference_count(releaseTwice, -1)); - - QirArray* maybeLeaked = __quantum__rt__array_create_1d(1, 1); - CHECK_THROWS(QirExecutionContext::Deinit()); - - __quantum__rt__array_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(QirExecutionContext::Deinit()); -} - -TEST_CASE("Allocation tracking for tuples", "[qir_support]") -{ - QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); - - PTuple bounce = __quantum__rt__tuple_create(1); - __quantum__rt__tuple_update_reference_count(bounce, -1); - CHECK_THROWS(__quantum__rt__tuple_update_reference_count(bounce, 1)); - - PTuple releaseTwice = __quantum__rt__tuple_create(1); - __quantum__rt__tuple_update_reference_count(releaseTwice, -1); - CHECK_THROWS(__quantum__rt__tuple_update_reference_count(releaseTwice, -1)); - - PTuple maybeLeaked = __quantum__rt__tuple_create(1); - CHECK_THROWS(QirExecutionContext::Deinit()); - - __quantum__rt__tuple_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(QirExecutionContext::Deinit()); -} - -static void NoopCallableEntry(PTuple, PTuple, PTuple) -{ -} -TEST_CASE("Allocation tracking for callables", "[qir_support]") -{ - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - - QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); - - QirCallable* bounce = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - __quantum__rt__callable_update_reference_count(bounce, -1); - CHECK_THROWS(__quantum__rt__callable_update_reference_count(bounce, 1)); - - QirCallable* releaseTwice = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - __quantum__rt__callable_update_reference_count(releaseTwice, -1); - CHECK_THROWS(__quantum__rt__callable_update_reference_count(releaseTwice, -1)); - - QirCallable* maybeLeaked = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - CHECK_THROWS(QirExecutionContext::Deinit()); - - __quantum__rt__callable_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(QirExecutionContext::Deinit()); -} - -TEST_CASE("Callables: copy elision", "[qir_support]") -{ - QirExecutionContext::Scoped qirctx(nullptr, true); - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - - QirCallable* original = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - - QirCallable* self = __quantum__rt__callable_copy(original, false); - CHECK(self == original); - - QirCallable* other1 = __quantum__rt__callable_copy(original, true); - CHECK(other1 != original); - - __quantum__rt__callable_update_alias_count(original, 1); - QirCallable* other2 = __quantum__rt__callable_copy(original, false); - CHECK(other2 != original); - __quantum__rt__callable_update_alias_count(original, -1); - - __quantum__rt__callable_update_reference_count(original, -1); - __quantum__rt__callable_update_reference_count(self, -1); - __quantum__rt__callable_update_reference_count(other1, -1); - __quantum__rt__callable_update_reference_count(other2, -1); -} - -TEST_CASE("Tuples: copy elision", "[qir_support]") -{ - PTuple original = __quantum__rt__tuple_create(1 /*size in bytes*/); - - PTuple self = __quantum__rt__tuple_copy(original, false); - CHECK(self == original); - - PTuple other1 = __quantum__rt__tuple_copy(original, true); - CHECK(other1 != original); - - __quantum__rt__tuple_update_alias_count(original, 1); - PTuple other2 = __quantum__rt__tuple_copy(original, false); - CHECK(other2 != original); - __quantum__rt__tuple_update_alias_count(original, -1); - - __quantum__rt__tuple_update_reference_count(original, -1); - __quantum__rt__tuple_update_reference_count(self, -1); - __quantum__rt__tuple_update_reference_count(other1, -1); - __quantum__rt__tuple_update_reference_count(other2, -1); -} - -// Adjoints for R and Exp are implemented by qis, so let's check they at least do the angle invertion in adjoints. -struct AdjointsTestSimulator : public SimulatorStub -{ - QubitIdType lastId = -1; - double rotationAngle = 0.0; - double exponentAngle = 0.0; - - QubitIdType AllocateQubit() override - { - return ++this->lastId; - } - void ReleaseQubit(QubitIdType /*qubit*/) override - { - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - - void R(PauliId, QubitIdType, double theta) override - { - this->rotationAngle += theta; - } - void Exp(long count, PauliId* paulis, QubitIdType*, double theta) override - { - this->exponentAngle += theta; - - // check that paulis were unpacked correctly (this assumes that the tests always invoke with the same axes) - REQUIRE(count == 2); - CHECK(paulis[0] == PauliId_Z); - CHECK(paulis[1] == PauliId_Y); - } - void ControlledR(long, QubitIdType*, PauliId, QubitIdType, double theta) override - { - this->rotationAngle += theta; - } - void ControlledExp(long, QubitIdType*, long count, PauliId* paulis, QubitIdType*, double theta) override - { - this->exponentAngle += theta; - - // check that paulis were unpacked correctly (this assumes that the tests always invoke with the same axes) - REQUIRE(count == 2); - CHECK(paulis[0] == PauliId_Z); - CHECK(paulis[1] == PauliId_Y); - } -}; -TEST_CASE("Adjoints of R should use inverse of the angle", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - const double angle = 0.42; - - QUBIT* target = __quantum__rt__qubit_allocate(); - QirArray* ctrls = __quantum__rt__qubit_allocate_array(2); - - __quantum__qis__r__body(PauliId_Y, angle, target); - __quantum__qis__r__adj(PauliId_Y, angle, target); - QirRTuple args = {PauliId_X, angle, target}; - __quantum__qis__r__ctl(ctrls, &args); - __quantum__qis__r__ctladj(ctrls, &args); - - __quantum__rt__qubit_release_array(ctrls); // The `ctrls` is a dangling pointer from now on. - __quantum__rt__qubit_release(target); - - REQUIRE(qapi->rotationAngle == Approx(0).epsilon(0.0001)); -} - -TEST_CASE("Adjoints of Exp should use inverse of the angle", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - const double angle = 0.42; - - QirArray* targets = __quantum__rt__qubit_allocate_array(2); - QirArray* ctrls = __quantum__rt__qubit_allocate_array(2); - QirArray* axes = __quantum__rt__array_create_1d(1 /*element size*/, 2 /*count*/); - axes->buffer[0] = PauliId_Z; - axes->buffer[1] = PauliId_Y; - - __quantum__qis__exp__body(axes, angle, targets); - __quantum__qis__exp__adj(axes, angle, targets); - QirExpTuple args = {axes, angle, targets}; - __quantum__qis__exp__ctl(ctrls, &args); - __quantum__qis__exp__ctladj(ctrls, &args); - - __quantum__rt__array_update_reference_count(axes, -1); - __quantum__rt__qubit_release_array(ctrls); // The `ctrls` is a dangling pointer from now on. - __quantum__rt__qubit_release_array(targets); // The `targets` is a dangling pointer from now on. - - REQUIRE(qapi->exponentAngle == Approx(0).epsilon(0.0001)); -} diff --git a/src/Qir/Runtime/unittests/QubitManagerTests.cpp b/src/Qir/Runtime/unittests/QubitManagerTests.cpp deleted file mode 100644 index 4099e7fff79..00000000000 --- a/src/Qir/Runtime/unittests/QubitManagerTests.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// 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(); - QubitIdType q = qm->Allocate(); - qm->Release(q); -} - -TEST_CASE("Allocation and reallocation of one qubit", "[QubitManagerBasic]") -{ - std::unique_ptr qm = std::make_unique(1, false); - REQUIRE(qm->GetFreeQubitCount() == 1); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - QubitIdType q = qm->Allocate(); - REQUIRE(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); - QubitIdType q0 = qm->Allocate(); - REQUIRE(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); - - QubitIdType q0 = qm->Allocate(); - REQUIRE(qm->IsValidQubit(q0)); - REQUIRE(qm->IsExplicitlyAllocatedQubit(q0)); - REQUIRE(!qm->IsDisabledQubit(q0)); - REQUIRE(!qm->IsFreeQubitId(q0)); - REQUIRE(qm->GetAllocatedQubitCount() == 1); - - qm->Disable(q0); - REQUIRE(qm->IsValidQubit(q0)); - REQUIRE(!qm->IsExplicitlyAllocatedQubit(q0)); - REQUIRE(qm->IsDisabledQubit(q0)); - REQUIRE(!qm->IsFreeQubitId(q0)); - REQUIRE(qm->GetDisabledQubitCount() == 1); - - qm->Release(q0); - REQUIRE(!qm->IsFreeQubitId(q0)); - REQUIRE(qm->GetFreeQubitCount() == 1); - - QubitIdType q1 = qm->Allocate(); - REQUIRE(q0 != q1); - - REQUIRE(qm->IsValidQubit(q1)); - REQUIRE(qm->IsExplicitlyAllocatedQubit(q1)); - REQUIRE(!qm->IsDisabledQubit(q1)); - REQUIRE(!qm->IsFreeQubitId(q1)); - - REQUIRE(qm->GetAllocatedQubitCount() == 1); - REQUIRE(qm->GetDisabledQubitCount() == 1); - REQUIRE(qm->GetFreeQubitCount() == 0); - - qm->Release(q1); - REQUIRE(qm->IsFreeQubitId(q1)); -} - -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); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - REQUIRE(qm->GetDisabledQubitCount() == 0); - - QubitIdType* qubits = new QubitIdType[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 QubitIdType[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 QubitIdType[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); - REQUIRE(qm->GetFreeQubitCount() == 2); - QubitIdType q0 = qm->Allocate(); - QubitIdType q1 = qm->Allocate(); - REQUIRE(q0 == 0); // Qubit ids should be in order - REQUIRE(q1 == 1); - REQUIRE_THROWS(qm->Allocate()); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 2); - - qm->Release(q0); - QubitIdType q0a = qm->Allocate(); - REQUIRE(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); - - QubitIdType q0b = qm->Allocate(); - QubitIdType q1a = qm->Allocate(); - REQUIRE(q0b == 0); // By default reuse is encouraged, LIFO is used - REQUIRE(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); - - QubitIdType q0 = qm->Allocate(); - REQUIRE(q0 == 0); - QubitIdType q1 = qm->Allocate(); // This should double capacity - REQUIRE(q1 == 1); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 2); - - qm->Release(q0); - QubitIdType q0a = qm->Allocate(); - REQUIRE(q0a == 0); - QubitIdType q2 = qm->Allocate(); // This should double capacity again - REQUIRE(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); - - QubitIdType* qqq = new QubitIdType[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); - - QubitIdType q0 = qm->Allocate(); - REQUIRE(q0 == 0); - - qm->StartRestrictedReuseArea(); - - // Allocates fresh qubit - QubitIdType q1 = qm->Allocate(); - REQUIRE(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. - QubitIdType q2 = qm->Allocate(); - REQUIRE(q2 == 2); - qm->Release(q2); - - QubitIdType q2a = qm->Allocate(); // Getting the same one as the one that's just released. - REQUIRE(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. - QubitIdType* qqq = new QubitIdType[2]; - qm->Allocate(qqq, 2); - // OK to destruct qubit manager while qubits are still allocated. - REQUIRE_THROWS(qm->Allocate()); - delete[] qqq; -} - -TEST_CASE("Nested Restricted Areas", "[QubitManager]") -{ - constexpr int n = 10; - std::unique_ptr qm = std::make_unique(n, false); - for (int i = 0; i < n; i++) - { - QubitIdType q = qm->Allocate(); // Reuse qubit from previous area - qm->Release(q); // Put a free qubit in the innermost area - qm->StartRestrictedReuseArea(); - } - REQUIRE(qm->GetFreeQubitCount() == n); - // First new allocation will be served from the previous area and the next allocation - // will check entire list up to the outermost area. But after that allocation will - // jump right into outermost area. So allocation isn't O(1), but is amortized O(1). - QubitIdType* qubits = new QubitIdType[n]; - qm->Allocate(qubits, n); // Check that we still can allocate - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(qubits, n); - for (int i = 0; i < n; i++) - { - qm->EndRestrictedReuseArea(); - } - qm->Allocate(qubits, n); - qm->Release(qubits, n); - REQUIRE(qm->GetFreeQubitCount() == n); - delete[] qubits; -} - -TEST_CASE("Nested Restricted Areas With Qubits", "[QubitManager]") -{ - constexpr int n = 100; - - std::unique_ptr qm = std::make_unique(n, false); - QubitIdType* qubits = new QubitIdType[n]; - qm->Allocate(qubits, n); // Allocate All - REQUIRE(qm->GetFreeQubitCount() == 0); - - for (int i = 0; i < n; i++) - { - qm->StartRestrictedReuseArea(); - qm->Release(qubits[i]); // Put a free qubit in this area - } - REQUIRE(qm->GetFreeQubitCount() == n); // There're n free qubits, one in each area - qm->Allocate(qubits, n); // And we can allocate them all - - for (int i = 0; i < n; i++) - { - qm->EndRestrictedReuseArea(); - qm->Release(qubits[i]); // Release out of order - } - - REQUIRE(qm->GetFreeQubitCount() == n); // There're n free qubits still - qm->Allocate(qubits, n); // And we can allocate them. - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(qubits, n); - REQUIRE(qm->GetFreeQubitCount() == n); - delete[] qubits; -} - -TEST_CASE("Many Nested Restricted Areas", "[QubitManager]") -{ - constexpr int n = 10000; - std::unique_ptr qm = std::make_unique(1, false); - REQUIRE(qm->GetFreeQubitCount() == 1); - - // Allocate a lot of nested areas - for (int i = 0; i < n; i++) - { - qm->StartRestrictedReuseArea(); - } - - REQUIRE(qm->GetFreeQubitCount() == 1); - QubitIdType q = qm->Allocate(); - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(q); - REQUIRE(qm->GetFreeQubitCount() == 1); - - for (int i = 0; i < n; i++) - { - qm->EndRestrictedReuseArea(); - } - - REQUIRE(qm->GetFreeQubitCount() == 1); - q = qm->Allocate(); - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(q); - REQUIRE(qm->GetFreeQubitCount() == 1); -} diff --git a/src/Qir/Runtime/unittests/ToffoliTests.cpp b/src/Qir/Runtime/unittests/ToffoliTests.cpp deleted file mode 100644 index 08cc0847cf1..00000000000 --- a/src/Qir/Runtime/unittests/ToffoliTests.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include - -#include "catch.hpp" - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; - -static Result MZ(IQuantumGateSet* iqa, QubitIdType q) -{ - PauliId pauliZ[1] = {PauliId_Z}; - QubitIdType qs[1] = {q}; - return iqa->Measure(1, pauliZ, 1, qs); -} - -TEST_CASE("Basis vector", "[toffoli]") -{ - std::unique_ptr sim = CreateToffoliSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - constexpr int n = 1000; - std::vector qubits; - qubits.reserve(n); - for (int i = 0; i < n; i++) - { - QubitIdType q = sim->AllocateQubit(); - qubits.push_back(q); - if ((i & 0x1) == 1) - { - iqa->X(q); - } - } - - long sum = 0; - for (QubitIdType q : qubits) - { - if (sim->GetResultValue(MZ(iqa, q)) == Result_One) - { - sum++; - } - } - REQUIRE(sum == n / 2); -} - -TEST_CASE("Controlled X", "[toffoli]") -{ - std::unique_ptr sim = CreateToffoliSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q[4]; - q[0] = sim->AllocateQubit(); - q[1] = sim->AllocateQubit(); - q[2] = sim->AllocateQubit(); - q[3] = sim->AllocateQubit(); - - // qubits state: |0000> - iqa->ControlledX(1, &q[0], q[1]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[1])) == Result_Zero); - iqa->ControlledX(2, &q[0], q[2]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_Zero); - iqa->ControlledX(3, &q[0], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_Zero); - - iqa->X(q[0]); - - // qubits state: |1000> - iqa->ControlledX(2, &q[0], q[2]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_Zero); - iqa->ControlledX(3, &q[0], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[3])) == Result_Zero); - iqa->ControlledX(1, &q[0], q[2]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_One); - - // qubits state: |1010> - iqa->ControlledX(3, &q[0], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[3])) == Result_Zero); - - iqa->X(q[1]); - - // qubits state: |1110> - iqa->ControlledX(2, &q[1], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[3])) == Result_One); - - // qubits state: |1111> - iqa->ControlledX(3, &q[1], q[0]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[0])) == Result_Zero); -} - -TEST_CASE("Measure and assert probability", "[toffoli]") -{ - std::unique_ptr sim = CreateToffoliSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - const int count = 3; - QubitIdType qs[count]; - for (int i = 0; i < count; i++) - { - qs[i] = sim->AllocateQubit(); - } - - PauliId zzz[count] = {PauliId_Z, PauliId_Z, PauliId_Z}; - PauliId ziz[count] = {PauliId_Z, PauliId_I, PauliId_Z}; - - // initial state is |000> - IDiagnostics* idig = dynamic_cast(sim.get()); - REQUIRE(sim->GetResultValue(iqa->Measure(count, zzz, count, qs)) == Result_Zero); - REQUIRE(sim->GetResultValue(iqa->Measure(count, ziz, count, qs)) == Result_Zero); - REQUIRE(idig->Assert(count, zzz, qs, sim->UseZero(), "")); - REQUIRE(idig->AssertProbability(count, zzz, qs, 1.0, 0.01, "")); - - // set state to: |010> - iqa->X(qs[1]); - REQUIRE(sim->GetResultValue(iqa->Measure(count, zzz, count, qs)) == Result_One); - REQUIRE(sim->GetResultValue(iqa->Measure(count, ziz, count, qs)) == Result_Zero); - REQUIRE(idig->Assert(count, zzz, qs, sim->UseOne(), "")); - REQUIRE(idig->AssertProbability(count, ziz, qs, 1.0, 0.01, "")); - - // set state to: |111> - iqa->X(qs[0]); - iqa->X(qs[2]); - REQUIRE(sim->GetResultValue(iqa->Measure(count, zzz, count, qs)) == Result_One); - REQUIRE(sim->GetResultValue(iqa->Measure(count, ziz, count, qs)) == Result_Zero); - REQUIRE(idig->Assert(count, ziz, qs, sim->UseZero(), "")); - REQUIRE(idig->AssertProbability(count, zzz, qs, 0.0, 0.01, "")); -} diff --git a/src/Qir/Runtime/unittests/TracerTests.cpp b/src/Qir/Runtime/unittests/TracerTests.cpp deleted file mode 100644 index dc68a41576b..00000000000 --- a/src/Qir/Runtime/unittests/TracerTests.cpp +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include - -#include "catch.hpp" - -#include "CoreTypes.hpp" -#include "tracer.hpp" - -using namespace Microsoft::Quantum; - -TEST_CASE("Layering distinct single-qubit operations of non-zero durations", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); // L(0,3) should be created - CHECK(0 == tr->TraceSingleQubitOp(2, 2, q1)); // add the op into L(0,3) - CHECK(0 == tr->TraceSingleQubitOp(3, 1, q2)); // add the op into L(0,3) - CHECK(1 == tr->TraceSingleQubitOp(4, 3, q2)); // create new layer L(3,3) - CHECK(2 == tr->TraceSingleQubitOp(5, 4, q2)); // long op! create new layer L(6,4) - CHECK(1 == tr->TraceSingleQubitOp(6, 2, q1)); // add the op into L(3,3) - CHECK(0 == tr->TraceSingleQubitOp(7, 1, q3)); // add the op into L(0,3) - CHECK(2 == tr->TraceSingleQubitOp(8, 4, q3)); // long op! but fits into existing L(6,4) - CHECK(3 == tr->TraceSingleQubitOp(9, 5, q1)); // long op! add the op into L(10,5) - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 4); - CHECK(layers[0].startTime == 0); - CHECK(layers[0].operations.size() == 4); - CHECK(layers[1].startTime == 3); - CHECK(layers[1].operations.size() == 2); - CHECK(layers[2].startTime == 6); - CHECK(layers[2].operations.size() == 2); - CHECK(layers[3].startTime == 10); - CHECK(layers[3].operations.size() == 1); -} - -TEST_CASE("Layering single-qubit operations of zero duration", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); // L(0,3) should be created - CHECK(0 == tr->TraceSingleQubitOp(2, 0, q1)); // add the op into L(0,3) - CHECK(INVALID == tr->TraceSingleQubitOp(3, 0, q3)); // pending zero op (will remain orphan) - CHECK(INVALID == tr->TraceSingleQubitOp(4, 0, q2)); // pending zero op - CHECK(INVALID == tr->TraceSingleQubitOp(5, 0, q2)); // another pending zero op - CHECK(0 == tr->TraceSingleQubitOp(6, 1, q2)); // add the op into L(0,3) together with the pending ones - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 1); - CHECK(layers[0].operations.size() == 5); -} - -TEST_CASE("Layering distinct controlled single-qubit operations", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - QubitIdType q6 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceMultiQubitOp(1, 1, 1 /*nFirst*/, &q1 /*first*/, 1 /*nSecond*/, &q2 /*second*/)); - CHECK(0 == tr->TraceMultiQubitOp(2, 2, 0 /*nFirst*/, nullptr /*first*/, 1 /*nSecond*/, &q2 /*second*/)); - // q2 now is at the limit of the layer duration - - QubitIdType qs12[2] = {q1, q2}; - CHECK(1 == tr->TraceMultiQubitOp(3, 1, 0 /*nFirst*/, nullptr /*first*/, 2 /*nSecond*/, qs12 /*second*/)); - CHECK(1 == tr->TraceMultiQubitOp(4, 1, 1 /*nFirst*/, &q2 /*first*/, 1 /*nSecond*/, &q3 /*second*/)); - // because of q2, both ops should have been added to a new layer, which now "catches" q1, q2, q3 - - CHECK(0 == tr->TraceMultiQubitOp(5, 0, 1 /*nFirst*/, &q4 /*first*/, 1 /*nSecond*/, &q5 /*second*/)); - CHECK(0 == tr->TraceSingleQubitOp(6, 1, q6)); - // these ops should fall through into the first layer (notice no special handling of duration zero) - - CHECK(1 == tr->TraceMultiQubitOp(7, 1, 1 /*nFirst*/, &q1 /*first*/, 1 /*nSecond*/, &q6 /*second*/)); - CHECK(1 == tr->TraceMultiQubitOp(8, 1, 1 /*nFirst*/, &q3 /*first*/, 1 /*nSecond*/, &q4 /*second*/)); - // because of q1 and q3, thiese ops should be added into the second layer, which now has all but q5 - - CHECK(0 == tr->TraceSingleQubitOp(9, 1, q5)); - // should fall through to the first layer - - QubitIdType qs46[2] = {q4, q6}; - CHECK(1 == tr->TraceMultiQubitOp(10, 1, 2 /*nFirst*/, qs46 /*first*/, 1 /*nSecond*/, &q5 /*second*/)); - // because of the controls, should be added into the second layer - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 2); - - CHECK(layers[0].operations.size() == 5); - const auto& ops0 = layers[0].operations; - CHECK(ops0.find(1) != ops0.end()); - CHECK(ops0.find(2) != ops0.end()); - CHECK(ops0.find(5) != ops0.end()); - CHECK(ops0.find(6) != ops0.end()); - CHECK(ops0.find(9) != ops0.end()); - - CHECK(layers[1].operations.size() == 5); - const auto& ops1 = layers[1].operations; - CHECK(ops1.find(3) != ops1.end()); - CHECK(ops1.find(4) != ops1.end()); - CHECK(ops1.find(7) != ops1.end()); - CHECK(ops1.find(8) != ops1.end()); - CHECK(ops1.find(10) != ops1.end()); -} - -// TODO: add multi-qubit ops -TEST_CASE("Operations with same id are counted together", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - // All of these ops should fit into a single layer L(0,3) - tr->TraceSingleQubitOp(1, 1, q1); - tr->TraceSingleQubitOp(2, 2, q1); - tr->TraceSingleQubitOp(1, 1, q2); - tr->TraceSingleQubitOp(2, 1, q2); - tr->TraceSingleQubitOp(1, 1, q2); - tr->TraceSingleQubitOp(3, 2, q3); - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 1); - CHECK(layers[0].operations.size() == 3); - const auto& ops = layers[0].operations; - CHECK(ops.find(1)->second == 3); - CHECK(ops.find(2)->second == 2); - CHECK(ops.find(3)->second == 1); -} - -TEST_CASE("Global barrier", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(2 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 4, q1)); // L(0,4) created - CHECK(0 == tr->TraceSingleQubitOp(2, 1, q4)); // added to L(0,4) - CHECK(1 == tr->InjectGlobalBarrier(42, 1)); // creates L(4,2) - - CHECK(2 == tr->TraceMultiQubitOp(3, 1, 1 /*nFirst*/, &q2 /*first*/, 1 /*nSecond*/, &q3 /*second*/)); - // the barrier shouldn't allow this op to fall through into L(0,4), so should create L(6,2) - - CHECK(INVALID == tr->TraceSingleQubitOp(4, 0, q1)); - // the barrier shouldn't allow this op to fall through into L(0,4), so should create pending op - - CHECK(2 == tr->TraceSingleQubitOp(5, 1, q1)); - // should be added into L(6,2) together with the pending op `3` - - CHECK(3 == tr->TraceSingleQubitOp(6, 3, q2)); - // long op, with no existing wide layers to host it, so should create L(8,3) - - CHECK(3 == tr->TraceSingleQubitOp(7, 3, q4)); - // long op but can be added into L(8,3), which is post the barrier - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 4); - CHECK(layers[0].operations.size() == 2); - CHECK(layers[1].operations.size() == 0); - CHECK(layers[2].operations.size() == 3); - CHECK(layers[3].operations.size() == 2); - - const auto& ops0 = layers[0].operations; - CHECK(ops0.find(1) != ops0.end()); - CHECK(ops0.find(2) != ops0.end()); - - CHECK(42 == layers[1].barrierId); - - const auto& ops2 = layers[2].operations; - CHECK(ops2.find(3) != ops2.end()); - CHECK(ops2.find(4) != ops2.end()); - CHECK(ops2.find(5) != ops2.end()); - - const auto& ops3 = layers[3].operations; - CHECK(ops3.find(6) != ops3.end()); - CHECK(ops3.find(7) != ops3.end()); -} - -// For layering purposes, measurements behave pretty much the same as other operations -TEST_CASE("Layering measurements", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - - CHECK(0 == tr->GetLayerIdOfSourceMeasurement(tr->TraceSingleQubitMeasurement(1, 1, q1))); - QubitIdType qs12[2] = {q1, q2}; - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(tr->TraceMultiQubitMeasurement(2, 1, 2, qs12))); - CHECK(0 == tr->TraceSingleQubitOp(3, 1, q4)); - CHECK(0 == tr->GetLayerIdOfSourceMeasurement(tr->TraceSingleQubitMeasurement(4, 1, q3))); - QubitIdType qs23[2] = {q2, q3}; - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(tr->TraceMultiQubitMeasurement(5, 1, 2, qs23))); - CHECK(1 == tr->TraceSingleQubitOp(3, 1, q4)); -} - -TEST_CASE("Conditionals: noops", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 3, q1)); - CHECK(1 == tr->TraceSingleQubitOp(1, 3, q1)); - Result one = tr->UseOne(); - { - CTracer::FenceScope fs(tr.get(), 1, &one, 0, nullptr); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - } - { - CTracer::FenceScope fs(tr.get(), 0, nullptr, 1, &one); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - } - { - CTracer::FenceScope fs(tr.get(), 0, nullptr, 0, nullptr); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - } -} - -TEST_CASE("Conditionals: a new layer because of the fence", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r, 0, nullptr); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q2)); - } - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q3)); -} - -TEST_CASE("Conditionals: single fence", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r)); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r, 0, nullptr); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q2)); - } - - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q1)); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q3)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q3)); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q3)); -} - -TEST_CASE("Conditionals: fence from two result arrays", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q2)); - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r1, 1, &r2); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q3)); - } - - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); -} - -TEST_CASE("Conditionals: nested fence is later than parent", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q2)); - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r1, 0, nullptr); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q3)); - { - CTracer::FenceScope fs2(tr.get(), 0, nullptr, 1, &r2); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q4)); - } - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q5)); - } - - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); -} - -TEST_CASE("Conditionals: nested fence is earlier than parent", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q2)); - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r2, 0, nullptr); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q3)); - { - CTracer::FenceScope fs2(tr.get(), 0, nullptr, 1, &r1); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q4)); - } - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q5)); - } - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); -} - -TEST_CASE("Conditionals: fences and barriers", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - - CHECK(2 == tr->InjectGlobalBarrier(42, 1)); - - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(3 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r1, 0, nullptr); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q3)); - } - { - CTracer::FenceScope fs(tr.get(), 0, nullptr, 1, &r2); - CHECK(4 == tr->TraceSingleQubitOp(1, 1, q4)); - } - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q5)); -} - -TEST_CASE("Output: to string", "[tracer]") -{ - std::unordered_map opNames = {{1, "X"}, {2, "Y"}, {3, "Z"}, {4, "b"}}; - std::shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); - - QubitIdType q1 = tr->AllocateQubit(); - tr->TraceSingleQubitOp(3, 1, q1); - tr->TraceSingleQubitOp(5, 1, q1); - tr->InjectGlobalBarrier(4, 2); - tr->TraceSingleQubitOp(3, 4, q1); - tr->TraceSingleQubitOp(2, 1, q1); - - { - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - std::string metrics = out.str(); - - std::stringstream expected; - expected << "layer_id,name,Y,Z,5" << std::endl; - expected << "0,,0,1,0" << std::endl; - expected << "1,,0,0,1" << std::endl; - expected << "2,b,0,0,0" << std::endl; - expected << "4,,0,1,0" << std::endl; - expected << "8,,1,0,0" << std::endl; - - INFO(metrics); - CHECK(metrics == expected.str()); - } - - { - std::stringstream out; - tr->PrintLayerMetrics(out, ",", false /*printZeroMetrics*/); - std::string metrics = out.str(); - - std::stringstream expected; - expected << "layer_id,name,Y,Z,5" << std::endl; - expected << "0,,,1," << std::endl; - expected << "1,,,,1" << std::endl; - expected << "2,b,,," << std::endl; - expected << "4,,,1," << std::endl; - expected << "8,,1,," << std::endl; - - INFO(metrics); - CHECK(metrics == expected.str()); - } -} - -TEST_CASE("Output: to file", "[tracer]") -{ - std::unordered_map opNames = {{1, "X"}, {2, "Y"}, {3, "Z"}, {4, "b"}}; - std::shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); - - QubitIdType q1 = tr->AllocateQubit(); - tr->TraceSingleQubitOp(3, 1, q1); - tr->TraceSingleQubitOp(5, 1, q1); - tr->InjectGlobalBarrier(4, 2); - tr->TraceSingleQubitOp(3, 4, q1); - tr->TraceSingleQubitOp(2, 1, q1); - - const std::string fileName = "tracer-test.txt"; - std::ofstream out; - out.open(fileName); - tr->PrintLayerMetrics(out, "\t", false /*printZeroMetrics*/); - out.close(); - - std::ifstream in(fileName); - std::string line; - REQUIRE(in.is_open()); - std::string metrics(std::istreambuf_iterator{in}, {}); - in.close(); - - std::stringstream expected; - expected << "layer_id\tname\tY\tZ\t5" << std::endl; - expected << "0\t\t\t1\t" << std::endl; - expected << "1\t\t\t\t1" << std::endl; - expected << "2\tb\t\t\t" << std::endl; - expected << "4\t\t\t1\t" << std::endl; - expected << "8\t\t1\t\t" << std::endl; - - INFO(metrics); - CHECK(metrics == expected.str()); -} diff --git a/src/Qir/Runtime/unittests/driver.cpp b/src/Qir/Runtime/unittests/driver.cpp deleted file mode 100644 index b90b6434bd2..00000000000 --- a/src/Qir/Runtime/unittests/driver.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -// https://github.com/catchorg/Catch2/blob/master/docs/tutorial.md diff --git a/src/Qir/Samples/CMakeLists.txt b/src/Qir/Samples/CMakeLists.txt index 7854f614a00..037cb639641 100644 --- a/src/Qir/Samples/CMakeLists.txt +++ b/src/Qir/Samples/CMakeLists.txt @@ -18,11 +18,15 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") -set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") -set(runtime_lib_path "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") +# set the environment path for loading shared libs the tests are using +if(DEFINED ENV{NATIVE_SIMULATOR}) + set(simulator_lib_path $ENV{NATIVE_SIMULATOR}) +else() + set(simulator_lib_path "${PROJECT_SOURCE_DIR}/../../Simulation/Native/build/drop") +endif() include(qir_cmake_include) diff --git a/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt index 8006d3a5694..cfc5615a429 100644 --- a/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt +++ b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt @@ -9,17 +9,14 @@ add_executable(qir-input-reference-standalone target_source_from_qir(qir-input-reference-standalone qsharp/obj/qsharp/qir-standalone-input-reference.bc) target_link_libraries(qir-input-reference-standalone PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) set(standalone_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/CLI11") target_include_directories(qir-input-reference-standalone PUBLIC "${standalone_includes}" - "${public_includes}" ) install(TARGETS qir-input-reference-standalone RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") @@ -37,9 +34,8 @@ else() set(TEST_DEPS1 "${PROJECT_SOURCE_DIR}/../../Simulation/native/build/drop") endif() -set(TEST_DEPS2 "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") set_property(TEST qir-input-reference-standalone PROPERTY ENVIRONMENT - "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${LD_LIBRARY_PATH}" - "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${PATH}" - "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${DYLD_LIBRARY_PATH}" + "LD_LIBRARY_PATH=${TEST_DEPS1}:${LD_LIBRARY_PATH}" + "PATH=${TEST_DEPS1}\;${PATH}" + "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${DYLD_LIBRARY_PATH}" ) diff --git a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp index 71c224bcbc5..eb71153902d 100644 --- a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp +++ b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp @@ -15,11 +15,6 @@ #pragma clang diagnostic pop // clang-format on -#include "QirContext.hpp" -#include "SimFactory.hpp" -#include "OutputStream.hpp" - -using namespace Microsoft::Quantum; using namespace std; namespace // == `static` @@ -68,10 +63,7 @@ static map BoolAsCharMap{{"0", InteropFalseAsChar}, {"1", InteropTrueAsChar}, {"true", InteropTrueAsChar}}; -static map PauliMap{{"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z}}; +static map PauliMap{{"PauliI", 0}, {"PauliX", 1}, {"PauliY", 3}, {"PauliZ", 2}}; static const char InteropResultZeroAsChar = 0x0; static const char InteropResultOneAsChar = 0x1; @@ -102,11 +94,6 @@ void FreePointerVector(vector& v) } } -static char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} - template void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) { @@ -129,17 +116,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point Inputs Reference"); - // Initialize simulator. - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output options. - // N.B. This option should be present in all standalone drivers. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = - app.add_option("-s,--simulation-output", simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add the options that correspond to the parameters that the QIR entry-point needs. // Option for a Q# Int type. int64_t intValue = 0; @@ -172,13 +148,13 @@ int main(int argc, char* argv[]) ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); // Option for Q# Pauli type. - PauliId pauliValue = PauliId::PauliId_I; + char pauliValue = 0; app.add_option("--pauli-value", pauliValue, "A Pauli value") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); // Option for a Q# Array type. - std::vector pauliVector; + std::vector pauliVector; app.add_option("--pauli-array", pauliVector, "A Pauli array") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); @@ -232,12 +208,10 @@ int main(int argc, char* argv[]) unique_ptr boolArray = CreateInteropArray(boolAsCharVector); // Translate a PauliID value to its char representation. - char pauliAsCharValue = TranslatePauliToChar(pauliValue); + char pauliAsCharValue = pauliValue; // Create an interop array of Pauli values represented as chars. - vector pauliAsCharVector; - TranslateVector(pauliVector, pauliAsCharVector, TranslatePauliToChar); - unique_ptr pauliArray = CreateInteropArray(pauliAsCharVector); + unique_ptr pauliArray = CreateInteropArray(pauliVector); // Create an interop range. unique_ptr rangeValue = CreateInteropRange(rangeTuple); @@ -253,16 +227,6 @@ int main(int argc, char* argv[]) TranslateVector(stringVector, stringBufferVector, TranslateStringToCharBuffer); unique_ptr stringArray = CreateInteropArray(stringBufferVector); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - Microsoft::Quantum::OutputStream::Set(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Run simulation and write the output of the operation to the corresponding stream. Quantum__StandaloneSupportedInputs__ExerciseInputs(intValue, integerArray.get(), doubleValue, doubleArray.get(), boolAsCharValue, boolArray.get(), pauliAsCharValue, @@ -270,9 +234,5 @@ int main(int argc, char* argv[]) resultArray.get(), stringValue.c_str()); FreePointerVector(rangeVector); - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); } diff --git a/src/Qir/Tests/.clang-tidy b/src/Qir/Tests/.clang-tidy deleted file mode 100644 index 8b9489eeabb..00000000000 --- a/src/Qir/Tests/.clang-tidy +++ /dev/null @@ -1,3 +0,0 @@ -InheritParentConfig: true -Checks: - '-cert-err58-cpp' diff --git a/src/Qir/Tests/CMakeLists.txt b/src/Qir/Tests/CMakeLists.txt index 7e8d960f2dc..c0e18d2d029 100644 --- a/src/Qir/Tests/CMakeLists.txt +++ b/src/Qir/Tests/CMakeLists.txt @@ -18,20 +18,19 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") -set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") -set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") +set(public_includes "${PROJECT_SOURCE_DIR}/../drops/include") set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") -set(runtime_lib_path "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") +# set the environment path for loading shared libs the tests are using +if(DEFINED ENV{NATIVE_SIMULATOR}) + set(simulator_lib_path $ENV{NATIVE_SIMULATOR}) +else() + set(simulator_lib_path "${PROJECT_SOURCE_DIR}/../../Simulation/Native/build/drop") +endif() include(qir_cmake_include) include(unit_test_include) -# Temporarily exclude the `FullstateSimulator` test from the sanitized (Debug) build on Linux: -if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Linux" OR NOT "${CMAKE_BUILD_TYPE}" MATCHES "Debug") # TODO(kuzminrobin, #1078): Remove this condition after the bug is fixed. add_subdirectory(FullstateSimulator) -endif() - add_subdirectory(QIR-dynamic) add_subdirectory(QIR-static) -add_subdirectory(QIR-tracer) diff --git a/src/Qir/Tests/FullstateSimulator/CMakeLists.txt b/src/Qir/Tests/FullstateSimulator/CMakeLists.txt index a304922fe12..c5d55e43c8f 100644 --- a/src/Qir/Tests/FullstateSimulator/CMakeLists.txt +++ b/src/Qir/Tests/FullstateSimulator/CMakeLists.txt @@ -1,18 +1,14 @@ -add_executable(fullstate-simulator-tests - FullstateSimulatorTests.cpp) +add_executable(fullstate-simulator-tests FullstateSimulatorTests.cpp) target_source_from_qir(fullstate-simulator-tests qsharp/obj/qsharp/qir-test-simulator.bc) target_link_libraries(fullstate-simulator-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(fullstate-simulator-tests PUBLIC ${test_includes} - ${public_includes} ) install(TARGETS fullstate-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index 563a1601f85..877fd3a985c 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -9,431 +9,16 @@ #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" - -using namespace Microsoft::Quantum; using namespace std; -// The tests rely on the implementation detail of CFullstateSimulator that the qubits are nothing more but contiguously -// incremented ids. -static unsigned GetQubitId(QubitIdType q) -{ - return static_cast(static_cast(q)); -} - -static Result MZ(IQuantumGateSet* iqa, QubitIdType q) -{ - PauliId pauliZ[1] = {PauliId_Z}; - QubitIdType qs[1] = {q}; - return iqa->Measure(1, pauliZ, 1, qs); -} - -TEST_CASE("Fullstate simulator: allocate qubits", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - - QubitIdType q0 = sim->AllocateQubit(); - QubitIdType q1 = sim->AllocateQubit(); - REQUIRE(GetQubitId(q0) == 0); - REQUIRE(GetQubitId(q1) == 1); - sim->ReleaseQubit(q0); - sim->ReleaseQubit(q1); -} - -TEST_CASE("Fullstate simulator: multiple instances", "[fullstate_simulator]") -{ - std::unique_ptr sim1 = CreateFullstateSimulator(); - QubitIdType q1 = sim1->AllocateQubit(); - - std::unique_ptr sim2 = CreateFullstateSimulator(); - QubitIdType q2 = sim2->AllocateQubit(); - - REQUIRE(GetQubitId(q1) == 0); - REQUIRE(GetQubitId(q2) == 0); - - sim1->ReleaseQubit(q1); - sim2->ReleaseQubit(q2); -} - -TEST_CASE("Fullstate simulator: X and measure", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType 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); -} - -TEST_CASE("Fullstate simulator: X, M, reuse, M", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType 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); - - QubitIdType 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(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q1 = sim->AllocateQubit(); - QubitIdType q2 = sim->AllocateQubit(); - - iqa->H(q1); - iqa->ControlledX(1, &q1, q2); - - Result r1 = MZ(iqa, q1); - Result r2 = MZ(iqa, q2); - REQUIRE(sim->AreEqualResults(r1, r2)); - - sim->ReleaseQubit(q1); - sim->ReleaseQubit(q2); -} - -TEST_CASE("Fullstate simulator: ZZ measure", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q[2]; - PauliId paulis[2] = {PauliId_Z, PauliId_Z}; - - q[0] = sim->AllocateQubit(); - q[1] = sim->AllocateQubit(); - iqa->H(q[0]); - iqa->ControlledX(1, &q[0], q[1]); - Result rZero = iqa->Measure(2, paulis, 2, q); - REQUIRE(Result_Zero == sim->GetResultValue(rZero)); - - iqa->X(q[1]); - Result rOne = iqa->Measure(2, paulis, 2, q); - REQUIRE(Result_One == sim->GetResultValue(rOne)); - - iqa->X(q[1]); - iqa->ControlledX(1, &q[0], q[1]); - iqa->H(q[0]); - - sim->ReleaseQubit(q[0]); - sim->ReleaseQubit(q[1]); -} - -TEST_CASE("Fullstate simulator: assert probability", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType qs[2]; - qs[0] = sim->AllocateQubit(); - qs[1] = sim->AllocateQubit(); - iqa->X(qs[0]); - - PauliId zz[2] = {PauliId_Z, PauliId_Z}; - PauliId iz[2] = {PauliId_I, PauliId_Z}; - PauliId xi[2] = {PauliId_X, PauliId_I}; - - IDiagnostics* idig = dynamic_cast(sim.get()); - REQUIRE(idig->AssertProbability(2, zz, qs, 0.0, 1e-10, "")); - REQUIRE(idig->AssertProbability(2, iz, qs, 1.0, 1e-10, "")); - REQUIRE(idig->AssertProbability(2, xi, qs, 0.5, 1e-10, "")); - - REQUIRE(idig->Assert(2, zz, qs, sim->UseOne(), "")); - REQUIRE(idig->Assert(2, iz, qs, sim->UseZero(), "")); - REQUIRE(!idig->Assert(2, xi, qs, sim->UseZero(), "")); - REQUIRE(!idig->Assert(2, xi, qs, sim->UseOne(), "")); - - iqa->X(qs[0]); - - sim->ReleaseQubit(qs[0]); - sim->ReleaseQubit(qs[1]); -} - -TEST_CASE("Fullstate simulator: toffoli", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType qs[3]; - for (int i = 0; i < 3; i++) - { - qs[i] = sim->AllocateQubit(); - } - - iqa->X(qs[0]); - iqa->ControlledX(2, qs, qs[2]); - REQUIRE(Result_Zero == sim->GetResultValue(MZ(iqa, qs[2]))); - - iqa->X(qs[1]); - iqa->ControlledX(2, qs, qs[2]); - REQUIRE(Result_One == sim->GetResultValue(MZ(iqa, qs[2]))); - - iqa->X(qs[1]); - iqa->X(qs[0]); - - for (int i = 0; i < 3; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -TEST_CASE("Fullstate simulator: SSZ=Id", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - - bool identitySSZ = true; - for (int i = 0; i < 100 && identitySSZ; i++) - { - iqa->H(q); - iqa->S(q); - iqa->S(q); - iqa->Z(q); - iqa->H(q); - identitySSZ = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identitySSZ); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: TTSAdj=Id", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - - bool identityTTSAdj = true; - for (int i = 0; i < 100 && identityTTSAdj; i++) - { - iqa->H(q); - iqa->T(q); - iqa->T(q); - iqa->AdjointS(q); - iqa->H(q); - identityTTSAdj = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identityTTSAdj); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: TTAdj=Id", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - - bool identityTTadj = true; - for (int i = 0; i < 100 && identityTTadj; i++) - { - iqa->H(q); - iqa->T(q); - iqa->AdjointT(q); - iqa->H(q); - identityTTadj = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identityTTadj); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: R", "[fullstate_simulator]") -{ - constexpr double pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062; - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - bool identity = true; - for (int i = 0; i < 100 && identity; i++) - { - iqa->H(q); - iqa->R(PauliId_X, q, 0.42); - iqa->R(PauliId_Y, q, 0.17); - iqa->T(q); - iqa->R(PauliId_Z, q, -pi / 4.0); - iqa->R(PauliId_Y, q, -0.17); - iqa->R(PauliId_X, q, -0.42); - iqa->H(q); - identity = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identity); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: exponents", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - const int n = 5; - - QubitIdType qs[n]; - for (int i = 0; i < n; i++) - { - qs[i] = sim->AllocateQubit(); - } - - PauliId paulis[3] = {PauliId_X, PauliId_Y, PauliId_Z}; - iqa->Exp(2, paulis, qs, 0.42); - iqa->ControlledExp(2, qs, 3, paulis, &qs[2], 0.17); - iqa->ControlledExp(2, qs, 3, paulis, &qs[2], -0.17); - iqa->Exp(2, paulis, qs, -0.42); - - // not crashes? consider it passing - REQUIRE(true); - - for (int i = 0; i < n; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -TEST_CASE("Fullstate simulator: get qubit state of Bell state", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - const int n = 3; - static double norm = 0.0; - - QubitIdType qs[n]; - for (int i = 0; i < n; i++) - { - qs[i] = sim->AllocateQubit(); - } - - iqa->H(qs[0]); - iqa->ControlledX(1, &qs[0], qs[1]); - // 1/sqrt(2)(|00> + |11>)x|0> - - dynamic_cast(sim.get())->GetState( - [](const char* idxStr, double re, double im) - { - norm += re * re + im * im; - size_t idx = 0; - for (size_t i = 0; idxStr[i] != '\0'; ++i) - { - idx |= (idxStr[i] == '1' ? 1u : 0u) << i; - } - REQUIRE(idx < 4); - switch (idx) - { - case 0: - case 3: - REQUIRE((1 / sqrt(2.0) == Approx(re).epsilon(0.0001))); - REQUIRE(im == 0.0); - break; - default: - REQUIRE(re == 0.0); - REQUIRE(im == 0.0); - break; - } - return idx < 3; // the last qubit is in separable |0> state - }); - REQUIRE(1.0 == Approx(norm).epsilon(0.0001)); - norm = 0.0; - - iqa->Y(qs[2]); - // 1/sqrt(2)(|00> + |11>)xi|1> - - dynamic_cast(sim.get())->GetState( - [](const char* idxStr, double re, double im) - { - norm += re * re + im * im; - size_t idx = 0; - for (size_t i = 0; idxStr[i] != '\0'; ++i) - { - idx |= (idxStr[i] == '1' ? 1u : 0u) << i; - } - switch (idx) - { - case 4: - case 7: - REQUIRE(re == 0.0); - REQUIRE(1 / sqrt(2.0) == Approx(im).epsilon(0.0001)); - break; - default: - REQUIRE(re == 0.0); - REQUIRE(im == 0.0); - break; - } - return true; // get full state - }); - REQUIRE(1.0 == Approx(norm).epsilon(0.0001)); - norm = 0.0; - - iqa->Y(qs[2]); - iqa->ControlledX(1, &qs[0], qs[1]); - iqa->H(qs[0]); - - for (int i = 0; i < n; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -extern "C" int Microsoft__Quantum__Testing__QIR__InvalidRelease__Interop(); // NOLINT -TEST_CASE("QIR: Simulator rejects unmeasured, non-zero release", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); - REQUIRE_THROWS(Microsoft__Quantum__Testing__QIR__InvalidRelease__Interop()); -} - extern "C" int Microsoft__Quantum__Testing__QIR__MeasureRelease__Interop(); // NOLINT TEST_CASE("QIR: Simulator accepts measured release", "[fullstate_simulator]") { - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__MeasureRelease__Interop()); } extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(); // NOLINT TEST_CASE("QIR: invoke all standard Q# gates against the fullstate simulator", "[fullstate_simulator]") { - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); - REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop()); } diff --git a/src/Qir/Tests/QIR-dynamic/CMakeLists.txt b/src/Qir/Tests/QIR-dynamic/CMakeLists.txt index 0a205dd9f58..da7bb8245bd 100644 --- a/src/Qir/Tests/QIR-dynamic/CMakeLists.txt +++ b/src/Qir/Tests/QIR-dynamic/CMakeLists.txt @@ -1,29 +1,22 @@ -set(TEST_FILES - qsharp/obj/qsharp/qir-test-random.bc -) +set(TEST_FILES qsharp/obj/qsharp/qir-test-random.bc) #============================================================================== # This executable target links test code against the dynamic libraries rather than the explicit # static QIR/RT libs (qir will statically link in the bridge via transitivity of target_link_libraries). # -add_executable(qir-dynamic-tests - qir-driver.cpp -) +add_executable(qir-dynamic-tests qir-driver.cpp) foreach(file ${TEST_FILES}) target_source_from_qir(qir-dynamic-tests ${file}) endforeach() target_link_libraries(qir-dynamic-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(qir-dynamic-tests PUBLIC ${test_includes} - ${public_includes} ) install(TARGETS qir-dynamic-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") diff --git a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp index 97c065c67e4..557c7647404 100644 --- a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp +++ b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp @@ -9,10 +9,6 @@ #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "OutputStream.hpp" - extern "C" { int64_t Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); // NOLINT @@ -38,13 +34,8 @@ extern "C" void Microsoft__Quantum__Testing__QIR__AssertMeasProbMessageTest__Interop(const char*); // NOLINT } // extern "C" -using namespace Microsoft::Quantum; - TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") { - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - const int64_t ret1 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); const int64_t ret2 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); const int64_t ret3 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); @@ -57,146 +48,8 @@ TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") CHECK(ret2 != ret3); } - -static bool FileExists(const char* filePath) -{ - return std::ifstream(filePath).operator bool(); -} - -TEST_CASE("QIR: DumpMachine", "[qir][DumpMachine]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // Dump to the std::cout: - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpMachineTest__Interop(); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to empty string location (std::cout): - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__Interop(""); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to a file: - const char* filePath = "DumpMachineTest.log"; - - // Remove the `filePath`, if exists. - if (FileExists(filePath)) - { - CHECK(0 == remove(filePath)); - } - - REQUIRE(!FileExists(filePath)); - - // Dump the machine state to that `filePath`: - Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__Interop(filePath); - - // Make sure the file has been created. - REQUIRE(FileExists(filePath)); - - // If we got here then the test has succeeded, we don't need the file. - // Otherwise (test fails) we don't get here, and the file is kept for the subsequent analysis. - // Remove the file, ignore the failure: - (void)remove(filePath); -} - - -TEST_CASE("QIR: DumpRegister", "[qir][DumpRegister]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // Dump to the std::cout: - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpRegisterTest__Interop(); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to empty string location (std::cout): - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__Interop(""); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to a file: - const char* filePath = "DumpRegisterTest.log"; - - // Remove the `filePath` if exists. - if (FileExists(filePath)) - { - CHECK(0 == remove(filePath)); - } - - REQUIRE(!FileExists(filePath)); - - // Dump to that `filePath`: - Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__Interop(filePath); - - // Make sure the file has been created. - REQUIRE(FileExists(filePath)); - - // If we got here then the test has succeeded, we don't need the file. - // Otherwise (test fails) we don't get here, and the file is kept for the subsequent analysis. - // Remove the file, ignore the failure: - (void)remove(filePath); -} - -static void AssertMeasMessageTest(void (*funcPtr)(const char*)) -{ - const char* const testStr = "Testing the Assertion Failure Message"; - std::ostringstream outStrStream; - - // Redirect the output from std::cout to outStrStream: - Microsoft::Quantum::OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - // Log something (to the redirected output): - REQUIRE_THROWS(funcPtr(testStr)); // Returns with exception caught. Leaks any instances allocated (in .ll) - // from the moment of a call to the moment of the exception throw. - // TODO: Extract into a separate .cpp compiled with leak detection off. - REQUIRE(outStrStream.str() == (std::string(testStr) + "\n")); -} - - TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") { - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasAlloc1OKTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasProbAlloc1HalfProbTest__Interop()); @@ -207,8 +60,4 @@ TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertBellPairMeasurementsAreCorrectTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasMixedBasesTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertGHZMeasurementsTest__Interop()); - - AssertMeasMessageTest(Microsoft__Quantum__Testing__QIR__AssertMeasMessageTest__Interop); - AssertMeasMessageTest(Microsoft__Quantum__Testing__QIR__AssertMeasProbMessageTest__Interop); - } // TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") diff --git a/src/Qir/Tests/QIR-static/CMakeLists.txt b/src/Qir/Tests/QIR-static/CMakeLists.txt index 323d8222a7e..0bf9fa112e0 100644 --- a/src/Qir/Tests/QIR-static/CMakeLists.txt +++ b/src/Qir/Tests/QIR-static/CMakeLists.txt @@ -1,5 +1,4 @@ set(TEST_FILES - qir-test-noqsharp.ll qsharp/qir/qir-gen.ll ) @@ -8,10 +7,8 @@ set(TEST_FILES # add_executable(qir-static-tests qir-driver.cpp - qir-test-conditionals.cpp qir-test-math.cpp qir-test-strings.cpp - qir-test-ouput.cpp qir-test-other.cpp ) @@ -20,18 +17,14 @@ foreach(file ${TEST_FILES}) endforeach() target_link_libraries(qir-static-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(qir-static-tests PUBLIC ${test_includes} - ${common_includes} ${public_includes} ) -# target_compile_definitions(qir-static-tests PRIVATE EXPORT_QIR_API) install(TARGETS qir-static-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") add_unit_test(qir-static-tests) diff --git a/src/Qir/Common/Include/FloatUtils.hpp b/src/Qir/Tests/QIR-static/FloatUtils.hpp similarity index 100% rename from src/Qir/Common/Include/FloatUtils.hpp rename to src/Qir/Tests/QIR-static/FloatUtils.hpp diff --git a/src/Qir/Tests/QIR-static/qir-driver.cpp b/src/Qir/Tests/QIR-static/qir-driver.cpp index ac8d24ca0e9..d7b57cb151c 100644 --- a/src/Qir/Tests/QIR-static/qir-driver.cpp +++ b/src/Qir/Tests/QIR-static/qir-driver.cpp @@ -9,25 +9,17 @@ #include #include -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "SimulatorStub.hpp" - #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" +#include "qir_stdlib.h" + +class QUBIT; + // Identifiers exposed externally: extern "C" void __quantum__qis__k__body(QUBIT* q); // NOLINT extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q); // NOLINT -// Used by a couple test simulators. Catch's REQUIRE macro doesn't deal well with static class members so making it -// into a global constant. -constexpr int RELEASED = -1; - -using namespace Microsoft::Quantum; using namespace std; /* @@ -56,8 +48,6 @@ extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop( // NO Array* array, int64_t index, int64_t val); TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") { - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); - constexpr int64_t n = 5; int64_t values[n] = {0, 1, 2, 3, 4}; auto array = Array{n, values}; @@ -67,263 +57,36 @@ TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") } extern "C" void Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__Interop(); // NOLINT -struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - // no intelligent reuse, we just want to check that QIR releases all qubits - vector qubits; // released, or |0>, or |1> states (no entanglement allowed) - vector results = {0, 1}; // released, or Zero(0) or One(1) - - uint64_t GetQubitId(QubitIdType qubit) const - { - const uint64_t id = (uint64_t)qubit; - REQUIRE(id < this->qubits.size()); - - return id; - } - - uint8_t GetResultId(Result result) const - { - const uint8_t id = (uint8_t)(uintptr_t)result; - REQUIRE(id < this->results.size()); - - return id; - } - - QubitIdType AllocateQubit() override - { - qubits.push_back(0); - return static_cast(this->qubits.size() - 1); - } - - void ReleaseQubit(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // no double-release - this->qubits[id] = RELEASED; - } - - void X(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->qubits[id] = 1 - this->qubits[id]; - } - - Result Measure([[maybe_unused]] long numBases, PauliId* /* bases */, long /* numTargets */, - QubitIdType targets[]) override - { - assert(numBases == 1 && "QubitsResultsTestSimulator doesn't support joint measurements"); - - const uint64_t id = GetQubitId(targets[0]); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->results.push_back(this->qubits[id]); - return reinterpret_cast(this->results.size() - 1); - } - - bool AreEqualResults(Result r1, Result r2) override - { - uint8_t i1 = GetResultId(r1); - uint8_t i2 = GetResultId(r2); - REQUIRE(this->results[i1] != RELEASED); - REQUIRE(this->results[i2] != RELEASED); - - return (results[i1] == results[i2]); - } - - void ReleaseResult(Result result) override - { - uint8_t i = GetResultId(result); - REQUIRE(this->results[i] != RELEASED); // no double release - this->results[i] = RELEASED; - } - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][qir.result]") { - unique_ptr sim = make_unique(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__Interop()); - - // check that all qubits have been released - for (size_t id = 0; id < sim->qubits.size(); id++) - { - INFO(std::string("unreleased qubit: ") + std::to_string(id)); - CHECK(sim->qubits[id] == RELEASED); - } - - // check that all results, allocated by measurements have been released - // TODO: enable after https://github.com/microsoft/qsharp-compiler/issues/780 is fixed - // for (size_t id = 2; id < sim->results.size(); id++) - // { - // INFO(std::string("unreleased results: ") + std::to_string(id)); - // CHECK(sim->results[id] == RELEASED); - // } -} - -#ifdef _WIN32 -// A non-sensical function that creates a 3D array with given dimensions, then projects on the index = 1 of the -// second dimension and returns a function of the sizes of the dimensions of the projection and a the provided value, -// that is written to the original array at [1,1,1] and then retrieved from [1,1]. -// Thus, all three dimensions must be at least 2. -extern "C" int64_t TestMultidimArrays(char value, int64_t dim0, int64_t dim1, int64_t dim2); -TEST_CASE("QIR: multidimensional arrays", "[qir][qir.arrMultid]") -{ - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); - - REQUIRE(42 + (2 + 8) / 2 == TestMultidimArrays(42, 2, 4, 8)); - REQUIRE(17 + (3 + 7) / 2 == TestMultidimArrays(17, 3, 5, 7)); -} -#else // not _WIN32 -// TODO: The bridge for variadic functions is broken on Linux! -#endif - -// Manually authored QIR to test dumping range [0..2..6] into string and then raising a failure with it -extern "C" void TestFailWithRangeString(int64_t start, int64_t step, int64_t end); -TEST_CASE("QIR: Report range in a failure message", "[qir][qir.range]") -{ - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); - - bool failed = false; - try - { - TestFailWithRangeString(0, 5, 42); // Returns with exception. Leaks the instances created from the moment of - // call to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - } - catch (const std::exception& e) - { - failed = true; - REQUIRE(std::string(e.what()) == "0..5..42"); - } - REQUIRE(failed); } // TestPartials subtracts the second argument from the first and returns the result. extern "C" int64_t Microsoft__Quantum__Testing__QIR__TestPartials__Interop(int64_t, int64_t); // NOLINT TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") { - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); - const int64_t res = Microsoft__Quantum__Testing__QIR__TestPartials__Interop(42, 17); REQUIRE(res == 42 - 17); } // The Microsoft__Quantum__Testing__QIR__TestFunctors__Interop tests needs proper semantics of X and M, and nothing // else. The validation is done inside the test and it would throw in case of failure. -struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - std::vector qubits; - - uint64_t GetQubitId(QubitIdType qubit) const - { - const uint64_t id = (uint64_t)qubit; - REQUIRE(id < this->qubits.size()); - return id; - } - - QubitIdType AllocateQubit() override - { - this->qubits.push_back(0); - return static_cast(this->qubits.size() - 1); - } - - void ReleaseQubit(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); - this->qubits[id] = RELEASED; - } - - void X(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->qubits[id] = 1 - this->qubits[id]; - } - - void ControlledX(long numControls, QubitIdType controls[], QubitIdType qubit) override - { - for (long i = 0; i < numControls; i++) - { - const uint64_t id = GetQubitId(controls[i]); - REQUIRE(this->qubits[id] != RELEASED); - if (this->qubits[id] == 0) - { - return; - } - } - X(qubit); - } - - Result Measure([[maybe_unused]] long numBases, PauliId* /* bases */, long /* numTargets */, - QubitIdType targets[]) override - { - assert(numBases == 1 && "FunctorsTestSimulator doesn't support joint measurements"); - - const uint64_t id = GetQubitId(targets[0]); - REQUIRE(this->qubits[id] != RELEASED); - return reinterpret_cast(this->qubits[id]); - } - - bool AreEqualResults(Result r1, Result r2) override - { - // those are bogus pointers but it's ok to compare them _as pointers_ - return (r1 == r2); - } - - void ReleaseResult(Result /*result*/) override - { - } // the results aren't allocated by this test simulator - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; -static FunctorsTestSimulator* g_ctrqapi = nullptr; -static int g_cKCalls = 0; -static int g_cKCallsControlled = 0; +extern "C" void __quantum__qis__x__body(QUBIT* q); // NOLINT +extern "C" void __quantum__qis__x__ctl(QirArray* controls, QUBIT* q); // NOLINT extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__Interop(); // NOLINT extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__Interop(); // NOLINT extern "C" void __quantum__qis__k__body(QUBIT* q) // NOLINT { - g_cKCalls++; - g_ctrqapi->X(reinterpret_cast(q)); + __quantum__qis__x__body(q); } extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q) // NOLINT { - g_cKCallsControlled++; - g_ctrqapi->ControlledX((long)(controls->count), reinterpret_cast(controls->buffer), - reinterpret_cast(q)); + __quantum__qis__x__ctl(controls, q); } + TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") { - unique_ptr qapi = make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); - g_ctrqapi = qapi.get(); - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__Interop()); - - const int cKCalls = g_cKCalls; - const int cKCallsControlled = g_cKCallsControlled; CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__Interop()); - CHECK(g_cKCalls - cKCalls == 3); - CHECK(g_cKCallsControlled - cKCallsControlled == 5); - - g_ctrqapi = nullptr; } diff --git a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp deleted file mode 100644 index ecd5b47a2ce..00000000000 --- a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include -#include -#include -#include - -#include "catch.hpp" - -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimulatorStub.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -// TestConditionalOnResult() is authored in a way that the expected path through the function only applies X operator -// for the chosen sequence of measurement results, and all other paths apply Y. Thus, the correct execution must get the -// expected maximum number of X and ControlledX callbacks. -struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - int nGateCallback = 0; - vector xCallbacks; - vector cxCallbacks; - vector otherCallbacks; - - vector mockMeasurements; - size_t nextMeasureResult = 0; - - explicit ConditionalsTestSimulator(vector&& results) : mockMeasurements(results) - { - } - - std::string GetHistory() - { - std::stringstream out; - out << "X: "; - for (int i : this->xCallbacks) - { - out << i << ","; - } - - out << std::endl << "CX: "; - for (int i : this->cxCallbacks) - { - out << i << ","; - } - - out << std::endl << "Other: "; - for (int i : this->otherCallbacks) - { - out << i << ","; - } - return out.str(); - } - - QubitIdType AllocateQubit() override - { - return 0; - } - void ReleaseQubit(QubitIdType /*qubit*/) override - { - } - - void X(QubitIdType) override - { - this->xCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - void ControlledX(long /* numControls */, QubitIdType* /* controls */, QubitIdType /* qubit */) override - { - this->cxCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - void Y(QubitIdType) override - { - this->otherCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - void ControlledY(long /* numControls */, QubitIdType* /* controls */, QubitIdType /* qubit */) override - { - this->otherCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - - Result Measure(long /* numBases */, PauliId* /* bases */, long /* numTargets */, - QubitIdType* /* targets */) override - { - assert(this->nextMeasureResult < this->mockMeasurements.size() && - "ConditionalsTestSimulator isn't set up correctly"); - - Result r = (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); - this->nextMeasureResult++; - return r; - } - - bool AreEqualResults(Result r1, Result r2) override - { - // those are bogus pointers but it's ok to compare them _as pointers_ - return (r1 == r2); - } - - void ReleaseResult(Result /*result*/) override - { - } // the results aren't allocated by this test simulator - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; - -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIf__Interop(); // NOLINT -TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") -{ - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__Interop()); - - INFO(qapi->GetHistory()); - CHECK(qapi->xCallbacks.size() == 8); - CHECK(qapi->cxCallbacks.size() == 0); - CHECK(qapi->otherCallbacks.size() == 0); -} - -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__Interop(); // NOLINT -TEST_CASE("QIR: ApplyIf with functors", "[qir][qir.conditionals]") -{ - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__Interop()); - - INFO(qapi->GetHistory()); - CHECK(qapi->xCallbacks.size() == 5); - CHECK(qapi->cxCallbacks.size() == 7); - CHECK(qapi->otherCallbacks.size() == 0); -} - -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__Interop(); // NOLINT -TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") -{ - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__Interop()); - - INFO(qapi->GetHistory()); - CHECK(qapi->xCallbacks.size() == 4); - CHECK(qapi->cxCallbacks.size() == 2); - CHECK(qapi->otherCallbacks.size() == 0); -} diff --git a/src/Qir/Tests/QIR-static/qir-test-math.cpp b/src/Qir/Tests/QIR-static/qir-test-math.cpp index 87765cb22e4..d9df0d68c83 100644 --- a/src/Qir/Tests/QIR-static/qir-test-math.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-math.cpp @@ -6,7 +6,6 @@ #include "catch.hpp" -#include "qsharp__foundation_internal.hpp" #include "FloatUtils.hpp" extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__SqrtTest__Interop(); // NOLINT @@ -106,166 +105,9 @@ TEST_CASE("QIR: Math.Exponent.builtin", "[qir.math][qir.Math.Exponent.builtin]") TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") { - // Test that the Q# random number generator is a wrapper around the C++ generator: - size_t times = 1000; - while (--times) - { - const int64_t qsRndNum = Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop( - std::numeric_limits::min(), std::numeric_limits::max()); - const int64_t cppRndNum = Quantum::Qis::Internal:: - GetLastGeneratedRandomI64(); // This call must be done - // _after_ the - // Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(). - REQUIRE(qsRndNum == cppRndNum); - } - - // Make sure the correct exception is thrown if min > max: - REQUIRE_THROWS_AS(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(10, 5), std::runtime_error); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - - // Check the exception string: - try - { - (void)Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(10, 5); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - } - catch (std::runtime_error const& exc) - { - REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); - } - // Test equal minimum and maximum: for (int64_t num : {-5, 0, 3}) { REQUIRE(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(num, num) == num); } - - // clang-format off - // There is a strong difference in the opinions about how the random number generator must be tested. - // More or less agreed-upon items are: - // * The test must be 100% deterministic, i.e. must not fail, even with a very low probability. - // Otherwise it will bother the unrelated CI builds/test-runs, that are done a lot. - // * The test must not be platform-, compiler-, etc. dependent. - // Otherwise it can break upon migration to a newer compiler, OS update, new OS added to the test runs, etc. - // - // The code below is platform-dependent, can also depend on the compiler version. - // Commenting out for now. - - // #ifdef __APPLE__ - // std::vector expectedLargeNumbers( { - // -2160833387943730151 /*0xe2032d7b74cf6419*/, 7375078072468444798 /*0x66598a6e9c41167e*/, 7708428399011769513 /*0x6af9d6c9b3a12ca9*/, - // -8929332642100591101 /*0x8414a3458a4b4603*/, 9131959130339861073 /*0x7ebb3c72234ae251*/, 2129461186021157660 /*0x1d8d5daa93c5eb1c*/, - // -4466415676527644493 /*0xc2041a9b355570b3*/, 2654403080104352464 /*0x24d65589a8529ad0*/, 3948910203829515833 /*0x36cd58e87d2c7639*/, - // 3600951923571138577 /*0x31f926b221b7a011*/, -7454003569285620820 /*0x988e0f3f2a3c9bac*/, -2896776822558058671 /*0xd7cc94d3e1d79751*/, - // -2510694579170103717 /*0xdd2838951d112e5b*/, -8679035075952589054 /*0x878ddf94f8b0c702*/, -8480296875123573728 /*0x8a4feeec30677c20*/, - - // -8613430109842542716 /*0x8876f2f3752e6f84*/, 2140032717197149199 /*0x1db2ec6afc40040f*/, -917262003397267527 /*0xf3453b11598aefb9*/, - // -3734430349428794203 /*0xcc2ca3620fdbdca5*/, 5134567830016493736 /*0x4741a63cbf4808a8*/, -8243723698983337761 /*0x8d9868f90fa100df*/, - // 5560736588152128922 /*0x4d2bb4770253459a*/, 50526560201835791 /* 0xb381a3888d850f*/, 1288735234894005209 /*0x11e281de3d6303d9*/, - // 3656101241126025060 /*0x32bd14b53c2e7764*/, 872395409727236160 /* 0xc1b5f00c4792840*/, 7628415731883617240 /*0x69dd93b0e9ecabd8*/, - // -1986081594003691539 /*0xe47005501e77e7ed*/, 7532118334194327900 /*0x688775b7d3dc215c*/, -4186893097968929306 /*0xc5e52ae91706f5e6*/ - // } ); - // std::vector expectedSmallNumbers( { 1, 4, 2, 4, 5, -2, -4, 9, 4, -4, -9, 9, -1, 5, 7, - // -8, 0, 2, 5, 0, -1, 1, 9, -3, 5, -8, -9, 6, -1, 6 } ); - // #else - // std::vector expectedLargeNumbers( { - // -5906760355100746824 /*0xae06f8c09cdc1bb8*/, -5720189720460620649 /*0xb09dcdc1901a8c97*/, -439612500227010677 /*0xf9e62ec29d25cf8b*/, - // -4480907261563067469 /*0xc1d09e962310a3b3*/, 8861952245290091527 /*0x7afbfa9d4cfdc407*/, 8955350353842143311 /*0x7c47cbb307ee004f*/, - // -6280323296958344769 /*0xa8d7cf3c6a3011bf*/, 3137151747734999458 /*0x2b8966e4aa3d91a2*/, 4939508655077151009 /*0x448ca8f37ed75121*/, - // 6238374286314258160 /*0x5693285c6fb13ef0*/, -6040247118112373857 /*0xac2cbb3fa955c39f*/, -6824740380414679031 /*0xa149a6c8751e6809*/, - // -3380739839894412592 /*0xd11533070d1522d0*/, 7062538648911045657 /*0x62032d7b74cf6419*/, -1848293964386331010 /*0xe6598a6e9c41167e*/, - - // -1514943637843006295 /*0xeaf9d6c9b3a12ca9*/, 294039394754184707 /* 0x414a3458a4b4603*/, -91412906514914735 /*0xfebb3c72234ae251*/, - // -7093910850833618148 /*0x9d8d5daa93c5eb1c*/, 4756956360327131315 /*0x42041a9b355570b3*/, -6568968956750423344 /*0xa4d65589a8529ad0*/, - // -5274461833025259975 /*0xb6cd58e87d2c7639*/, -5622420113283637231 /*0xb1f926b221b7a011*/, 1769368467569154988 /*0x188e0f3f2a3c9bac*/, - // 6326595214296717137 /*0x57cc94d3e1d79751*/, 6712677457684672091 /*0x5d2838951d112e5b*/, 544336960902186754 /* 0x78ddf94f8b0c702*/, - // 743075161731202080 /* 0xa4feeec30677c20*/, 609941927012233092 /* 0x876f2f3752e6f84*/, -7083339319657626609 /*0x9db2ec6afc40040f*/ - // } ); - // #ifdef _WIN32 - // std::vector expectedSmallNumbers( { -7, 7, 0, -4, 9, -8, -6, 2, 10, -2, -2, 5, -6, 7, -6, - // -8, -7, 7, 1, 0, -7, -4, -4, -5, 9, 6, 9, 8, 0, 10 } ); - // #else - // std::vector expectedSmallNumbers( { -7, 10, -10, 2, 1, -9, 3, -2, 7, 9, -2, 3, 9, -3, -5, - // -1, 6, 4, 1, -4, -7, 2, 1, -7, -7, -10, 1, 4, -2, 9 } ); - // #endif // #ifdef _WIN32 - // #endif // #ifdef __APPLE__ - - // // Use const seed (and 100%-predictable sequence of pseudo-random numbers): - // Quantum::Qis::Internal::RandomizeSeed(false); - - // size_t times = 30; - // std::vector actualNumbers; - // // Get the actual pseudo-random numbers: - // actualNumbers.reserve(times); - // while (times--) - // { - // actualNumbers.emplace_back(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(-10, 10)); - // } - - // // Compare the actual numbers with the expected ones: - // for (auto iterExp = expectedSmallNumbers.begin(), iterAct = actualNumbers.begin(); - // iterExp != expectedSmallNumbers.end(); ++iterExp, ++iterAct) - // { - // REQUIRE(*iterExp == *iterAct); - // } - - - // // Repeat for large numbers: - // times = 30; - // actualNumbers.clear(); - // while (times--) - // { - // actualNumbers.emplace_back( - // Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(std::numeric_limits::min(), - // std::numeric_limits::max())); - // } - - // for (auto iterExp = expectedLargeNumbers.begin(), iterAct = actualNumbers.begin(); - // iterExp != expectedLargeNumbers.end(); ++iterExp, ++iterAct) - // { - // REQUIRE(*iterExp == *iterAct); - // } - - // clang-format on - } // TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") - -TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") -{ - // Test that the Q# random number generator is a wrapper around the C++ generator: - size_t times = 1000; - while (--times) - { - const double qsRndNum = Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop( - std::numeric_limits::min(), std::numeric_limits::max()); - const double cppRndNum = Quantum::Qis::Internal:: - GetLastGeneratedRandomDouble(); // This call must be done - // _after_ the - // Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop(). - REQUIRE(Close(qsRndNum, cppRndNum)); - } - - // Make sure the correct exception is thrown if min > max: - REQUIRE_THROWS_AS(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop(10.0, 5.0), - std::runtime_error); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - - // Check the exception string: - try - { - (void)Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop(10.0, 5.0); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - } - catch (std::runtime_error const& exc) - { - REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); - } -} // TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") diff --git a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll deleted file mode 100644 index a2b465ea7bd..00000000000 --- a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll +++ /dev/null @@ -1,48 +0,0 @@ -%Range = type { i64, i64, i64 } -%Array = type opaque -%String = type opaque - -@EmptyRange = internal constant %Range { i64 0, i64 1, i64 -1 } -declare %Array* @__quantum__rt__array_create(i32, i32, ...) -declare i64 @__quantum__rt__array_get_size(%Array*, i32) -declare i8* @__quantum__rt__array_get_element_ptr(%Array*, ...) -declare i32 @__quantum__rt__array_get_dim(%Array*) -declare %Array* @__quantum__rt__array_project(%Array*, i32, i64) -declare void @__quantum__rt__array_update_reference_count(%Array*, i32) - -; manually authored test for multi-dimensional arrays (Q# doesn't support multi-dimentional arrays yet) -define i64 @TestMultidimArrays(i8 %val, i64 %dim0, i64 %dim1, i64 %dim2) -{ - %.ar = call %Array* (i32, i32, ...) @__quantum__rt__array_create(i32 1, i32 3, i64 %dim0, i64 %dim1, i64 %dim2) - %elem_ptr = call i8* (%Array*, ...) @__quantum__rt__array_get_element_ptr(%Array* %.ar, i64 1, i64 1, i64 1) - store i8 %val, i8* %elem_ptr - %.project = call %Array* @__quantum__rt__array_project(%Array* %.ar, i32 1, i64 1) - %project_dims = call i32 @__quantum__rt__array_get_dim(%Array* %.project) - %project_dim0 = call i64 @__quantum__rt__array_get_size(%Array* %.project, i32 0) - %project_dim1 = call i64 @__quantum__rt__array_get_size(%Array* %.project, i32 1) - %project_elem_ptr = call i8* (%Array*, ...) @__quantum__rt__array_get_element_ptr(%Array* %.project, i64 1, i64 1) - %project_val = load i8, i8* %project_elem_ptr - %val64 = sext i8 %project_val to i64 - - call void @__quantum__rt__array_update_reference_count(%Array* %.ar, i32 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %.project, i32 -1) - - %t1 = add i64 %project_dim0, %project_dim1 - %t2 = sext i32 %project_dims to i64 - %av = udiv i64 %t1, %t2 - %t3 = add i64 %av, %val64 - ret i64 %t3 -} - -; manually authored test for dumping range into a string and raising a failure with it -declare %String* @__quantum__rt__range_to_string(%Range) -declare void @__quantum__rt__fail(%String*) -define void @TestFailWithRangeString(i64 %start, i64 %step, i64 %end){ - %re = load %Range, %Range* @EmptyRange - %r0 = insertvalue %Range %re, i64 %start, 0 - %r1 = insertvalue %Range %r0, i64 %step, 1 - %r2 = insertvalue %Range %r1, i64 %end, 2 - %str = call %String* @__quantum__rt__range_to_string(%Range %r2) - call void @__quantum__rt__fail(%String* %str) ; Leaks the `%str`. TODO: Extract into a separate file compiled with leak check off. - ret void -} diff --git a/src/Qir/Tests/QIR-static/qir-test-other.cpp b/src/Qir/Tests/QIR-static/qir-test-other.cpp index bb1417234ff..9454f18ef5e 100644 --- a/src/Qir/Tests/QIR-static/qir-test-other.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-other.cpp @@ -3,24 +3,14 @@ #include "catch.hpp" -extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop(); // NOLINT -extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop(); // NOLINT -extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntFailTest__Interop(); // NOLINT - -TEST_CASE("QIR: Other.PauliArrayAsIntFail", "[qir.Other][qir.Other.PauliArrayAsIntFail]") -{ - REQUIRE_THROWS(Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntFailTest__Interop()); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. -} +extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop(); // NOLINT +extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop(); // NOLINT TEST_CASE("QIR: Other.PauliArrayAsInt", "[qir.Other][qir.Other.PauliArrayAsInt]") { REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop()); } - TEST_CASE("QIR: Other.Parity", "[qir.Other][qir.Other.Parity]") { REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop()); diff --git a/src/Qir/Tests/QIR-static/qir-test-ouput.cpp b/src/Qir/Tests/QIR-static/qir-test-ouput.cpp deleted file mode 100644 index ece08cb50f2..00000000000 --- a/src/Qir/Tests/QIR-static/qir-test-ouput.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include - -#include "catch.hpp" - -#include "OutputStream.hpp" - -extern "C" void Microsoft__Quantum__Testing__QIR__Out__MessageTest__Interop(const char[]); // NOLINT - -TEST_CASE("QIR: Out.Message", "[qir.Out][qir.Out.Message]") -{ - const std::string testStr1 = "Test String 1"; - const std::string testStr2 = "Test String 2"; - - std::ostringstream outStrStream; - - { - // Redirect the output from std::cout to outStrStream: - Microsoft::Quantum::OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - // Log something (to the redirected output): - Microsoft__Quantum__Testing__QIR__Out__MessageTest__Interop(testStr1.c_str()); - Microsoft__Quantum__Testing__QIR__Out__MessageTest__Interop(testStr2.c_str()); - - } // Recover the output stream. - - REQUIRE(outStrStream.str() == (testStr1 + "\n" + testStr2 + "\n")); -} diff --git a/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs deleted file mode 100644 index 14faa88321c..00000000000 --- a/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -namespace Microsoft.Quantum.Testing.QIR { - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.ClassicalControl; - - @EntryPoint() - operation TestApplyIf() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - let r1 = M(q1); // expected: r1 = Zero - X(q2); - let r2 = M(q2); // expected: r2 = One - - ApplyIfElseR(r1, (X, q1), (Y, q1)); - ApplyIfElseR(r2, (Y, q1), (X, q1)); - - // Other variants - ApplyIfElseRA(r1, (X, q1), (Y, q1)); - ApplyIfElseRC(r1, (X, q1), (Y, q1)); - ApplyIfElseRCA(r1, (X, q1), (Y, q1)); - ApplyIfOne(r2, (X, q1)); - ApplyIfZero(r1, (X, q1)); - } - - @EntryPoint() - operation TestApplyIfWithFunctors() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - let r1 = M(q1); - X(q2); - let r2 = M(q2); - - Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); - Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); - Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); - Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); - Controlled ApplyIfElseRC([q2], (r1, (X, q1), (Y, q1))); - Adjoint ApplyIfOneA(r2, (X, q1)); - Controlled ApplyIfOneC([q2], (r2, (X, q1))); - Adjoint Controlled ApplyIfOneCA([q2], (r2, (X, q1))); - Adjoint ApplyIfZeroA(r1, (X, q1)); - Controlled ApplyIfZeroC([q2], (r1, (X, q1))); - Adjoint Controlled ApplyIfZeroCA([q2], (r1, (X, q1))); - } - - @EntryPoint() - operation TestApplyConditionally() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - let r1 = M(q1); - X(q2); - let r2 = M(q2); - - ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); - ApplyConditionally([r1, One], [Zero, r2], (X, q1), (Y, q1)); - - Adjoint ApplyConditionallyA([r1], [r2], (Y, q1), (X, q1)); - Controlled ApplyConditionallyC([q2], ([r1], [r2], (Y, q1), (X, q1))); - Adjoint Controlled ApplyConditionallyCA([q2], ([r1], [r2], (Y, q1), (X, q1))); - } - -} \ No newline at end of file diff --git a/src/Qir/Tests/QIR-tracer/CMakeLists.txt b/src/Qir/Tests/QIR-tracer/CMakeLists.txt deleted file mode 100644 index e19d93f587e..00000000000 --- a/src/Qir/Tests/QIR-tracer/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -set(TEST_FILES - qsharp/obj/qsharp/tracer-qir.bc -) - -#============================================================================== -# The executable target for QIR tests triggers the custom actions to compile ll files -# -add_executable(qir-tracer-tests - qir-tracer-driver.cpp - tracer-config.cpp -) - -foreach(file ${TEST_FILES}) - target_source_from_qir(qir-tracer-tests ${file}) -endforeach() - -target_link_libraries(qir-tracer-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.Tracer -) - -target_include_directories(qir-tracer-tests PUBLIC - ${test_includes} - ${public_includes} - "${PROJECT_SOURCE_DIR}/../Runtime/lib/Tracer" # TODO: Remove this when tracer api is put into public headers. -) - -install(TARGETS qir-tracer-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-tracer-tests) diff --git a/src/Qir/Tests/QIR-tracer/generate.py b/src/Qir/Tests/QIR-tracer/generate.py deleted file mode 100644 index 20354e3da9a..00000000000 --- a/src/Qir/Tests/QIR-tracer/generate.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys, os, platform, subprocess, datetime, shutil - -# ============================================================================= -# Generates QIR files for all *.qs files in this folder -# Accepts arguments: -# path to qsc.exe (absolute or rely on Path env) -# -# For example: "generate.py qsc.exe" or "generate.py c:\qsharp-compiler\qsc.exe" -# ============================================================================= - -# ============================================================================= -def log(message): - now = datetime.datetime.now() - current_time = now.strftime("%H:%M:%S") - print(current_time + ": " + message) -# ============================================================================= - -if __name__ == '__main__': - # this script is executed as script - root_dir = os.path.dirname(os.path.abspath(__file__)) - - # parameters - qsc = sys.argv[1] # argv[0] is the name of this script file - - # find all qs files in this folder - files_to_process = "" - output_file = "tracer-qir" - for file in os.listdir(root_dir): - (file_name, ext) = os.path.splitext(file) - if ext == ".qs": - files_to_process = files_to_process + " " + file - - # Compile as a lib so all functions are retained and don't have to workaround the current limitations of - # @EntryPoint attribute. - command = (qsc + " build --qir qir --input " + files_to_process + " --proj " + output_file) - log("Executing: " + command) - subprocess.run(command, shell = True) - - # copy the generated file into tracer's input files - generated_file = os.path.join(root_dir, "qir", output_file) + ".ll" - build_input_file = os.path.join(root_dir, output_file) + ".ll" - shutil.copyfile(generated_file, build_input_file) - diff --git a/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp b/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp deleted file mode 100644 index d82d05935b3..00000000000 --- a/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -#include "QirContext.hpp" -#include "tracer-config.hpp" -#include "tracer.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -namespace TracerUser -{ - -TEST_CASE("Invoke each intrinsic from Q# core once", "[qir-tracer]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/, g_operationNames); - QirExecutionContext::Scoped qirctx(tr.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__Interop()); - const vector& layers = tr->UseLayers(); - - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - INFO(out.str()); - - // TestCoreIntrinsics happens to produce 24 layers right now and we are not checking whether that's expected -- as - // testing of layering logic is better done by unit tests. - CHECK(layers.size() == 24); -} - -TEST_CASE("Conditional execution on measurement result", "[qir-tracer]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/, g_operationNames); - QirExecutionContext::Scoped qirctx(tr.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestMeasurements__Interop()); - - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - INFO(out.str()); - CHECK(tr->UseLayers().size() == 5); -} -} // namespace TracerUser diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs deleted file mode 100644 index b6b198858bb..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Testing.Tracer { - open Microsoft.Quantum.Intrinsic; - - // Private helper operations. - operation Delay(op : (Qubit => Unit), arg : Qubit, aux : Unit) : Unit { - op(arg); - } - - @EntryPoint() - operation TestMeasurements() : Unit { - use qs = Qubit[6]; - T(qs[0]); // layer 0 - let r0 = M(qs[0]); // layer 1 - T(qs[1]); // layer 0 - CNOT(qs[1], qs[2]); // layer 1 - let qs12 = [qs[1], qs[2]]; - let r12 = Measure([PauliY, PauliX], qs12); // layer 2 - - ApplyIfElseIntrinsic(r0, Delay(X, qs[3], _), Delay(Y, qs[3], _)); // layers 2, 3 - ApplyIfElseIntrinsic(r12, Delay(Z, qs[4], _), Delay(S, qs[4], _)); // layer 3, 4 - Rx(4.2, qs[5]); // layer 0 - } -} \ No newline at end of file diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs deleted file mode 100644 index 84e57ae32e3..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Core { - - @Attribute() - newtype Attribute = Unit; - - @Attribute() - newtype Inline = Unit; - - @Attribute() - newtype EntryPoint = Unit; - - function Length<'T> (array : 'T[]) : Int { body intrinsic; } - - function RangeStart (range : Range) : Int { body intrinsic; } - - function RangeStep (range : Range) : Int { body intrinsic; } - - function RangeEnd (range : Range) : Int { body intrinsic; } - - function RangeReverse (range : Range) : Range { body intrinsic; } -} - -namespace Microsoft.Quantum.Targeting { - - @Attribute() - newtype TargetInstruction = String; -} \ No newline at end of file diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs deleted file mode 100644 index d110ff10eaf..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Testing.Tracer { - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Tracer; - - @EntryPoint() - operation TestCoreIntrinsics() : Unit { - use qs = Qubit[3]; - - X(qs[0]); - Y(qs[0]); - Z(qs[1]); - H(qs[1]); - CNOT(qs[1], qs[2]); - Rx(0.3, qs[0]); - Ry(0.4, qs[1]); - Rz(0.5, qs[2]); - //SWAP(qs[0], qs[2]); - S(qs[1]); - T(qs[2]); - - Barrier(42, 0); - - Adjoint X(qs[0]); - Adjoint Y(qs[0]); - Adjoint Z(qs[1]); - Adjoint H(qs[1]); - Adjoint CNOT(qs[1], qs[2]); - Adjoint Rx(0.3, qs[0]); - Adjoint Ry(0.4, qs[1]); - Adjoint Rz(0.5, qs[2]); - //Adjoint SWAP(qs[0], qs[2]); - Adjoint S(qs[1]); - Adjoint T(qs[2]); - - use c = Qubit() { - Controlled X([c], (qs[0])); - Controlled Y([c], (qs[0])); - Controlled Z([c], (qs[1])); - Controlled H([c], (qs[1])); - Controlled Rx([c], (0.3, qs[0])); - Controlled Ry([c], (0.4, qs[1])); - Controlled Rz([c], (0.5, qs[2])); - //Controlled SWAP([c], (qs[0], qs[2])); - Controlled S([c], (qs[1])); - Controlled T([c], (qs[2])); - } - - use cc = Qubit[2] { - Controlled X(cc, (qs[0])); - Controlled Y(cc, (qs[0])); - Controlled Z(cc, (qs[1])); - Controlled H(cc, (qs[1])); - Controlled Rx(cc, (0.3, qs[0])); - Controlled Ry(cc, (0.4, qs[1])); - Controlled Rz(cc, (0.5, qs[2])); - //Controlled SWAP(cc, (qs[0], qs[2])); - Controlled S(cc, (qs[1])); - Controlled T(cc, (qs[2])); - } - } -} diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj b/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj deleted file mode 100644 index f8677e7b9a4..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - Exe - net6.0 - True - false - false - - - diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs deleted file mode 100644 index f07b57c4304..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Instructions { - - // We'll use TargetInstruction attribute to suppress Q#'s compiler decoration of names for the generated callbacks. - open Microsoft.Quantum.Targeting; - - @TargetInstruction("single_qubit_op") - operation single_qubit_op(op_id: Int, duration: Int, qb : Qubit) : Unit { - body intrinsic; - } - - @TargetInstruction("multi_qubit_op") - operation multi_qubit_op(op_id: Int, duration: Int, qbs : Qubit[]) : Unit { - body intrinsic; - } - - @TargetInstruction("single_qubit_op_ctl") - operation single_qubit_op_ctl(op_id: Int, duration: Int, ctl : Qubit[], qb : Qubit) : Unit { - body intrinsic; - } - - @TargetInstruction("multi_qubit_op_ctl") - operation multi_qubit_op_ctl(op_id: Int, duration: Int, ctl : Qubit[], qbs : Qubit[]) : Unit { - body intrinsic; - } - - @TargetInstruction("single_qubit_measure") - operation single_qubit_measure(op_id: Int, duration: Int, qb : Qubit) : Result { - body intrinsic; - } - - @TargetInstruction("joint_measure") - operation joint_measure(op_id: Int, duration: Int, qbs : Qubit[]) : Result { - body intrinsic; - } - - @TargetInstruction("apply_conditionally") - operation apply_conditionally( - measurementResults : Result[], resultsValues : Result[], - onEqualOp : (Unit => Unit) , onNonEqualOp : (Unit => Unit)) : Unit { - body intrinsic; - } - - // Operations, used in Hadamard frame tracking - @Inline() - operation Tz(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(11, 1, qb); } - adjoint (...) { single_qubit_op(11, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(12, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(12, 1, ctls, qb); } - } - - @Inline() - operation Tx(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(13, 1, qb); } - adjoint (...) { single_qubit_op(13, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(14, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(14, 1, ctls, qb); } - } - - - @Inline() - operation Sz(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(15, 1, qb); } - adjoint (...) { single_qubit_op(15, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(16, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(16, 1, ctls, qb); } - } - - @Inline() - operation Sx(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(17, 1, qb); } - adjoint (...) { single_qubit_op(17, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(18, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(18, 1, ctls, qb); } - } - - @Inline() - operation Mz(qb : Qubit) : Result { - body (...) { return single_qubit_measure(100, 1, qb); } - } - - @Inline() - operation Mx(qb : Qubit) : Result { - body (...) { return single_qubit_measure(101, 1, qb); } - } - - @Inline() - operation Mzz(qubits : Qubit[]) : Result { - body (...) { return joint_measure(102, 1, qubits); } - } - - @Inline() - operation Mxz(qubits : Qubit[]) : Result { - body (...) { return joint_measure(103, 1, qubits); } - } - - @Inline() - operation Mzx(qubits : Qubit[]) : Result { - body (...) { return joint_measure(104, 1, qubits); } - } - - @Inline() - operation Mxx(qubits : Qubit[]) : Result { - body (...) { return joint_measure(105, 1, qubits); } - } -} - -namespace Microsoft.Quantum.Tracer { - - open Microsoft.Quantum.Targeting; - - @TargetInstruction("inject_barrier") - operation Barrier(id : Int, duration : Int) : Unit { - body intrinsic; - } -} - -namespace Microsoft.Quantum.Intrinsic { - - open Microsoft.Quantum.Core; - open Microsoft.Quantum.Instructions as Phys; - open Microsoft.Quantum.Targeting; - - @Inline() - operation X(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(0, 1, qb); } - adjoint self; - controlled (ctls, ...) { - if Length(ctls) == 1 { Phys.single_qubit_op_ctl(1, 1, ctls, qb); } - else { Phys.single_qubit_op_ctl(2, 1, ctls, qb); } - } - } - - operation CNOT(control : Qubit, target : Qubit) : Unit - is Adj + Ctl { - body (...) { Controlled X([control], target); } - adjoint self; - controlled (ctls, ...) { Controlled X(ctls + [control], target); } - } - - @Inline() - operation Y(qb : Qubit) : Unit - is Adj + Ctl{ - body (...) { Phys.single_qubit_op(3, 1, qb); } - adjoint self; - controlled (ctls, ...) { - if Length(ctls) == 1 { Phys.single_qubit_op_ctl(4, 1, ctls, qb); } - else { Phys.single_qubit_op_ctl(5, 1, ctls, qb); } - } - } - - @Inline() - operation Z(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(6, 1, qb); } - adjoint self; - controlled (ctls, ...) { - if Length(ctls) == 1 { Phys.single_qubit_op_ctl(7, 1, ctls, qb); } - else { Phys.single_qubit_op_ctl(8, 1, ctls, qb); } - } - } - - @Inline() - operation H(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(9, 1, qb); } - adjoint self; - controlled (ctls, ...) { Phys.single_qubit_op_ctl(10, 1, ctls, qb); } - } - - @Inline() - operation T(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.Tz(qb); } - adjoint (...) { Adjoint Phys.Tz(qb); } - controlled (ctls, ...) { Controlled Phys.Tz(ctls, qb); } - controlled adjoint (ctls, ...) { Controlled Adjoint Phys.Tz(ctls, qb); } - } - - @Inline() - operation S(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.Sz(qb); } - adjoint (...) { Adjoint Phys.Sz(qb); } - controlled (ctls, ...) { Controlled Phys.Sz(ctls, qb); } - controlled adjoint (ctls, ...) { Controlled Adjoint Phys.Sz(ctls, qb); } - } - - @Inline() - operation Rx(theta : Double, qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(19, 1, qb); } - adjoint (...) { Phys.single_qubit_op(19, 1, qb); } - controlled (ctls, ...) { Phys.single_qubit_op_ctl(20, 1, ctls, qb); } - controlled adjoint (ctls, ...) { Phys.single_qubit_op_ctl(20, 1, ctls, qb); } - } - - @Inline() - operation Ry(theta : Double, qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(21, 1, qb); } - adjoint (...) { Phys.single_qubit_op(21, 1, qb); } - controlled (ctls, ...) { Phys.single_qubit_op_ctl(22, 1, ctls, qb); } - controlled adjoint (ctls, ...) { Phys.single_qubit_op_ctl(22, 1, ctls, qb); } - } - - @Inline() - operation Rz(theta : Double, qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(23, 1, qb); } - adjoint (...) { Phys.single_qubit_op(23, 1, qb); } - controlled (ctls, ...) { Phys.single_qubit_op_ctl(24, 1, ctls, qb); } - controlled adjoint (ctls, ...) { Phys.single_qubit_op_ctl(24, 1, ctls, qb); } - } - - @Inline() - operation M(qb : Qubit) : Result { - body (...) { return Phys.Mz(qb); } - } - - @Inline() - operation Measure(paulis : Pauli[], qubits : Qubit[]) : Result { - body (...) { - mutable res = One; - mutable haveY = false; - // Measurements that involve PauliY or PauliI - for i in 0..Length(paulis)-1 { - if paulis[i] == PauliY or paulis[i] == PauliI { - set haveY = true; - } - } - if haveY { set res = Phys.joint_measure(106, 1, qubits); } - - // More than two qubits (but no PauliY or PauliI) - elif Length(paulis) > 2 { set res = Phys.joint_measure(107, 1, qubits); } - - // Single qubit measurement -- differentiate between Mx and Mz - elif Length(paulis) == 1 { - if (paulis[0] == PauliX) { set res = Phys.Mx(qubits[0]); } - else { set res = Phys.Mz(qubits[0]); } - } - - // Specialize for two-qubit measurements: Mxx, Mxz, Mzx, Mzz - elif paulis[0] == PauliX and paulis[1] == PauliX { set res = Phys.Mxx(qubits); } - elif paulis[0] == PauliX and paulis[1] == PauliZ { set res = Phys.Mxz(qubits); } - elif paulis[0] == PauliZ and paulis[1] == PauliX { set res = Phys.Mzx(qubits); } - elif paulis[0] == PauliZ and paulis[1] == PauliZ { set res = Phys.Mzz(qubits); } - - //shouldn't get here - return res; - } - } - - operation ApplyConditionallyIntrinsic( - measurementResults : Result[], resultsValues : Result[], - onEqualOp : (Unit => Unit) , onNonEqualOp : (Unit => Unit)) : Unit { - body (...) { return Phys.apply_conditionally(measurementResults, resultsValues, onEqualOp, onNonEqualOp); } - } - - operation ApplyIfElseIntrinsic( - measurementResult : Result, onResultZeroOp : (Unit => Unit) , onResultOneOp : (Unit => Unit)) : Unit { - body (...) { return Phys.apply_conditionally([measurementResult], [Zero], onResultZeroOp, onResultOneOp); } - } - - // operation SWAP(a : Qubit, b : Qubit) : Unit - // is Adj { - // body intrinsic; - // adjoint self; - // } - - -} diff --git a/src/Qir/Tests/QIR-tracer/tracer-config.cpp b/src/Qir/Tests/QIR-tracer/tracer-config.cpp deleted file mode 100644 index 41f6410feb8..00000000000 --- a/src/Qir/Tests/QIR-tracer/tracer-config.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// TODO: ideally, this file should be generated by the Q# compiler alongside the qir, using the mappings specified in -// target.qs. - -#include - -#include "QirRuntimeApi_I.hpp" -#include "tracer-config.hpp" - -namespace TracerUser -{ -const std::unordered_map g_operationNames = { - {0, "X"}, {1, "CX"}, {2, "MCX"}, {3, "Y"}, {4, "CY"}, {5, "MCY"}, {6, "Z"}, - {7, "CZ"}, {8, "MCZ"}, {19, "Rx"}, {20, "MCRx"}, {21, "Ry"}, {22, "MCRy"}, {23, "Rz"}, - {24, "MCRz"}, {9, "H"}, {10, "MCH"}, {11, "T"}, {12, "MCT"}, {15, "S"}, {16, "MCS"} /*etc.*/}; -} diff --git a/src/Qir/Tests/QIR-tracer/tracer-config.hpp b/src/Qir/Tests/QIR-tracer/tracer-config.hpp deleted file mode 100644 index 621bd269347..00000000000 --- a/src/Qir/Tests/QIR-tracer/tracer-config.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// TODO: ideally, this file should be generated by the Q# compiler alongside the qir - -#pragma once - -#include -#include - -#include "TracerTypes.hpp" - -namespace TracerUser -{ -extern const std::unordered_map g_operationNames; -} // namespace TracerUser - -// Available function in generated QIR -extern "C" void Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__Interop(); // NOLINT -extern "C" void Microsoft__Quantum__Testing__Tracer__TestMeasurements__Interop(); // NOLINT diff --git a/src/Qir/Tests/TestUtils.cpp b/src/Qir/Tests/TestUtils.cpp deleted file mode 100644 index 045b3730160..00000000000 --- a/src/Qir/Tests/TestUtils.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -// Can manually add calls to DebugLog in the ll files for debugging. -extern "C" void DebugLog(int64_t value) -{ - std::cout << value << std::endl; -} -extern "C" void DebugLogPtr(char* value) -{ - std::cout << (const void*)value << std::endl; -} diff --git a/src/Qir/Tests/TestUtils.hpp b/src/Qir/Tests/TestUtils.hpp deleted file mode 100644 index c4239f5ce27..00000000000 --- a/src/Qir/Tests/TestUtils.hpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#ifndef TESTUTILS_HPP -#define TESTUTILS_HPP - -#include - -extern "C" void DebugLog(int64_t value); -extern "C" void DebugLogPtr(char* value); - -#endif // #ifndef TESTUTILS_HPP diff --git a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs index 395877bcf61..ea65c7069e5 100644 --- a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs +++ b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs @@ -23,10 +23,6 @@ public class QirDriverGeneratorTests "UseNoArgs", new EntryPointOperation{Name = "UseNoArgs"} }, - { - "UseNoArgsDebug", - new EntryPointOperation{Name = "UseNoArgsDebug"} - }, { "UseBoolArg", new EntryPointOperation @@ -150,26 +146,25 @@ private static string RemoveLineEndings(string str) => str.Replace("\n", string.Empty).Replace("\r", string.Empty); [Theory] - [InlineData("UseNoArgs", false)] - [InlineData("UseNoArgsDebug", true)] - [InlineData("UseBoolArg", false)] - [InlineData("UseBoolArrayArg", false)] - [InlineData("UseDoubleArg", false)] - [InlineData("UseDoubleArrayArg", false)] - [InlineData("UseIntegerArg", false)] - [InlineData("UseIntegerArrayArg", false)] - [InlineData("UsePauliArg", false)] - [InlineData("UsePauliArrayArg", false)] - [InlineData("UseRangeArg", false)] - [InlineData("UseRangeArrayArg", false)] - [InlineData("UseResultArg", false)] - [InlineData("UseResultArrayArg", false)] - [InlineData("UseStringArg", false)] - [InlineData("UseMiscArgs", false)] - public void GenerateFullStateSimulatorDriver(string testCase, bool debug) + [InlineData("UseNoArgs")] + [InlineData("UseBoolArg")] + [InlineData("UseBoolArrayArg")] + [InlineData("UseDoubleArg")] + [InlineData("UseDoubleArrayArg")] + [InlineData("UseIntegerArg")] + [InlineData("UseIntegerArrayArg")] + [InlineData("UsePauliArg")] + [InlineData("UsePauliArrayArg")] + [InlineData("UseRangeArg")] + [InlineData("UseRangeArrayArg")] + [InlineData("UseResultArg")] + [InlineData("UseResultArrayArg")] + [InlineData("UseStringArg")] + [InlineData("UseMiscArgs")] + public void GenerateFullStateSimulatorDriver(string testCase) { var entryPointOperation = TestCases[testCase]; - var driverGenerator = new QirFullStateDriverGenerator(debug); + var driverGenerator = new QirFullStateDriverGenerator(); var driverFileName = $"{testCase}.cpp"; var verificationCppSourceCode = RemoveLineEndings(File.ReadAllText(Path.Combine(TestCasesDirectory, driverFileName))); Directory.CreateDirectory(TestArtifactsDirectory); @@ -185,7 +180,7 @@ public void GenerateFullStateSimulatorDriver(string testCase, bool debug) [MemberData(nameof(CommandLineArgumentsData))] public void GenerateCommandLineArguments(string expected, ExecutionInformation info) { - var generator = new QirFullStateDriverGenerator(false); + var generator = new QirFullStateDriverGenerator(); Assert.Equal(expected, generator.GetCommandLineArguments(info)); } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp index aa3513a5208..0be0cbf61eb 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Bool type. @@ -39,17 +33,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. char BoolArgCli; BoolArgCli = InteropFalseAsChar; @@ -63,27 +46,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. char BoolArgInterop = BoolArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseBoolArg( BoolArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp index 9984a990f80..e8525958eed 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -64,17 +58,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector BoolArrayArgCli; app.add_option("--BoolArrayArg", BoolArrayArgCli, "Option to provide a value for the BoolArrayArg parameter") @@ -88,27 +71,13 @@ int main(int argc, char* argv[]) unique_ptr BoolArrayArgUniquePtr = CreateInteropArray(BoolArrayArgCli); InteropArray* BoolArrayArgInterop = BoolArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseBoolArrayArg( BoolArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp index 4f906afb33b..d36739d3a7b 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; extern "C" void UseDoubleArg( @@ -29,17 +23,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. double_t DoubleArgCli; DoubleArgCli = 0.0; @@ -52,27 +35,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. double_t DoubleArgInterop = DoubleArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseDoubleArg( DoubleArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp index 6c764126b06..1f1914ce79d 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -54,17 +48,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector DoubleArrayArgCli; app.add_option("--DoubleArrayArg", DoubleArrayArgCli, "Option to provide a value for the DoubleArrayArg parameter") @@ -77,27 +60,13 @@ int main(int argc, char* argv[]) unique_ptr DoubleArrayArgUniquePtr = CreateInteropArray(DoubleArrayArgCli); InteropArray* DoubleArrayArgInterop = DoubleArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseDoubleArrayArg( DoubleArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp index 1f95ee9bdf4..5e70b972c67 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; extern "C" void UseIntegerArg( @@ -29,17 +23,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. int64_t IntegerArgCli; IntegerArgCli = 0; @@ -52,27 +35,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. int64_t IntegerArgInterop = IntegerArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseIntegerArg( IntegerArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp index dc7fc6ae65c..4bcafda3a32 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -54,17 +48,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector IntegerArrayArgCli; app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") @@ -77,27 +60,13 @@ int main(int argc, char* argv[]) unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseIntegerArrayArg( IntegerArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp index 301dbec64a5..72b9185064a 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -104,17 +98,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. char BoolArgCli; BoolArgCli = InteropFalseAsChar; @@ -147,16 +130,6 @@ int main(int argc, char* argv[]) const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseMiscArgs( BoolArgInterop, @@ -166,11 +139,7 @@ int main(int argc, char* argv[]) ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp index cfece3ca337..bffb97c35a7 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; extern "C" void UseNoArgs( @@ -28,40 +22,15 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // After all the options have been added, parse arguments from the command line. CLI11_PARSE(app, argc, argv); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseNoArgs( ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp deleted file mode 100644 index 847066fd763..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; -using namespace std; - -extern "C" void UseNoArgsDebug( -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - - // Execute the entry point operation. - UseNoArgsDebug( - ); - - // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp index 16804e3eb26..73e4a90fa7b 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp @@ -13,27 +13,16 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Pauli type. -map PauliMap{ - {"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z} +map PauliMap{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} }; -char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} - extern "C" void UsePauliArg( char PauliArg ); // QIR interop function. @@ -42,20 +31,9 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. - PauliId PauliArgCli; - PauliArgCli = PauliId::PauliId_I; + char PauliArgCli; + PauliArgCli = 0; app.add_option("--PauliArg", PauliArgCli, "Option to provide a value for the PauliArg parameter") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); @@ -64,17 +42,7 @@ int main(int argc, char* argv[]) CLI11_PARSE(app, argc, argv); // Cast parsed arguments to its interop types. - char PauliArgInterop = TranslatePauliToChar(PauliArgCli); - - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } + char PauliArgInterop = PauliArgCli; // Execute the entry point operation. UsePauliArg( @@ -82,11 +50,7 @@ int main(int argc, char* argv[]) ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp index c889fd73493..5ca5cc7de70 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -47,18 +41,13 @@ void TranslateVector(vector& sourceVector, vector& destinationVector, func } // Auxiliary functions for interop with Q# Pauli type. -map PauliMap{ - {"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z} +map PauliMap{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} }; -char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} - extern "C" void UsePauliArrayArg( InteropArray* PauliArrayArg ); // QIR interop function. @@ -67,19 +56,8 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. - vector PauliArrayArgCli; + vector PauliArrayArgCli; app.add_option("--PauliArrayArg", PauliArrayArgCli, "Option to provide a value for the PauliArrayArg parameter") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); @@ -88,32 +66,16 @@ int main(int argc, char* argv[]) CLI11_PARSE(app, argc, argv); // Cast parsed arguments to its interop types. - vector PauliArrayArgIntermediate; - TranslateVector(PauliArrayArgCli, PauliArrayArgIntermediate, TranslatePauliToChar); - unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgIntermediate); + unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgCli); InteropArray* PauliArrayArgInterop = PauliArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UsePauliArrayArg( PauliArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp index 8226efa86ed..82790f8e1c4 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Range type. @@ -60,17 +54,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. RangeTuple RangeArgCli; app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") @@ -82,27 +65,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseRangeArg( RangeArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp index df879070ec8..2695c05d772 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -95,17 +89,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector RangeArrayArgCli; app.add_option("--RangeArrayArg", RangeArrayArgCli, "Option to provide a value for the RangeArrayArg parameter") @@ -120,27 +103,13 @@ int main(int argc, char* argv[]) unique_ptr RangeArrayArgUniquePtr = CreateInteropArray(RangeArrayArgIntermediate); InteropArray* RangeArrayArgInterop = RangeArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseRangeArrayArg( RangeArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp index 383eb03d9e1..dc51c0ea2d1 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Result type. @@ -39,17 +33,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. char ResultArgCli; ResultArgCli = InteropResultZeroAsChar; @@ -63,27 +46,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. char ResultArgInterop = ResultArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseResultArg( ResultArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp index ba5b8f426e8..cbbfd2f08cc 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -64,17 +58,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector ResultArrayArgCli; app.add_option("--ResultArrayArg", ResultArrayArgCli, "Option to provide a value for the ResultArrayArg parameter") @@ -88,27 +71,13 @@ int main(int argc, char* argv[]) unique_ptr ResultArrayArgUniquePtr = CreateInteropArray(ResultArrayArgCli); InteropArray* ResultArrayArgInterop = ResultArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseResultArrayArg( ResultArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp index c7400f6c53a..e883e1bca1a 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# String type. @@ -35,17 +29,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. string StringArgCli; app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") @@ -57,27 +40,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseStringArg( StringArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj index b920292cdd9..f74161bb917 100644 --- a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj +++ b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj @@ -64,9 +64,6 @@ Always - - Always - diff --git a/src/Qir/Tests/build-qir-tests.ps1 b/src/Qir/Tests/build-qir-tests.ps1 index 51673d69b53..a6e66e8f365 100644 --- a/src/Qir/Tests/build-qir-tests.ps1 +++ b/src/Qir/Tests/build-qir-tests.ps1 @@ -16,7 +16,6 @@ Write-Host "##[info]Compile Q# Test Projects into QIR" Build-QirProject (Join-Path $PSScriptRoot QIR-static qsharp) -SkipQSharpBuild:$SkipQSharpBuild Build-QirProject (Join-Path $PSScriptRoot QIR-dynamic qsharp) -SkipQSharpBuild:$SkipQSharpBuild -Build-QirProject (Join-Path $PSScriptRoot QIR-tracer qsharp) -SkipQSharpBuild:$SkipQSharpBuild Build-QirProject (Join-Path $PSScriptRoot FullstateSimulator qsharp) -SkipQSharpBuild:$SkipQSharpBuild if (-not (Build-CMakeProject $PSScriptRoot "QIR Tests")) { diff --git a/src/Qir/Tools/Driver/QirCppDriver.cs b/src/Qir/Tools/Driver/QirCppDriver.cs index dfa267f783e..ef666584511 100644 --- a/src/Qir/Tools/Driver/QirCppDriver.cs +++ b/src/Qir/Tools/Driver/QirCppDriver.cs @@ -39,16 +39,13 @@ public virtual string TransformText() #include ""CLI11.hpp"" -#include ""QirRuntime.hpp"" -#include ""QirContext.hpp"" - "); foreach (var header in RuntimeInitializer.Headers) { this.Write("#include \""); this.Write(this.ToStringHelper.ToStringWithCulture(header)); this.Write("\"\r\n"); } - this.Write("\r\nusing namespace Microsoft::Quantum;\r\nusing namespace std;\r\n"); + this.Write("\r\nusing namespace std;\r\n"); if (EntryPoint.ContainsArgumentType(DataType.ArrayType)) { this.Write(@" // Auxiliary functions for interop with Q# Array type. @@ -124,12 +121,10 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) "InteropTrueAsChar},\r\n {\"true\", InteropTrueAsChar}\r\n};\r\n"); } if (EntryPoint.ContainsArgumentType(DataType.PauliType) || EntryPoint.ContainsArrayType(DataType.PauliType)) { - this.Write("\r\n// Auxiliary functions for interop with Q# Pauli type.\r\nmap "); + this.Write("\r\n// Auxiliary functions for interop with Q# Pauli type.\r\nmap "); this.Write(this.ToStringHelper.ToStringWithCulture(QirCppInterop.CliOptionTransformerMapName(DataType.PauliType))); - this.Write("{\r\n {\"PauliI\", PauliId::PauliId_I},\r\n {\"PauliX\", PauliId::PauliId_X},\r\n " + - "{\"PauliY\", PauliId::PauliId_Y},\r\n {\"PauliZ\", PauliId::PauliId_Z}\r\n};\r\n\r\nchar " + - "TranslatePauliToChar(PauliId& pauli)\r\n{\r\n return static_cast(pauli);\r\n}" + - "\r\n"); + this.Write("{\r\n {\"PauliI\", 0},\r\n {\"PauliX\", 1},\r\n " + + "{\"PauliY\", 3},\r\n {\"PauliZ\", 2}\r\n};\r\n\r\n"); } if (EntryPoint.ContainsArgumentType(DataType.ResultType) || EntryPoint.ContainsArrayType(DataType.ResultType)) { this.Write("\r\n// Auxiliary functions for interop with Q# Result type.\r\nconst char InteropResu" + @@ -157,7 +152,7 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) this.Write("\r\n"); } this.Write("); // QIR interop function.\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n CLI::App " + - "app(\"QIR Standalone Entry Point\");\r\n\r\n // Initialize runtime.\r\n"); + "app(\"QIR Standalone Entry Point\");\r\n\r\n"); var initializerReader = new StringReader(RuntimeInitializer.Generate()); string line; while((line = initializerReader.ReadLine()) != null) { @@ -165,15 +160,6 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) this.Write(this.ToStringHelper.ToStringWithCulture(line)); this.Write("\r\n"); } - this.Write(@" - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - ""--simulation-output"", - simulationOutputFile, - ""File where the output produced during the simulation is written""); - -"); if (EntryPoint.Parameters.Count > 0) { this.Write(" // Add a command line option for each entry-point parameter.\r\n"); } @@ -275,17 +261,7 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) } this.Write("\r\n"); } - this.Write(@" // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - - // Execute the entry point operation. + this.Write(@" // Execute the entry point operation. "); this.Write(this.ToStringHelper.ToStringWithCulture(EntryPoint.Name)); this.Write("(\r\n"); @@ -297,9 +273,8 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) this.Write(this.ToStringHelper.ToStringWithCulture((isLastArg) ? "" : ",")); this.Write("\r\n"); } - this.Write(" );\r\n\r\n // Flush the output of the simulation.\r\n simulatorOutputStream->" + - "flush();\r\n if (simulationOutputFileStream.is_open())\r\n {\r\n simulati" + - "onOutputFileStream.close();\r\n }\r\n\r\n return 0;\r\n}\r\n"); + this.Write(" );\r\n\r\n // Flush the output of the simulation.\r\n cout." + + "flush();\r\n\r\n return 0;\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } } diff --git a/src/Qir/Tools/Driver/QirCppDriver.tt b/src/Qir/Tools/Driver/QirCppDriver.tt index 475a06ec943..a2d68db0877 100644 --- a/src/Qir/Tools/Driver/QirCppDriver.tt +++ b/src/Qir/Tools/Driver/QirCppDriver.tt @@ -16,14 +16,10 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - <# foreach (var header in RuntimeInitializer.Headers) { #> #include "<#= header #>" <# } #> -using namespace Microsoft::Quantum; using namespace std; <# if (EntryPoint.ContainsArgumentType(DataType.ArrayType)) { #> @@ -112,17 +108,13 @@ map <#= QirCppInterop.CliOptionTransformerMapName(DataType.BoolTyp <# if (EntryPoint.ContainsArgumentType(DataType.PauliType) || EntryPoint.ContainsArrayType(DataType.PauliType)) { #> // Auxiliary functions for interop with Q# Pauli type. -map <#= QirCppInterop.CliOptionTransformerMapName(DataType.PauliType) #>{ - {"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z} +map <#= QirCppInterop.CliOptionTransformerMapName(DataType.PauliType) #>{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} }; -char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} <# } #> <# if (EntryPoint.ContainsArgumentType(DataType.ResultType) || EntryPoint.ContainsArrayType(DataType.ResultType)) { #> @@ -157,20 +149,12 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. <# var initializerReader = new StringReader(RuntimeInitializer.Generate()); string line; while((line = initializerReader.ReadLine()) != null) { #> <#= line #> <# } #> - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - <# if (EntryPoint.Parameters.Count > 0) { #> // Add a command line option for each entry-point parameter. <# } #> @@ -218,16 +202,6 @@ int main(int argc, char* argv[]) <# } #> <# } #> - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. <#= EntryPoint.Name #>( <# for (int i = 0; i < EntryPoint.Parameters.Count; i++) { @@ -238,11 +212,7 @@ int main(int argc, char* argv[]) ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs b/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs deleted file mode 100644 index eaa6e5d4cf5..00000000000 --- a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs +++ /dev/null @@ -1,303 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version: 16.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -namespace Microsoft.Quantum.Qir.Runtime.Tools.Driver -{ - using System; - - /// - /// Class to produce the template output - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - public partial class QirCppFullStateSimulatorInitializer : QirCppFullStateSimulatorInitializerBase - { - /// - /// Create the template output - /// - public virtual string TransformText() - { - this.Write("unique_ptr sim = CreateFullstateSimulator();\r\nQirContextScope qirctx(sim.get(), "); - this.Write(this.ToStringHelper.ToStringWithCulture(this.debug ? "true" : "false")); - this.Write(" /*trackAllocatedObjects*/);"); - return this.GenerationEnvironment.ToString(); - } - } - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - public class QirCppFullStateSimulatorInitializerBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - protected System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; - } - set - { - this.generationEnvironmentField = value; - } - } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); - } - return this.errorsField; - } - } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); - } - return this.indentLengthsField; - } - } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; - } - } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; - } - } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); - } - } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; - } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); - } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); - } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); - } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; - } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; - } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; - } - set - { - if ((value != null)) - { - this.formatProviderField = value; - } - } - } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { - throw new global::System.ArgumentNullException("objectToConvert"); - } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); - } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); - } - } - } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion - } - #endregion -} diff --git a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt b/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt deleted file mode 100644 index 9b9eb0f4457..00000000000 --- a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt +++ /dev/null @@ -1,3 +0,0 @@ -<#@ template language="C#" linePragmas="false" #> -unique_ptr sim = CreateFullstateSimulator(); -QirContextScope qirctx(sim.get(), <#= this.debug ? "true" : "false" #> /*trackAllocatedObjects*/); \ No newline at end of file diff --git a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs b/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs deleted file mode 100644 index 793965e657f..00000000000 --- a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Microsoft.Quantum.Qir.Runtime.Tools.Driver -{ - public partial class QirCppFullStateSimulatorInitializer - { - private readonly bool debug; - - internal QirCppFullStateSimulatorInitializer(bool debug) => this.debug = debug; - } -} diff --git a/src/Qir/Tools/Driver/QirCppInterop.cs b/src/Qir/Tools/Driver/QirCppInterop.cs index 6d795608106..02cd0244c84 100644 --- a/src/Qir/Tools/Driver/QirCppInterop.cs +++ b/src/Qir/Tools/Driver/QirCppInterop.cs @@ -32,7 +32,7 @@ public static string CliOptionType(DataType? dataType) => DataType.BoolType => "char", DataType.IntegerType => "int64_t", DataType.DoubleType => "double_t", - DataType.PauliType => "PauliId", + DataType.PauliType => "char", DataType.RangeType => "RangeTuple", DataType.ResultType => "char", DataType.StringType => "string", @@ -46,7 +46,7 @@ public static string CliOptionType(DataType? dataType) => DataType.BoolType => null, DataType.IntegerType => null, DataType.DoubleType => null, - DataType.PauliType => "TranslatePauliToChar", + DataType.PauliType => null, DataType.RangeType => "TranslateRangeTupleToInteropRangePointer", DataType.ResultType => null, DataType.StringType => "TranslateStringToCharBuffer", @@ -60,7 +60,7 @@ public static string CliOptionType(DataType? dataType) => DataType.BoolType => "InteropFalseAsChar", DataType.IntegerType => "0", DataType.DoubleType => "0.0", - DataType.PauliType => "PauliId::PauliId_I", + DataType.PauliType => "0", DataType.RangeType => null, DataType.ResultType => "InteropResultZeroAsChar", DataType.StringType => null, diff --git a/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs b/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs index aadfa0a4577..b65500b1c03 100644 --- a/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs +++ b/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs @@ -11,8 +11,8 @@ public class QirFullStateDriverGenerator: IQirDriverGenerator { private readonly QirCppDriverGenerator DriverGenerator; - public QirFullStateDriverGenerator(bool debug) => - DriverGenerator = new QirCppDriverGenerator(new QirFullStateSimulatorInitializer(debug)); + public QirFullStateDriverGenerator() => + DriverGenerator = new QirCppDriverGenerator(new QirFullStateSimulatorInitializer()); public async Task GenerateAsync(EntryPointOperation entryPoint, Stream stream) => await DriverGenerator.GenerateAsync(entryPoint, stream); diff --git a/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs b/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs index ca5af5d863a..03256773197 100644 --- a/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs +++ b/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs @@ -7,15 +7,11 @@ namespace Microsoft.Quantum.Qir.Runtime.Tools.Driver { public class QirFullStateSimulatorInitializer : IQirRuntimeInitializer { - private readonly bool debug; + internal QirFullStateSimulatorInitializer() {} - internal QirFullStateSimulatorInitializer(bool debug) => this.debug = debug; + public string Generate() => ""; - public string Generate() => new QirCppFullStateSimulatorInitializer(this.debug).TransformText(); - - public IEnumerable Headers => new [] { - "SimFactory.hpp" - }; + public IEnumerable Headers => new string[0]; public IEnumerable LinkLibraries => new string[0]; } diff --git a/src/Qir/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Tools/Executable/QirFullStateExecutable.cs index c626427e6d4..8d161a5af38 100644 --- a/src/Qir/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Tools/Executable/QirFullStateExecutable.cs @@ -20,18 +20,17 @@ public class QirFullStateExecutable : QirExecutable { public override string DriverFileExtension => "cpp"; - public override IList LinkLibraries => new List { - "Microsoft.Quantum.Qir.Runtime", - "Microsoft.Quantum.Qir.QSharp.Foundation", - "Microsoft.Quantum.Qir.QSharp.Core" - }; + public override IList LinkLibraries => new List + { + "Microsoft.Quantum.Simulator.Runtime" + }; public override IList HeaderDirectories { get; } = new List(); public override IList LibraryDirectories { get; } = new List(); - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBitcode, bool debug, ILogger? logger = null) - : base(executableFile, qirBitcode, new QirFullStateDriverGenerator(debug), logger) + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBitcode, ILogger? logger = null) + : base(executableFile, qirBitcode, new QirFullStateDriverGenerator(), logger) { var thisModulePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrWhiteSpace(thisModulePath)) diff --git a/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj index 1893686133a..87bfebb6937 100644 --- a/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj +++ b/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj @@ -22,16 +22,6 @@ true PreserveNewest - - runtimes\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - - - runtimes\any\native\include\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - @@ -44,10 +34,6 @@ TextTemplatingFilePreprocessor QirCppDriver.cs - - TextTemplatingFilePreprocessor - QirCppFullStateSimulatorInitializer.cs - @@ -60,11 +46,6 @@ True QirCppDriver.tt - - True - True - QirCppFullStateSimulatorInitializer.tt - diff --git a/src/Qir/Tools/QirTools.cs b/src/Qir/Tools/QirTools.cs index a159d75f072..d82960fc3c7 100644 --- a/src/Qir/Tools/QirTools.cs +++ b/src/Qir/Tools/QirTools.cs @@ -23,13 +23,11 @@ public static class QirTools /// Directory where the libraries to link to are located. /// Directory where the headers needed for compilation are located. /// Directory where the created executables are placed. - /// Enable additional debugging checks at runtime. public static async Task BuildFromQSharpDll( FileInfo qsharpDll, IList libraryDirectories, IList includeDirectories, - DirectoryInfo executablesDirectory, - bool debug) + DirectoryInfo executablesDirectory) { using var qirContentStream = new MemoryStream(); if (!AssemblyLoader.LoadQirBitcode(qsharpDll, qirContentStream)) @@ -44,7 +42,7 @@ public static async Task BuildFromQSharpDll( foreach (var entryPointOp in EntryPointLoader.LoadEntryPointOperations(qsharpDll)) { var exeFileInfo = new FileInfo(Path.Combine(executablesDirectory.FullName, $"{entryPointOp.Name}.exe")); - var exe = new QirFullStateExecutable(exeFileInfo, qirContentStream.ToArray(), debug); + var exe = new QirFullStateExecutable(exeFileInfo, qirContentStream.ToArray()); await exe.BuildAsync(entryPointOp, libraryDirectories, includeDirectories); } diff --git a/src/Qir/build_all.ps1 b/src/Qir/build_all.ps1 deleted file mode 100644 index 9901b96cfef..00000000000 --- a/src/Qir/build_all.ps1 +++ /dev/null @@ -1 +0,0 @@ -& Runtime/build-qir-runtime.ps1 && & Tests/build-qir-tests.ps1 && & Samples/build-qir-samples.ps1 diff --git a/src/Qir/test_all.ps1 b/src/Qir/test_all.ps1 deleted file mode 100644 index fe2cb3d6658..00000000000 --- a/src/Qir/test_all.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -& Runtime/test-qir-runtime.ps1 -& Tests/test-qir-tests.ps1 -& Samples/test-qir-samples.ps1 diff --git a/src/README.md b/src/README.md index fa705e51899..d1a14f50c74 100644 --- a/src/README.md +++ b/src/README.md @@ -22,7 +22,6 @@ aspects of the code style. * [Clang-Tidy Checks](https://clang.llvm.org/extra/clang-tidy/checks/list.html) * Example of the checks used * [Qir/.clang-tidy](Qir/.clang-tidy) - * [Qir/Tests/.clang-tidy](Qir/Tests/.clang-tidy) * [Qir/Samples/StandaloneInputReference/.clang-tidy](Qir/Samples/StandaloneInputReference/.clang-tidy) * Use * Install: On Win and Mac is installed together with LLVM or Clang, to install on Linux search for "tidy" in @@ -53,7 +52,3 @@ Clang-format is a tool that enforces the code style. * Links to the documentation, example of the options are in [Qir/.clang-format](Qir/.clang-format). * Installation: Search for "format" in [Qir/Runtime/prerequisites.ps1](Qir/Runtime/prerequisites.ps1). * Run: [Qir/check-sources-formatted.ps1](Qir/check-sources-formatted.ps1). - -## See also - -* [Coding style and conventions](Qir/Runtime/README.md#coding-style-and-conventions) \ No newline at end of file diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index 28eac72abc0..07253c84044 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -22,6 +22,7 @@ + diff --git a/src/Simulation/Native/build-native-simulator.ps1 b/src/Simulation/Native/build-native-simulator.ps1 index 4ff668f9e71..a5bcbe2a4b9 100644 --- a/src/Simulation/Native/build-native-simulator.ps1 +++ b/src/Simulation/Native/build-native-simulator.ps1 @@ -1,6 +1,8 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +& (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) + Write-Host "##[info]Build Native simulator for $Env:BUILD_CONFIGURATION" if ($IsMacOS) { diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 5f062824764..5ede9d3af78 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -13,7 +13,7 @@ configure_file(version.hpp.in ${PROJECT_BINARY_DIR}/src/version.hpp) add_subdirectory(util) add_subdirectory(simulator) -set(SOURCES simulator/factory.cpp simulator/capi.cpp simulator/simulator.cpp util/openmp.cpp simulator/simulatoravx.cpp simulator/simulatoravx2.cpp simulator/simulatoravx512.cpp ) +set(SOURCES simulator/factory.cpp simulator/capi.cpp simulator/simulator.cpp util/openmp.cpp simulator/simulatoravx.cpp simulator/simulatoravx2.cpp simulator/simulatoravx512.cpp simulator/qir.cpp) if(BUILD_SHARED_LIBS) add_library(Microsoft.Quantum.Simulator.Runtime SHARED ${SOURCES}) set_source_files_properties(simulator/capi.cpp PROPERTIES COMPILE_FLAGS ${AVXFLAGS}) @@ -34,9 +34,46 @@ else(BUILD_SHARED_LIBS) set_source_files_properties(simulator/simulatoravx512.cpp PROPERTIES COMPILE_FLAGS ${AVX512FLAGS}) endif(BUILD_SHARED_LIBS) -target_link_libraries(Microsoft.Quantum.Simulator.Runtime ${SPECTRE_LIBS}) +target_include_directories(Microsoft.Quantum.Simulator.Runtime PRIVATE "${PROJECT_BINARY_DIR}/../../../Qir/drops/include") + +if (WIN32) + target_link_libraries(Microsoft.Quantum.Simulator.Runtime + ${SPECTRE_LIBS} + "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" + qir_stdlib + Ws2_32 + Bcrypt + Userenv + ) + # On Windows, use a .def file to force export stdlib functions from the static library. + # See https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files + target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") +elseif (APPLE) + # On MacOS, use -force_load and explicit path to pull in entire contents of stdlib static archive. + # See https://stackoverflow.com/questions/16926608/including-static-libraries-with-all-load-flag + # and https://github.com/bioinformatics-centre/kaiju/issues/30 + target_link_libraries(Microsoft.Quantum.Simulator.Runtime + "-Wl,-force_load,${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native/libqir_stdlib.a" + pthread + dl + ) +else() + # On Unix systems, use --whole-archive to to pull in entire contents of stdlib static archive. + # Note that this must be turned off with --no-whole-archive to prevent the setting from interfering with + # normal loading of other dependencies. + # See https://www.man7.org/linux/man-pages/man1/ld.1.html + target_link_libraries(Microsoft.Quantum.Simulator.Runtime + "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" + "-Wl,--whole-archive" + qir_stdlib + "-Wl,--no-whole-archive" + pthread + dl + ) +endif() install(TARGETS Microsoft.Quantum.Simulator.Runtime RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/drop" LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/drop" + ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/drop" ) diff --git a/src/Simulation/Native/src/simulator/capi.cpp b/src/Simulation/Native/src/simulator/capi.cpp index 50c3d7ffd42..48e7d491f8e 100644 --- a/src/Simulation/Native/src/simulator/capi.cpp +++ b/src/Simulation/Native/src/simulator/capi.cpp @@ -64,6 +64,11 @@ extern "C" return Microsoft::Quantum::Simulator::get(sid)->InjectState(qubits, amplitudes); } + MICROSOFT_QUANTUM_DECL unsigned allocate(unsigned id) + { + return Microsoft::Quantum::Simulator::get(id)->allocate(); + } + MICROSOFT_QUANTUM_DECL void allocateQubit(unsigned id, unsigned q) { Microsoft::Quantum::Simulator::get(id)->allocateQubit(q); diff --git a/src/Simulation/Native/src/simulator/capi.hpp b/src/Simulation/Native/src/simulator/capi.hpp index c033060dc19..22a86bd4e99 100644 --- a/src/Simulation/Native/src/simulator/capi.hpp +++ b/src/Simulation/Native/src/simulator/capi.hpp @@ -61,6 +61,7 @@ extern "C" ); // allocate and release + MICROSOFT_QUANTUM_DECL unsigned allocate(unsigned sid); // NOLINT MICROSOFT_QUANTUM_DECL void allocateQubit(unsigned sid, unsigned qid); // NOLINT MICROSOFT_QUANTUM_DECL bool release(unsigned sid, unsigned q); // NOLINT MICROSOFT_QUANTUM_DECL unsigned num_qubits(unsigned sid); // NOLINT diff --git a/src/Simulation/Native/src/simulator/qir.cpp b/src/Simulation/Native/src/simulator/qir.cpp new file mode 100644 index 00000000000..41ebe2531d1 --- /dev/null +++ b/src/Simulation/Native/src/simulator/qir.cpp @@ -0,0 +1,499 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +#include "capi.hpp" +#include "qir.hpp" + +unsigned SimId() +{ + // Using `thread_local` ensures each thread gets a unique simulator instance. This matches + // expected QIR usage where multithreaded program execution is not supported. + thread_local unsigned _sim = init(); + return _sim; +} + +// Pauli consts are {i2} in QIR, likely stored as {i8} in arrays, but we are using the standard C++ enum type based on +// {i32} so cannot pass through the buffer and have to allocate a new one instead and copy. +std::vector ExtractPauliIds(QirArray* paulis) +{ + const auto count = __quantum__rt__array_get_size_1d(paulis); + std::vector pauliIds; + pauliIds.reserve(count); + for (auto i = 0; i < count; i++) + { + pauliIds.push_back(static_cast(*__quantum__rt__array_get_element_ptr_1d(paulis, i))); + } + return pauliIds; +} + +std::vector GetQubitIds(long num, QubitIdType* qubits) +{ + std::vector ids; + ids.reserve((size_t)num); + for (long i = 0; i < num; i++) + { + ids.push_back(static_cast(qubits[i])); + } + return ids; +} + +std::vector GetBases(long num, PauliId* paulis) +{ + std::vector convertedBases; + convertedBases.reserve((size_t)num); + for (auto i = 0; i < num; i++) + { + convertedBases.push_back(static_cast(paulis[i])); + } + return convertedBases; +} + +// Comparing floating point values with `==` or `!=` is not reliable. +// The values can be extremely close but still not exactly equal. +// It is more reliable to check if one value is within certain small tolerance near the other value. +// This template function is for comparing two floating point values. +template +inline bool Close(TFloat val1, TFloat val2) +{ + assert( + std::is_floating_point_v> && + "Unexpected type is passed as a template argument"); + + constexpr TFloat tolerance = 1e-10; + + // Both val1 and val2 can be close (or equal) to the maximum (or minimum) value representable with its type. + // Adding to (or subtracting from) such a value can cause overflow (or underflow). + // To avoid the overflow (or underflow) we don't add to the greater value (and don't sutract from a lesser value). + if (val1 < val2) + { + return (val2 - val1) <= tolerance; + } + return (val1 - val2) <= tolerance; +} + +std::ostream& GetOutStream(void* location, std::ofstream& outFileStream) +{ + // If the location is not nullptr and not empty string then dump to a file: + if ((location != nullptr) && ((__quantum__rt__string_get_length(static_cast(location))) != 0)) + { + // Open the file for appending: + const std::string& filePath = __quantum__rt__string_get_data(static_cast(location)); + + bool openException = false; + try + { + outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); + } + catch (const std::ofstream::failure& e) + { + openException = true; + std::cerr << "Exception caught: \"" << e.what() << "\".\n"; + } + + if (((outFileStream.rdstate() & std::ofstream::failbit) != 0) || openException) + { + std::cerr << "Failed to open dump file \"" + filePath + "\".\n"; + return std::cout; + } + + return outFileStream; + } + + // Otherwise dump to std::cout: + return std::cout; +} + +TDumpToLocationCallback const dumpToLocationCallback = + [](size_t idx, double re, double im, TDumpLocation location) -> bool +{ + std::ostream& outStream = *reinterpret_cast(location); + + if (!Close(re, 0.0) || !Close(im, 0.0)) + { + outStream << "|" << std::bitset<8>(idx) << ">: " << re << "+" << im << "i" << std::endl; + } + return true; +}; + +void DumpMachineImpl(std::ostream& outStream) +{ + outStream << "# wave function for qubits (least to most significant qubit ids):" << std::endl; + DumpToLocation(SimId(), dumpToLocationCallback, (TDumpLocation)&outStream); + outStream.flush(); +} + +void DumpRegisterImpl(std::ostream& outStream, QirArray* qubits) +{ + outStream << "# wave function for qubits with ids (least to most significant): "; + + auto count = __quantum__rt__array_get_size_1d(qubits); + std::vector ids = + GetQubitIds(count, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); + for (auto idx = 0; idx < ids.size(); ++idx) + { + if (idx != 0) + { + outStream << "; "; + } + outStream << ids[idx]; + } + outStream << ':' << std::endl; + + if (!DumpQubitsToLocation(SimId(), ids.size(), ids.data(), dumpToLocationCallback, (TDumpLocation)&outStream)) + { + outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" + << std::endl; + } + outStream.flush(); +} + +bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) +{ + assert( + rs1 != nullptr && rs2 != nullptr && + __quantum__rt__array_get_size_1d(rs1) == __quantum__rt__array_get_size_1d(rs2)); + + RESULT** results1 = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(rs1, 0)); + RESULT** results2 = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(rs2, 0)); + for (auto i = 0; i < __quantum__rt__array_get_size_1d(rs1); i++) + { + if (!__quantum__rt__result_equal(results1[i], results2[i])) + { + return false; + } + } + return true; +} + +extern "C" +{ + QUBIT* __quantum__rt__qubit_allocate() + { + return reinterpret_cast(allocate(SimId())); + } + + QirArray* __quantum__rt__qubit_allocate_array(int64_t count) + { + QirArray* array = __quantum__rt__array_create_1d(sizeof(intptr_t), count); + for (auto i = 0; i < count; i++) + { + *reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(array, i)) = + __quantum__rt__qubit_allocate(); + } + return array; + } + + void __quantum__rt__qubit_release(QUBIT* qubit) + { + release(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__rt__qubit_release_array(QirArray* array) + { + auto count = __quantum__rt__array_get_size_1d(array); + for (auto i = 0; i < count; i++) + { + __quantum__rt__qubit_release(*reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(array, i))); + } + __quantum__rt__array_update_reference_count(array, -1); + } + + QirString* __quantum__rt__qubit_to_string(QUBIT* qubit) + { + return __quantum__rt__string_create(std::to_string(reinterpret_cast(qubit)).c_str()); + } + + RESULT* __quantum__rt__result_get_one() + { + return reinterpret_cast(1); + } + + RESULT* __quantum__rt__result_get_zero() + { + return reinterpret_cast(0); + } + + bool __quantum__rt__result_equal(RESULT* r1, RESULT* r2) + { + return r1 == r2; + } + + void __quantum__rt__result_update_reference_count(RESULT* r, int32_t increment) + { + // No-op + } + + QirString* __quantum__rt__result_to_string(RESULT* result) + { + return __quantum__rt__result_equal(result, __quantum__rt__result_get_zero()) + ? __quantum__rt__string_create("Zero") + : __quantum__rt__string_create("One"); + } + + void __quantum__rt__result_record_output(RESULT* res) + { + if (__quantum__rt__result_equal(res, __quantum__rt__result_get_zero())) + { + __quantum__rt__message(__quantum__rt__string_create("RESULT\t0\n")); + } + else + { + __quantum__rt__message(__quantum__rt__string_create("RESULT\t1\n")); + } + } + + void __quantum__qis__exp__body(QirArray* paulis, double angle, QirArray* qubits) + { + assert(__quantum__rt__array_get_size_1d(paulis) == __quantum__rt__array_get_size_1d(qubits)); + std::vector pauliIds = ExtractPauliIds(paulis); + auto numTargets = pauliIds.size(); + auto targets = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0)); + std::vector ids = GetQubitIds(numTargets, targets); + std::vector convertedBases = GetBases(numTargets, pauliIds.data()); + Exp(SimId(), (unsigned)numTargets, convertedBases.data(), angle, ids.data()); + } + + void __quantum__qis__exp__adj(QirArray* paulis, double angle, QirArray* qubits) + { + __quantum__qis__exp__body(paulis, -angle, qubits); + } + + void __quantum__qis__exp__ctl(QirArray* ctls, QirExpTuple* args) + { + assert(__quantum__rt__array_get_size_1d(args->paulis) == __quantum__rt__array_get_size_1d(args->targets)); + + std::vector pauliIds = ExtractPauliIds(args->paulis); + auto numControls = __quantum__rt__array_get_size_1d(ctls); + auto numTargets = pauliIds.size(); + auto controls = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0)); + auto targets = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(args->targets, 0)); + std::vector idsTargets = GetQubitIds(numTargets, targets); + std::vector idsControls = GetQubitIds(numControls, controls); + std::vector convertedBases = GetBases(numTargets, pauliIds.data()); + MCExp( + SimId(), (unsigned)numTargets, convertedBases.data(), args->angle, (unsigned)numControls, idsControls.data(), + idsTargets.data()); + } + + void __quantum__qis__exp__ctladj(QirArray* ctls, QirExpTuple* args) + { + assert(__quantum__rt__array_get_size_1d(args->paulis) == __quantum__rt__array_get_size_1d(args->targets)); + + QirExpTuple updatedArgs = {args->paulis, -(args->angle), args->targets}; + + __quantum__qis__exp__ctl(ctls, &updatedArgs); + } + + void __quantum__qis__h__body(QUBIT* qubit) + { + H(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__h__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCH(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + RESULT* __quantum__qis__measure__body(QirArray* paulis, QirArray* qubits) + { + assert(__quantum__rt__array_get_size_1d(qubits) == __quantum__rt__array_get_size_1d(paulis)); + + std::vector pauliIds = ExtractPauliIds(paulis); + auto count = pauliIds.size(); + std::vector convertedBases = GetBases(count, pauliIds.data()); + std::vector ids = + GetQubitIds(count, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); + return Measure(SimId(), count, convertedBases.data(), ids.data()) ? __quantum__rt__result_get_one() + : __quantum__rt__result_get_zero(); + } + + void __quantum__qis__r__body(PauliId axis, double angle, QUBIT* qubit) + { + R(SimId(), static_cast(axis), angle, reinterpret_cast(qubit)); + } + + void __quantum__qis__r__adj(PauliId axis, double angle, QUBIT* qubit) + { + __quantum__qis__r__body(axis, -angle, qubit); + } + + void __quantum__qis__r__ctl(QirArray* ctls, QirRTuple* args) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector controls = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCR(SimId(), static_cast(args->pauli), args->angle, numControls, controls.data(), + reinterpret_cast(args->target)); + } + + void __quantum__qis__r__ctladj(QirArray* ctls, QirRTuple* args) + { + QirRTuple updated = {args->pauli, -args->angle, args->target}; + __quantum__qis__r__ctl(ctls, &updated); + } + + void __quantum__qis__s__body(QUBIT* qubit) + { + S(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__s__adj(QUBIT* qubit) + { + AdjS(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__s__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCS(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__s__ctladj(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCAdjS(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__t__body(QUBIT* qubit) + { + T(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__t__adj(QUBIT* qubit) + { + AdjT(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__t__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCT(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__t__ctladj(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCAdjT(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__x__body(QUBIT* qubit) + { + X(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__x__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCX(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__y__body(QUBIT* qubit) + { + Y(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__y__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCY(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__z__body(QUBIT* qubit) + { + Z(SimId(), reinterpret_cast(qubit)); + } + + void __quantum__qis__z__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCZ(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__dumpmachine__body(uint8_t* location) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpMachineImpl(outStream); + } + + void __quantum__qis__dumpregister__body(uint8_t* location, QirArray* qubits) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpRegisterImpl(outStream, qubits); + } + + void __quantum__qis__assertmeasurementprobability__body( + QirArray* bases, + QirArray* qubits, + RESULT* result, + double prob, + QirString* msg, + double tol) + { + if (__quantum__rt__array_get_size_1d(bases) != __quantum__rt__array_get_size_1d(qubits)) + { + __quantum__rt__fail( + __quantum__rt__string_create("Both input arrays - bases, qubits - for AssertMeasurementProbability(), " + "must be of same size.")); + } + + if (__quantum__rt__result_equal(result, __quantum__rt__result_get_one())) + { + prob = 1.0 - prob; + } + + // Convert paulis from sequence of bytes to sequence of PauliId: + std::vector paulis((uint64_t)__quantum__rt__array_get_size_1d(bases)); + for (auto i = 0; i < __quantum__rt__array_get_size_1d(bases); ++i) + { + paulis[i] = (PauliId)*__quantum__rt__array_get_element_ptr_1d(bases, i); + } + + std::vector ids = GetQubitIds( + paulis.size(), reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); + std::vector convertedBases = GetBases(paulis.size(), paulis.data()); + double actualProbability = + 1.0 - JointEnsembleProbability( + SimId(), (unsigned)paulis.size(), reinterpret_cast(convertedBases.data()), ids.data()); + + if (!(std::abs(actualProbability - prob) < 1e-10)) + { + __quantum__rt__fail(msg); + } + } + + void __quantum__qis__assertmeasurementprobability__ctl( + QirArray* /* ctls */, + QirAssertMeasurementProbabilityTuple* args) + { + // Controlled AssertMeasurementProbability ignores control bits. See the discussion on + // https://github.com/microsoft/qsharp-runtime/pull/450 for more details. + __quantum__qis__assertmeasurementprobability__body( + args->bases, args->qubits, args->result, args->prob, args->msg, args->tol); + } +} diff --git a/src/Simulation/Native/src/simulator/qir.hpp b/src/Simulation/Native/src/simulator/qir.hpp new file mode 100644 index 00000000000..c40eb8d3f99 --- /dev/null +++ b/src/Simulation/Native/src/simulator/qir.hpp @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "qir_stdlib.h" +#include + +#ifdef _WIN32 +#define QIR_EXPORT_API __declspec(dllexport) +#else +#define QIR_EXPORT_API +#endif + +/*============================================================================== + Qubit & Result + + These two types are opaque to the clients: clients cannot directly create, delete, + copy or check state of qubits and results. QUBIT* and RESULT* should never be + dereferenced in client's code. +==============================================================================*/ + +// Although QIR uses an opaque pointer to the type "QUBIT", it never points to an actual memory +// and is never intended to be dereferenced anywhere - in the client code or in our runtime. +// Runtime always operates in terms of qubit ids, which are integers. Qubit ids are casted +// to this pointer type and stored as pointer values. This is done to ensure that qubit type +// is a unique type in the QIR. + +class QUBIT; +typedef intptr_t QubitIdType; + +class RESULT; + +extern "C" +{ + // This struct matches the argument tuple for a controlled R specialization in Q#. It must + // be kept in sync with src\Simulation\TargetDefinitions\Intrinsic\R.qs and Q# compiler behavior. + typedef struct + { + PauliId pauli; + double angle; + QUBIT* target; + } QirRTuple; + + // This struct matches the argument tuple for a controlled Exp specialization in Q#. It must + // be kept in sync with src\Simulation\TargetDefinitions\Intrinsic\Exp.qs and Q# compiler behavior. + typedef struct + { + QirArray* paulis; + double angle; + QirArray* targets; + } QirExpTuple; + + // This struct matches the argument tuple for a controlled AsssertMeasurementProbability specialization in Q#. It must + // be kept in sync with src\Simulation\QSharpFoundation\Diagnostics\Assert.qs and Q# compiler behavior. + typedef struct + { + QirArray* bases; + QirArray* qubits; + RESULT* result; + double prob; + QirString* msg; + double tol; + } QirAssertMeasurementProbabilityTuple; + + // Quantum Runtime + QIR_EXPORT_API QUBIT* __quantum__rt__qubit_allocate(); // NOLINT + QIR_EXPORT_API QirArray* __quantum__rt__qubit_allocate_array(int64_t count); // NOLINT + QIR_EXPORT_API void __quantum__rt__qubit_release(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__rt__qubit_release_array(QirArray*); // NOLINT + QIR_EXPORT_API QirString* __quantum__rt__qubit_to_string(QUBIT*); // NOLINT + QIR_EXPORT_API bool __quantum__rt__result_equal(RESULT*, RESULT*); // NOLINT + QIR_EXPORT_API void __quantum__rt__result_update_reference_count(RESULT*, int32_t); // NOLINT + QIR_EXPORT_API RESULT* __quantum__rt__result_get_one(); // NOLINT + QIR_EXPORT_API RESULT* __quantum__rt__result_get_zero(); // NOLINT + QIR_EXPORT_API QirString* __quantum__rt__result_to_string(RESULT*); // NOLINT + QIR_EXPORT_API void __quantum__rt__result_record_output(RESULT*); // NOLINT + + // Q# Gate Set + QIR_EXPORT_API void __quantum__qis__exp__body(QirArray*, double, QirArray*); // NOLINT + QIR_EXPORT_API void __quantum__qis__exp__adj(QirArray*, double, QirArray*); // NOLINT + QIR_EXPORT_API void __quantum__qis__exp__ctl(QirArray*, QirExpTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__exp__ctladj(QirArray*, QirExpTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__h__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__h__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API RESULT* __quantum__qis__measure__body(QirArray*, QirArray*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__body(PauliId, double, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__adj(PauliId, double, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__ctl(QirArray*, QirRTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__ctladj(QirArray*, QirRTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__adj(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__ctladj(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__adj(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__ctladj(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__x__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__x__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__y__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__y__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__z__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__z__ctl(QirArray*, QUBIT*); // NOLINT + + // Q# Dump: + // Note: The param `location` must be `const void*`, + // but it is called from .ll, where `const void*` is not supported. + QIR_EXPORT_API void __quantum__qis__dumpmachine__body(uint8_t* location); // NOLINT + QIR_EXPORT_API void __quantum__qis__dumpregister__body(uint8_t* location, QirArray* qubits); // NOLINT + + // Q# Assert Measurement: + QIR_EXPORT_API void __quantum__qis__assertmeasurementprobability__body( // NOLINT + QirArray* bases, + QirArray* qubits, + RESULT* result, + double prob, + QirString* msg, + double tol); + QIR_EXPORT_API void __quantum__qis__assertmeasurementprobability__ctl( // NOLINT + QirArray* ctls, + QirAssertMeasurementProbabilityTuple* args); +} diff --git a/src/Simulation/Native/src/simulator/simulatorinterface.hpp b/src/Simulation/Native/src/simulator/simulatorinterface.hpp index 0d075951c68..0c9e239be37 100644 --- a/src/Simulation/Native/src/simulator/simulatorinterface.hpp +++ b/src/Simulation/Native/src/simulator/simulatorinterface.hpp @@ -36,6 +36,7 @@ class SimulatorInterface const std::vector& amplitudes) = 0; // allocate and release + virtual unsigned allocate() = 0; virtual void allocateQubit(unsigned q) = 0; virtual bool release(unsigned q) = 0; virtual unsigned num_qubits() const = 0; 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 303e3e30fe7..5901bb1c913 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -14,7 +14,7 @@ Push-Location $PSScriptRoot # 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 + cargo clippy --all-targets -- -D warnings $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); diff --git a/src/Simulation/qdk_sim_rs/prerequisites.ps1 b/src/Simulation/qdk_sim_rs/prerequisites.ps1 index 5ad244230c4..c8a597edf2c 100644 --- a/src/Simulation/qdk_sim_rs/prerequisites.ps1 +++ b/src/Simulation/qdk_sim_rs/prerequisites.ps1 @@ -22,5 +22,5 @@ if (-not (Get-Command rustup -ErrorAction SilentlyContinue)) { # rustfmt and clippy is available. rustup install nightly rustup toolchain install nightly -rustup component add rustfmt clippy -rustup component add rustfmt clippy --toolchain nightly +rustup component add rustfmt clippy llvm-tools-preview +rustup component add rustfmt clippy llvm-tools-preview --toolchain nightly diff --git a/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs b/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs index 5049d78d51b..5b34f06a724 100644 --- a/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs +++ b/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs @@ -118,12 +118,14 @@ mod tests { use cauchy::c64; use super::{EigenvalueDecomposition, ExplicitEigenvalueDecomposition}; + use core::f64::consts::FRAC_1_SQRT_2; #[test] pub fn ident_applies_to_eig_x() -> Result<(), Box> { let eigs: ExplicitEigenvalueDecomposition = ExplicitEigenvalueDecomposition { values: array![c64!(-1.0), c64!(1.0)], - vectors: c64!(0.70710678) * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], + vectors: c64::new(FRAC_1_SQRT_2, 0.0) + * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], }; let actual = eigs.apply_mtx_fn(|x| *x)?; let expected = array![[c64!(0.0), c64!(1.0)], [c64!(1.0), c64!(0.0),],]; @@ -137,7 +139,8 @@ mod tests { pub fn mtx_fn_applies_to_eig_x() -> Result<(), Box> { let eigs: ExplicitEigenvalueDecomposition = ExplicitEigenvalueDecomposition { values: array![c64!(-1.0), c64!(1.0)], - vectors: c64!(0.70710678) * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], + vectors: c64::new(FRAC_1_SQRT_2, 0.0) + * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], }; let actual = eigs.apply_mtx_fn(|x| (c64!(-1.0 i) * x).exp())?; let expected = array![ diff --git a/src/Simulation/qdk_sim_rs/src/math/sp_func.rs b/src/Simulation/qdk_sim_rs/src/math/sp_func.rs index 9a6b19ce26f..815db2e2111 100644 --- a/src/Simulation/qdk_sim_rs/src/math/sp_func.rs +++ b/src/Simulation/qdk_sim_rs/src/math/sp_func.rs @@ -55,7 +55,7 @@ mod tests { // We expect approximate_factorial(123.0) to return 123! ≈ 1.214 × 10²⁰⁵. // https://www.wolframalpha.com/input?i=123%21 - let expected = 1.214630436702533e+205; + let expected = 1.214630436702533e205; assert_abs_diff_eq!( approximate_factorial::(123.0), expected, diff --git a/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs b/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs index 4cd5c1d90a6..f8e8a6cd5cc 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs @@ -144,6 +144,7 @@ impl From for GeneratorCoset { #[cfg(test)] mod tests { use approx::assert_abs_diff_eq; + use core::f64::consts::FRAC_1_SQRT_2; use crate::{c64, ProcessData, VariantName}; @@ -254,8 +255,18 @@ mod tests { vectors: array![ [c64!(0.0), c64!(1.0), c64!(0.0), c64!(0.0)], [c64!(0.0), c64!(0.0), c64!(1.0), c64!(0.0)], - [c64!(-0.70710678), c64!(0.0), c64!(0.0), c64!(0.70710678)], - [c64!(0.70710678), c64!(0.0), c64!(0.0), c64!(0.70710678)], + [ + c64::new(-FRAC_1_SQRT_2, 0.0), + c64!(0.0), + c64!(0.0), + c64::new(FRAC_1_SQRT_2, 0.0) + ], + [ + c64::new(FRAC_1_SQRT_2, 0.0), + c64!(0.0), + c64!(0.0), + c64::new(FRAC_1_SQRT_2, 0.0) + ], ], }, } @@ -312,7 +323,12 @@ mod tests { vectors: array![ [c64!(0.0), c64!(1.0), c64!(0.0), c64!(0.0)], [c64!(0.0), c64!(0.0), c64!(1.0), c64!(0.0)], - [c64!(-0.70710678), c64!(0.0), c64!(0.0), c64!(0.70710678)], + [ + c64::new(-FRAC_1_SQRT_2, 0.0), + c64!(0.0), + c64!(0.0), + c64::new(FRAC_1_SQRT_2, 0.0) + ], ], }, } diff --git a/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs index 6253b47f2c5..78162d7d4bd 100644 --- a/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs +++ b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs @@ -10,7 +10,7 @@ static IDEAL_NOISE_MODEL_JSON: &str = include_str!("data/ideal-noise-model.json" 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(); + serde_json::from_str(&(serde_json::to_string(&noise_model).unwrap())).unwrap(); assert_json_eq!(noise_model, expected); }