From 21f5fab97682d9bc0ae8e3b58f679fdb952f1c24 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 2 Apr 2021 14:17:44 -0700 Subject: [PATCH 1/7] Separate QIR Runtime from Tests and Samples (#598) This change separates the QIR Runtime (production code) from the tests and samples. Since the Runtime has no Q# dependencies, we can now pull it into the native build step of the e2e pipeline to better produce cross-platform binaries from the builds. --- .gitignore | 1 - Simulation.sln | 209 +- bootstrap.ps1 | 8 - build/build.ps1 | 21 +- build/manifest.ps1 | 9 +- build/test.ps1 | 13 +- src/Qir/{Runtime => }/.clang-format | 74 +- src/Qir/{Runtime => }/.clang-tidy | 58 +- src/Qir/.gitignore | 2 + .../test => Common/Include}/SimulatorStub.hpp | 276 +-- .../Include}/qsharp__foundation_internal.hpp | 10 +- src/Qir/Common/cmake/qir_cmake_include.cmake | 41 + .../cmake/unit_test_include.cmake | 9 +- .../externals/CLI11/CLI11.hpp | 0 .../externals/catch2/catch.hpp | 0 .../externals/cgmanifest.json | 58 +- .../{Runtime => Common}/externals/readme.md | 18 +- src/Qir/Runtime/.gitignore | 10 - src/Qir/Runtime/CMakeLists.txt | 18 +- src/Qir/Runtime/README.md | 6 +- src/Qir/Runtime/build-qir-runtime.ps1 | 114 +- src/Qir/Runtime/cmake/qir_cmake_include.cmake | 68 - src/Qir/Runtime/lib/QIR/CMakeLists.txt | 13 +- src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 14 +- .../lib/QSharpFoundation/CMakeLists.txt | 24 +- .../QSharpFoundation/qsharp-foundation-qis.ll | 2 +- src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 9 - src/Qir/Runtime/lib/Tracer/CMakeLists.txt | 11 +- src/Qir/Runtime/samples/CMakeLists.txt | 1 - src/Qir/Runtime/test-qir-runtime.ps1 | 31 +- src/Qir/Runtime/test/CMakeLists.txt | 7 - .../test/FullstateSimulator/CMakeLists.txt | 28 - .../Runtime/test/QIR-static/CMakeLists.txt | 49 - src/Qir/Runtime/test/unittests/CMakeLists.txt | 29 - src/Qir/Runtime/unittests/CMakeLists.txt | 30 + .../{test => }/unittests/QirRuntimeTests.cpp | 2190 ++++++++--------- .../{test => }/unittests/ToffoliTests.cpp | 258 +- .../{test => }/unittests/TracerTests.cpp | 966 ++++---- .../Runtime/{test => }/unittests/driver.cpp | 16 +- src/Qir/Samples/CMakeLists.txt | 37 + .../StandaloneInputReference/.clang-tidy | 0 .../StandaloneInputReference/CMakeLists.txt | 13 +- .../StandaloneInputReference/qir-driver.cpp | 0 .../qir-standalone-input-reference.csproj | 2 +- .../qsharp/qir-standalone-input-reference.qs | 0 .../StandaloneInputReference/readme.md | 0 src/Qir/Samples/build-qir-samples.ps1 | 18 + src/Qir/Samples/test-qir-samples.ps1 | 8 + src/Qir/Tests/CMakeLists.txt | 41 + .../Tests/FullstateSimulator/CMakeLists.txt | 20 + .../FullstateSimulatorTests.cpp | 724 +++--- .../qsharp/qir-test-simulator.csproj | 2 +- .../qsharp/qir-test-simulator.qs | 0 .../test => Tests}/QIR-dynamic/CMakeLists.txt | 67 +- .../test => Tests}/QIR-dynamic/qir-driver.cpp | 72 +- .../QIR-dynamic/qsharp/qir-test-random.csproj | 2 +- .../QIR-dynamic/qsharp/qir-test-random.qs | 0 src/Qir/Tests/QIR-static/CMakeLists.txt | 38 + .../test => Tests}/QIR-static/qir-driver.cpp | 670 ++--- .../QIR-static/qir-test-conditionals.cpp | 0 .../QIR-static/qir-test-math.cpp | 0 .../QIR-static/qir-test-noqsharp.ll | 98 +- .../QIR-static/qir-test-other.cpp | 0 .../QIR-static/qir-test-ouput.cpp | 0 .../QIR-static/qir-test-strings.cpp | 0 .../test => Tests}/QIR-static/qsharp/Math.qs | 68 +- .../QIR-static/qsharp/qir-gen.csproj | 28 +- .../QIR-static/qsharp/qir-test-arrays.qs | 0 .../qsharp/qir-test-conditionals.qs | 124 +- .../QIR-static/qsharp/qir-test-functors.qs | 210 +- .../QIR-static/qsharp/qir-test-math.qs | 0 .../QIR-static/qsharp/qir-test-other.qs | 0 .../QIR-static/qsharp/qir-test-output.qs | 0 .../QIR-static/qsharp/qir-test-partials.qs | 26 +- .../qsharp/qir-test-qubits-results.qs | 34 +- .../QIR-static/qsharp/qir-test-strings.qs | 0 .../test => Tests}/QIR-tracer/CMakeLists.txt | 66 +- .../test => Tests}/QIR-tracer/generate.py | 92 +- .../QIR-tracer/qir-tracer-driver.cpp | 96 +- .../QIR-tracer/qsharp/tracer-conditionals.qs | 0 .../QIR-tracer/qsharp/tracer-core.qs | 0 .../QIR-tracer/qsharp/tracer-intrinsics.qs | 0 .../QIR-tracer/qsharp/tracer-qir.csproj | 0 .../QIR-tracer/qsharp/tracer-target.qs | 0 .../QIR-tracer/tracer-config.cpp | 36 +- .../QIR-tracer/tracer-config.hpp | 40 +- src/Qir/Tests/build-qir-tests.ps1 | 21 + src/Qir/Tests/test-qir-tests.ps1 | 8 + src/Qir/qir-utils.ps1 | 132 + 89 files changed, 3766 insertions(+), 3728 deletions(-) rename src/Qir/{Runtime => }/.clang-format (96%) rename src/Qir/{Runtime => }/.clang-tidy (97%) create mode 100644 src/Qir/.gitignore rename src/Qir/{Runtime/test => Common/Include}/SimulatorStub.hpp (96%) rename src/Qir/{Runtime/lib/QSharpFoundation => Common/Include}/qsharp__foundation_internal.hpp (61%) create mode 100644 src/Qir/Common/cmake/qir_cmake_include.cmake rename src/Qir/{Runtime => Common}/cmake/unit_test_include.cmake (63%) rename src/Qir/{Runtime => Common}/externals/CLI11/CLI11.hpp (100%) rename src/Qir/{Runtime => Common}/externals/catch2/catch.hpp (100%) rename src/Qir/{Runtime => Common}/externals/cgmanifest.json (96%) rename src/Qir/{Runtime => Common}/externals/readme.md (97%) delete mode 100644 src/Qir/Runtime/.gitignore delete mode 100644 src/Qir/Runtime/cmake/qir_cmake_include.cmake delete mode 100644 src/Qir/Runtime/samples/CMakeLists.txt delete mode 100644 src/Qir/Runtime/test/CMakeLists.txt delete mode 100644 src/Qir/Runtime/test/FullstateSimulator/CMakeLists.txt delete mode 100644 src/Qir/Runtime/test/QIR-static/CMakeLists.txt delete mode 100644 src/Qir/Runtime/test/unittests/CMakeLists.txt create mode 100644 src/Qir/Runtime/unittests/CMakeLists.txt rename src/Qir/Runtime/{test => }/unittests/QirRuntimeTests.cpp (97%) rename src/Qir/Runtime/{test => }/unittests/ToffoliTests.cpp (96%) rename src/Qir/Runtime/{test => }/unittests/TracerTests.cpp (97%) rename src/Qir/Runtime/{test => }/unittests/driver.cpp (97%) create mode 100644 src/Qir/Samples/CMakeLists.txt rename src/Qir/{Runtime/samples => Samples}/StandaloneInputReference/.clang-tidy (100%) rename src/Qir/{Runtime/samples => Samples}/StandaloneInputReference/CMakeLists.txt (80%) rename src/Qir/{Runtime/samples => Samples}/StandaloneInputReference/qir-driver.cpp (100%) rename src/Qir/{Runtime/samples => Samples}/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj (70%) rename src/Qir/{Runtime/samples => Samples}/StandaloneInputReference/qsharp/qir-standalone-input-reference.qs (100%) rename src/Qir/{Runtime/samples => Samples}/StandaloneInputReference/readme.md (100%) create mode 100644 src/Qir/Samples/build-qir-samples.ps1 create mode 100644 src/Qir/Samples/test-qir-samples.ps1 create mode 100644 src/Qir/Tests/CMakeLists.txt create mode 100644 src/Qir/Tests/FullstateSimulator/CMakeLists.txt rename src/Qir/{Runtime/test => Tests}/FullstateSimulator/FullstateSimulatorTests.cpp (96%) rename src/Qir/{Runtime/test => Tests}/FullstateSimulator/qsharp/qir-test-simulator.csproj (75%) rename src/Qir/{Runtime/test => Tests}/FullstateSimulator/qsharp/qir-test-simulator.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-dynamic/CMakeLists.txt (63%) rename src/Qir/{Runtime/test => Tests}/QIR-dynamic/qir-driver.cpp (97%) rename src/Qir/{Runtime/test => Tests}/QIR-dynamic/qsharp/qir-test-random.csproj (75%) rename src/Qir/{Runtime/test => Tests}/QIR-dynamic/qsharp/qir-test-random.qs (100%) create mode 100644 src/Qir/Tests/QIR-static/CMakeLists.txt rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-driver.cpp (97%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-test-conditionals.cpp (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-test-math.cpp (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-test-noqsharp.ll (97%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-test-other.cpp (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-test-ouput.cpp (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qir-test-strings.cpp (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/Math.qs (94%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-gen.csproj (72%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-arrays.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-conditionals.qs (97%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-functors.qs (96%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-math.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-other.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-output.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-partials.qs (96%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-qubits-results.qs (97%) rename src/Qir/{Runtime/test => Tests}/QIR-static/qsharp/qir-test-strings.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/CMakeLists.txt (53%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/generate.py (97%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/qir-tracer-driver.cpp (97%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/qsharp/tracer-conditionals.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/qsharp/tracer-core.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/qsharp/tracer-intrinsics.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/qsharp/tracer-qir.csproj (100%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/qsharp/tracer-target.qs (100%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/tracer-config.cpp (97%) rename src/Qir/{Runtime/test => Tests}/QIR-tracer/tracer-config.hpp (96%) create mode 100644 src/Qir/Tests/build-qir-tests.ps1 create mode 100644 src/Qir/Tests/test-qir-tests.ps1 create mode 100644 src/Qir/qir-utils.ps1 diff --git a/.gitignore b/.gitignore index 27c0297d074..5b33b48524e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ bld/ [Ll]og/ [Dd]rops/ [Dd]ocs/ -**/qir-gen.ll # Visual Studio 2015/2017 cache/options directory .vs/ diff --git a/Simulation.sln b/Simulation.sln index da0f6964563..9a65f34a9db 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -85,23 +85,33 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Quantum.Type3.Cor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Microsoft.Quantum.Simulators.Type3", "src\Simulation\Simulators.Type3.Tests\Tests.Microsoft.Quantum.Simulators.Type3.csproj", "{7F80466B-A6B5-4EF1-A9E9-22ABAE3C20C1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "qir", "qir", "{C637C9DF-14AA-48CB-95F3-73CE0AC5F9B1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{020356B7-C3FC-4100-AE37-97E5D8288D1D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "runtime", "runtime", "{4C0C5775-4562-453F-A395-FAD259AE40C0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qir", "Qir", "{F6C2D4C0-12DC-40E3-9C86-FA5308D9B567}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{92FA1569-DA5F-41C5-8CC7-E5CA36419B61}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{7F7BB60A-5DCB-469E-8546-1BE9E3CAC833}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3FFDEA49-C6E8-45BB-BCA5-BBC5378F704F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FullstateSimulator", "FullstateSimulator", "{EAC5EAE7-D1B3-4726-AFDB-73000E62176A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-standalone-input-reference", "src\Qir\Runtime\samples\StandaloneInputReference\qsharp\qir-standalone-input-reference.csproj", "{C66A582B-B1D8-48AC-AA95-5B25E10B59B3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-test-simulator", "src\Qir\Tests\FullstateSimulator\qsharp\qir-test-simulator.csproj", "{C7531119-9730-497A-9D11-8BBB3761B726}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-test-simulator", "src\Qir\Runtime\test\FullstateSimulator\qsharp\qir-test-simulator.csproj", "{8E9D8C26-DBE5-49BE-9D35-3AC95C435072}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-dynamic", "QIR-dynamic", "{3DFACF7F-D5C2-455B-AB8A-26908DEF7E2D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-test-random", "src\Qir\Runtime\test\QIR-dynamic\qsharp\qir-test-random.csproj", "{9DF6FEB7-3111-4244-B4EE-A69C64043967}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-test-random", "src\Qir\Tests\QIR-dynamic\qsharp\qir-test-random.csproj", "{5DBF6402-D9CD-4470-A309-3755CFDA42CF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-gen", "src\Qir\Runtime\test\QIR-static\qsharp\qir-gen.csproj", "{241693D7-4AA6-47C9-9F27-78F2A1EE0904}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-static", "QIR-static", "{54000816-122C-4AA0-9FE9-B0ABB9547232}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tracer-qir", "src\Qir\Runtime\test\QIR-tracer\qsharp\tracer-qir.csproj", "{5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-standalone-input-reference", "src\Qir\Samples\StandaloneInputReference\qsharp\qir-standalone-input-reference.csproj", "{D7D34736-A719-4B45-A33F-2723F59EC29D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -659,86 +669,86 @@ Global {7F80466B-A6B5-4EF1-A9E9-22ABAE3C20C1}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU {7F80466B-A6B5-4EF1-A9E9-22ABAE3C20C1}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU {7F80466B-A6B5-4EF1-A9E9-22ABAE3C20C1}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Debug|x64.ActiveCfg = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Debug|x64.Build.0 = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Release|Any CPU.Build.0 = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Release|x64.ActiveCfg = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.Release|x64.Build.0 = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Debug|x64.ActiveCfg = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Debug|x64.Build.0 = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Release|Any CPU.Build.0 = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Release|x64.ActiveCfg = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.Release|x64.Build.0 = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Debug|x64.ActiveCfg = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Debug|x64.Build.0 = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Release|Any CPU.Build.0 = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Release|x64.ActiveCfg = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.Release|x64.Build.0 = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {9DF6FEB7-3111-4244-B4EE-A69C64043967}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Debug|Any CPU.Build.0 = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Debug|x64.ActiveCfg = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Debug|x64.Build.0 = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Release|Any CPU.ActiveCfg = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Release|Any CPU.Build.0 = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Release|x64.ActiveCfg = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.Release|x64.Build.0 = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {241693D7-4AA6-47C9-9F27-78F2A1EE0904}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Debug|x64.ActiveCfg = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Debug|x64.Build.0 = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Release|Any CPU.Build.0 = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Release|x64.ActiveCfg = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.Release|x64.Build.0 = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Debug|x64.ActiveCfg = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Debug|x64.Build.0 = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Release|Any CPU.Build.0 = Release|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Release|x64.ActiveCfg = Release|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.Release|x64.Build.0 = Release|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {C7531119-9730-497A-9D11-8BBB3761B726}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Debug|x64.Build.0 = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Release|Any CPU.Build.0 = Release|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Release|x64.ActiveCfg = Release|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.Release|x64.Build.0 = Release|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {5DBF6402-D9CD-4470-A309-3755CFDA42CF}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Debug|x64.ActiveCfg = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Debug|x64.Build.0 = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Release|Any CPU.Build.0 = Release|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Release|x64.ActiveCfg = Release|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.Release|x64.Build.0 = Release|Any CPU + {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {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 + {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|x64.Build.0 = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.Release|Any CPU.Build.0 = Release|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.Release|x64.ActiveCfg = Release|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.Release|x64.Build.0 = Release|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -779,14 +789,19 @@ Global {789C86D9-CE77-40DA-BDDD-979436952512} = {93409CC3-8DF9-45FA-AE21-16A19FDEF650} {7E24885B-D86D-477E-A840-06FA53C33FE1} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} {7F80466B-A6B5-4EF1-A9E9-22ABAE3C20C1} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} - {4C0C5775-4562-453F-A395-FAD259AE40C0} = {C637C9DF-14AA-48CB-95F3-73CE0AC5F9B1} - {92FA1569-DA5F-41C5-8CC7-E5CA36419B61} = {4C0C5775-4562-453F-A395-FAD259AE40C0} - {3FFDEA49-C6E8-45BB-BCA5-BBC5378F704F} = {4C0C5775-4562-453F-A395-FAD259AE40C0} - {C66A582B-B1D8-48AC-AA95-5B25E10B59B3} = {92FA1569-DA5F-41C5-8CC7-E5CA36419B61} - {8E9D8C26-DBE5-49BE-9D35-3AC95C435072} = {3FFDEA49-C6E8-45BB-BCA5-BBC5378F704F} - {9DF6FEB7-3111-4244-B4EE-A69C64043967} = {3FFDEA49-C6E8-45BB-BCA5-BBC5378F704F} - {241693D7-4AA6-47C9-9F27-78F2A1EE0904} = {3FFDEA49-C6E8-45BB-BCA5-BBC5378F704F} - {5917D4C5-0CD2-4BD1-A859-19B9B97FF8A7} = {3FFDEA49-C6E8-45BB-BCA5-BBC5378F704F} + {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} = {020356B7-C3FC-4100-AE37-97E5D8288D1D} + {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} + {EAC5EAE7-D1B3-4726-AFDB-73000E62176A} = {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} + {C7531119-9730-497A-9D11-8BBB3761B726} = {EAC5EAE7-D1B3-4726-AFDB-73000E62176A} + {3DFACF7F-D5C2-455B-AB8A-26908DEF7E2D} = {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} + {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821} diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 0365a9f856b..7ad031ef42d 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -7,14 +7,6 @@ Push-Location (Join-Path $PSScriptRoot "build") .\prerequisites.ps1 Pop-Location -# Temporary hack until switch qdk build pipeline to use the new build scripts (as a result it will build the native -# simulator twice, but the second build should be mostly noop) -if (($Env:CI -eq $null) -and ($Env:ENABLE_NATIVE -ne "false")) { - Push-Location (Join-Path $PSScriptRoot "src/Simulation/Native") - .\build-native-simulator.ps1 - Pop-Location -} - if (-not (Test-Path Env:AGENT_OS)) { if ($Env:ENABLE_NATIVE -ne "false") { Write-Host "Build release flavor of the native simulator" diff --git a/build/build.ps1 b/build/build.ps1 index 4a58b8b2d0d..dec45cec756 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -16,6 +16,16 @@ if ($Env:ENABLE_NATIVE -ne "false") { Write-Host "Skipping build of native simulator because ENABLE_NATIVE variable is set to: $Env:ENABLE_NATIVE." } +if ($Env:ENABLE_QIRRUNTIME -ne "false") { + $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") + & "$qirRuntime/build-qir-runtime.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } +} else { + Write-Host "Skipping build of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME" +} + function Build-One { param( [string]$action, @@ -46,13 +56,18 @@ Build-One 'publish' '../src/Simulation/CSharpGeneration.App' Build-One 'build' '../Simulation.sln' if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirRuntime/build-qir-runtime.ps1" + $qirTests = (Join-Path $PSScriptRoot "../src/Qir/Tests") + & "$qirTests/build-qir-tests.ps1" -SkipQSharpBuild + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } + $qirSamples = (Join-Path $PSScriptRoot "../src/Qir/Samples") + & "$qirSamples/build-qir-samples.ps1" -SkipQSharpBuild if ($LastExitCode -ne 0) { $script:all_ok = $False } } else { - Write-Host "Skipping build of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME" + Write-Host "Skipping build of qir tests because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME" } if (-not $all_ok) { diff --git a/build/manifest.ps1 b/build/manifest.ps1 index 177c66f10e3..ac2821339bb 100644 --- a/build/manifest.ps1 +++ b/build/manifest.ps1 @@ -26,6 +26,8 @@ $artifacts = @{ "Microsoft.Quantum.QSharp.Core", "Microsoft.Quantum.Type1.Core", "Microsoft.Quantum.Type2.Core", + "Microsoft.Quantum.Type3.Core", + "Microsoft.Quantum.QSharp.Foundation" "Microsoft.Quantum.Runtime.Core", "Microsoft.Quantum.Simulators", "Microsoft.Quantum.Xunit" @@ -41,6 +43,7 @@ $artifacts = @{ ".\src\Simulation\QSharpCore\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.QSharp.Core.dll", ".\src\Simulation\Type1Core\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Type1.Core.dll", ".\src\Simulation\Type2Core\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Type2.Core.dll", + ".\src\Simulation\Type3Core\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Type3.Core.dll", ".\src\Simulation\QSharpFoundation\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.QSharp.Foundation.dll", ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Simulation.Common.dll", ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime.dll", @@ -49,7 +52,11 @@ $artifacts = @{ ) | ForEach-Object { Join-Path $PSScriptRoot (Join-Path ".." $_) }; 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.Simulator.Runtime.dll", + ".\src\Qir\Runtime\build\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.QSharp.Core.dll", + ".\src\Qir\Runtime\build\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.QSharp.Foundation.dll", + ".\src\Qir\Runtime\build\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.Runtime.dll", + ".\src\Qir\Runtime\build\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.Tracer.dll" ) | ForEach-Object { Join-Path $PSScriptRoot (Join-Path ".." $_) }; } diff --git a/build/test.ps1 b/build/test.ps1 index b80d5ab936f..974c2a61615 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -43,9 +43,18 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if ($LastExitCode -ne 0) { $script:all_ok = $False } + $qirTests = (Join-Path $PSScriptRoot "../src/Qir/Tests") + & "$qirTests/test-qir-tests.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } + $qirSamples = (Join-Path $PSScriptRoot "../src/Qir/Samples") + & "$qirSamples/test-qir-samples.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } } else { - Write-Host "Skipping test of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME ` - and ENABLE_NATIVE variable is set to: $Env:ENABLE_NATIVE." + Write-Host "Skipping test of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME." } if (-not $all_ok) { diff --git a/src/Qir/Runtime/.clang-format b/src/Qir/.clang-format similarity index 96% rename from src/Qir/Runtime/.clang-format rename to src/Qir/.clang-format index d5ac39db3de..34a87bcbbd9 100644 --- a/src/Qir/Runtime/.clang-format +++ b/src/Qir/.clang-format @@ -1,37 +1,37 @@ -# https://clang.llvm.org/docs/ClangFormatStyleOptions.html - ---- -Language: Cpp -BasedOnStyle: Microsoft - -# page width -ColumnLimit: 120 -ReflowComments: true - -# tabs and indents -UseTab: Never -IndentWidth: 4 -TabWidth: 4 -AccessModifierOffset: -2 -NamespaceIndentation: Inner - -# line and statements layout -BreakBeforeBraces: Allman -BinPackParameters: false -AlignAfterOpenBracket: AlwaysBreak -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortFunctionsOnASingleLine: Empty -AllowAllConstructorInitializersOnNextLine: false -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: false -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeComma - -# misc -Cpp11BracedListStyle: true -FixNamespaceComments: true -IncludeBlocks: Preserve -SpaceBeforeInheritanceColon : true -SpaceBeforeParens: ControlStatements -DerivePointerAlignment: false -PointerAlignment: Left +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html + +--- +Language: Cpp +BasedOnStyle: Microsoft + +# page width +ColumnLimit: 120 +ReflowComments: true + +# tabs and indents +UseTab: Never +IndentWidth: 4 +TabWidth: 4 +AccessModifierOffset: -2 +NamespaceIndentation: Inner + +# line and statements layout +BreakBeforeBraces: Allman +BinPackParameters: false +AlignAfterOpenBracket: AlwaysBreak +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortFunctionsOnASingleLine: Empty +AllowAllConstructorInitializersOnNextLine: false +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma + +# misc +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Preserve +SpaceBeforeInheritanceColon : true +SpaceBeforeParens: ControlStatements +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/src/Qir/Runtime/.clang-tidy b/src/Qir/.clang-tidy similarity index 97% rename from src/Qir/Runtime/.clang-tidy rename to src/Qir/.clang-tidy index 86a9dc17ad9..8fb71d1375a 100644 --- a/src/Qir/Runtime/.clang-tidy +++ b/src/Qir/.clang-tidy @@ -1,29 +1,29 @@ -Checks: - '-*,bugprone-*,-readability-*,readability-identifier-*,readability-braces-around-statements' -WarningsAsErrors: '*' -HeaderFilterRegex: '.*' - -CheckOptions: - - key: readability-identifier-naming.ClassCase - value: 'CamelCase' - - key: readability-identifier-naming.ClassPrefix - value: 'C' - - key: readability-identifier-naming.AbstractClassPrefix - value: 'I' - - key: readability-identifier-naming.StructCase - value: 'CamelCase' - - key: readability-identifier-naming.ParameterCase - value: 'camelBack' - - key: readability-identifier-naming.PrivateMemberCase - value: 'camelBack' - - key: readability-identifier-naming.LocalVariableCase - value: 'camelBack' - - key: readability-identifier-naming.TypeAliasCase - value: 'CamelCase' - - key: readability-identifier-naming.UnionCase - value: 'CamelCase' - - key: readability-identifier-naming.FunctionCase - value: 'CamelCase' - - key: readability-identifier-naming.NamespaceCase - value: 'CamelCase' - +Checks: + '-*,bugprone-*,-readability-*,readability-identifier-*,readability-braces-around-statements' +WarningsAsErrors: '*' +HeaderFilterRegex: '.*' + +CheckOptions: + - key: readability-identifier-naming.ClassCase + value: 'CamelCase' + - key: readability-identifier-naming.ClassPrefix + value: 'C' + - key: readability-identifier-naming.AbstractClassPrefix + value: 'I' + - key: readability-identifier-naming.StructCase + value: 'CamelCase' + - key: readability-identifier-naming.ParameterCase + value: 'camelBack' + - key: readability-identifier-naming.PrivateMemberCase + value: 'camelBack' + - key: readability-identifier-naming.LocalVariableCase + value: 'camelBack' + - key: readability-identifier-naming.TypeAliasCase + value: 'CamelCase' + - key: readability-identifier-naming.UnionCase + value: 'CamelCase' + - key: readability-identifier-naming.FunctionCase + value: 'CamelCase' + - key: readability-identifier-naming.NamespaceCase + value: 'CamelCase' + diff --git a/src/Qir/.gitignore b/src/Qir/.gitignore new file mode 100644 index 00000000000..44889a5d35b --- /dev/null +++ b/src/Qir/.gitignore @@ -0,0 +1,2 @@ +# Ignore the generated qir files from Q# compiler +qir/ diff --git a/src/Qir/Runtime/test/SimulatorStub.hpp b/src/Qir/Common/Include/SimulatorStub.hpp similarity index 96% rename from src/Qir/Runtime/test/SimulatorStub.hpp rename to src/Qir/Common/Include/SimulatorStub.hpp index 6f3d198f4e9..c4a8fb25001 100644 --- a/src/Qir/Runtime/test/SimulatorStub.hpp +++ b/src/Qir/Common/Include/SimulatorStub.hpp @@ -1,139 +1,139 @@ -#pragma once - -#include - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct SimulatorStub : public IRuntimeDriver, public IQuantumGateSet - { - Qubit AllocateQubit() override - { - throw std::logic_error("not_implemented"); - } - void ReleaseQubit(Qubit qubit) override - { - throw std::logic_error("not_implemented"); - } - virtual std::string QubitToString(Qubit qubit) override - { - throw std::logic_error("not_implemented"); - } - void X(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void Y(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void Z(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void H(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void S(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void T(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void R(PauliId axis, Qubit target, double theta) override - { - throw std::logic_error("not_implemented"); - } - void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override - { - throw std::logic_error("not_implemented"); - } - void ControlledX(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledY(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledZ(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledH(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledS(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledT(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override - { - throw std::logic_error("not_implemented"); - } - void ControlledExp( - long numControls, - Qubit controls[], - long numTargets, - PauliId paulis[], - Qubit targets[], - double theta) override - { - throw std::logic_error("not_implemented"); - } - void AdjointS(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void AdjointT(Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override - { - throw std::logic_error("not_implemented"); - } - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override - { - throw std::logic_error("not_implemented"); - } - void ReleaseResult(Result result) override - { - throw std::logic_error("not_implemented"); - } - bool AreEqualResults(Result r1, Result r2) override - { - throw std::logic_error("not_implemented"); - } - ResultValue GetResultValue(Result result) override - { - throw std::logic_error("not_implemented"); - } - Result UseZero() override - { - throw std::logic_error("not_implemented"); - } - Result UseOne() override - { - throw std::logic_error("not_implemented"); - } - }; - -} // namespace Quantum +#pragma once + +#include + +#include "QirRuntimeApi_I.hpp" +#include "QSharpSimApi_I.hpp" + +namespace Microsoft +{ +namespace Quantum +{ + struct SimulatorStub : public IRuntimeDriver, public IQuantumGateSet + { + Qubit AllocateQubit() override + { + throw std::logic_error("not_implemented"); + } + void ReleaseQubit(Qubit qubit) override + { + throw std::logic_error("not_implemented"); + } + virtual std::string QubitToString(Qubit qubit) override + { + throw std::logic_error("not_implemented"); + } + void X(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void Y(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void Z(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void H(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void S(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void T(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void R(PauliId axis, Qubit target, double theta) override + { + throw std::logic_error("not_implemented"); + } + void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override + { + throw std::logic_error("not_implemented"); + } + void ControlledX(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledY(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledZ(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledH(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledS(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledT(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override + { + throw std::logic_error("not_implemented"); + } + void ControlledExp( + long numControls, + Qubit controls[], + long numTargets, + PauliId paulis[], + Qubit targets[], + double theta) override + { + throw std::logic_error("not_implemented"); + } + void AdjointS(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void AdjointT(Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override + { + throw std::logic_error("not_implemented"); + } + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + throw std::logic_error("not_implemented"); + } + void ReleaseResult(Result result) override + { + throw std::logic_error("not_implemented"); + } + bool AreEqualResults(Result r1, Result r2) override + { + throw std::logic_error("not_implemented"); + } + ResultValue GetResultValue(Result result) override + { + throw std::logic_error("not_implemented"); + } + Result UseZero() override + { + throw std::logic_error("not_implemented"); + } + Result UseOne() override + { + throw std::logic_error("not_implemented"); + } + }; + +} // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation_internal.hpp b/src/Qir/Common/Include/qsharp__foundation_internal.hpp similarity index 61% rename from src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation_internal.hpp rename to src/Qir/Common/Include/qsharp__foundation_internal.hpp index 92ecfeb23f3..df2e844e5a1 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation_internal.hpp +++ b/src/Qir/Common/Include/qsharp__foundation_internal.hpp @@ -6,6 +6,8 @@ // 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. { @@ -13,11 +15,11 @@ namespace Qis { namespace Internal { - extern char const excStrDrawRandomVal[]; + QIR_SHARED_API extern char const excStrDrawRandomVal[]; - void RandomizeSeed(bool randomize); - int64_t GetLastGeneratedRandomI64(); - double GetLastGeneratedRandomDouble(); + 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/qir_cmake_include.cmake b/src/Qir/Common/cmake/qir_cmake_include.cmake new file mode 100644 index 00000000000..25e66fbd104 --- /dev/null +++ b/src/Qir/Common/cmake/qir_cmake_include.cmake @@ -0,0 +1,41 @@ +#=============================================================================== +# compiling from IR +macro(target_source_from_qir target_name source_file) + set(CLANG_ARGS "-c") + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CLANG_ARGS + "${CLANG_ARGS}" + "-O0" + "-DDEBUG" + ) + endif() + + get_filename_component(file_name ${source_file} NAME_WLE) + + set(INFILE + "${CMAKE_CURRENT_SOURCE_DIR}/${source_file}" + ) + set(OBJFILE + "${CMAKE_CURRENT_BINARY_DIR}/${file_name}.obj" + ) + + set(OBJFILE_COMPILE "${file_name}-compile") + add_custom_command(OUTPUT ${OBJFILE_COMPILE} + COMMAND ${CMAKE_CXX_COMPILER} + ARGS ${CLANG_ARGS} ${INFILE} "-o" ${OBJFILE} + DEPENDS ${INFILE} + BYPRODUCTS ${OBJFILE} + COMMENT "Compiling ${source_file}" + VERBATIM + ) + add_custom_target(${target_name}_${file_name}_compile DEPENDS ${OBJFILE_COMPILE}) + + set_source_files_properties( + ${OBJFILE} + PROPERTIES + EXTERNAL_OBJECT true + ) + target_sources(${target_name} PUBLIC + ${OBJFILE} + ) +endmacro() diff --git a/src/Qir/Runtime/cmake/unit_test_include.cmake b/src/Qir/Common/cmake/unit_test_include.cmake similarity index 63% rename from src/Qir/Runtime/cmake/unit_test_include.cmake rename to src/Qir/Common/cmake/unit_test_include.cmake index 483c15c17bd..4ecaf1e6237 100644 --- a/src/Qir/Runtime/cmake/unit_test_include.cmake +++ b/src/Qir/Common/cmake/unit_test_include.cmake @@ -1,5 +1,3 @@ -set(test_includes "${PROJECT_SOURCE_DIR}/externals/catch2" "${PROJECT_SOURCE_DIR}/test") - include(CTest) macro(add_unit_test target) add_test( @@ -15,9 +13,10 @@ macro(add_unit_test target) endif() set(TEST_DEPS2 "${CMAKE_BINARY_DIR}/bin") + set(TEST_DEPS3 "${PROJECT_SOURCE_DIR}/../Runtime/build/${CMAKE_BUILD_TYPE}/bin") set_property(TEST ${target} 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}:${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}" ) endmacro(add_unit_test) diff --git a/src/Qir/Runtime/externals/CLI11/CLI11.hpp b/src/Qir/Common/externals/CLI11/CLI11.hpp similarity index 100% rename from src/Qir/Runtime/externals/CLI11/CLI11.hpp rename to src/Qir/Common/externals/CLI11/CLI11.hpp diff --git a/src/Qir/Runtime/externals/catch2/catch.hpp b/src/Qir/Common/externals/catch2/catch.hpp similarity index 100% rename from src/Qir/Runtime/externals/catch2/catch.hpp rename to src/Qir/Common/externals/catch2/catch.hpp diff --git a/src/Qir/Runtime/externals/cgmanifest.json b/src/Qir/Common/externals/cgmanifest.json similarity index 96% rename from src/Qir/Runtime/externals/cgmanifest.json rename to src/Qir/Common/externals/cgmanifest.json index 1bd19eceaee..612833415ba 100644 --- a/src/Qir/Runtime/externals/cgmanifest.json +++ b/src/Qir/Common/externals/cgmanifest.json @@ -1,30 +1,30 @@ -{ - "Registrations":[ - { - "Component": { - "Type": "other", - "Other": { - "Name": "llvm-ar.exe", - "Version": "10.0.0", - "DownloadUrl": "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe" - } - } - }, - { - "Component": { - "Type": "git", - "Git": { - "RepositoryUrl": "https://github.com/catchorg/Catch2", - "CommitHash": "87074da73ecb1c2e7c35fd14f50ca21c4c002adc" } - } - }, - { - "Component": { - "Type": "git", - "Git": { - "RepositoryUrl": "https://github.com/CLIUtils/CLI11", - "CommitHash": "5cb3efabce007c3a0230e4cc2e27da491c646b6c" } - } - } - ] +{ + "Registrations":[ + { + "Component": { + "Type": "other", + "Other": { + "Name": "llvm-ar.exe", + "Version": "10.0.0", + "DownloadUrl": "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe" + } + } + }, + { + "Component": { + "Type": "git", + "Git": { + "RepositoryUrl": "https://github.com/catchorg/Catch2", + "CommitHash": "87074da73ecb1c2e7c35fd14f50ca21c4c002adc" } + } + }, + { + "Component": { + "Type": "git", + "Git": { + "RepositoryUrl": "https://github.com/CLIUtils/CLI11", + "CommitHash": "5cb3efabce007c3a0230e4cc2e27da491c646b6c" } + } + } + ] } \ No newline at end of file diff --git a/src/Qir/Runtime/externals/readme.md b/src/Qir/Common/externals/readme.md similarity index 97% rename from src/Qir/Runtime/externals/readme.md rename to src/Qir/Common/externals/readme.md index 3bef64779af..be24f8dcff5 100644 --- a/src/Qir/Runtime/externals/readme.md +++ b/src/Qir/Common/externals/readme.md @@ -1,9 +1,9 @@ -# External components - -## Catch2 - -We are using v2.12.1 single-header distribution of catch2 native framework from https://github.com/catchorg/Catch2 (2e61d38c7c3078e600c331257b5bebfb81aaa685). - -## CLI11 - -We are using v1.9.1 single-header distribution of CLI11 command line parser for C++ from (https://github.com/CLIUtils/CLI11) (5cb3efabce007c3a0230e4cc2e27da491c646b6c). +# External components + +## Catch2 + +We are using v2.12.1 single-header distribution of catch2 native framework from https://github.com/catchorg/Catch2 (2e61d38c7c3078e600c331257b5bebfb81aaa685). + +## CLI11 + +We are using v1.9.1 single-header distribution of CLI11 command line parser for C++ from (https://github.com/CLIUtils/CLI11) (5cb3efabce007c3a0230e4cc2e27da491c646b6c). diff --git a/src/Qir/Runtime/.gitignore b/src/Qir/Runtime/.gitignore deleted file mode 100644 index b01c4cb32d9..00000000000 --- a/src/Qir/Runtime/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# These auto-generated files bother when switching between the branches. -samples/StandaloneInputReference/qir-standalone-input-reference.ll -samples/StandaloneInputReference/qsharp/qir/ -test/FullstateSimulator/qir-test-simulator.ll -test/FullstateSimulator/qsharp/qir/ -test/QIR-dynamic/qir-test-random.ll -test/QIR-dynamic/qsharp/qir/ -test/QIR-tracer/qsharp/qir/ -test/QIR-tracer/tracer-qir.ll - diff --git a/src/Qir/Runtime/CMakeLists.txt b/src/Qir/Runtime/CMakeLists.txt index 06f29c7fe5c..da7f2eaceff 100644 --- a/src/Qir/Runtime/CMakeLists.txt +++ b/src/Qir/Runtime/CMakeLists.txt @@ -24,24 +24,12 @@ 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}/cmake") +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) -if (WIN32) - set(QIR_BRIDGE_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/QIR/bridge-rt-u.lib") - set(QSHARP_FOUNDATION_BRIDGE_QIS_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/QSharpFoundation/qsharp-foundation-qis-u.lib") - set(QSHARP_CORE_BRIDGE_QIS_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/QSharpCore/qsharp-core-qis-u.lib") - set(QIR_BRIDGE_TRACER_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/Tracer/tracer-bridge-u.lib") -else() - set(QIR_BRIDGE_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/QIR/libbridge-rt-u.a") - set(QSHARP_FOUNDATION_BRIDGE_QIS_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/QSharpFoundation/libqsharp-foundation-qis-u.a") - set(QSHARP_CORE_BRIDGE_QIS_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/QSharpCore/libqsharp-core-qis-u.a") - set(QIR_BRIDGE_TRACER_UTILITY_LIB "${PROJECT_BINARY_DIR}/lib/Tracer/libtracer-bridge-u.a") -endif() - add_subdirectory(lib) -add_subdirectory(samples) -add_subdirectory(test) +add_subdirectory(unittests) diff --git a/src/Qir/Runtime/README.md b/src/Qir/Runtime/README.md index b1145ad2432..40d9de3eae7 100644 --- a/src/Qir/Runtime/README.md +++ b/src/Qir/Runtime/README.md @@ -7,7 +7,7 @@ The QIR runtime includes an implementation of the - `public` folder contains the public headers - `lib` folder contains the implementation of the runtime and the simulators. -- `test` folder contains tests for the runtime +- `unittests` folder contains tests for the runtime - `externals` folder contains external dependencies. We'll strive to keep those minimal. ## Build @@ -71,6 +71,10 @@ If the variable is not set then the default is specified in [`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 diff --git a/src/Qir/Runtime/build-qir-runtime.ps1 b/src/Qir/Runtime/build-qir-runtime.ps1 index fbca1e2c047..d9e8f6e06df 100644 --- a/src/Qir/Runtime/build-qir-runtime.ps1 +++ b/src/Qir/Runtime/build-qir-runtime.ps1 @@ -1,118 +1,8 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -& (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) +. (Join-Path $PSScriptRoot .. qir-utils.ps1) -function Build-QirProject { - param ( - [string] - $FolderPath - ) - - if (!(Test-Path (Join-Path $FolderPath qir *.ll))) { - Write-Host "##[info]Build Q# project for QIR tests '$FolderPath'" - dotnet build $FolderPath -c $Env:BUILD_CONFIGURATION -v $Env:BUILD_VERBOSITY - if ($LastExitCode -ne 0) { - Write-Host "##vso[task.logissue type=error;]Failed to compile Q# project at '$FolderPath' into QIR." - throw "Failed to compile Q# project at '$FolderPath' into QIR." - } - } - Copy-Item -Path (Join-Path $FolderPath qir *.ll) -Destination (Split-Path $FolderPath -Parent) - # Also copy to drops so it ends up in build artifacts, for easier post-build debugging. - Copy-Item -Path (Join-Path $FolderPath qir *.ll) -Destination $Env:DROPS_DIR -} - -Write-Host "##[info]Compile Q# Projects into QIR" -Build-QirProject (Join-Path $PSScriptRoot test QIR-static qsharp) -Build-QirProject (Join-Path $PSScriptRoot test QIR-dynamic qsharp) -Build-QirProject (Join-Path $PSScriptRoot test QIR-tracer qsharp) -Build-QirProject (Join-Path $PSScriptRoot test FullstateSimulator qsharp) -Build-QirProject (Join-Path $PSScriptRoot samples StandaloneInputReference qsharp) - - -Write-Host "##[info]Build QIR Runtime" -$oldCC = $env:CC -$oldCXX = $env:CXX -$oldRC = $env:RC - -$clangTidy = "" - -if (($IsMacOS) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Darwin")))) -{ - Write-Host "On MacOS build QIR Runtime using the default C/C++ compiler (should be AppleClang)" -} -elseif (($IsLinux) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Lin")))) -{ - Write-Host "On Linux build QIR Runtime using Clang" - $env:CC = "clang-11" - $env:CXX = "clang++-11" - $env:RC = "clang++-11" - $clangTidy = "-DCMAKE_CXX_CLANG_TIDY=clang-tidy-11" -} -elseif (($IsWindows) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Win")))) -{ - Write-Host "On Windows build QIR Runtime using Clang" - $env:CC = "clang.exe" - $env:CXX = "clang++.exe" - $env:RC = "clang++.exe" - - if (!(Get-Command clang -ErrorAction SilentlyContinue) -and (choco find --idonly -l llvm) -contains "llvm") { - # LLVM was installed by Chocolatey, so add the install location to the path. - $env:PATH += ";$($env:SystemDrive)\Program Files\LLVM\bin" - } - - if (Get-Command clang-tidy -ErrorAction SilentlyContinue) { - # Only run clang-tidy if it's installed. This is because the package used by chocolatey on - # the build pipeline doesn't include clang-tidy, so we allow skipping that there and let - # the Linux build catch tidy issues. - $clangTidy = "-DCMAKE_CXX_CLANG_TIDY=clang-tidy" - } -} else { - Write-Host "##vso[task.logissue type=error;]Failed to identify the OS. Will use default CXX compiler" -} - -$qirRuntimeBuildFolder = (Join-Path $PSScriptRoot build $Env:BUILD_CONFIGURATION) -if (-not (Test-Path $qirRuntimeBuildFolder)) { - New-Item -Path $qirRuntimeBuildFolder -ItemType "directory" -} - -$all_ok = $true - -Push-Location $qirRuntimeBuildFolder - -cmake -G Ninja $clangTidy -D CMAKE_BUILD_TYPE="$Env:BUILD_CONFIGURATION" ../.. -if ($LastExitCode -ne 0) { - Write-Host "##vso[task.logissue type=error;]Failed to generate QIR Runtime." - $all_ok = $false -} else { - cmake --build . --target install - if ($LastExitCode -ne 0) { - Write-Host "##vso[task.logissue type=error;]Failed to build QIR Runtime." - $all_ok = $false - } -} - -$os = "win" -$pattern = @("*.dll", "*.lib") -if ($IsMacOS) { - $os = "osx" - $pattern = @("*.dylib") -} elseif ($IsLinux) { - $os = "linux" - $pattern = @("*.so") -} -$osQirDropFolder = Join-Path $Env:DROPS_DIR QIR $os -if (!(Test-Path $osQirDropFolder)) { - New-Item -Path $osQirDropFolder -ItemType "directory" -} -$pattern | Foreach-Object { Copy-Item (Join-Path . bin $_) $osQirDropFolder -ErrorAction SilentlyContinue } - -Pop-Location - -$env:CC = $oldCC -$env:CXX = $oldCXX -$env:RC = $oldRC - -if (-not $all_ok) { +if (-not (Build-CMakeProject $PSScriptRoot "QIR Runtime")) { throw "At least one project failed to compile. Check the logs." } diff --git a/src/Qir/Runtime/cmake/qir_cmake_include.cmake b/src/Qir/Runtime/cmake/qir_cmake_include.cmake deleted file mode 100644 index 7328a745ee0..00000000000 --- a/src/Qir/Runtime/cmake/qir_cmake_include.cmake +++ /dev/null @@ -1,68 +0,0 @@ -#=============================================================================== -# compiling from IR -# -# CMake doesn't support LLVM IR files as sources so we compile them with custom -# commands, which produce UTILITY libs that can only be linked in using abs paths -# (rather than the target name): -# Target "qir_bridge_qis" of type UTILITY may not be linked into another -# target. One may link only to INTERFACE, OBJECT, STATIC or SHARED -# libraries, or to executables with the ENABLE_EXPORTS property set. -# -macro(compile_from_qir source_file target) - set(CLANG_ARGS "-c") - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CLANG_ARGS - "${CLANG_ARGS}" - "-O0" - "-D_DEBUG" - ) - endif() - - set(INFILE - "${CMAKE_CURRENT_SOURCE_DIR}/${source_file}.ll" - ) - set(OBJFILE - "${CMAKE_CURRENT_BINARY_DIR}/${source_file}.obj" - ) - - set(OBJFILE_COMPILE "${source_file}-compile") - add_custom_command(OUTPUT ${OBJFILE_COMPILE} - COMMAND ${CMAKE_CXX_COMPILER} - ARGS ${CLANG_ARGS} ${INFILE} "-o" ${OBJFILE} - DEPENDS ${INFILE} - BYPRODUCTS ${OBJFILE} - COMMENT "Compiling ${source_file}.ll" - VERBATIM - ) - - add_custom_target(${source_file}_compile DEPENDS ${OBJFILE_COMPILE}) - - if (WIN32) - set(QIR_UTILITY_LIB "${CMAKE_CURRENT_BINARY_DIR}/${source_file}-u.lib" ) - else() - set(QIR_UTILITY_LIB "${CMAKE_CURRENT_BINARY_DIR}/lib${source_file}-u.a") - endif() - - add_custom_command(OUTPUT ${QIR_UTILITY_LIB} - COMMAND ${CMAKE_AR} - ARGS "rc" ${QIR_UTILITY_LIB} ${OBJFILE} - DEPENDS ${source_file}_compile ${INFILE} - COMMENT "Creating a lib from ${source_file}.ll" - VERBATIM - ) - - if (NOT ${target} STREQUAL "") - add_custom_target(${target} DEPENDS ${QIR_UTILITY_LIB}) - endif() -endmacro(compile_from_qir) - -macro(target_source_from_qir_obj target_name source_file) - set_source_files_properties( - "${source_file}.obj" - PROPERTIES - EXTERNAL_OBJECT true - ) - target_sources(${target_name} PUBLIC - "${CMAKE_CURRENT_BINARY_DIR}/${source_file}.obj" - ) -endmacro() \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QIR/CMakeLists.txt b/src/Qir/Runtime/lib/QIR/CMakeLists.txt index a9afbe799ef..4bae717ec85 100644 --- a/src/Qir/Runtime/lib/QIR/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QIR/CMakeLists.txt @@ -5,12 +5,6 @@ # qir-rt #+++++++++++++++++++++++++++++++++++++ -#=============================================================================== -# create a utility lib from bridge-rt.ll -# -set(bridge_rt_target "bridge_rt_target") -compile_from_qir(bridge-rt ${bridge_rt_target}) - #=============================================================================== # create qir-rt-support lib from the C++ sources # @@ -25,14 +19,9 @@ set(rt_sup_source_files rtOut.cpp ) -add_library(qir-rt-support ${rt_sup_source_files}) -target_include_directories(qir-rt-support PUBLIC ${public_includes}) -add_dependencies(qir-rt-support ${bridge_rt_target}) -target_compile_definitions(qir-rt-support PRIVATE EXPORT_QIR_API) - # 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_obj(qir-rt-support-obj bridge-rt) +target_source_from_qir(qir-rt-support-obj bridge-rt.ll) target_include_directories(qir-rt-support-obj PUBLIC ${public_includes}) set_property(TARGET qir-rt-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) target_compile_definitions(qir-rt-support-obj PRIVATE EXPORT_QIR_API) diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt index e0f334f5789..721ea8b0a46 100644 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt @@ -2,12 +2,6 @@ # qsharp-core-qis #+++++++++++++++++++++++++++++++++++++ -#=============================================================================== -# create a utility lib from qsharp-core-qis.ll -# -set(qsharp_core_qis_target "qsharp_core_qis_target") -compile_from_qir(qsharp-core-qis ${qsharp_core_qis_target}) - #=============================================================================== # create qsharp-core-qis-support lib from the C++ sources # @@ -15,14 +9,9 @@ set(qsharp_core_sup_source_files intrinsics.cpp ) -add_library(qsharp-core-qis-support ${qsharp_core_sup_source_files}) -target_include_directories(qsharp-core-qis-support PUBLIC ${public_includes}) -add_dependencies(qsharp-core-qis-support ${qsharp_core_qis_target}) -target_compile_definitions(qsharp-core-qis-support PRIVATE EXPORT_QIR_API) - # 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_source_from_qir_obj(qsharp-core-qis-support-obj qsharp-core-qis) +target_source_from_qir(qsharp-core-qis-support-obj qsharp-core-qis.ll) 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) @@ -39,6 +28,7 @@ target_link_libraries(Microsoft.Quantum.Qir.QSharp.Core "-L${CMAKE_BINARY_DIR}/lib/QIR" -lMicrosoft.Quantum.Qir.Runtime ) +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) diff --git a/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt index 216b971b9d8..47ced481af0 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt @@ -2,12 +2,6 @@ # qsharp-foundation-qis #+++++++++++++++++++++++++++++++++++++ -#=============================================================================== -# create a utility lib from qsharp-foundation-qis.ll -# -set(qsharp_foundation_qis_target "qsharp_foundation_qis_target") -compile_from_qir(qsharp-foundation-qis ${qsharp_foundation_qis_target}) - #=============================================================================== # create qsharp-foundation-qis-support lib from the C++ sources # @@ -16,15 +10,13 @@ set(qsharp_foundation_sup_source_files conditionals.cpp ) -add_library(qsharp-foundation-qis-support ${qsharp_foundation_sup_source_files}) -target_include_directories(qsharp-foundation-qis-support PUBLIC ${public_includes}) -add_dependencies(qsharp-foundation-qis-support ${qsharp_foundation_qis_target}) -target_compile_definitions(qsharp-foundation-qis-support PRIVATE EXPORT_QIR_API) - # 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_source_from_qir_obj(qsharp-foundation-qis-support-obj qsharp-foundation-qis) -target_include_directories(qsharp-foundation-qis-support-obj PUBLIC ${public_includes}) +target_source_from_qir(qsharp-foundation-qis-support-obj qsharp-foundation-qis.ll) +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) @@ -39,8 +31,12 @@ target_link_libraries(Microsoft.Quantum.Qir.QSharp.Foundation "-L${CMAKE_BINARY_DIR}/lib/QIR" -lMicrosoft.Quantum.Qir.Runtime ) +add_dependencies(Microsoft.Quantum.Qir.QSharp.Foundation Microsoft.Quantum.Qir.Runtime) -target_include_directories(Microsoft.Quantum.Qir.QSharp.Foundation PUBLIC ${public_includes}) +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) diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll index ac63ad53302..3e8a749e187 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll +++ b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll @@ -194,7 +194,7 @@ define dllexport i64 @__quantum__qis__drawrandomint__body(i64 %min, i64 %max) { ; operation DrawRandomDouble (min : Double, max : Double) : Double ; https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.random.drawrandomdouble -define double @__quantum__qis__drawrandomdouble__body(double %min, double %max) { +define dllexport double @__quantum__qis__drawrandomdouble__body(double %min, double %max) { %result = call double @quantum__qis__drawrandomdouble__body(double %min, double %max) ret double %result } diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index abbceb8e16e..77d9ffb860d 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -8,15 +8,6 @@ set(includes "${public_includes}" ) - -#=============================================================================== -# Produce static lib for users to link directly to - -add_library(simulators STATIC ${source_files}) -target_include_directories(simulators PUBLIC ${includes}) -target_link_libraries(simulators ${CMAKE_DL_LIBS}) -target_compile_definitions(simulators PRIVATE EXPORT_QIR_API) - #=============================================================================== # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/Qir/Runtime/lib/Tracer/CMakeLists.txt b/src/Qir/Runtime/lib/Tracer/CMakeLists.txt index 4fb8034fd8e..a73cfeb28fd 100644 --- a/src/Qir/Runtime/lib/Tracer/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Tracer/CMakeLists.txt @@ -1,6 +1,3 @@ -# build the utility lib for tracer's bridge -compile_from_qir(tracer-bridge tracer-bridge) - # build the native part of the tracer set(source_files "tracer-qis.cpp" @@ -12,14 +9,9 @@ set(includes "${PROJECT_SOURCE_DIR}/lib/QIR" ) -add_library(tracer STATIC ${source_files}) -target_include_directories(tracer PUBLIC ${includes}) -add_dependencies(tracer tracer-bridge) -target_compile_definitions(tracer PRIVATE EXPORT_QIR_API) - # Produce object lib we'll use to create a shared lib (so/dll) later on add_library(tracer-obj OBJECT ${source_files}) -target_source_from_qir_obj(tracer-obj tracer-bridge) +target_source_from_qir(tracer-obj tracer-bridge.ll) 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) @@ -35,6 +27,7 @@ target_link_libraries(Microsoft.Quantum.Qir.Tracer "-L${CMAKE_BINARY_DIR}/lib/QIR" -lMicrosoft.Quantum.Qir.Runtime ) +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) diff --git a/src/Qir/Runtime/samples/CMakeLists.txt b/src/Qir/Runtime/samples/CMakeLists.txt deleted file mode 100644 index 247f423c7a8..00000000000 --- a/src/Qir/Runtime/samples/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(StandaloneInputReference) diff --git a/src/Qir/Runtime/test-qir-runtime.ps1 b/src/Qir/Runtime/test-qir-runtime.ps1 index bddb09b4230..5f24e3f7cdb 100644 --- a/src/Qir/Runtime/test-qir-runtime.ps1 +++ b/src/Qir/Runtime/test-qir-runtime.ps1 @@ -1,35 +1,8 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -& (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) -$all_ok = $true +. (Join-Path $PSScriptRoot .. qir-utils.ps1) -Write-Host "##[info]Test QIR Runtime" - -Push-Location (Join-Path $PSScriptRoot build $Env:BUILD_CONFIGURATION test) - -ctest --verbose - -if ($LastExitCode -ne 0) { - Write-Host "##vso[task.logissue type=error;]Failed to test QIR Runtime" - $script:all_ok = $False -} - -Pop-Location - -Write-Host "##[info]Test QIR Standalone Sample" - -Push-Location (Join-Path $PSScriptRoot build $Env:BUILD_CONFIGURATION samples StandaloneInputReference) - -ctest --verbose - -if ($LastExitCode -ne 0) { - Write-Host "##vso[task.logissue type=error;]Failed to test QIR Standalone Sample" - $script:all_ok = $False -} - -Pop-Location - -if (-not $all_ok) { +if (-not (Test-CTest (Join-Path $PSScriptRoot build $Env:BUILD_CONFIGURATION unittests) "QIR Runtime")) { throw "At least one project failed testing. Check the logs." } diff --git a/src/Qir/Runtime/test/CMakeLists.txt b/src/Qir/Runtime/test/CMakeLists.txt deleted file mode 100644 index aaeee4ad5a7..00000000000 --- a/src/Qir/Runtime/test/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -include(unit_test_include) - -add_subdirectory(FullstateSimulator) -add_subdirectory(QIR-dynamic) -add_subdirectory(QIR-static) -add_subdirectory(QIR-tracer) -add_subdirectory(unittests) diff --git a/src/Qir/Runtime/test/FullstateSimulator/CMakeLists.txt b/src/Qir/Runtime/test/FullstateSimulator/CMakeLists.txt deleted file mode 100644 index 21c10647ba4..00000000000 --- a/src/Qir/Runtime/test/FullstateSimulator/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ - -compile_from_qir(qir-test-simulator qir_test_simulator_target) - -add_executable(fullstate-simulator-tests - FullstateSimulatorTests.cpp) - -target_link_libraries(fullstate-simulator-tests PUBLIC - ${QIR_UTILITY_LIB} # created by compile_from_qir - ${QIR_BRIDGE_UTILITY_LIB} - ${QSHARP_FOUNDATION_BRIDGE_QIS_UTILITY_LIB} - ${QSHARP_CORE_BRIDGE_QIS_UTILITY_LIB} - qir-rt-support - qsharp-foundation-qis-support - qsharp-core-qis-support - simulators -) - -target_include_directories(fullstate-simulator-tests PUBLIC - "${test_includes}" - "${public_includes}" - "${PROJECT_SOURCE_DIR}/lib/QIR" -) -target_compile_definitions(fullstate-simulator-tests PRIVATE EXPORT_QIR_API) -add_dependencies(fullstate-simulator-tests qir_test_simulator_target) - -install(TARGETS fullstate-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(fullstate-simulator-tests) - diff --git a/src/Qir/Runtime/test/QIR-static/CMakeLists.txt b/src/Qir/Runtime/test/QIR-static/CMakeLists.txt deleted file mode 100644 index 7e6c2f1234e..00000000000 --- a/src/Qir/Runtime/test/QIR-static/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# compile test ll files into a library - -set(TEST_FILES - qir-test-noqsharp - qir-gen -) - -foreach(file ${TEST_FILES}) - compile_from_qir(${file} "") # don't create a target per file - list(APPEND QIR_TESTS_LIBS ${QIR_UTILITY_LIB}) -endforeach() - -add_custom_target(qir_static_test_lib DEPENDS ${QIR_TESTS_LIBS}) - -#============================================================================== -# The executable target for QIR tests triggers the custom actions to compile ll 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 -) - -target_link_libraries(qir-static-tests PUBLIC - ${QIR_TESTS_LIBS} - ${QIR_BRIDGE_UTILITY_LIB} - ${QSHARP_FOUNDATION_BRIDGE_QIS_UTILITY_LIB} - ${QSHARP_CORE_BRIDGE_QIS_UTILITY_LIB} - qir-rt-support - qsharp-foundation-qis-support - qsharp-core-qis-support - simulators -) - -target_include_directories(qir-static-tests PUBLIC - "${test_includes}" - "${public_includes}" - "${PROJECT_SOURCE_DIR}/lib/QIR" - "${PROJECT_SOURCE_DIR}/lib/QSharpFoundation" -) -target_compile_definitions(qir-static-tests PRIVATE EXPORT_QIR_API) -add_dependencies(qir-static-tests qir_static_test_lib) - -install(TARGETS qir-static-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-static-tests) - diff --git a/src/Qir/Runtime/test/unittests/CMakeLists.txt b/src/Qir/Runtime/test/unittests/CMakeLists.txt deleted file mode 100644 index 29e3357f695..00000000000 --- a/src/Qir/Runtime/test/unittests/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -#============================================================================== -# produce the unit tests binary: qir-runtime-unittests.exe -# -add_executable(qir-runtime-unittests - driver.cpp - QirRuntimeTests.cpp - ToffoliTests.cpp - TracerTests.cpp -) - -target_link_libraries(qir-runtime-unittests PUBLIC - qir-rt-support - qsharp-foundation-qis-support - qsharp-core-qis-support - simulators - tracer -) - -target_include_directories(qir-runtime-unittests PUBLIC - "${test_includes}" - ${public_includes} - "${PROJECT_SOURCE_DIR}/lib/QIR" - "${PROJECT_SOURCE_DIR}/lib/QSharpFoundation" - "${PROJECT_SOURCE_DIR}/lib/QSharpCore" - "${PROJECT_SOURCE_DIR}/lib/Tracer" -) -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/CMakeLists.txt b/src/Qir/Runtime/unittests/CMakeLists.txt new file mode 100644 index 00000000000..40c41776d2a --- /dev/null +++ b/src/Qir/Runtime/unittests/CMakeLists.txt @@ -0,0 +1,30 @@ +include(unit_test_include) + +#============================================================================== +# produce the unit tests binary: qir-runtime-unittests.exe +# +add_executable(qir-runtime-unittests + driver.cpp + QirRuntimeTests.cpp + ToffoliTests.cpp + TracerTests.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/test/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp similarity index 97% rename from src/Qir/Runtime/test/unittests/QirRuntimeTests.cpp rename to src/Qir/Runtime/unittests/QirRuntimeTests.cpp index f149f388e8d..d76f37725b3 100644 --- a/src/Qir/Runtime/test/unittests/QirRuntimeTests.cpp +++ b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp @@ -1,1095 +1,1095 @@ -// 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; - -struct ResultsReferenceCountingTestQAPI : public SimulatorStub -{ - int lastId = -1; - const int maxResults; - std::vector allocated; - - static int GetResultId(Result r) - { - return static_cast(reinterpret_cast(r)); - } - - ResultsReferenceCountingTestQAPI(int maxResults) - : maxResults(maxResults), - allocated(maxResults, false) - { - } - - Result Measure(long, PauliId[], long, Qubit[]) override - { - assert(this->lastId < this->maxResults); - this->lastId++; - this->allocated.at(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(id)); - this->allocated.at(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); - QirContextScope 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]") -{ - QirContextScope qirctx(nullptr); - 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(a, 0) == 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(ab, 0) == 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]") -{ - QirContextScope qirctx(nullptr); - - const int64_t count = 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 (int i = 0; i < count; i++) - { - data[i] = 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]") -{ - QirContextScope qirctx(nullptr); - - 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]") -{ - QirContextScope qirctx(nullptr); - - 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(a, 0) == 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(a1, 0) == 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(ac, 0) == 5); - QirArray* ca = quantum__rt__array_concatenate(c, a); - REQUIRE(quantum__rt__array_get_size(ca, 0) == 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); -} - -TEST_CASE("Arrays: check the slice range", "[qir_support]") -{ - QirContextScope qirctx(nullptr); - - 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})); - - // violated bounds - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 1, dim0})); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 1, dim1})); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 1, dim0 - 1})); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -1, dim0})); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -1, 0})); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -1, -1})); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 3, dim0})); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 3, dim1 + 2})); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 3, dim0 - 1})); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -3, dim0})); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -3, 0})); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -3, -3})); - - // empty range should produce empty array - slice = quantum__rt__array_slice(a, 0, {dim0 - 1, 1, 0}); - 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}); - 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]") -{ - QirContextScope qirctx(nullptr); - - 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}); - 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}); - 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}); - 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]") -{ - QirContextScope qirctx(nullptr); - 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}); - 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); - - // reversed slices are alwayes disconnected - slice = quantum__rt__array_slice(a, 0, {dim - 1, -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]") -{ - QirContextScope qirctx(nullptr); - - 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 int64_t count = dim0 * dim1 * dim2; // 60 - std::vector data(count, 0); - for (int i = 0; i < count; i++) - { - data[i] = 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}); - 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}); // 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}); // 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}); // 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}); // 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}); // 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}); // 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]") -{ - QirContextScope qirctx(nullptr); - - 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 int64_t count = dim0 * dim1 * dim2; // 60 - std::vector data(count, 0); - for (int i = 0; i < count; i++) - { - data[i] = 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}); // 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}); - 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}); // 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); -} - -TEST_CASE("Arrays: project of 3D array", "[qir_support]") -{ - QirContextScope qirctx(nullptr); - - 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 int64_t count = dim0 * dim1 * dim2; // 60 - std::vector data(count, 0); - for (int i = 0; i < count; i++) - { - data[i] = 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]") -{ - QirContextScope qirctx(nullptr); - - 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]") -{ - QirContextScope qirctx(nullptr); - - 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]") -{ - QirContextScope qirctx(nullptr); - - 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]") -{ - QirContextScope qirctx(nullptr); - - 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; - const int maxQubits; - std::vector allocated; - - static int GetQubitId(Qubit q) - { - return static_cast(reinterpret_cast(q)); - } - - QubitTestQAPI(int maxQubits) - : maxQubits(maxQubits), - allocated(maxQubits, false) - { - } - - Qubit AllocateQubit() override - { - assert(this->lastId < this->maxQubits); - this->lastId++; - this->allocated.at(this->lastId) = true; - return reinterpret_cast(this->lastId); - } - void ReleaseQubit(Qubit qubit) override - { - const int id = GetQubitId(qubit); - INFO(id); - REQUIRE(this->allocated.at(id)); - this->allocated.at(id).flip(); - } - std::string QubitToString(Qubit qubit) override - { - const int 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); - QirContextScope 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); - REQUIRE(!qapi->HaveQubitsInFlight()); - - // both arrays now contain dangling pointers to qubits, but we still must release them - quantum__rt__array_update_reference_count(qs, -1); - quantum__rt__array_update_reference_count(copy, -1); -} - -QirTupleHeader* FlattenControlArrays(QirTupleHeader* nestedTuple, int depth); -struct ControlledCallablesTestSimulator : public SimulatorStub -{ - int lastId = -1; - Qubit AllocateQubit() override - { - return reinterpret_cast(++this->lastId); - } - void ReleaseQubit(Qubit 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(); - QirContextScope 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(/*QirArrray*/ void*) + sizeof(/*Qubit*/ void*)); - TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); - innerWithControls->controls = controlsInner; - *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/*QirArrray*/ void*)) = target; - - PTuple outer = quantum__rt__tuple_create(sizeof(/*QirArrray*/ 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); - quantum__rt__array_update_reference_count(controlsOuter, -1); - quantum__rt__qubit_release_array(controlsInner); - quantum__rt__array_update_reference_count(controlsInner, -1); - quantum__rt__qubit_release(target); -} - -TEST_CASE("Unpacking input tuples of nested callables (case1)", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirContextScope 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(/*QirArrray*/ void*) + sizeof(/*Tuple*/ void*)); - TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); - innerWithControls->controls = controlsInner; - *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/*QirArrray*/ void*)) = args; - - PTuple outer = quantum__rt__tuple_create(sizeof(/*QirArrray*/ 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(/*QirArrray*/ 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); - quantum__rt__array_update_reference_count(controlsOuter, -1); - quantum__rt__qubit_release_array(controlsInner); - quantum__rt__array_update_reference_count(controlsInner, -1); - quantum__rt__qubit_release(target); -} - -TEST_CASE("Allocation tracking for arrays", "[qir_support]") -{ - InitializeQirContext(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(ReleaseQirContext()); - - quantum__rt__array_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(ReleaseQirContext()); -} - -TEST_CASE("Allocation tracking for tuples", "[qir_support]") -{ - InitializeQirContext(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(ReleaseQirContext()); - - quantum__rt__tuple_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(ReleaseQirContext()); -} - -static void NoopCallableEntry(PTuple, PTuple, PTuple) {} -TEST_CASE("Allocation tracking for callables", "[qir_support]") -{ - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - - InitializeQirContext(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(ReleaseQirContext()); - - quantum__rt__callable_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(ReleaseQirContext()); -} - -TEST_CASE("Callables: copy elision", "[qir_support]") -{ - QirContextScope 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 -{ - int lastId = -1; - double rotationAngle = 0.0; - double exponentAngle = 0.0; - - Qubit AllocateQubit() override - { - return reinterpret_cast(++this->lastId); - } - void ReleaseQubit(Qubit qubit) override {} - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - - void R(PauliId, Qubit, double theta) override - { - this->rotationAngle += theta; - } - void Exp(long count, PauliId* paulis, Qubit*, 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, Qubit*, PauliId, Qubit, double theta) override - { - this->rotationAngle += theta; - } - void ControlledExp(long, Qubit*, long count, PauliId* paulis, Qubit*, 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(); - QirContextScope 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); - quantum__qis__r__ctl(ctrls, PauliId_X, angle, target); - quantum__qis__r__ctladj(ctrls, PauliId_X, angle, target); - - quantum__rt__qubit_release_array(ctrls); - quantum__rt__array_update_reference_count(ctrls, -1); - 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(); - QirContextScope 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] = 2; - axes->buffer[1] = 3; - - quantum__qis__exp__body(axes, angle, targets); - quantum__qis__exp__adj(axes, angle, targets); - quantum__qis__exp__ctl(ctrls, axes, angle, targets); - quantum__qis__exp__ctladj(ctrls, axes, angle, targets); - - quantum__rt__qubit_release_array(ctrls); - quantum__rt__array_update_reference_count(ctrls, -1); - quantum__rt__qubit_release_array(targets); - quantum__rt__array_update_reference_count(targets, -1); - - REQUIRE(qapi->exponentAngle == Approx(0).epsilon(0.0001)); -} +// 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; + +struct ResultsReferenceCountingTestQAPI : public SimulatorStub +{ + int lastId = -1; + const int maxResults; + std::vector allocated; + + static int GetResultId(Result r) + { + return static_cast(reinterpret_cast(r)); + } + + ResultsReferenceCountingTestQAPI(int maxResults) + : maxResults(maxResults), + allocated(maxResults, false) + { + } + + Result Measure(long, PauliId[], long, Qubit[]) override + { + assert(this->lastId < this->maxResults); + this->lastId++; + this->allocated.at(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(id)); + this->allocated.at(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); + QirContextScope 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]") +{ + QirContextScope qirctx(nullptr); + 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(a, 0) == 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(ab, 0) == 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]") +{ + QirContextScope qirctx(nullptr); + + const int64_t count = 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 (int i = 0; i < count; i++) + { + data[i] = 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]") +{ + QirContextScope qirctx(nullptr); + + 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]") +{ + QirContextScope qirctx(nullptr); + + 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(a, 0) == 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(a1, 0) == 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(ac, 0) == 5); + QirArray* ca = quantum__rt__array_concatenate(c, a); + REQUIRE(quantum__rt__array_get_size(ca, 0) == 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); +} + +TEST_CASE("Arrays: check the slice range", "[qir_support]") +{ + QirContextScope qirctx(nullptr); + + 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})); + + // violated bounds + CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 1, dim0})); + CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 1, dim1})); + CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 1, dim0 - 1})); + + CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -1, dim0})); + CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -1, 0})); + CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -1, -1})); + + CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 3, dim0})); + CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 3, dim1 + 2})); + CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 3, dim0 - 1})); + + CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -3, dim0})); + CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -3, 0})); + CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -3, -3})); + + // empty range should produce empty array + slice = quantum__rt__array_slice(a, 0, {dim0 - 1, 1, 0}); + 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}); + 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]") +{ + QirContextScope qirctx(nullptr); + + 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}); + 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}); + 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}); + 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]") +{ + QirContextScope qirctx(nullptr); + 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}); + 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); + + // reversed slices are alwayes disconnected + slice = quantum__rt__array_slice(a, 0, {dim - 1, -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]") +{ + QirContextScope qirctx(nullptr); + + 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 int64_t count = dim0 * dim1 * dim2; // 60 + std::vector data(count, 0); + for (int i = 0; i < count; i++) + { + data[i] = 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}); + 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}); // 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}); // 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}); // 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}); // 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}); // 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}); // 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]") +{ + QirContextScope qirctx(nullptr); + + 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 int64_t count = dim0 * dim1 * dim2; // 60 + std::vector data(count, 0); + for (int i = 0; i < count; i++) + { + data[i] = 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}); // 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}); + 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}); // 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); +} + +TEST_CASE("Arrays: project of 3D array", "[qir_support]") +{ + QirContextScope qirctx(nullptr); + + 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 int64_t count = dim0 * dim1 * dim2; // 60 + std::vector data(count, 0); + for (int i = 0; i < count; i++) + { + data[i] = 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]") +{ + QirContextScope qirctx(nullptr); + + 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]") +{ + QirContextScope qirctx(nullptr); + + 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]") +{ + QirContextScope qirctx(nullptr); + + 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]") +{ + QirContextScope qirctx(nullptr); + + 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; + const int maxQubits; + std::vector allocated; + + static int GetQubitId(Qubit q) + { + return static_cast(reinterpret_cast(q)); + } + + QubitTestQAPI(int maxQubits) + : maxQubits(maxQubits), + allocated(maxQubits, false) + { + } + + Qubit AllocateQubit() override + { + assert(this->lastId < this->maxQubits); + this->lastId++; + this->allocated.at(this->lastId) = true; + return reinterpret_cast(this->lastId); + } + void ReleaseQubit(Qubit qubit) override + { + const int id = GetQubitId(qubit); + INFO(id); + REQUIRE(this->allocated.at(id)); + this->allocated.at(id).flip(); + } + std::string QubitToString(Qubit qubit) override + { + const int 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); + QirContextScope 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); + REQUIRE(!qapi->HaveQubitsInFlight()); + + // both arrays now contain dangling pointers to qubits, but we still must release them + quantum__rt__array_update_reference_count(qs, -1); + quantum__rt__array_update_reference_count(copy, -1); +} + +QirTupleHeader* FlattenControlArrays(QirTupleHeader* nestedTuple, int depth); +struct ControlledCallablesTestSimulator : public SimulatorStub +{ + int lastId = -1; + Qubit AllocateQubit() override + { + return reinterpret_cast(++this->lastId); + } + void ReleaseQubit(Qubit 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(); + QirContextScope 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(/*QirArrray*/ void*) + sizeof(/*Qubit*/ void*)); + TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); + innerWithControls->controls = controlsInner; + *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/*QirArrray*/ void*)) = target; + + PTuple outer = quantum__rt__tuple_create(sizeof(/*QirArrray*/ 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); + quantum__rt__array_update_reference_count(controlsOuter, -1); + quantum__rt__qubit_release_array(controlsInner); + quantum__rt__array_update_reference_count(controlsInner, -1); + quantum__rt__qubit_release(target); +} + +TEST_CASE("Unpacking input tuples of nested callables (case1)", "[qir_support]") +{ + std::unique_ptr qapi = std::make_unique(); + QirContextScope 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(/*QirArrray*/ void*) + sizeof(/*Tuple*/ void*)); + TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); + innerWithControls->controls = controlsInner; + *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/*QirArrray*/ void*)) = args; + + PTuple outer = quantum__rt__tuple_create(sizeof(/*QirArrray*/ 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(/*QirArrray*/ 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); + quantum__rt__array_update_reference_count(controlsOuter, -1); + quantum__rt__qubit_release_array(controlsInner); + quantum__rt__array_update_reference_count(controlsInner, -1); + quantum__rt__qubit_release(target); +} + +TEST_CASE("Allocation tracking for arrays", "[qir_support]") +{ + InitializeQirContext(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(ReleaseQirContext()); + + quantum__rt__array_update_reference_count(maybeLeaked, -1); + CHECK_NOTHROW(ReleaseQirContext()); +} + +TEST_CASE("Allocation tracking for tuples", "[qir_support]") +{ + InitializeQirContext(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(ReleaseQirContext()); + + quantum__rt__tuple_update_reference_count(maybeLeaked, -1); + CHECK_NOTHROW(ReleaseQirContext()); +} + +static void NoopCallableEntry(PTuple, PTuple, PTuple) {} +TEST_CASE("Allocation tracking for callables", "[qir_support]") +{ + t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; + + InitializeQirContext(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(ReleaseQirContext()); + + quantum__rt__callable_update_reference_count(maybeLeaked, -1); + CHECK_NOTHROW(ReleaseQirContext()); +} + +TEST_CASE("Callables: copy elision", "[qir_support]") +{ + QirContextScope 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 +{ + int lastId = -1; + double rotationAngle = 0.0; + double exponentAngle = 0.0; + + Qubit AllocateQubit() override + { + return reinterpret_cast(++this->lastId); + } + void ReleaseQubit(Qubit qubit) override {} + Result UseZero() override + { + return reinterpret_cast(0); + } + Result UseOne() override + { + return reinterpret_cast(1); + } + + void R(PauliId, Qubit, double theta) override + { + this->rotationAngle += theta; + } + void Exp(long count, PauliId* paulis, Qubit*, 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, Qubit*, PauliId, Qubit, double theta) override + { + this->rotationAngle += theta; + } + void ControlledExp(long, Qubit*, long count, PauliId* paulis, Qubit*, 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(); + QirContextScope 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); + quantum__qis__r__ctl(ctrls, PauliId_X, angle, target); + quantum__qis__r__ctladj(ctrls, PauliId_X, angle, target); + + quantum__rt__qubit_release_array(ctrls); + quantum__rt__array_update_reference_count(ctrls, -1); + 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(); + QirContextScope 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] = 2; + axes->buffer[1] = 3; + + quantum__qis__exp__body(axes, angle, targets); + quantum__qis__exp__adj(axes, angle, targets); + quantum__qis__exp__ctl(ctrls, axes, angle, targets); + quantum__qis__exp__ctladj(ctrls, axes, angle, targets); + + quantum__rt__qubit_release_array(ctrls); + quantum__rt__array_update_reference_count(ctrls, -1); + quantum__rt__qubit_release_array(targets); + quantum__rt__array_update_reference_count(targets, -1); + + REQUIRE(qapi->exponentAngle == Approx(0).epsilon(0.0001)); +} diff --git a/src/Qir/Runtime/test/unittests/ToffoliTests.cpp b/src/Qir/Runtime/unittests/ToffoliTests.cpp similarity index 96% rename from src/Qir/Runtime/test/unittests/ToffoliTests.cpp rename to src/Qir/Runtime/unittests/ToffoliTests.cpp index 84777cacf77..b0fb3643032 100644 --- a/src/Qir/Runtime/test/unittests/ToffoliTests.cpp +++ b/src/Qir/Runtime/unittests/ToffoliTests.cpp @@ -1,130 +1,130 @@ -// 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, Qubit q) -{ - PauliId pauliZ[1] = {PauliId_Z}; - Qubit 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++) - { - Qubit q = sim->AllocateQubit(); - qubits.push_back(q); - if ((i & 0x1) == 1) - { - iqa->X(q); - } - } - - long sum = 0; - for (Qubit 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()); - - Qubit 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; - Qubit 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, "")); +// 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, Qubit q) +{ + PauliId pauliZ[1] = {PauliId_Z}; + Qubit 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++) + { + Qubit q = sim->AllocateQubit(); + qubits.push_back(q); + if ((i & 0x1) == 1) + { + iqa->X(q); + } + } + + long sum = 0; + for (Qubit 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()); + + Qubit 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; + Qubit 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, "")); } \ No newline at end of file diff --git a/src/Qir/Runtime/test/unittests/TracerTests.cpp b/src/Qir/Runtime/unittests/TracerTests.cpp similarity index 97% rename from src/Qir/Runtime/test/unittests/TracerTests.cpp rename to src/Qir/Runtime/unittests/TracerTests.cpp index d1ff4711036..802d0a429e8 100644 --- a/src/Qir/Runtime/test/unittests/TracerTests.cpp +++ b/src/Qir/Runtime/unittests/TracerTests.cpp @@ -1,483 +1,483 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include - -#include "catch.hpp" - -#include "CoreTypes.hpp" -#include "tracer.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -TEST_CASE("Layering distinct single-qubit operations of non-zero durations", "[tracer]") -{ - shared_ptr tr = CreateTracer(3 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit 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 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]") -{ - shared_ptr tr = CreateTracer(3 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit 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 vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 1); - CHECK(layers[0].operations.size() == 5); -} - -TEST_CASE("Layering distinct controlled single-qubit operations", "[tracer]") -{ - shared_ptr tr = CreateTracer(3 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit q3 = tr->AllocateQubit(); - Qubit q4 = tr->AllocateQubit(); - Qubit q5 = tr->AllocateQubit(); - Qubit 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 - - Qubit 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 - - Qubit 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 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]") -{ - shared_ptr tr = CreateTracer(3 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit 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 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]") -{ - shared_ptr tr = CreateTracer(2 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit q3 = tr->AllocateQubit(); - Qubit 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 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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit q3 = tr->AllocateQubit(); - Qubit q4 = tr->AllocateQubit(); - - CHECK(0 == tr->GetLayerIdOfSourceMeasurement(tr->TraceSingleQubitMeasurement(1, 1, q1))); - Qubit 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))); - Qubit 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]") -{ - shared_ptr tr = CreateTracer(3 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit 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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit 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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit 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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit 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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit q3 = tr->AllocateQubit(); - Qubit q4 = tr->AllocateQubit(); - Qubit 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 fs(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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit q3 = tr->AllocateQubit(); - Qubit q4 = tr->AllocateQubit(); - Qubit 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 fs(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]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/); - - Qubit q1 = tr->AllocateQubit(); - Qubit q2 = tr->AllocateQubit(); - Qubit q3 = tr->AllocateQubit(); - Qubit q4 = tr->AllocateQubit(); - Qubit 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"}}; - shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); - - Qubit 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"}}; - shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); - - Qubit 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); - 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()); -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include + +#include "catch.hpp" + +#include "CoreTypes.hpp" +#include "tracer.hpp" + +using namespace std; +using namespace Microsoft::Quantum; + +TEST_CASE("Layering distinct single-qubit operations of non-zero durations", "[tracer]") +{ + shared_ptr tr = CreateTracer(3 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit 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 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]") +{ + shared_ptr tr = CreateTracer(3 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit 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 vector& layers = tr->UseLayers(); + REQUIRE(layers.size() == 1); + CHECK(layers[0].operations.size() == 5); +} + +TEST_CASE("Layering distinct controlled single-qubit operations", "[tracer]") +{ + shared_ptr tr = CreateTracer(3 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit q3 = tr->AllocateQubit(); + Qubit q4 = tr->AllocateQubit(); + Qubit q5 = tr->AllocateQubit(); + Qubit 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 + + Qubit 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 + + Qubit 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 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]") +{ + shared_ptr tr = CreateTracer(3 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit 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 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]") +{ + shared_ptr tr = CreateTracer(2 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit q3 = tr->AllocateQubit(); + Qubit 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 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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit q3 = tr->AllocateQubit(); + Qubit q4 = tr->AllocateQubit(); + + CHECK(0 == tr->GetLayerIdOfSourceMeasurement(tr->TraceSingleQubitMeasurement(1, 1, q1))); + Qubit 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))); + Qubit 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]") +{ + shared_ptr tr = CreateTracer(3 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit 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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit 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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit 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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit 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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit q3 = tr->AllocateQubit(); + Qubit q4 = tr->AllocateQubit(); + Qubit 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 fs(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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit q3 = tr->AllocateQubit(); + Qubit q4 = tr->AllocateQubit(); + Qubit 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 fs(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]") +{ + shared_ptr tr = CreateTracer(1 /*layer duration*/); + + Qubit q1 = tr->AllocateQubit(); + Qubit q2 = tr->AllocateQubit(); + Qubit q3 = tr->AllocateQubit(); + Qubit q4 = tr->AllocateQubit(); + Qubit 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"}}; + shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); + + Qubit 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"}}; + shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); + + Qubit 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); + 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/test/unittests/driver.cpp b/src/Qir/Runtime/unittests/driver.cpp similarity index 97% rename from src/Qir/Runtime/test/unittests/driver.cpp rename to src/Qir/Runtime/unittests/driver.cpp index f8f43d73242..9795d69e2c7 100644 --- a/src/Qir/Runtime/test/unittests/driver.cpp +++ b/src/Qir/Runtime/unittests/driver.cpp @@ -1,8 +1,8 @@ -// 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 - +// 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 new file mode 100644 index 00000000000..10a8a657b63 --- /dev/null +++ b/src/Qir/Samples/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.10) + +message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") + +# set the project name and version +project(qir-samples) + +# specify the C++ standard, compiler and other tools +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") + +if(WIN32) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DLL -D_MT -Xclang --dependent-lib=msvcrt") + endif() +endif() + +# 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}/../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/build/${CMAKE_BUILD_TYPE}/bin") + +include(qir_cmake_include) + +add_subdirectory(StandaloneInputReference) diff --git a/src/Qir/Runtime/samples/StandaloneInputReference/.clang-tidy b/src/Qir/Samples/StandaloneInputReference/.clang-tidy similarity index 100% rename from src/Qir/Runtime/samples/StandaloneInputReference/.clang-tidy rename to src/Qir/Samples/StandaloneInputReference/.clang-tidy diff --git a/src/Qir/Runtime/samples/StandaloneInputReference/CMakeLists.txt b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt similarity index 80% rename from src/Qir/Runtime/samples/StandaloneInputReference/CMakeLists.txt rename to src/Qir/Samples/StandaloneInputReference/CMakeLists.txt index 293d1fb433f..081ebd87ca0 100644 --- a/src/Qir/Runtime/samples/StandaloneInputReference/CMakeLists.txt +++ b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt @@ -6,27 +6,22 @@ add_executable(qir-input-reference-standalone ) # This is where the generated QIR file is specified. -compile_from_qir(qir-standalone-input-reference qir_standalone_input_reference_target) +target_source_from_qir(qir-input-reference-standalone qsharp/qir/qir-standalone-input-reference.ll) target_link_libraries(qir-input-reference-standalone PUBLIC - ${QIR_UTILITY_LIB} # created by compile_from_qir - "-L${CMAKE_BINARY_DIR}/lib/QIR" + "-L${runtime_lib_path}" -lMicrosoft.Quantum.Qir.Runtime - "-L${CMAKE_BINARY_DIR}/lib/QSharpFoundation" -lMicrosoft.Quantum.Qir.QSharp.Foundation - "-L${CMAKE_BINARY_DIR}/lib/QSharpCore" -lMicrosoft.Quantum.Qir.QSharp.Core ) -set(standalone_includes "${PROJECT_SOURCE_DIR}/externals/CLI11") +set(standalone_includes "${PROJECT_SOURCE_DIR}/../Common/externals/CLI11") target_include_directories(qir-input-reference-standalone PUBLIC "${standalone_includes}" "${public_includes}" ) -add_dependencies(qir-input-reference-standalone qir_standalone_input_reference_target) - install(TARGETS qir-input-reference-standalone RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") include(CTest) @@ -42,7 +37,7 @@ else() set(TEST_DEPS1 "${PROJECT_SOURCE_DIR}/../../Simulation/native/build/${CMAKE_BUILD_TYPE}") endif() -set(TEST_DEPS2 "${CMAKE_BINARY_DIR}/bin") +set(TEST_DEPS2 "${PROJECT_SOURCE_DIR}/../Runtime/build/${CMAKE_BUILD_TYPE}/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}" diff --git a/src/Qir/Runtime/samples/StandaloneInputReference/qir-driver.cpp b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp similarity index 100% rename from src/Qir/Runtime/samples/StandaloneInputReference/qir-driver.cpp rename to src/Qir/Samples/StandaloneInputReference/qir-driver.cpp diff --git a/src/Qir/Runtime/samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj b/src/Qir/Samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj similarity index 70% rename from src/Qir/Runtime/samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj rename to src/Qir/Samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj index d016002f5fd..cb77ce14d52 100644 --- a/src/Qir/Runtime/samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj +++ b/src/Qir/Samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Qir/Runtime/samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.qs b/src/Qir/Samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.qs similarity index 100% rename from src/Qir/Runtime/samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.qs rename to src/Qir/Samples/StandaloneInputReference/qsharp/qir-standalone-input-reference.qs diff --git a/src/Qir/Runtime/samples/StandaloneInputReference/readme.md b/src/Qir/Samples/StandaloneInputReference/readme.md similarity index 100% rename from src/Qir/Runtime/samples/StandaloneInputReference/readme.md rename to src/Qir/Samples/StandaloneInputReference/readme.md diff --git a/src/Qir/Samples/build-qir-samples.ps1 b/src/Qir/Samples/build-qir-samples.ps1 new file mode 100644 index 00000000000..e56bc082786 --- /dev/null +++ b/src/Qir/Samples/build-qir-samples.ps1 @@ -0,0 +1,18 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +[CmdletBinding()] +param ( + [Parameter()] + [Switch] + $SkipQSharpBuild +) + +. (Join-Path $PSScriptRoot .. qir-utils.ps1) + +Write-Host "##[info]Compile Q# Projects into QIR" +Build-QirProject (Join-Path $PSScriptRoot StandaloneInputReference qsharp) -SkipQSharpBuild:$SkipQSharpBuild + +if (-not (Build-CMakeProject $PSScriptRoot "QIR Samples")) { + throw "At least one project failed to compile. Check the logs." +} \ No newline at end of file diff --git a/src/Qir/Samples/test-qir-samples.ps1 b/src/Qir/Samples/test-qir-samples.ps1 new file mode 100644 index 00000000000..f4825010c94 --- /dev/null +++ b/src/Qir/Samples/test-qir-samples.ps1 @@ -0,0 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +. (Join-Path $PSScriptRoot .. qir-utils.ps1) + +if (-not (Test-CTest (Join-Path $PSScriptRoot build $Env:BUILD_CONFIGURATION StandaloneInputReference) "QIR Samples (StandaloneInputReference)")) { + throw "At least one project failed testing. Check the logs." +} diff --git a/src/Qir/Tests/CMakeLists.txt b/src/Qir/Tests/CMakeLists.txt new file mode 100644 index 00000000000..66fe8e15d8b --- /dev/null +++ b/src/Qir/Tests/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.10) + +message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") + +# set the project name and version +project(qir-tests) + +# specify the C++ standard, compiler and other tools +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") + +if(WIN32) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DLL -D_MT -Xclang --dependent-lib=msvcrt") + endif() +endif() + +# 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}/../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/build/${CMAKE_BUILD_TYPE}/bin") + +include(qir_cmake_include) +include(unit_test_include) + +add_subdirectory(FullstateSimulator) +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 new file mode 100644 index 00000000000..8ff31f20eba --- /dev/null +++ b/src/Qir/Tests/FullstateSimulator/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(fullstate-simulator-tests + FullstateSimulatorTests.cpp) + +target_source_from_qir(fullstate-simulator-tests qsharp/qir/qir-test-simulator.ll) + +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 +) + +target_include_directories(fullstate-simulator-tests PUBLIC + ${test_includes} + ${public_includes} +) + +install(TARGETS fullstate-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +add_unit_test(fullstate-simulator-tests) + diff --git a/src/Qir/Runtime/test/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp similarity index 96% rename from src/Qir/Runtime/test/FullstateSimulator/FullstateSimulatorTests.cpp rename to src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index 2e9bc0fb351..39a168f2ccc 100644 --- a/src/Qir/Runtime/test/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -1,363 +1,363 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -#include "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. -unsigned GetQubitId(Qubit q) -{ - return static_cast(reinterpret_cast(q)); -} - -static Result MZ(IQuantumGateSet* iqa, Qubit q) -{ - PauliId pauliZ[1] = {PauliId_Z}; - Qubit qs[1] = {q}; - return iqa->Measure(1, pauliZ, 1, qs); -} - -TEST_CASE("Fullstate simulator: allocate qubits", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - - Qubit q0 = sim->AllocateQubit(); - Qubit 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(); - Qubit q1 = sim1->AllocateQubit(); - - std::unique_ptr sim2 = CreateFullstateSimulator(); - Qubit 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()); - - Qubit q = sim->AllocateQubit(); - Result r1 = MZ(iqa, q); - REQUIRE(Result_Zero == sim->GetResultValue(r1)); - REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); - - iqa->X(q); - Result r2 = MZ(iqa, q); - REQUIRE(Result_One == sim->GetResultValue(r2)); - REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); - - sim->ReleaseQubit(q); - sim->ReleaseResult(r1); - sim->ReleaseResult(r2); -} - -TEST_CASE("Fullstate simulator: measure Bell state", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - Qubit q1 = sim->AllocateQubit(); - Qubit 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()); - - Qubit 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)); - - 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()); - - Qubit 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(), "")); - - 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()); - - Qubit 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]))); - - 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()); - - Qubit 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()); - - Qubit 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()); - - Qubit 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()); - - Qubit 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; - - Qubit 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); - - // 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; - - Qubit 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([](size_t idx, double re, double im) { - norm += re * re + im * im; - 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([](size_t idx, double re, double im) { - norm += re * re + im * im; - 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; - - for (int i = 0; i < n; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body(); // NOLINT -TEST_CASE("QIR: invoke all standard Q# gates against the fullstate simulator", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/); - - REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body()); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +#include "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. +unsigned GetQubitId(Qubit q) +{ + return static_cast(reinterpret_cast(q)); +} + +static Result MZ(IQuantumGateSet* iqa, Qubit q) +{ + PauliId pauliZ[1] = {PauliId_Z}; + Qubit qs[1] = {q}; + return iqa->Measure(1, pauliZ, 1, qs); +} + +TEST_CASE("Fullstate simulator: allocate qubits", "[fullstate_simulator]") +{ + std::unique_ptr sim = CreateFullstateSimulator(); + + Qubit q0 = sim->AllocateQubit(); + Qubit 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(); + Qubit q1 = sim1->AllocateQubit(); + + std::unique_ptr sim2 = CreateFullstateSimulator(); + Qubit 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()); + + Qubit q = sim->AllocateQubit(); + Result r1 = MZ(iqa, q); + REQUIRE(Result_Zero == sim->GetResultValue(r1)); + REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); + + iqa->X(q); + Result r2 = MZ(iqa, q); + REQUIRE(Result_One == sim->GetResultValue(r2)); + REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); + + sim->ReleaseQubit(q); + sim->ReleaseResult(r1); + sim->ReleaseResult(r2); +} + +TEST_CASE("Fullstate simulator: measure Bell state", "[fullstate_simulator]") +{ + std::unique_ptr sim = CreateFullstateSimulator(); + IQuantumGateSet* iqa = dynamic_cast(sim.get()); + + Qubit q1 = sim->AllocateQubit(); + Qubit 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()); + + Qubit 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)); + + 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()); + + Qubit 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(), "")); + + 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()); + + Qubit 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]))); + + 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()); + + Qubit 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()); + + Qubit 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()); + + Qubit 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()); + + Qubit 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; + + Qubit 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); + + // 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; + + Qubit 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([](size_t idx, double re, double im) { + norm += re * re + im * im; + 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([](size_t idx, double re, double im) { + norm += re * re + im * im; + 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; + + for (int i = 0; i < n; i++) + { + sim->ReleaseQubit(qs[i]); + } +} + +extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body(); // NOLINT +TEST_CASE("QIR: invoke all standard Q# gates against the fullstate simulator", "[fullstate_simulator]") +{ + std::unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/); + + REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body()); } \ No newline at end of file diff --git a/src/Qir/Runtime/test/FullstateSimulator/qsharp/qir-test-simulator.csproj b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.csproj similarity index 75% rename from src/Qir/Runtime/test/FullstateSimulator/qsharp/qir-test-simulator.csproj rename to src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.csproj index c5081e012d5..7e736af46ba 100644 --- a/src/Qir/Runtime/test/FullstateSimulator/qsharp/qir-test-simulator.csproj +++ b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Qir/Runtime/test/FullstateSimulator/qsharp/qir-test-simulator.qs b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs similarity index 100% rename from src/Qir/Runtime/test/FullstateSimulator/qsharp/qir-test-simulator.qs rename to src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs diff --git a/src/Qir/Runtime/test/QIR-dynamic/CMakeLists.txt b/src/Qir/Tests/QIR-dynamic/CMakeLists.txt similarity index 63% rename from src/Qir/Runtime/test/QIR-dynamic/CMakeLists.txt rename to src/Qir/Tests/QIR-dynamic/CMakeLists.txt index ffdbcaeed53..a0b8a6f9dc2 100644 --- a/src/Qir/Runtime/test/QIR-dynamic/CMakeLists.txt +++ b/src/Qir/Tests/QIR-dynamic/CMakeLists.txt @@ -1,37 +1,30 @@ -set(TEST_FILES - qir-test-random -) - -foreach(file ${TEST_FILES}) - compile_from_qir(${file} "") # don't create a target per file - list(APPEND QIR_TESTS_LIBS ${QIR_UTILITY_LIB}) -endforeach() - -add_custom_target(qir_dynamic_test_lib DEPENDS ${QIR_TESTS_LIBS}) - -#============================================================================== -# 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 -) - -target_link_libraries(qir-dynamic-tests PUBLIC - ${QIR_TESTS_LIBS} - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - "-L${CMAKE_BINARY_DIR}/lib/QSharpFoundation" - -lMicrosoft.Quantum.Qir.QSharp.Foundation - "-L${CMAKE_BINARY_DIR}/lib/QSharpCore" - -lMicrosoft.Quantum.Qir.QSharp.Core -) - -target_include_directories(qir-dynamic-tests PUBLIC - ${test_includes} - ${public_includes} -) -add_dependencies(qir-dynamic-tests qir_dynamic_test_lib) - -install(TARGETS qir-dynamic-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-dynamic-tests) +set(TEST_FILES + qsharp/qir/qir-test-random.ll +) + +#============================================================================== +# 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 +) + +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 +) + +target_include_directories(qir-dynamic-tests PUBLIC + ${test_includes} + ${public_includes} +) + +install(TARGETS qir-dynamic-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +add_unit_test(qir-dynamic-tests) diff --git a/src/Qir/Runtime/test/QIR-dynamic/qir-driver.cpp b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp similarity index 97% rename from src/Qir/Runtime/test/QIR-dynamic/qir-driver.cpp rename to src/Qir/Tests/QIR-dynamic/qir-driver.cpp index b96458c79a3..fc6a6129b8a 100644 --- a/src/Qir/Runtime/test/QIR-dynamic/qir-driver.cpp +++ b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp @@ -1,36 +1,36 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -// 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; -} - -extern "C" void SetupQirToRunOnFullStateSimulator(); -extern "C" int64_t Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); // NOLINT -TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") -{ - SetupQirToRunOnFullStateSimulator(); - - const int ret1 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); - const int ret2 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); - const int ret3 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); - INFO( - std::string("Three random numbers: ") + std::to_string(ret1) + ", " + std::to_string(ret2) + ", " + - std::to_string(ret3)); - - // Check that the returned numbers are at least somewhat random... - CHECK(ret1 != ret2); - CHECK(ret1 != ret3); - CHECK(ret2 != ret3); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +// 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; +} + +extern "C" void SetupQirToRunOnFullStateSimulator(); +extern "C" int64_t Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); // NOLINT +TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") +{ + SetupQirToRunOnFullStateSimulator(); + + const int ret1 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); + const int ret2 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); + const int ret3 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); + INFO( + std::string("Three random numbers: ") + std::to_string(ret1) + ", " + std::to_string(ret2) + ", " + + std::to_string(ret3)); + + // Check that the returned numbers are at least somewhat random... + CHECK(ret1 != ret2); + CHECK(ret1 != ret3); + CHECK(ret2 != ret3); +} diff --git a/src/Qir/Runtime/test/QIR-dynamic/qsharp/qir-test-random.csproj b/src/Qir/Tests/QIR-dynamic/qsharp/qir-test-random.csproj similarity index 75% rename from src/Qir/Runtime/test/QIR-dynamic/qsharp/qir-test-random.csproj rename to src/Qir/Tests/QIR-dynamic/qsharp/qir-test-random.csproj index c5081e012d5..7e736af46ba 100644 --- a/src/Qir/Runtime/test/QIR-dynamic/qsharp/qir-test-random.csproj +++ b/src/Qir/Tests/QIR-dynamic/qsharp/qir-test-random.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Qir/Runtime/test/QIR-dynamic/qsharp/qir-test-random.qs b/src/Qir/Tests/QIR-dynamic/qsharp/qir-test-random.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-dynamic/qsharp/qir-test-random.qs rename to src/Qir/Tests/QIR-dynamic/qsharp/qir-test-random.qs diff --git a/src/Qir/Tests/QIR-static/CMakeLists.txt b/src/Qir/Tests/QIR-static/CMakeLists.txt new file mode 100644 index 00000000000..323d8222a7e --- /dev/null +++ b/src/Qir/Tests/QIR-static/CMakeLists.txt @@ -0,0 +1,38 @@ +set(TEST_FILES + qir-test-noqsharp.ll + qsharp/qir/qir-gen.ll +) + +#============================================================================== +# The executable target for QIR tests triggers the custom actions to compile ll 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 +) + +foreach(file ${TEST_FILES}) + target_source_from_qir(qir-static-tests ${file}) +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 +) + +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/Runtime/test/QIR-static/qir-driver.cpp b/src/Qir/Tests/QIR-static/qir-driver.cpp similarity index 97% rename from src/Qir/Runtime/test/QIR-static/qir-driver.cpp rename to src/Qir/Tests/QIR-static/qir-driver.cpp index a400a69f76b..5d8180e2206 100644 --- a/src/Qir/Runtime/test/QIR-static/qir-driver.cpp +++ b/src/Qir/Tests/QIR-static/qir-driver.cpp @@ -1,335 +1,335 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include - -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "SimulatorStub.hpp" -#include "QirRuntime.hpp" - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -// 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; - -// 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; -} - -/* -Forward declared `extern "C"` methods below are implemented in the *.ll files. Most of the files are generated by Q# -compiler, in which case the corresponding *.qs file was used as a source. Some files might have been authored manually -or manually edited. - -To update the *.ll files to a newer version: -- enlist and build qsharp-compiler -- find \qsc.exe and \QirCore.qs, \QirTarget.qs built files -- [if different] copy QirCore.qs and QirTarget.qs into the "compiler" folder -- run: qsc.exe build --qir s --build-exe --input name.qs compiler\qircore.qs compiler\qirtarget.qs --proj name -- the generated file name.ll will be placed into `s` folder -*/ - -struct Array { - int64_t itemSize; - void* buffer; -}; - -// The function replaces array[index] with value, then creates a new array that consists of every other element up to -// index (starting from index backwards) and every element from index to the end. It returns the sum of elements in this -// new array -extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop( // NOLINT - Array* array, - int64_t index, - int64_t val, - bool compilerDecoy); -TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") -{ - QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); - - constexpr int64_t n = 5; - int64_t values[n] = {0, 1, 2, 3, 4}; - auto array = Array{5, values}; - - int64_t res = Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop(&array, 2, 42, false); - REQUIRE(res == (0 + 42) + (42 + 3 + 4)); -} - -extern "C" void Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__body(); // 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) - - int GetQubitId(Qubit qubit) const - { - const int id = static_cast(reinterpret_cast(qubit)); - REQUIRE(id >= 0); - REQUIRE(id < this->qubits.size()); - - return id; - } - - int GetResultId(Result result) const - { - const int id = static_cast(reinterpret_cast(result)); - REQUIRE(id >= 0); - REQUIRE(id < this->results.size()); - - return id; - } - - Qubit AllocateQubit() override - { - qubits.push_back(0); - return reinterpret_cast(this->qubits.size() - 1); - } - - void ReleaseQubit(Qubit qubit) override - { - const int id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // no double-release - this->qubits[id] = RELEASED; - } - - void X(Qubit qubit) override - { - const int id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->qubits[id] = 1 - this->qubits[id]; - } - - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override - { - assert(numBases == 1 && "QubitsResultsTestSimulator doesn't support joint measurements"); - - const int 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 - { - int i1 = GetResultId(r1); - int i2 = GetResultId(r2); - REQUIRE(this->results[i1] != RELEASED); - REQUIRE(this->results[i2] != RELEASED); - - return (results[i1] == results[i2]); - } - - void ReleaseResult(Result result) override - { - int 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(); - QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__body()); - - // 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]") -{ - QirContextScope 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]") -{ - QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); - - bool failed = false; - try - { - TestFailWithRangeString(0, 5, 42); - } - catch (const std::exception& e) - { - failed = true; - REQUIRE(std::string(e.what()) == "0..5..42"); - } - REQUIRE(failed); -} - -#if 0 // TODO: Q# compiler crashes generating QIR for TestPartials -// TestPartials subtracts the second argument from the first and returns the result. -extern "C" int64_t Microsoft__Quantum__Testing__QIR__TestPartials__body(int64_t, int64_t); // NOLINT -TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") -{ - QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); - - const int64_t res = Microsoft__Quantum__Testing__QIR__TestPartials__body(42, 17); - REQUIRE(res == 42 - 17); -} -#endif - -// The Microsoft__Quantum__Testing__QIR__TestFunctors__body 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; - - int GetQubitId(Qubit qubit) const - { - const int id = static_cast(reinterpret_cast(qubit)); - REQUIRE(id >= 0); - REQUIRE(id < this->qubits.size()); - return id; - } - - Qubit AllocateQubit() override - { - this->qubits.push_back(0); - return reinterpret_cast(this->qubits.size() - 1); - } - - void ReleaseQubit(Qubit qubit) override - { - const int id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); - this->qubits[id] = RELEASED; - } - - void X(Qubit qubit) override - { - const int id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->qubits[id] = 1 - this->qubits[id]; - } - - void ControlledX(long numControls, Qubit controls[], Qubit qubit) override - { - for (long i = 0; i < numControls; i++) - { - const int id = GetQubitId(controls[i]); - REQUIRE(this->qubits[id] != RELEASED); - if (this->qubits[id] == 0) - { - return; - } - } - X(qubit); - } - - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override - { - assert(numBases == 1 && "FunctorsTestSimulator doesn't support joint measurements"); - - const int 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); - } -}; -FunctorsTestSimulator* g_ctrqapi = nullptr; -static int g_cKCalls = 0; -static int g_cKCallsControlled = 0; -extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__body(); // NOLINT -extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__body(); // NOLINT -extern "C" void __quantum__qis__k__body(Qubit q) // NOLINT -{ - g_cKCalls++; - g_ctrqapi->X(q); -} -extern "C" void __quantum__qis__k__ctl(QirArray* controls, Qubit q) // NOLINT -{ - g_cKCallsControlled++; - g_ctrqapi->ControlledX(controls->count, reinterpret_cast(controls->buffer), q); -} -TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") -{ - unique_ptr qapi = make_unique(); - QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); - g_ctrqapi = qapi.get(); - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__body()); - - const int cKCalls = g_cKCalls; - const int cKCallsControlled = g_cKCallsControlled; - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__body()); - CHECK(g_cKCalls - cKCalls == 3); - CHECK(g_cKCallsControlled - cKCallsControlled == 5); - - g_ctrqapi = nullptr; -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +#include "CoreTypes.hpp" +#include "QirContext.hpp" +#include "QirTypes.hpp" +#include "QirRuntimeApi_I.hpp" +#include "SimFactory.hpp" +#include "SimulatorStub.hpp" +#include "QirRuntime.hpp" + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +// 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; + +// 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; +} + +/* +Forward declared `extern "C"` methods below are implemented in the *.ll files. Most of the files are generated by Q# +compiler, in which case the corresponding *.qs file was used as a source. Some files might have been authored manually +or manually edited. + +To update the *.ll files to a newer version: +- enlist and build qsharp-compiler +- find \qsc.exe and \QirCore.qs, \QirTarget.qs built files +- [if different] copy QirCore.qs and QirTarget.qs into the "compiler" folder +- run: qsc.exe build --qir s --build-exe --input name.qs compiler\qircore.qs compiler\qirtarget.qs --proj name +- the generated file name.ll will be placed into `s` folder +*/ + +struct Array { + int64_t itemSize; + void* buffer; +}; + +// The function replaces array[index] with value, then creates a new array that consists of every other element up to +// index (starting from index backwards) and every element from index to the end. It returns the sum of elements in this +// new array +extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop( // NOLINT + Array* array, + int64_t index, + int64_t val, + bool compilerDecoy); +TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") +{ + QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); + + constexpr int64_t n = 5; + int64_t values[n] = {0, 1, 2, 3, 4}; + auto array = Array{5, values}; + + int64_t res = Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop(&array, 2, 42, false); + REQUIRE(res == (0 + 42) + (42 + 3 + 4)); +} + +extern "C" void Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__body(); // 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) + + int GetQubitId(Qubit qubit) const + { + const int id = static_cast(reinterpret_cast(qubit)); + REQUIRE(id >= 0); + REQUIRE(id < this->qubits.size()); + + return id; + } + + int GetResultId(Result result) const + { + const int id = static_cast(reinterpret_cast(result)); + REQUIRE(id >= 0); + REQUIRE(id < this->results.size()); + + return id; + } + + Qubit AllocateQubit() override + { + qubits.push_back(0); + return reinterpret_cast(this->qubits.size() - 1); + } + + void ReleaseQubit(Qubit qubit) override + { + const int id = GetQubitId(qubit); + REQUIRE(this->qubits[id] != RELEASED); // no double-release + this->qubits[id] = RELEASED; + } + + void X(Qubit qubit) override + { + const int id = GetQubitId(qubit); + REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive + this->qubits[id] = 1 - this->qubits[id]; + } + + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + assert(numBases == 1 && "QubitsResultsTestSimulator doesn't support joint measurements"); + + const int 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 + { + int i1 = GetResultId(r1); + int i2 = GetResultId(r2); + REQUIRE(this->results[i1] != RELEASED); + REQUIRE(this->results[i2] != RELEASED); + + return (results[i1] == results[i2]); + } + + void ReleaseResult(Result result) override + { + int 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(); + QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/); + + REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__body()); + + // 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]") +{ + QirContextScope 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]") +{ + QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); + + bool failed = false; + try + { + TestFailWithRangeString(0, 5, 42); + } + catch (const std::exception& e) + { + failed = true; + REQUIRE(std::string(e.what()) == "0..5..42"); + } + REQUIRE(failed); +} + +#if 0 // TODO: Q# compiler crashes generating QIR for TestPartials +// TestPartials subtracts the second argument from the first and returns the result. +extern "C" int64_t Microsoft__Quantum__Testing__QIR__TestPartials__body(int64_t, int64_t); // NOLINT +TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") +{ + QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); + + const int64_t res = Microsoft__Quantum__Testing__QIR__TestPartials__body(42, 17); + REQUIRE(res == 42 - 17); +} +#endif + +// The Microsoft__Quantum__Testing__QIR__TestFunctors__body 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; + + int GetQubitId(Qubit qubit) const + { + const int id = static_cast(reinterpret_cast(qubit)); + REQUIRE(id >= 0); + REQUIRE(id < this->qubits.size()); + return id; + } + + Qubit AllocateQubit() override + { + this->qubits.push_back(0); + return reinterpret_cast(this->qubits.size() - 1); + } + + void ReleaseQubit(Qubit qubit) override + { + const int id = GetQubitId(qubit); + REQUIRE(this->qubits[id] != RELEASED); + this->qubits[id] = RELEASED; + } + + void X(Qubit qubit) override + { + const int id = GetQubitId(qubit); + REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive + this->qubits[id] = 1 - this->qubits[id]; + } + + void ControlledX(long numControls, Qubit controls[], Qubit qubit) override + { + for (long i = 0; i < numControls; i++) + { + const int id = GetQubitId(controls[i]); + REQUIRE(this->qubits[id] != RELEASED); + if (this->qubits[id] == 0) + { + return; + } + } + X(qubit); + } + + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + assert(numBases == 1 && "FunctorsTestSimulator doesn't support joint measurements"); + + const int 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); + } +}; +FunctorsTestSimulator* g_ctrqapi = nullptr; +static int g_cKCalls = 0; +static int g_cKCallsControlled = 0; +extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__body(); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__body(); // NOLINT +extern "C" void __quantum__qis__k__body(Qubit q) // NOLINT +{ + g_cKCalls++; + g_ctrqapi->X(q); +} +extern "C" void __quantum__qis__k__ctl(QirArray* controls, Qubit q) // NOLINT +{ + g_cKCallsControlled++; + g_ctrqapi->ControlledX(controls->count, reinterpret_cast(controls->buffer), q); +} +TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") +{ + unique_ptr qapi = make_unique(); + QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + g_ctrqapi = qapi.get(); + + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__body()); + + const int cKCalls = g_cKCalls; + const int cKCallsControlled = g_cKCallsControlled; + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__body()); + CHECK(g_cKCalls - cKCalls == 3); + CHECK(g_cKCallsControlled - cKCallsControlled == 5); + + g_ctrqapi = nullptr; +} diff --git a/src/Qir/Runtime/test/QIR-static/qir-test-conditionals.cpp b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qir-test-conditionals.cpp rename to src/Qir/Tests/QIR-static/qir-test-conditionals.cpp diff --git a/src/Qir/Runtime/test/QIR-static/qir-test-math.cpp b/src/Qir/Tests/QIR-static/qir-test-math.cpp similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qir-test-math.cpp rename to src/Qir/Tests/QIR-static/qir-test-math.cpp diff --git a/src/Qir/Runtime/test/QIR-static/qir-test-noqsharp.ll b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll similarity index 97% rename from src/Qir/Runtime/test/QIR-static/qir-test-noqsharp.ll rename to src/Qir/Tests/QIR-static/qir-test-noqsharp.ll index cce6f8f54c5..3d08b24f10c 100644 --- a/src/Qir/Runtime/test/QIR-static/qir-test-noqsharp.ll +++ b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll @@ -1,49 +1,49 @@ -%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) -declare void @DebugLog(i64) - -; 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) - ret void -} +%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) +declare void @DebugLog(i64) + +; 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) + ret void +} diff --git a/src/Qir/Runtime/test/QIR-static/qir-test-other.cpp b/src/Qir/Tests/QIR-static/qir-test-other.cpp similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qir-test-other.cpp rename to src/Qir/Tests/QIR-static/qir-test-other.cpp diff --git a/src/Qir/Runtime/test/QIR-static/qir-test-ouput.cpp b/src/Qir/Tests/QIR-static/qir-test-ouput.cpp similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qir-test-ouput.cpp rename to src/Qir/Tests/QIR-static/qir-test-ouput.cpp diff --git a/src/Qir/Runtime/test/QIR-static/qir-test-strings.cpp b/src/Qir/Tests/QIR-static/qir-test-strings.cpp similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qir-test-strings.cpp rename to src/Qir/Tests/QIR-static/qir-test-strings.cpp diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/Math.qs b/src/Qir/Tests/QIR-static/qsharp/Math.qs similarity index 94% rename from src/Qir/Runtime/test/QIR-static/qsharp/Math.qs rename to src/Qir/Tests/QIR-static/qsharp/Math.qs index 6edba1a7860..bedfc70a1c8 100644 --- a/src/Qir/Runtime/test/QIR-static/qsharp/Math.qs +++ b/src/Qir/Tests/QIR-static/qsharp/Math.qs @@ -1,34 +1,34 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Intrinsic { - - open Microsoft.Quantum.Targeting; - open Microsoft.Quantum.Core; - - @Inline() - function NAN() : Double { - body intrinsic; - } - - @Inline() - function IsNan(d: Double) : Bool { - body intrinsic; - } - - @Inline() - function INFINITY() : Double { - body intrinsic; - } - - @Inline() - function IsInf(d: Double) : Bool { - body intrinsic; - } - - @Inline() - function IsNegativeInfinity(d : Double) : Bool { - body intrinsic; - } - -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + + open Microsoft.Quantum.Targeting; + open Microsoft.Quantum.Core; + + @Inline() + function NAN() : Double { + body intrinsic; + } + + @Inline() + function IsNan(d: Double) : Bool { + body intrinsic; + } + + @Inline() + function INFINITY() : Double { + body intrinsic; + } + + @Inline() + function IsInf(d: Double) : Bool { + body intrinsic; + } + + @Inline() + function IsNegativeInfinity(d : Double) : Bool { + body intrinsic; + } + +} diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-gen.csproj b/src/Qir/Tests/QIR-static/qsharp/qir-gen.csproj similarity index 72% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-gen.csproj rename to src/Qir/Tests/QIR-static/qsharp/qir-gen.csproj index 8c5dd882d26..7e736af46ba 100644 --- a/src/Qir/Runtime/test/QIR-static/qsharp/qir-gen.csproj +++ b/src/Qir/Tests/QIR-static/qsharp/qir-gen.csproj @@ -1,14 +1,14 @@ - - - - Exe - netcoreapp3.1 - True - false - - - - - - - + + + + Exe + netcoreapp3.1 + True + false + + + + + + + diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-arrays.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-arrays.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-arrays.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-arrays.qs diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-conditionals.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs similarity index 97% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-conditionals.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs index 28a1b755a64..00a9919c956 100644 --- a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-conditionals.qs +++ b/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs @@ -1,63 +1,63 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -namespace Microsoft.Quantum.Testing.QIR { - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Simulation.QuantumProcessor.Extensions; - - 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)); - } - - 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))); - } - - 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))); - } - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace Microsoft.Quantum.Testing.QIR { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Simulation.QuantumProcessor.Extensions; + + 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)); + } + + 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))); + } + + 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/Runtime/test/QIR-static/qsharp/qir-test-functors.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-functors.qs similarity index 96% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-functors.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-functors.qs index 0d8c283dc87..4bc6b8cb38b 100644 --- a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-functors.qs +++ b/src/Qir/Tests/QIR-static/qsharp/qir-test-functors.qs @@ -1,106 +1,106 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// For the test to pass implement K to be the same as X. We need it for the test, because the standard bridge doesn't -// support multi-controlled X. -namespace Microsoft.Quantum.Intrinsic { - open Microsoft.Quantum.Core; - @Inline() - operation K(q : Qubit) : Unit - is Adj+Ctl { - body intrinsic; - adjoint self; - controlled intrinsic; - } -} - -namespace Microsoft.Quantum.Testing.QIR { - open Microsoft.Quantum.Intrinsic; - - operation Qop(q : Qubit, n : Int) : Unit - is Adj+Ctl { - body (...) { - if n%2 == 1 { K(q); } - } - adjoint self; - controlled (ctrls, ...) { - if n%2 == 1 { Controlled K(ctrls, q); } - } - } - - // We want to test for conditional measurements which requires us to generate QIR with --runtime set to - // BasicMeasurementFeedback, which in turn doesn't allow updating mutables inside measurement conditionals. - // this means, we cannot easily get detailed failure information back from Q#, but the test driver can mock - // the simulator to track the point of failure. - operation TestFunctors() : Unit { - let qop = Qop(_, 1); - let adj_qop = Adjoint qop; - let ctl_qop = Controlled qop; - let adj_ctl_qop = Adjoint Controlled qop; - let ctl_ctl_qop = Controlled ctl_qop; - - use (q1, q2, q3) = (Qubit(), Qubit(), Qubit()) { - qop(q1); - if (M(q1) != One) { fail("error code: 1"); } - - adj_qop(q2); - if (M(q2) != One) { fail("error code: 2"); } - - ctl_qop([q1], q3); - if (M(q3) != One) { fail("error code: 3"); } - - adj_ctl_qop([q2], q3); - if (M(q3) != Zero) { fail("error code: 2"); } - - ctl_ctl_qop([q1], ([q2], q3)); - if (M(q3) != One) { fail("error code: 5"); } - - Controlled qop([q1, q2], q3); - if (M(q3) != Zero) { fail("error code: 6"); } - - use q4 = Qubit() { - Adjoint qop(q3); - Adjoint Controlled ctl_ctl_qop([q1], ([q2], ([q3], q4))); - if (M(q4) != One) { fail("error code: 7"); } - } - } - } - - // The operation is not sensical but in tests we can mock K operator to check that it actually executes - operation NoArgs() : Unit - is Adj+Ctl { - body (...) { - use q = Qubit(); - K(q); - } - adjoint self; - controlled (ctrls, ...) { - use q = Qubit(); - Controlled K(ctrls, q); - } - } - - operation TestFunctorsNoArgs() : Unit { - NoArgs(); - let qop = NoArgs; - let adj_qop = Adjoint qop; - let ctl_qop = Controlled qop; - let adj_ctl_qop = Adjoint Controlled qop; - let ctl_ctl_qop = Controlled ctl_qop; - - use (q1, q2, q3) = (Qubit(), Qubit(), Qubit()) { - X(q1); - X(q2); - X(q3); - - qop(); - adj_qop(); - ctl_qop([q1], ()); - adj_ctl_qop([q1], ()); - ctl_ctl_qop([q1], ([q2], ())); - - Controlled qop([q1, q2], ()); - Adjoint Controlled ctl_ctl_qop([q1], ([q2], ([q3], ()))); - } - } +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// For the test to pass implement K to be the same as X. We need it for the test, because the standard bridge doesn't +// support multi-controlled X. +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Core; + @Inline() + operation K(q : Qubit) : Unit + is Adj+Ctl { + body intrinsic; + adjoint self; + controlled intrinsic; + } +} + +namespace Microsoft.Quantum.Testing.QIR { + open Microsoft.Quantum.Intrinsic; + + operation Qop(q : Qubit, n : Int) : Unit + is Adj+Ctl { + body (...) { + if n%2 == 1 { K(q); } + } + adjoint self; + controlled (ctrls, ...) { + if n%2 == 1 { Controlled K(ctrls, q); } + } + } + + // We want to test for conditional measurements which requires us to generate QIR with --runtime set to + // BasicMeasurementFeedback, which in turn doesn't allow updating mutables inside measurement conditionals. + // this means, we cannot easily get detailed failure information back from Q#, but the test driver can mock + // the simulator to track the point of failure. + operation TestFunctors() : Unit { + let qop = Qop(_, 1); + let adj_qop = Adjoint qop; + let ctl_qop = Controlled qop; + let adj_ctl_qop = Adjoint Controlled qop; + let ctl_ctl_qop = Controlled ctl_qop; + + use (q1, q2, q3) = (Qubit(), Qubit(), Qubit()) { + qop(q1); + if (M(q1) != One) { fail("error code: 1"); } + + adj_qop(q2); + if (M(q2) != One) { fail("error code: 2"); } + + ctl_qop([q1], q3); + if (M(q3) != One) { fail("error code: 3"); } + + adj_ctl_qop([q2], q3); + if (M(q3) != Zero) { fail("error code: 2"); } + + ctl_ctl_qop([q1], ([q2], q3)); + if (M(q3) != One) { fail("error code: 5"); } + + Controlled qop([q1, q2], q3); + if (M(q3) != Zero) { fail("error code: 6"); } + + use q4 = Qubit() { + Adjoint qop(q3); + Adjoint Controlled ctl_ctl_qop([q1], ([q2], ([q3], q4))); + if (M(q4) != One) { fail("error code: 7"); } + } + } + } + + // The operation is not sensical but in tests we can mock K operator to check that it actually executes + operation NoArgs() : Unit + is Adj+Ctl { + body (...) { + use q = Qubit(); + K(q); + } + adjoint self; + controlled (ctrls, ...) { + use q = Qubit(); + Controlled K(ctrls, q); + } + } + + operation TestFunctorsNoArgs() : Unit { + NoArgs(); + let qop = NoArgs; + let adj_qop = Adjoint qop; + let ctl_qop = Controlled qop; + let adj_ctl_qop = Adjoint Controlled qop; + let ctl_ctl_qop = Controlled ctl_qop; + + use (q1, q2, q3) = (Qubit(), Qubit(), Qubit()) { + X(q1); + X(q2); + X(q3); + + qop(); + adj_qop(); + ctl_qop([q1], ()); + adj_ctl_qop([q1], ()); + ctl_ctl_qop([q1], ([q2], ())); + + Controlled qop([q1, q2], ()); + Adjoint Controlled ctl_ctl_qop([q1], ([q2], ([q3], ()))); + } + } } \ No newline at end of file diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-math.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-math.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-math.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-math.qs diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-other.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-other.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-other.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-other.qs diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-output.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-output.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-output.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-output.qs diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-partials.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-partials.qs similarity index 96% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-partials.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-partials.qs index 05e63538699..21659197b8a 100644 --- a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-partials.qs +++ b/src/Qir/Tests/QIR-static/qsharp/qir-test-partials.qs @@ -1,13 +1,13 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Testing.QIR { - function Subtract(from : Int, what : Int) : Int { - return from - what; - } - - function TestPartials(x : Int, y : Int) : Int { - let subtractor = Subtract(x, _); - return subtractor(y); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Testing.QIR { + function Subtract(from : Int, what : Int) : Int { + return from - what; + } + + function TestPartials(x : Int, y : Int) : Int { + let subtractor = Subtract(x, _); + return subtractor(y); + } +} diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-qubits-results.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-qubits-results.qs similarity index 97% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-qubits-results.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-qubits-results.qs index dc6c74fb9ee..83ef174d76c 100644 --- a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-qubits-results.qs +++ b/src/Qir/Tests/QIR-static/qsharp/qir-test-qubits-results.qs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -namespace Microsoft.Quantum.Testing.QIR { - open Microsoft.Quantum.Intrinsic; - - operation TestQubitResultManagement() : Unit { - // exercise __quantum__rt__qubit_allocate_array - use qs = Qubit[2] { - X(qs[1]); - // exercise __quantum__rt__qubit_allocate - use q = Qubit() { - // exercise __quantum__rt__result_equal and accessing result constants - if (M(qs[1]) == One) { X(q); } - if (M(qs[0]) == M(q)) { fail("Unexpected measurement result"); } - } // exercise __quantum__rt__qubit_release - } // exercise __quantum__rt__qubit_release_array - } +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace Microsoft.Quantum.Testing.QIR { + open Microsoft.Quantum.Intrinsic; + + operation TestQubitResultManagement() : Unit { + // exercise __quantum__rt__qubit_allocate_array + use qs = Qubit[2] { + X(qs[1]); + // exercise __quantum__rt__qubit_allocate + use q = Qubit() { + // exercise __quantum__rt__result_equal and accessing result constants + if (M(qs[1]) == One) { X(q); } + if (M(qs[0]) == M(q)) { fail("Unexpected measurement result"); } + } // exercise __quantum__rt__qubit_release + } // exercise __quantum__rt__qubit_release_array + } } \ No newline at end of file diff --git a/src/Qir/Runtime/test/QIR-static/qsharp/qir-test-strings.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-strings.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-static/qsharp/qir-test-strings.qs rename to src/Qir/Tests/QIR-static/qsharp/qir-test-strings.qs diff --git a/src/Qir/Runtime/test/QIR-tracer/CMakeLists.txt b/src/Qir/Tests/QIR-tracer/CMakeLists.txt similarity index 53% rename from src/Qir/Runtime/test/QIR-tracer/CMakeLists.txt rename to src/Qir/Tests/QIR-tracer/CMakeLists.txt index dae4001b1d5..50fd5dc0073 100644 --- a/src/Qir/Runtime/test/QIR-tracer/CMakeLists.txt +++ b/src/Qir/Tests/QIR-tracer/CMakeLists.txt @@ -1,36 +1,30 @@ -set(TEST_FILES - tracer-qir -) - -foreach(file ${TEST_FILES}) - compile_from_qir(${file} "") # don't create a target per file - list(APPEND QIR_TESTS_LIBS ${QIR_UTILITY_LIB}) -endforeach() - -add_custom_target(tracer_qir_test_lib DEPENDS ${QIR_TESTS_LIBS}) - -#============================================================================== -# 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 -) - -target_link_libraries(qir-tracer-tests PUBLIC - ${QIR_TESTS_LIBS} - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - "-L${CMAKE_BINARY_DIR}/lib/Tracer" - -lMicrosoft.Quantum.Qir.Tracer -) - -target_include_directories(qir-tracer-tests PUBLIC - "${test_includes}" - "${public_includes}" - "${PROJECT_SOURCE_DIR}/lib/Tracer" # TODO: Remove this when tracer api is put into public headers. -) -add_dependencies(qir-tracer-tests tracer_qir_test_lib) - -install(TARGETS qir-tracer-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-tracer-tests) +set(TEST_FILES + qsharp/qir/tracer-qir.ll +) + +#============================================================================== +# 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/Runtime/test/QIR-tracer/generate.py b/src/Qir/Tests/QIR-tracer/generate.py similarity index 97% rename from src/Qir/Runtime/test/QIR-tracer/generate.py rename to src/Qir/Tests/QIR-tracer/generate.py index 473535416f9..20354e3da9a 100644 --- a/src/Qir/Runtime/test/QIR-tracer/generate.py +++ b/src/Qir/Tests/QIR-tracer/generate.py @@ -1,46 +1,46 @@ -# 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) - +# 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/Runtime/test/QIR-tracer/qir-tracer-driver.cpp b/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp similarity index 97% rename from src/Qir/Runtime/test/QIR-tracer/qir-tracer-driver.cpp rename to src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp index 8d386eb976b..1914b666ba6 100644 --- a/src/Qir/Runtime/test/QIR-tracer/qir-tracer-driver.cpp +++ b/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp @@ -1,49 +1,49 @@ -// 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); - QirContextScope qirctx(tr.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__body()); - 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); - QirContextScope qirctx(tr.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestMeasurements__body()); - - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - INFO(out.str()); - CHECK(tr->UseLayers().size() == 5); -} +// 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); + QirContextScope qirctx(tr.get(), true /*trackAllocatedObjects*/); + + REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__body()); + 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); + QirContextScope qirctx(tr.get(), true /*trackAllocatedObjects*/); + + REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestMeasurements__body()); + + std::stringstream out; + tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); + INFO(out.str()); + CHECK(tr->UseLayers().size() == 5); +} } // namespace TracerUser \ No newline at end of file diff --git a/src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-conditionals.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-conditionals.qs rename to src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs diff --git a/src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-core.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-core.qs rename to src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs diff --git a/src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-intrinsics.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-intrinsics.qs rename to src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs diff --git a/src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-qir.csproj b/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj similarity index 100% rename from src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-qir.csproj rename to src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj diff --git a/src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-target.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs similarity index 100% rename from src/Qir/Runtime/test/QIR-tracer/qsharp/tracer-target.qs rename to src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs diff --git a/src/Qir/Runtime/test/QIR-tracer/tracer-config.cpp b/src/Qir/Tests/QIR-tracer/tracer-config.cpp similarity index 97% rename from src/Qir/Runtime/test/QIR-tracer/tracer-config.cpp rename to src/Qir/Tests/QIR-tracer/tracer-config.cpp index 2e58fd225dc..b23fdfb334d 100644 --- a/src/Qir/Runtime/test/QIR-tracer/tracer-config.cpp +++ b/src/Qir/Tests/QIR-tracer/tracer-config.cpp @@ -1,18 +1,18 @@ -// 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.*/}; -} +// 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/Runtime/test/QIR-tracer/tracer-config.hpp b/src/Qir/Tests/QIR-tracer/tracer-config.hpp similarity index 96% rename from src/Qir/Runtime/test/QIR-tracer/tracer-config.hpp rename to src/Qir/Tests/QIR-tracer/tracer-config.hpp index ac7eef038d3..c8327411f3d 100644 --- a/src/Qir/Runtime/test/QIR-tracer/tracer-config.hpp +++ b/src/Qir/Tests/QIR-tracer/tracer-config.hpp @@ -1,20 +1,20 @@ -// 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__body(); // NOLINT -extern "C" void Microsoft__Quantum__Testing__Tracer__TestMeasurements__body(); // NOLINT +// 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__body(); // NOLINT +extern "C" void Microsoft__Quantum__Testing__Tracer__TestMeasurements__body(); // NOLINT diff --git a/src/Qir/Tests/build-qir-tests.ps1 b/src/Qir/Tests/build-qir-tests.ps1 new file mode 100644 index 00000000000..48383f3df5a --- /dev/null +++ b/src/Qir/Tests/build-qir-tests.ps1 @@ -0,0 +1,21 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +[CmdletBinding()] +param ( + [Parameter()] + [Switch] + $SkipQSharpBuild +) + +. (Join-Path $PSScriptRoot .. qir-utils.ps1) + +Write-Host "##[info]Compile Q# 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")) { + throw "At least one project failed to compile. Check the logs." +} \ No newline at end of file diff --git a/src/Qir/Tests/test-qir-tests.ps1 b/src/Qir/Tests/test-qir-tests.ps1 new file mode 100644 index 00000000000..72302788343 --- /dev/null +++ b/src/Qir/Tests/test-qir-tests.ps1 @@ -0,0 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +. (Join-Path $PSScriptRoot .. qir-utils.ps1) + +if (-not (Test-CTest (Join-Path $PSScriptRoot build $Env:BUILD_CONFIGURATION) "QIR Tests")) { + throw "At least one project failed testing. Check the logs." +} diff --git a/src/Qir/qir-utils.ps1 b/src/Qir/qir-utils.ps1 new file mode 100644 index 00000000000..38ae05e0724 --- /dev/null +++ b/src/Qir/qir-utils.ps1 @@ -0,0 +1,132 @@ +& (Join-Path $PSScriptRoot .. .. build set-env.ps1) + +function Build-QirProject { + param ( + [string] + $FolderPath, + + [Switch] + $SkipQSharpBuild + ) + + if (!$SkipQSharpBuild) { + Write-Host "##[info]Build Q# project for $Name '$FolderPath'" + dotnet build $FolderPath -c $Env:BUILD_CONFIGURATION -v $Env:BUILD_VERBOSITY + if ($LastExitCode -ne 0) { + Write-Host "##vso[task.logissue type=error;]Failed to compile Q# project at '$FolderPath' into QIR." + throw "Failed to compile Q# project at '$FolderPath' into QIR." + } + } +} + +function Build-CMakeProject { + [CmdletBinding()] + param ( + [Parameter()] + [String] + $Path, + + [Parameter()] + [String] + $Name + ) + + Write-Host "##[info]Build $Name" + $oldCC = $env:CC + $oldCXX = $env:CXX + $oldRC = $env:RC + + $clangTidy = "" + + if (($IsMacOS) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Darwin")))) + { + Write-Host "On MacOS build $Name using the default C/C++ compiler (should be AppleClang)" + } + elseif (($IsLinux) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Lin")))) + { + Write-Host "On Linux build $Name using Clang" + $env:CC = "clang-11" + $env:CXX = "clang++-11" + $env:RC = "clang++-11" + $clangTidy = "-DCMAKE_CXX_CLANG_TIDY=clang-tidy-11" + } + elseif (($IsWindows) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Win")))) + { + Write-Host "On Windows build $Name using Clang" + $env:CC = "clang.exe" + $env:CXX = "clang++.exe" + $env:RC = "clang++.exe" + + if (!(Get-Command clang -ErrorAction SilentlyContinue) -and (choco find --idonly -l llvm) -contains "llvm") { + # LLVM was installed by Chocolatey, so add the install location to the path. + $env:PATH += ";$($env:SystemDrive)\Program Files\LLVM\bin" + } + + if (Get-Command clang-tidy -ErrorAction SilentlyContinue) { + # Only run clang-tidy if it's installed. This is because the package used by chocolatey on + # the build pipeline doesn't include clang-tidy, so we allow skipping that there and let + # the Linux build catch tidy issues. + $clangTidy = "-DCMAKE_CXX_CLANG_TIDY=clang-tidy" + } + } else { + Write-Host "##vso[task.logissue type=warning;]Failed to identify the OS. Will use default CXX compiler" + } + + $cmakeBuildFolder = (Join-Path $Path build $Env:BUILD_CONFIGURATION) + if (-not (Test-Path $cmakeBuildFolder)) { + New-Item -Path $cmakeBuildFolder -ItemType "directory" + } + + $all_ok = $true + + Push-Location $cmakeBuildFolder + + cmake -G Ninja $clangTidy -D CMAKE_BUILD_TYPE="$Env:BUILD_CONFIGURATION" ../.. | Write-Host + if ($LastExitCode -ne 0) { + Write-Host "##vso[task.logissue type=error;]Failed to generate $Name." + $all_ok = $false + } else { + cmake --build . --target install | Write-Host + if ($LastExitCode -ne 0) { + Write-Host "##vso[task.logissue type=error;]Failed to build $Name." + $all_ok = $false + } + } + + Pop-Location + + $env:CC = $oldCC + $env:CXX = $oldCXX + $env:RC = $oldRC + + return $all_ok +} + +function Test-CTest { + [CmdletBinding()] + param ( + [Parameter()] + [string] + $Path, + + [Parameter()] + [string] + $Name + ) + + Write-Host "##[info]Test $Name" + + $all_ok = $true + Push-Location $Path + + ctest --verbose | Write-Host + + if ($LastExitCode -ne 0) { + Write-Host "##vso[task.logissue type=error;]Failed to test $Name" + $all_ok = $False + } + + Pop-Location + + return $all_ok +} \ No newline at end of file From b389b24188824edf864ff67e83dc6cd4b4fd034c Mon Sep 17 00:00:00 2001 From: DmitryVasilevsky <60718360+DmitryVasilevsky@users.noreply.github.com> Date: Sat, 3 Apr 2021 10:42:15 -0700 Subject: [PATCH 2/7] Making stack trace printout optional (#487) --- .../Simulators.Tests/StackTraceTests.cs | 13 ++++++++++++ .../QuantumSimulator/SimulatorBase.cs | 20 ++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/Simulation/Simulators.Tests/StackTraceTests.cs b/src/Simulation/Simulators.Tests/StackTraceTests.cs index c3fead7b586..e429475cfb9 100644 --- a/src/Simulation/Simulators.Tests/StackTraceTests.cs +++ b/src/Simulation/Simulators.Tests/StackTraceTests.cs @@ -381,6 +381,19 @@ public void ErrorLogTest() Assert.StartsWith(" at Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.AlwaysFail4 on", logs[5]); Assert.Equal("", logs[6]); } + + logs.Clear(); + sim.EnableStackTracePrinting = false; + try + { + QVoid res = sim.Execute(QVoid.Instance); + } + catch (ExecutionFailException) + { + Assert.Equal(2, logs.Count); + Assert.StartsWith("Unhandled exception. Microsoft.Quantum.Simulation.Core.ExecutionFailException: Always fail", logs[0]); + Assert.Equal("", logs[1]); + } } } } \ No newline at end of file diff --git a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs index eb8b27f1ee6..c84cb17936f 100644 --- a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs +++ b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs @@ -55,6 +55,13 @@ public abstract class SimulatorBase : Factory, IOperationFacto public abstract string Name { get; } + /// + /// When exception printing is enabled, the value of this property controls stack trace printout. + /// When set to true (default), the stack trace is printed in addition to the exception message." + /// When set to false, the exception message is printed without the stack trace." + /// + public bool EnableStackTracePrinting { get; set; } = true; + /// /// If the execution finishes in failure, this method returns the call-stack of the Q# operations /// executed up to the point when the failure happened. @@ -140,12 +147,15 @@ public override AbstractCallable CreateInstance(Type t) protected void WriteStackTraceToLog(Exception exception, IEnumerable callStack) { OnLog?.Invoke($"Unhandled exception. {exception.GetType().FullName}: {exception.Message}"); - var first = true; - foreach (var sf in callStack) + if (EnableStackTracePrinting) { - var msg = (first ? " ---> " : " at ") + sf.ToStringWithBestSourceLocation(); - OnLog?.Invoke(msg); - first = false; + var first = true; + foreach (var sf in callStack) + { + var msg = (first ? " ---> " : " at ") + sf.ToStringWithBestSourceLocation(); + OnLog?.Invoke(msg); + first = false; + } } OnLog?.Invoke(""); } From 525ea747c1163784503bc5498b7c75bb0da90fce Mon Sep 17 00:00:00 2001 From: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> Date: Mon, 5 Apr 2021 12:50:14 -0700 Subject: [PATCH 3/7] Enumerated File Names for Generated C# (#588) --- .../Circuits/SubDirectory/HelloOther.qs | 16 +++++ .../Circuits/SubDirectory/HelloWorld.qs | 16 +++++ .../SimulationCodeTests.fs | 60 +++++++++++++++++-- .../Tests.CSharpGeneration.fsproj | 6 ++ .../CSharpGeneration/RewriteStep.fs | 47 ++++++++++++--- 5 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloOther.qs create mode 100644 src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloWorld.qs diff --git a/src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloOther.qs b/src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloOther.qs new file mode 100644 index 00000000000..3b7cdc8cf2d --- /dev/null +++ b/src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloOther.qs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests.SubDirectory +{ + + operation HelloOther (n : Int) : Int + { + let r = n + 1; + + return r; + } + +} + + diff --git a/src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloWorld.qs b/src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloWorld.qs new file mode 100644 index 00000000000..ba40ec64cc5 --- /dev/null +++ b/src/Simulation/CSharpGeneration.Tests/Circuits/SubDirectory/HelloWorld.qs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests.SubDirectory +{ + + operation HelloWorld (n : Int) : Int + { + let r = n + 1; + + return r; + } + +} + + diff --git a/src/Simulation/CSharpGeneration.Tests/SimulationCodeTests.fs b/src/Simulation/CSharpGeneration.Tests/SimulationCodeTests.fs index dfe7a2e216d..433b86fa6ed 100644 --- a/src/Simulation/CSharpGeneration.Tests/SimulationCodeTests.fs +++ b/src/Simulation/CSharpGeneration.Tests/SimulationCodeTests.fs @@ -108,10 +108,10 @@ namespace N1 |> failwith let functorGenSuccessful = CodeGeneration.GenerateFunctorSpecializations(compilation, &compilation) // todo: we might want to raise an error here if the functor generation fails (which will be the case for incorrect code) - compilation.Namespaces + compilation with | e -> sprintf "compilation threw exception: \n%s" e.Message |> failwith // should never happen (all exceptions are caught by the compiler) - let syntaxTree = parse [ (Path.Combine("Circuits", "Intrinsic.qs")); (Path.Combine("Circuits", "CodegenTests.qs")) ] + let syntaxTree = parse [ (Path.Combine("Circuits", "Intrinsic.qs")); (Path.Combine("Circuits", "CodegenTests.qs")) ] |> (fun compilation -> compilation.Namespaces) let globalContext = CodegenContext.Create syntaxTree @@ -225,9 +225,9 @@ namespace N1 expected |> (fun s -> s.Replace("%%%", fullPath |> HttpUtility.JavaScriptStringEncode |> escapeCSharpString)) |> (fun s -> s.Replace("%%", fullPath |> escapeCSharpString)) - let tree = parse [Path.Combine ("Circuits", "Intrinsic.qs"); fileName] + let compilation = parse [Path.Combine ("Circuits", "Intrinsic.qs"); fileName] let actual = - CodegenContext.Create (tree, ImmutableDictionary.Empty) + CodegenContext.Create (compilation, ImmutableDictionary.Empty) |> generate (Path.GetFullPath fileName) Assert.Equal(expected |> clearFormatting, actual |> clearFormatting) @@ -248,6 +248,58 @@ namespace N1 List.zip expected actual |> List.iter Assert.Equal<'Z> + [] + let ``testGeneratedFileNames`` () = + + let outputDir = "output" |> Path.GetFullPath + let outputDirSrc = Path.Combine(outputDir, "src") + + let intrinsic = Path.Combine(outputDirSrc, "Intrinsic.g.cs") + let helloWorld = Path.Combine(outputDirSrc, "HelloWorld.g.cs") + let helloWorld1 = Path.Combine(outputDirSrc, "HelloWorld.g1.cs") + let helloOther = Path.Combine(outputDirSrc, "HelloOther.g.cs") + + let deleteFile filePath = + if File.Exists filePath then + File.Delete filePath + + intrinsic |> deleteFile + helloWorld |> deleteFile + helloWorld1 |> deleteFile + helloOther |> deleteFile + + let compilation = parse [ + Path.Combine("Circuits", "Intrinsic.qs") + Path.Combine("Circuits", "HelloWorld.qs") + Path.Combine("Circuits", "SubDirectory", "HelloWorld.qs") + Path.Combine("Circuits", "SubDirectory", "HelloOther.qs") + ] + let transformed = ref { Namespaces = ImmutableArray.Empty; EntryPoints = ImmutableArray.Empty } + let rewriteStep = Emitter() :> IRewriteStep + rewriteStep.AssemblyConstants.Add(AssemblyConstants.OutputPath, outputDir) + rewriteStep.Transformation(compilation, transformed) |> ignore + + let mutable allFilesFound = true + let checkAndDeleteFile filePath = + if File.Exists filePath then + File.Delete filePath + else + allFilesFound <- false + + intrinsic |> checkAndDeleteFile + helloWorld |> checkAndDeleteFile + helloWorld1 |> checkAndDeleteFile + helloOther |> checkAndDeleteFile + + let deleteDir dirPath = + if Directory.Exists dirPath && Directory.EnumerateFileSystemEntries dirPath |> Seq.isEmpty then + Directory.Delete dirPath + + outputDirSrc |> deleteDir + outputDir |> deleteDir + + Assert.True allFilesFound + [] let ``tupleBaseClassName test`` () = let testOne (_, udt) expected = diff --git a/src/Simulation/CSharpGeneration.Tests/Tests.CSharpGeneration.fsproj b/src/Simulation/CSharpGeneration.Tests/Tests.CSharpGeneration.fsproj index 0a421156c67..f19303756a8 100644 --- a/src/Simulation/CSharpGeneration.Tests/Tests.CSharpGeneration.fsproj +++ b/src/Simulation/CSharpGeneration.Tests/Tests.CSharpGeneration.fsproj @@ -8,6 +8,12 @@ + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Simulation/CSharpGeneration/RewriteStep.fs b/src/Simulation/CSharpGeneration/RewriteStep.fs index 10ea2242f05..cd8527bddfa 100644 --- a/src/Simulation/CSharpGeneration/RewriteStep.fs +++ b/src/Simulation/CSharpGeneration/RewriteStep.fs @@ -18,6 +18,31 @@ type Emitter() = let _AssemblyConstants = new Dictionary<_, _>() + let _FileNamesGenerated = new HashSet(); + + [] + let _EnumerationLimit = 100; + + member private this.WriteFile (fileId : string) outputFolder (fileEnding : string) content overwrite = + let mutable fileEnding = fileEnding + let withoutEnding = Path.GetFileNameWithoutExtension(fileId) + let mutable targetFile = Path.GetFullPath(Path.Combine(outputFolder, withoutEnding + fileEnding)) + + if (not overwrite) && _FileNamesGenerated.Contains(targetFile) then + let mutable enumeration = 1 + let pos = fileEnding.LastIndexOf('.') + let (beforeEnumeration, afterEnumeration) = + if pos = -1 + then "", fileEnding + else fileEnding.Substring(0, pos), fileEnding.Substring(pos) + while _FileNamesGenerated.Contains(targetFile) && enumeration < _EnumerationLimit do + fileEnding <- beforeEnumeration + enumeration.ToString() + afterEnumeration + targetFile <- Path.GetFullPath(Path.Combine(outputFolder, withoutEnding + fileEnding)) + enumeration <- enumeration + 1 + + _FileNamesGenerated.Add targetFile |> ignore + File.WriteAllText(targetFile, content) + interface IRewriteStep with member this.Name = "CSharpGeneration" @@ -34,19 +59,27 @@ type Emitter() = member this.Transformation (compilation, transformed) = let step = this :> IRewriteStep - let dir = step.AssemblyConstants.TryGetValue AssemblyConstants.OutputPath |> function - | true, outputFolder when outputFolder <> null -> Path.Combine(outputFolder, "src") - | _ -> step.Name + let dir = + step.AssemblyConstants.TryGetValue AssemblyConstants.OutputPath + |> function + | true, outputFolder when outputFolder <> null -> Path.Combine(outputFolder, "src") + | _ -> step.Name + |> (fun str -> (str.TrimEnd [| Path.DirectorySeparatorChar; Path.AltDirectorySeparatorChar |]) + Path.DirectorySeparatorChar.ToString() |> Uri) + |> (fun uri -> uri.LocalPath |> Path.GetDirectoryName) let context = CodegenContext.Create (compilation, step.AssemblyConstants) let allSources = GetSourceFiles.Apply compilation.Namespaces + + if (allSources.Count > 0 || not (compilation.EntryPoints.IsEmpty)) && not (Directory.Exists dir) then + Directory.CreateDirectory dir + |> ignore for source in allSources |> Seq.filter context.GenerateCodeForSource do let content = SimulationCode.generate source context - CompilationLoader.GeneratedFile(source, dir, ".g.cs", content) |> ignore + this.WriteFile source dir ".g.cs" content false for source in allSources |> Seq.filter (not << context.GenerateCodeForSource) do let content = SimulationCode.loadedViaTestNames source context - if content <> null then CompilationLoader.GeneratedFile(source, dir, ".dll.g.cs", content) |> ignore + this.WriteFile source dir ".dll.g.cs" content false if not compilation.EntryPoints.IsEmpty then @@ -60,11 +93,11 @@ type Emitter() = let mainSourceFile = (dir, "EntryPoint") |> Path.Combine |> Path.GetFullPath |> Uri |> CompilationUnitManager.GetFileId let content = EntryPoint.generateMainSource context entryPointCallables - CompilationLoader.GeneratedFile(mainSourceFile, dir, ".g.Main.cs", content) |> ignore + this.WriteFile mainSourceFile dir ".g.Main.cs" content false for (sourceFile, callables) in entryPointSources do let content = EntryPoint.generateSource context callables - CompilationLoader.GeneratedFile(sourceFile, dir, ".g.EntryPoint.cs", content) |> ignore + this.WriteFile sourceFile dir ".g.EntryPoint.cs" content false transformed <- compilation true From 75b616f710ea6ac6836265a2781a3a4d52037959 Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Mon, 5 Apr 2021 14:16:38 -0700 Subject: [PATCH 4/7] Add information on which method is not implemented (#604) --- src/Qir/Common/Include/SimulatorStub.hpp | 58 ++++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Qir/Common/Include/SimulatorStub.hpp b/src/Qir/Common/Include/SimulatorStub.hpp index c4a8fb25001..5a2a2459e9f 100644 --- a/src/Qir/Common/Include/SimulatorStub.hpp +++ b/src/Qir/Common/Include/SimulatorStub.hpp @@ -13,75 +13,75 @@ namespace Quantum { Qubit AllocateQubit() override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: AllocateQubit"); } void ReleaseQubit(Qubit qubit) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ReleaseQubit"); } virtual std::string QubitToString(Qubit qubit) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: QubitToString"); } void X(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: X"); } void Y(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: Y"); } void Z(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: Z"); } void H(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: H"); } void S(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: S"); } void T(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: T"); } void R(PauliId axis, Qubit target, double theta) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: R"); } void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: Exp"); } void ControlledX(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledX"); } void ControlledY(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledY"); } void ControlledZ(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledZ"); } void ControlledH(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledH"); } void ControlledS(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledS"); } void ControlledT(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledT"); } void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledR"); } void ControlledExp( long numControls, @@ -91,47 +91,47 @@ namespace Quantum Qubit targets[], double theta) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledExp"); } void AdjointS(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: AdjointS"); } void AdjointT(Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: AdjointT"); } void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledAdjointS"); } void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ControlledAdjointT"); } Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: Measure"); } void ReleaseResult(Result result) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: ReleaseResult"); } bool AreEqualResults(Result r1, Result r2) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: AreEqualResults"); } ResultValue GetResultValue(Result result) override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: GetResultValue"); } Result UseZero() override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: UseZero"); } Result UseOne() override { - throw std::logic_error("not_implemented"); + throw std::logic_error("not_implemented: UseOne"); } }; From 2ec3550401a9778d4761d0bb6ed55dc0e673bcca Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Tue, 6 Apr 2021 02:46:13 -0700 Subject: [PATCH 5/7] Fixed the `quantum__rt` prefix (#606) --- src/Qir/Runtime/lib/QIR/bridge-rt.ll | 12 ++++++------ src/Qir/Runtime/lib/QIR/strings.cpp | 4 ++-- src/Qir/Runtime/public/QirRuntime.hpp | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/lib/QIR/bridge-rt.ll index 71498842c92..c792e72cb82 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/lib/QIR/bridge-rt.ll @@ -104,8 +104,8 @@ declare %"struct.QirString"* @quantum__rt__result_to_string(%class.RESULT*) declare %"struct.QirString"* @quantum__rt__pauli_to_string(%PauliId) declare %"struct.QirString"* @quantum__rt__qubit_to_string(%class.QUBIT*) declare %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) -declare i8* @quantum__rt_string_get_data(%"struct.QirString"* %str) -declare i32 @quantum__rt_string_get_length(%"struct.QirString"* %str) +declare i8* @quantum__rt__string_get_data(%"struct.QirString"* %str) +declare i32 @quantum__rt__string_get_length(%"struct.QirString"* %str) ;------------------------------------------------------------------------------ @@ -490,15 +490,15 @@ define dllexport %String* @__quantum__rt__range_to_string(%Range %.range) { ret %String* %.str } -define dllexport i8* @__quantum__rt_string_get_data(%String* %.str) { +define dllexport i8* @__quantum__rt__string_get_data(%String* %.str) { %str = bitcast %String* %.str to %"struct.QirString"* - %result = call i8* @quantum__rt_string_get_data(%"struct.QirString"* %str) + %result = call i8* @quantum__rt__string_get_data(%"struct.QirString"* %str) ret i8* %result } -define dllexport i32 @__quantum__rt_string_get_length(%String* %.str) { +define dllexport i32 @__quantum__rt__string_get_length(%String* %.str) { %str = bitcast %String* %.str to %"struct.QirString"* - %result = call i32 @quantum__rt_string_get_length(%"struct.QirString"* %str) + %result = call i32 @quantum__rt__string_get_length(%"struct.QirString"* %str) ret i32 %result } diff --git a/src/Qir/Runtime/lib/QIR/strings.cpp b/src/Qir/Runtime/lib/QIR/strings.cpp index 49a2c40dffb..5027a88c1fd 100644 --- a/src/Qir/Runtime/lib/QIR/strings.cpp +++ b/src/Qir/Runtime/lib/QIR/strings.cpp @@ -156,12 +156,12 @@ extern "C" return quantum__rt__string_create(oss.str().c_str()); } - const char* quantum__rt_string_get_data(QirString* str) // NOLINT + 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 + uint32_t quantum__rt__string_get_length(QirString* str) // NOLINT { return str->str.size(); } diff --git a/src/Qir/Runtime/public/QirRuntime.hpp b/src/Qir/Runtime/public/QirRuntime.hpp index f7f393eaaa0..c4236ff0e1a 100644 --- a/src/Qir/Runtime/public/QirRuntime.hpp +++ b/src/Qir/Runtime/public/QirRuntime.hpp @@ -232,11 +232,11 @@ extern "C" // 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 + 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 + 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 From 56469cd85b70347cba5b363400b169c19f0c2b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Zaragoza=20Cort=C3=A9s?= Date: Tue, 6 Apr 2021 14:09:05 -0700 Subject: [PATCH 6/7] Add missing EndProject. --- Simulation.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/Simulation.sln b/Simulation.sln index ca6594be7e6..9a65f34a9db 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -112,6 +112,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandaloneInputReference", "StandaloneInputReference", "{A7DB7367-9FD6-4164-8263-A05077BE54AB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-standalone-input-reference", "src\Qir\Samples\StandaloneInputReference\qsharp\qir-standalone-input-reference.csproj", "{D7D34736-A719-4B45-A33F-2723F59EC29D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 1dbd8f61891f81d25c8195b102092ed492730352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Zaragoza=20Cort=C3=A9s?= Date: Tue, 6 Apr 2021 14:39:44 -0700 Subject: [PATCH 7/7] Re-add controller project to Simulation solution. --- Simulation.sln | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/Simulation.sln b/Simulation.sln index 9a65f34a9db..f888733494b 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -93,25 +93,29 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{7F7BB60A EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FullstateSimulator", "FullstateSimulator", "{EAC5EAE7-D1B3-4726-AFDB-73000E62176A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-test-simulator", "src\Qir\Tests\FullstateSimulator\qsharp\qir-test-simulator.csproj", "{C7531119-9730-497A-9D11-8BBB3761B726}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-test-simulator", "src\Qir\Tests\FullstateSimulator\qsharp\qir-test-simulator.csproj", "{C7531119-9730-497A-9D11-8BBB3761B726}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-dynamic", "QIR-dynamic", "{3DFACF7F-D5C2-455B-AB8A-26908DEF7E2D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-test-random", "src\Qir\Tests\QIR-dynamic\qsharp\qir-test-random.csproj", "{5DBF6402-D9CD-4470-A309-3755CFDA42CF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-test-random", "src\Qir\Tests\QIR-dynamic\qsharp\qir-test-random.csproj", "{5DBF6402-D9CD-4470-A309-3755CFDA42CF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-static", "QIR-static", "{54000816-122C-4AA0-9FE9-B0ABB9547232}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-gen", "src\Qir\Tests\QIR-static\qsharp\qir-gen.csproj", "{33ED37AB-61B1-4A49-9952-58D19AA8EF30}" +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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tracer-qir", "src\Qir\Tests\QIR-tracer\qsharp\tracer-qir.csproj", "{002174F4-BFA8-4675-908D-0E9C32ED951A}" +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}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-standalone-input-reference", "src\Qir\Samples\StandaloneInputReference\qsharp\qir-standalone-input-reference.csproj", "{D7D34736-A719-4B45-A33F-2723F59EC29D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-standalone-input-reference", "src\Qir\Samples\StandaloneInputReference\qsharp\qir-standalone-input-reference.csproj", "{D7D34736-A719-4B45-A33F-2723F59EC29D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controller", "Controller", "{4E07F247-ED93-4497-8B58-022314308E67}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QirController", "src\Qir\Controller\QirController.csproj", "{A77E6661-D143-4E3E-BCD1-8E321A966829}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -749,6 +753,22 @@ Global {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Debug|x64.ActiveCfg = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Debug|x64.Build.0 = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Release|Any CPU.Build.0 = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Release|x64.ActiveCfg = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.Release|x64.Build.0 = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -802,6 +822,8 @@ Global {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} + {4E07F247-ED93-4497-8B58-022314308E67} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} + {A77E6661-D143-4E3E-BCD1-8E321A966829} = {4E07F247-ED93-4497-8B58-022314308E67} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821}