From 5355a9d5d4c4d9619a0c785ff2212d46b44d5b81 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 04:55:38 -0700 Subject: [PATCH 01/11] Squashed all commits in the branch to minimize rebase/merge conflicts --- .../Microsoft.Azure.Quantum.Client.csproj | 2 +- src/Qir/.gitignore | 2 +- src/Qir/Runtime/README.md | 4 +- src/Qir/Runtime/lib/QIR/CMakeLists.txt | 4 +- src/Qir/Runtime/lib/QIR/Output.cpp | 13 ++ src/Qir/Runtime/lib/QIR/OutputStream.cpp | 39 +++++ src/Qir/Runtime/lib/QIR/QirRange.cpp | 19 +++ src/Qir/Runtime/lib/QIR/README.md | 61 ++++++++ .../Runtime/lib/QIR/allocationsTracker.hpp | 9 +- src/Qir/Runtime/lib/QIR/arrays.cpp | 21 ++- src/Qir/Runtime/lib/QIR/bridge-rt.ll | 2 +- src/Qir/Runtime/lib/QIR/callables.cpp | 38 ++--- src/Qir/Runtime/lib/QIR/context.cpp | 92 +++++++---- src/Qir/Runtime/lib/QIR/delegated.cpp | 18 +-- src/Qir/Runtime/lib/QIR/rtOut.cpp | 43 ----- src/Qir/Runtime/lib/QIR/strings.cpp | 11 +- src/Qir/Runtime/lib/QIR/utils.cpp | 9 +- src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 1 + src/Qir/Runtime/lib/QSharpCore/README.md | 30 ++++ src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp | 2 +- .../Runtime/lib/QSharpCore/intrinsicsDump.cpp | 23 +++ .../Runtime/lib/QSharpCore/qsharp-core-qis.ll | 22 +++ .../lib/QSharpCore/qsharp__core__qis.hpp | 21 +-- .../Runtime/lib/QSharpFoundation/README.md | 39 +++++ .../QSharpFoundation/qsharp-foundation-qis.ll | 24 ++- .../qsharp__foundation__qis.hpp | 14 +- src/Qir/Runtime/lib/README.md | 31 ++++ src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 3 +- .../lib/Simulators/FullstateSimulator.cpp | 117 +++++++++++++- src/Qir/Runtime/lib/Simulators/README.md | 49 ++++++ .../lib/Simulators/ToffoliSimulator.cpp | 11 ++ src/Qir/Runtime/lib/Simulators/setup.cpp | 18 --- src/Qir/Runtime/public/OutputStream.hpp | 35 +++++ src/Qir/Runtime/public/QSharpSimApi_I.hpp | 6 + src/Qir/Runtime/public/QirContext.hpp | 39 ++--- src/Qir/Runtime/public/QirRuntime.hpp | 27 +--- src/Qir/Runtime/public/QirTypes.hpp | 20 ++- src/Qir/Runtime/public/README.md | 55 +++++++ src/Qir/Runtime/unittests/QirRuntimeTests.cpp | 65 +++----- .../StandaloneInputReference/qir-driver.cpp | 5 +- .../FullstateSimulatorTests.cpp | 2 +- src/Qir/Tests/QIR-dynamic/qir-driver.cpp | 147 ++++++++++++++++-- .../Tests/QIR-dynamic/qsharp/qir-test-dump.qs | 31 ++++ src/Qir/Tests/QIR-static/qir-driver.cpp | 22 +-- .../QIR-static/qir-test-conditionals.cpp | 6 +- src/Qir/Tests/QIR-static/qir-test-noqsharp.ll | 2 +- src/Qir/Tests/QIR-static/qir-test-ouput.cpp | 25 +-- .../Tests/QIR-tracer/qir-tracer-driver.cpp | 4 +- src/Qir/Tests/TestUtils.cpp | 15 ++ src/Qir/Tests/TestUtils.hpp | 11 ++ src/Qir/build_all.ps1 | 3 + src/Qir/test_all.ps1 | 3 + ...Microsoft.Quantum.Simulation.Common.csproj | 2 +- src/Simulation/Native/src/simulator/capi.cpp | 17 ++ src/Simulation/Native/src/simulator/capi.hpp | 45 ++++-- .../Native/src/simulator/simulator.hpp | 32 ++++ .../src/simulator/simulatorinterface.hpp | 12 +- .../QuantumSimulator/StateDumper.cs | 2 +- src/Xunit/Microsoft.Quantum.Xunit.csproj | 2 +- 59 files changed, 1074 insertions(+), 353 deletions(-) create mode 100644 src/Qir/Runtime/lib/QIR/Output.cpp create mode 100644 src/Qir/Runtime/lib/QIR/OutputStream.cpp create mode 100644 src/Qir/Runtime/lib/QIR/QirRange.cpp create mode 100644 src/Qir/Runtime/lib/QIR/README.md delete mode 100644 src/Qir/Runtime/lib/QIR/rtOut.cpp create mode 100644 src/Qir/Runtime/lib/QSharpCore/README.md create mode 100644 src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp create mode 100644 src/Qir/Runtime/lib/QSharpFoundation/README.md create mode 100644 src/Qir/Runtime/lib/README.md create mode 100644 src/Qir/Runtime/lib/Simulators/README.md delete mode 100644 src/Qir/Runtime/lib/Simulators/setup.cpp create mode 100644 src/Qir/Runtime/public/OutputStream.hpp create mode 100644 src/Qir/Runtime/public/README.md create mode 100644 src/Qir/Tests/QIR-dynamic/qsharp/qir-test-dump.qs create mode 100644 src/Qir/Tests/TestUtils.cpp create mode 100644 src/Qir/Tests/TestUtils.hpp create mode 100644 src/Qir/build_all.ps1 create mode 100644 src/Qir/test_all.ps1 diff --git a/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj b/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj index 444751e9078..5cbb18eb53b 100644 --- a/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj +++ b/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/Qir/.gitignore b/src/Qir/.gitignore index 44889a5d35b..d8076182368 100644 --- a/src/Qir/.gitignore +++ b/src/Qir/.gitignore @@ -1,2 +1,2 @@ # Ignore the generated qir files from Q# compiler -qir/ +**/qsharp/qir/ diff --git a/src/Qir/Runtime/README.md b/src/Qir/Runtime/README.md index 40d9de3eae7..ed008c6b7f7 100644 --- a/src/Qir/Runtime/README.md +++ b/src/Qir/Runtime/README.md @@ -145,7 +145,7 @@ There are two ways to compile and run the QIR files against the runtime. QIR's architecture assumes a single target, whether that be hardware or a particular simulator. As a result, there is no provision in the QIR specifications to choose a target dynamically. To connect QIR to the simulators from this runtime, - we provide `InitializeQirContext` and `ReleaseQirContext` methods. Switching contexts while executing QIR isn't + we provide `QirExecutionContext::Init()` and `QirExecutionContext::Deinit()` methods. Switching contexts while executing QIR isn't supported and would yield undefined behavior. ### Building from IR files @@ -166,8 +166,6 @@ CMake doesn't support using LLVM's IR files as input so instead we invoke Clang 1. Variadic functions (e.g. `__quantum__rt__array_create`) require platform specific bridges. The currently implemented bridge is for Windows. 1. Qubit borrowing NYI (needs both bridge and simulator's support). -1. `@ResultZero` and `@ResultOne` global variables, used in QIR generated from Q#, cannot be treated in a - platform-agnostic way when linking against the shared qdk library. ## Coding style and conventions diff --git a/src/Qir/Runtime/lib/QIR/CMakeLists.txt b/src/Qir/Runtime/lib/QIR/CMakeLists.txt index 4bae717ec85..9dbd2b9cd74 100644 --- a/src/Qir/Runtime/lib/QIR/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QIR/CMakeLists.txt @@ -9,6 +9,9 @@ # create qir-rt-support lib from the C++ sources # set(rt_sup_source_files + QirRange.cpp + OutputStream.cpp + Output.cpp allocationsTracker.cpp arrays.cpp callables.cpp @@ -16,7 +19,6 @@ set(rt_sup_source_files delegated.cpp strings.cpp utils.cpp - rtOut.cpp ) # Produce object lib we'll use to create a shared lib (so/dll) later on diff --git a/src/Qir/Runtime/lib/QIR/Output.cpp b/src/Qir/Runtime/lib/QIR/Output.cpp new file mode 100644 index 00000000000..bd7ee79bf8c --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/Output.cpp @@ -0,0 +1,13 @@ +#include // std::endl +#include "QirTypes.hpp" +#include "QirRuntime.hpp" // QIR_SHARED_API for quantum__rt__message. +#include "OutputStream.hpp" + +// Public API: +extern "C" +{ + void quantum__rt__message(QirString* qstr) // NOLINT + { + Microsoft::Quantum::OutputStream::Get() << qstr->str << std::endl; + } +} // extern "C" diff --git a/src/Qir/Runtime/lib/QIR/OutputStream.cpp b/src/Qir/Runtime/lib/QIR/OutputStream.cpp new file mode 100644 index 00000000000..40d4a485337 --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/OutputStream.cpp @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// https://stackoverflow.com/a/5419388/6362941 +// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 +// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 + +#include +#include "OutputStream.hpp" + +namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. +{ +namespace Quantum +{ + std::ostream* OutputStream::currentOutputStream = &std::cout; // Output to std::cout by default. + + std::ostream& OutputStream::Get() + { + return *currentOutputStream; + } + + std::ostream& OutputStream::Set(std::ostream& newOStream) + { + std::ostream& oldOStream = *currentOutputStream; + currentOutputStream = &newOStream; + return oldOStream; + } + + OutputStream::ScopedRedirector::ScopedRedirector(std::ostream& newOstream) + : old(OutputStream::Set(newOstream)) + {} + + OutputStream::ScopedRedirector::~ScopedRedirector() + { + OutputStream::Set(old); + } + +} // namespace Quantum +} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/QirRange.cpp b/src/Qir/Runtime/lib/QIR/QirRange.cpp new file mode 100644 index 00000000000..e1531559b19 --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/QirRange.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include "QirTypes.hpp" + +QirRange::QirRange() + : start(0) + , step(0) + , end(0) +{ +} + +QirRange::QirRange(int64_t st, int64_t sp, int64_t en) + : start(st) + , step(sp) + , end(en) +{ +} \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QIR/README.md b/src/Qir/Runtime/lib/QIR/README.md new file mode 100644 index 00000000000..413f26efbcf --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/README.md @@ -0,0 +1,61 @@ +# API Dependency + +(See the raw file. Please keep the raw file readable rather than the browser-rendered one) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Directory + +**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + + +## Level 1 +**allocationsTracker.hpp** Defines `Microsoft::Quantum::AllocationsTracker` that tracks the allocations and detects the mem leaks. + Does not depend on anything of our code. + +**utils.cpp** Implements `quantum__rt__fail()`, `quantum__rt__memory_allocate()`, `quantum__rt__heap_{alloc,free}()`. + `quantum__rt__heap_alloc()` calls **strings.cpp**'s `quantum__rt__string_create()` - cyclical dependency. + +**strings.cpp** Implements `QirString`, `quantum__rt__string_*()`, `quantum__rt___to_string()` (except `qubit_to_string` and `result_to_string`). + Depends on **utils.cpp**'s `quantum__rt__fail()` - cyclical dependency. + + +## Level 2 +**allocationsTracker.cpp** Implements the internals of `Microsoft::Quantum::AllocationsTracker`. + Depends on `quantum__rt__fail()`, `quantum__rt__string_create()` + +**context.cpp** Implements the internals of `Microsoft::Quantum::QirExecutionContext`, + Depends on **allocationsTracker.hpp**'s `Microsoft::Quantum::AllocationsTracker`. + Gets/returns `IRuntimeDriver *`. + +## Level 3 +**delegated.cpp** Implements `quantum__rt__result_*()`, `quantum__rt__qubit_{allocate,release,to_string}()`. + Each API depends on `Microsoft::Quantum::GlobalContext()[->GetDriver()]`, + `quantum__rt__qubit_to_string()` also depends on strings.cpp's `quantum__rt__string_create()`. + `quantum__rt__result_to_string()` also depends on strings.cpp's `quantum__rt__string_create()`. + +**arrays.cpp** Implements {the internals of `QirArray`} and `quantum__rt__*array*`. + Depends on `Microsoft::Quantum::GlobalContext()`, `quantum__rt__fail()`, `quantum__rt__string_create()`, + **delegated.cpp**'s `quantum__rt__qubit_allocate()` + +## Level 4 +**callables.cpp** Defines the {internals of `QirTupleHeader`, `QirCallable`}, `quantum__rt__tuple_*()`, `quantum__rt__callable_*()` + Depends on `QirArray`, `Microsoft::Quantum::GlobalContext()`, `quantum__rt__fail()`, `quantum__rt__string_create()`, `TupleWithControls`, + Consider breaking up into **Tuples.cpp** and **Callables.cpp**. + +## Level 5 +**bridge-rt.ll** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). + The C++ Standard reserves the identifiers starting with double underscores `__`, that is why the definitions of `@__quantum__rt__*` + have been put to `.ll` file rather than `.cpp` file. + Depends on `quantum__rt__*` implementations (in **utils.cpp**, **strings.cpp**, **delegated.cpp**, **arrays.cpp**, **callables.cpp**). diff --git a/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp b/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp index b587d18ab68..4f22ade46e9 100644 --- a/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp +++ b/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp @@ -5,8 +5,6 @@ #include -#include "CoreTypes.hpp" - namespace Microsoft { namespace Quantum @@ -14,15 +12,16 @@ namespace Quantum // The tracker keeps a list of pointers to all qir objects that have been allocated during the lifetime of an // execution context and their reference counts, which allows us to check for double-releases and leaks when the // actual objects have been released. - struct QIR_SHARED_API AllocationsTracker + struct AllocationsTracker { - std::unordered_map allocatedObjects; - void OnAllocate(void* object); void OnAddRef(void* object); void OnRelease(void* object); void CheckForLeaks() const; + + private: + std::unordered_map allocatedObjects; }; } // namespace Quantum diff --git a/src/Qir/Runtime/lib/QIR/arrays.cpp b/src/Qir/Runtime/lib/QIR/arrays.cpp index 54c68d08b82..4c58691a3f3 100644 --- a/src/Qir/Runtime/lib/QIR/arrays.cpp +++ b/src/Qir/Runtime/lib/QIR/arrays.cpp @@ -12,16 +12,15 @@ #include "CoreTypes.hpp" #include "QirContext.hpp" #include "QirTypes.hpp" -#include "allocationsTracker.hpp" #include "QirRuntime.hpp" using namespace Microsoft::Quantum; int QirArray::AddRef() { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAddRef(this); + GlobalContext()->OnAddRef(this); } assert(this->refCount != 0 && "Cannot resurrect released array!"); @@ -33,9 +32,9 @@ int QirArray::AddRef() // should delete it, if allocated from the heap. int QirArray::Release() { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnRelease(this); + GlobalContext()->OnRelease(this); } assert(this->refCount != 0 && "Cannot release already released array!"); @@ -69,9 +68,9 @@ QirArray::QirArray(int64_t qubits_count) } this->dimensionSizes.push_back(this->count); - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(this); + GlobalContext()->OnAllocate(this); } } @@ -85,9 +84,9 @@ QirArray::QirArray(int64_t count_items, int item_size_bytes, int dimCount, std:: { assert(dimCount > 0); - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(this); + GlobalContext()->OnAllocate(this); } if (dimCount == 1) @@ -119,9 +118,9 @@ QirArray::QirArray(const QirArray* other) , ownsQubits(false) , refCount(1) { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(this); + GlobalContext()->OnAllocate(this); } const int64_t size = this->count * this->itemSizeInBytes; diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/lib/QIR/bridge-rt.ll index 3c54b56506e..a444ecf4d20 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/lib/QIR/bridge-rt.ll @@ -233,7 +233,7 @@ define dllexport %Array* @__quantum__rt__array_copy(%Array* %.ar, i1 %force) { ; TODO: This bridge isn't cross-platform! ; it works on Windows but on Linux %args ends up not being a valid pointer. -declare void @DebugLogPtr(i8*) +;declare void @DebugLogPtr(i8*) define dllexport %Array* @__quantum__rt__array_create(i32 %item_size, i32 %dim_count, ...) { %args1 = alloca i8*, align 8 %args2 = bitcast i8** %args1 to i8* diff --git a/src/Qir/Runtime/lib/QIR/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp index f63bc44dc03..a425c7251e4 100644 --- a/src/Qir/Runtime/lib/QIR/callables.cpp +++ b/src/Qir/Runtime/lib/QIR/callables.cpp @@ -10,7 +10,6 @@ #include "QirContext.hpp" #include "QirTypes.hpp" -#include "allocationsTracker.hpp" #include "QirRuntime.hpp" using namespace Microsoft::Quantum; @@ -114,8 +113,8 @@ extern "C" } QirCallable* quantum__rt__callable_create( - t_CallableEntry* entries, - t_CaptureCallback* captureCallbacks, + QirCallable::t_CallableEntry* entries, + QirCallable::t_CaptureCallback* captureCallbacks, PTuple capture) { assert(entries != nullptr); @@ -164,9 +163,9 @@ extern "C" ==============================================================================*/ int QirTupleHeader::AddRef() { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAddRef(this); + GlobalContext()->OnAddRef(this); } assert(this->refCount > 0); @@ -175,11 +174,12 @@ int QirTupleHeader::AddRef() int QirTupleHeader::Release() { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnRelease(this); + GlobalContext()->OnRelease(this); } + assert(this->refCount > 0); // doesn't guarantee we catch double releases but better than nothing int retVal = --this->refCount; if (this->refCount == 0) @@ -195,9 +195,9 @@ QirTupleHeader* QirTupleHeader::Create(int size) assert(size >= 0); char* buffer = new char[sizeof(QirTupleHeader) + size]; - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(buffer); + GlobalContext()->OnAllocate(buffer); } // at the beginning of the buffer place QirTupleHeader, leave the buffer uninitialized @@ -214,9 +214,9 @@ QirTupleHeader* QirTupleHeader::CreateWithCopiedData(QirTupleHeader* other) const int size = other->tupleSize; char* buffer = new char[sizeof(QirTupleHeader) + size]; - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(buffer); + GlobalContext()->OnAllocate(buffer); } // at the beginning of the buffer place QirTupleHeader @@ -253,9 +253,9 @@ QirCallable::QirCallable(const t_CallableEntry* ftEntries, const t_CaptureCallba memcpy(this->captureCallbacks, callbacks, sizeof(this->captureCallbacks)); } - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(this); + GlobalContext()->OnAllocate(this); } } @@ -268,9 +268,9 @@ QirCallable::QirCallable(const QirCallable& other) memcpy(this->functionTable, other.functionTable, sizeof(this->functionTable)); memcpy(this->captureCallbacks, other.captureCallbacks, sizeof(this->captureCallbacks)); - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAllocate(this); + GlobalContext()->OnAllocate(this); } } @@ -286,9 +286,9 @@ QirCallable* QirCallable::CloneIfShared() int QirCallable::AddRef() { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnAddRef(this); + GlobalContext()->OnAddRef(this); } int rc = ++this->refCount; @@ -298,9 +298,9 @@ int QirCallable::AddRef() int QirCallable::Release() { - if (g_context != nullptr && g_context->trackAllocatedObjects) + if (GlobalContext() != nullptr) { - g_context->allocationsTracker->OnRelease(this); + GlobalContext()->OnRelease(this); } assert(this->refCount > 0); diff --git a/src/Qir/Runtime/lib/QIR/context.cpp b/src/Qir/Runtime/lib/QIR/context.cpp index 23625950d6e..cd0931b1ec4 100644 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ b/src/Qir/Runtime/lib/QIR/context.cpp @@ -9,58 +9,90 @@ #include "QirRuntimeApi_I.hpp" #include "allocationsTracker.hpp" -// These two globals are used in QIR _directly_ so have to define them outside of the context. -extern "C" QIR_SHARED_API Result ResultOne = nullptr; -extern "C" QIR_SHARED_API Result ResultZero = nullptr; - namespace Microsoft { namespace Quantum { - thread_local std::unique_ptr g_context = nullptr; - std::unique_ptr& GlobalContext() { return g_context; } + std::unique_ptr& GlobalContext() + { + static std::unique_ptr g_context = nullptr; + + return g_context; + } - void InitializeQirContext(IRuntimeDriver* sim, bool trackAllocatedObjects) + QirExecutionContext::QirExecutionContext(IRuntimeDriver* simulator, bool trackAllocatedObjects) + : driver(simulator) + , trackAllocatedObjects(trackAllocatedObjects) { - assert(g_context == nullptr); - g_context = std::make_unique(sim, trackAllocatedObjects); - - if (g_context->driver != nullptr) + if (this->trackAllocatedObjects) { - ResultOne = g_context->driver->UseOne(); - ResultZero = g_context->driver->UseZero(); + this->allocationsTracker = std::make_unique(); } - else + } + + // If we just remove this user-declared-and-defined dtor + // then it will be automatically defined together with the class definition, + // which will require the `AllocationsTracker` to be a complete type + // everywhere where `public/QirContext.hpp` is included + // (we'll have to move `allocationsTracker.hpp` to `public/`). + QirExecutionContext::~QirExecutionContext() = default; + + void QirExecutionContext::Init(IRuntimeDriver* simulator, bool trackAllocatedObjects /*= false*/) + { + assert(GlobalContext() == nullptr); + GlobalContext() = std::make_unique(simulator, trackAllocatedObjects); + } + + void QirExecutionContext::Deinit() + { + assert(GlobalContext() != nullptr); + + if (GlobalContext()->trackAllocatedObjects) { - ResultOne = nullptr; - ResultZero = nullptr; + GlobalContext()->allocationsTracker->CheckForLeaks(); } + + GlobalContext().reset(nullptr); } - void ReleaseQirContext() + void QirExecutionContext::OnAddRef(void* object) { - assert(g_context != nullptr); - - if (g_context->trackAllocatedObjects) + if(trackAllocatedObjects) { - g_context->allocationsTracker->CheckForLeaks(); + this->allocationsTracker->OnAddRef(object); } + } - ResultOne = nullptr; - ResultZero = nullptr; - g_context.reset(nullptr); + void QirExecutionContext::OnRelease(void* object) + { + if(this->trackAllocatedObjects) + { + this->allocationsTracker->OnRelease(object); + } } - QirExecutionContext::QirExecutionContext(IRuntimeDriver* sim, bool trackAllocatedObjects) - : driver(sim) - , trackAllocatedObjects(trackAllocatedObjects) + void QirExecutionContext::OnAllocate(void* object) { - if (this->trackAllocatedObjects) + if(this->trackAllocatedObjects) { - this->allocationsTracker = std::make_unique(); + this->allocationsTracker->OnAllocate(object); } } - QirExecutionContext::~QirExecutionContext() = default; + + IRuntimeDriver* QirExecutionContext::GetDriver() const + { + return this->driver; + } + + QirExecutionContext::Scoped::Scoped(IRuntimeDriver* sim, bool trackAllocatedObjects /*= false*/) + { + QirExecutionContext::Init(sim, trackAllocatedObjects); + } + + QirExecutionContext::Scoped::~Scoped() + { + QirExecutionContext::Deinit(); + } } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QIR/delegated.cpp b/src/Qir/Runtime/lib/QIR/delegated.cpp index 8d466b086f4..84b1d77679e 100644 --- a/src/Qir/Runtime/lib/QIR/delegated.cpp +++ b/src/Qir/Runtime/lib/QIR/delegated.cpp @@ -29,22 +29,22 @@ extern "C" { Result quantum__rt__result_get_zero() { - return Microsoft::Quantum::g_context->driver->UseZero(); + return Microsoft::Quantum::GlobalContext()->GetDriver()->UseZero(); } Result quantum__rt__result_get_one() { - return Microsoft::Quantum::g_context->driver->UseOne(); + return Microsoft::Quantum::GlobalContext()->GetDriver()->UseOne(); } QUBIT* quantum__rt__qubit_allocate() // NOLINT { - return Microsoft::Quantum::g_context->driver->AllocateQubit(); + return Microsoft::Quantum::GlobalContext()->GetDriver()->AllocateQubit(); } void quantum__rt__qubit_release(QUBIT* qubit) // NOLINT { - Microsoft::Quantum::g_context->driver->ReleaseQubit(qubit); + Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseQubit(qubit); } void quantum__rt__result_update_reference_count(RESULT* r, int32_t increment) @@ -76,7 +76,7 @@ extern "C" if (rit == trackedResults.end()) { assert(increment == -1); - Microsoft::Quantum::g_context->driver->ReleaseResult(r); + Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseResult(r); } else { @@ -85,7 +85,7 @@ extern "C" if (newRefcount == 0) { trackedResults.erase(rit); - Microsoft::Quantum::g_context->driver->ReleaseResult(r); + Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseResult(r); } else { @@ -101,13 +101,13 @@ extern "C" { return true; } - return Microsoft::Quantum::g_context->driver->AreEqualResults(r1, r2); + return Microsoft::Quantum::GlobalContext()->GetDriver()->AreEqualResults(r1, r2); } // Returns a string representation of the result. QirString* quantum__rt__result_to_string(RESULT* result) // NOLINT { - ResultValue rv = Microsoft::Quantum::g_context->driver->GetResultValue(result); + ResultValue rv = Microsoft::Quantum::GlobalContext()->GetDriver()->GetResultValue(result); assert(rv != Result_Pending); return (rv == Result_Zero) ? quantum__rt__string_create("Zero") : quantum__rt__string_create("One"); @@ -116,6 +116,6 @@ extern "C" // Returns a string representation of the qubit. QirString* quantum__rt__qubit_to_string(QUBIT* qubit) // NOLINT { - return quantum__rt__string_create(Microsoft::Quantum::g_context->driver->QubitToString(qubit).c_str()); + return quantum__rt__string_create(Microsoft::Quantum::GlobalContext()->GetDriver()->QubitToString(qubit).c_str()); } } \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QIR/rtOut.cpp b/src/Qir/Runtime/lib/QIR/rtOut.cpp deleted file mode 100644 index 5f921c706fc..00000000000 --- a/src/Qir/Runtime/lib/QIR/rtOut.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -// Forward declarations: -static std::ostream& GetOutputStream(); - -// Public API: -extern "C" -{ - void quantum__rt__message(QirString* qstr) // NOLINT - { - GetOutputStream() << qstr->str << std::endl; - } -} // extern "C" - - -// Internal API: -static std::ostream* currentOutputStream = &std::cout; // Log to std::cout by default. - -static std::ostream& GetOutputStream() -{ - return *currentOutputStream; -} - - -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - std::ostream& SetOutputStream(std::ostream & newOStream) - { - std::ostream& oldOStream = *currentOutputStream; - currentOutputStream = &newOStream; - return oldOStream; - } -} // namespace Quantum -} // namespace Microsoft - diff --git a/src/Qir/Runtime/lib/QIR/strings.cpp b/src/Qir/Runtime/lib/QIR/strings.cpp index 5027a88c1fd..9dc33d4e4df 100644 --- a/src/Qir/Runtime/lib/QIR/strings.cpp +++ b/src/Qir/Runtime/lib/QIR/strings.cpp @@ -11,7 +11,7 @@ #include "QirTypes.hpp" #include "QirRuntime.hpp" -std::unordered_map& AllocatedStrings() +std::unordered_map& AllocatedStrings() // Cannot be static, is called by tests. { static std::unordered_map allocatedStrings; return allocatedStrings; @@ -36,11 +36,16 @@ static QirString* CreateOrReuseAlreadyAllocated(std::string&& str) } QirString::QirString(std::string&& str) - : refCount(1) - , str(std::move(str)) + : str(std::move(str)) { } +QirString::QirString(const char* cstr) + : str(cstr) +{ +} + + extern "C" { // Creates a string from an array of UTF-8 bytes. diff --git a/src/Qir/Runtime/lib/QIR/utils.cpp b/src/Qir/Runtime/lib/QIR/utils.cpp index 16ab94fa645..5b12469c4ca 100644 --- a/src/Qir/Runtime/lib/QIR/utils.cpp +++ b/src/Qir/Runtime/lib/QIR/utils.cpp @@ -9,11 +9,10 @@ #include #include -#include "QirRuntime.hpp" - #include "QirTypes.hpp" +#include "QirRuntime.hpp" -std::unordered_set& UseMemoryTracker() +static std::unordered_set& UseMemoryTracker() { static std::unordered_set memoryTracker; return memoryTracker; @@ -54,9 +53,7 @@ extern "C" // Fail the computation with the given error message. void quantum__rt__fail(QirString* msg) // NOLINT { - std::string str = msg->str; - quantum__rt__string_update_reference_count(msg, -1); - throw std::runtime_error(str); + throw std::runtime_error(msg->str); } } \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt index 721ea8b0a46..abf35258ede 100644 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt @@ -6,6 +6,7 @@ # create qsharp-core-qis-support lib from the C++ sources # set(qsharp_core_sup_source_files + intrinsicsDump.cpp intrinsics.cpp ) diff --git a/src/Qir/Runtime/lib/QSharpCore/README.md b/src/Qir/Runtime/lib/QSharpCore/README.md new file mode 100644 index 00000000000..87468f7c415 --- /dev/null +++ b/src/Qir/Runtime/lib/QSharpCore/README.md @@ -0,0 +1,30 @@ +# API Dependency + +(See the raw file. Please keep the raw file readable rather than the browser-rendered one) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Directory +**public\CoreTypes.hpp** (QUBIT, PauliId, RESULT)[, `public\QirTypes.hpp` (QirArray)]. +**public\QirContext.hpp** Declares `GlobalContext()`. +**public\QSharpSimApi_I.hpp** + Defines `IQuantumGateSet`. + +## Level +**qsharp__core__qis.hpp** Declares `quantum__qis__*()` gate set implementations. + Depends on `public\CoreTypes.hpp` (QUBIT, PauliId, RESULT) + Uses `QirArray *` from `public\QirTypes.hpp`. + +**intrinsics.cpp** Defines `quantum__qis__*()` gate set implementation. + Each API depends on `GlobalContext()`, `IQuantumGateSet`. + +## Level +**qsharp-core-qis.ll** Defines `@__quantum__qis__*()` quantum gate set entry points (to be called by the `.ll` files generated from users' `.qs` files). + The C++ Standard reserves the identifiers starting with double underscores `__`, that is why the definitions of `@__quantum__qis__*` + have been put to `.ll` file rather than `.cpp` file. + Depends on `quantum__qis__*` implementations (in **intrinsics.cpp**). diff --git a/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp b/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp index 9936ca85622..a79aa705e5b 100644 --- a/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp +++ b/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp @@ -32,7 +32,7 @@ static std::vector ExtractPauliIds(QirArray* paulis) static Microsoft::Quantum::IQuantumGateSet* GateSet() { - return dynamic_cast(Microsoft::Quantum::GlobalContext()->driver); + return dynamic_cast(Microsoft::Quantum::GlobalContext()->GetDriver()); } extern "C" diff --git a/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp b/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp new file mode 100644 index 00000000000..714235714f4 --- /dev/null +++ b/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp @@ -0,0 +1,23 @@ +#include "QirContext.hpp" +#include "QSharpSimApi_I.hpp" +#include "QirRuntimeApi_I.hpp" +#include "qsharp__core__qis.hpp" + +static Microsoft::Quantum::IDiagnostics* GetDiagnostics() +{ + return dynamic_cast(Microsoft::Quantum::GlobalContext()->GetDriver()); +} + +// Implementation: +extern "C" +{ + void quantum__qis__dumpmachine__body(uint8_t* location) + { + GetDiagnostics()->DumpMachine(location); + } + + void quantum__qis__dumpregister__body(uint8_t* location, const QirArray* qubits) + { + GetDiagnostics()->DumpRegister(location, qubits); + } +} // extern "C" diff --git a/src/Qir/Runtime/lib/QSharpCore/qsharp-core-qis.ll b/src/Qir/Runtime/lib/QSharpCore/qsharp-core-qis.ll index f39633e8e78..66f2a1752bf 100644 --- a/src/Qir/Runtime/lib/QSharpCore/qsharp-core-qis.ll +++ b/src/Qir/Runtime/lib/QSharpCore/qsharp-core-qis.ll @@ -60,6 +60,14 @@ declare void @quantum__qis__y__ctl(%struct.QirArray*, %class.QUBIT*) declare void @quantum__qis__z__body(%class.QUBIT*) declare void @quantum__qis__z__ctl(%struct.QirArray*, %class.QUBIT*) +;=============================================================================== +; quantum.qis dump functions declarations +; +; Must be `const void* %location`, but `void *` is invalid (LLVM), and `const` is not supported. +declare void @quantum__qis__dumpmachine__body(i8* %location) +declare void @quantum__qis__dumpregister__body(i8* %location, %struct.QirArray* %qubits) + + ;=============================================================================== ; quantum.qis namespace implementations ; @@ -278,3 +286,17 @@ define dllexport void @__quantum__qis__z__ctl(%Array* %.ctls, %Qubit* %.q) { ret void } + +;=============================================================================== +; quantum.qis dump functions implementation +; +define dllexport void @__quantum__qis__dumpmachine__body(i8* %location) { + call void @quantum__qis__dumpmachine__body(i8* %location) + ret void +} + +define dllexport void @__quantum__qis__dumpregister__body(i8* %location, %Array* %.qubits) { + %qubits = bitcast %Array* %.qubits to %struct.QirArray* + call void @quantum__qis__dumpregister__body(i8* %location, %struct.QirArray* %qubits) + ret void +} diff --git a/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp b/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp index fa8c3cc120e..5a35ad4541b 100644 --- a/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp +++ b/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp @@ -1,24 +1,11 @@ #pragma once -#include - -#include "CoreTypes.hpp" +#include "CoreTypes.hpp" // QUBIT, PauliId, RESULT // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. struct QirArray; -struct QirCallable; -struct QirString; -struct QirBigInt; - -namespace Microsoft -{ -namespace Quantum -{ - struct IQuantumGateSet; -} -} // namespace Microsoft /* Methods from __quantum__qis namespace are specific to the target. When QIR is generated it might limit or extend @@ -54,4 +41,8 @@ extern "C" QIR_SHARED_API void quantum__qis__z__body(QUBIT*); // NOLINT QIR_SHARED_API void quantum__qis__z__ctl(QirArray*, QUBIT*); // NOLINT -} \ No newline at end of file + // Q# Dump: + // Note: The param `location` must be `const void*`, but it is called from .ll, where `const void*` is not supported. + QIR_SHARED_API void quantum__qis__dumpmachine__body(uint8_t* location); // NOLINT + QIR_SHARED_API void quantum__qis__dumpregister__body(uint8_t* location, const QirArray* qubits); // NOLINT +} diff --git a/src/Qir/Runtime/lib/QSharpFoundation/README.md b/src/Qir/Runtime/lib/QSharpFoundation/README.md new file mode 100644 index 00000000000..23e26cf81fc --- /dev/null +++ b/src/Qir/Runtime/lib/QSharpFoundation/README.md @@ -0,0 +1,39 @@ +# API Dependency + +(See the raw file. Please keep the raw file readable rather than the browser-rendered one) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Directory +**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + + +## Level +**conditionals.cpp** Defines `quantum__qis__apply*__body()`. + Depends on QIR's `quantum__rt__result_{equal,get_zero}()`, declared in **public\QirRuntime.hpp**. + +**intrinsicsMath.cpp** Defines `quantum__qis__*` math funcs implementations, + `Quantum::Qis::Internal::{excStrDrawRandomVal,RandomizeSeed,GetLastGeneratedRandomI64,GetLastGeneratedRandomDouble}`. + Depends on `quantum__rt__fail()`, `quantum__rt__string_create()`, declared in **public\QirRuntime.hpp**. + + +## Level + +**qsharp__foundation__qis.hpp** + Declares `quantum__qis__*()` math funcs and ApplyIf. + Depends on **public\CoreTypes.hpp**, **public\QirTypes.hpp**. + +**qsharp-foundation-qis.ll** + Defines `@__quantum__qis__*()` math funcs and ApplyIf - entry points (to be called by the `.ll` files generated from users' `.qs` files). + Depends on `quantum__qis__*` implementations (in **intrinsicsMath.cpp**, **conditionals.cpp**, ). diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll index 3e8a749e187..650657a9766 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll +++ b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll @@ -31,7 +31,8 @@ %struct.QirString = type opaque %PauliId = type i32 -declare void @quantum__rt__message(%"struct.QirString"* %str) +; To do: remove this declaration after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. +declare dllimport void @quantum__rt__message(%"struct.QirString"* %str) ;=============================================================================== ; @@ -43,10 +44,11 @@ define dllexport void @__quantum__qis__message__body(%String* %.str) { } ;=============================================================================== -; quantum.qis math functions +; quantum.qis math functions declarations ; ; LLVM intrinsics (https://llvm.org/docs/LangRef.html): +; To do: consider calling these directly from the compiler-generated .ll code, rather than throug the QIR. declare double @llvm.sqrt.f64(double %.val) declare double @llvm.log.f64(double %Val) declare double @llvm.sin.f64(double %Val) @@ -66,6 +68,17 @@ declare double @quantum__qis__ieeeremainder__body(double %y, double %x) declare i64 @quantum__qis__drawrandomint__body(i64 %min, i64 %max) declare double @quantum__qis__drawrandomdouble__body(double %min, double %max) +;=============================================================================== +; quantum.qis conditional functions declarations +; +declare void @quantum__qis__applyifelseintrinsic__body(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyconditionallyintrinsic__body( + %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) + + +;=============================================================================== +; quantum.qis math functions implementation +; ; API for the user code: define dllexport double @__quantum__qis__nan__body() { ; Q#: function NAN() : Double http://www.cplusplus.com/reference/cmath/nan-function/ %result = call double @llvm.sqrt.f64(double -1.0) ; sqrt() -> NaN @@ -202,12 +215,8 @@ define dllexport double @__quantum__qis__drawrandomdouble__body(double %min, dou ;=============================================================================== -; quantum.qis conditional functions +; quantum.qis conditional functions implementation ; -declare void @quantum__qis__applyifelseintrinsic__body(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) -declare void @quantum__qis__applyconditionallyintrinsic__body( - %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) - define dllexport void @__quantum__qis__applyifelseintrinsic__body( %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { @@ -231,4 +240,3 @@ define dllexport void @__quantum__qis__applyconditionallyintrinsic__body( %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) ret void } - diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp b/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp index aa234b36170..21ee2550d55 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp +++ b/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp @@ -3,22 +3,13 @@ #include #include "CoreTypes.hpp" +#include "QirTypes.hpp" // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. struct QirArray; struct QirCallable; -struct QirString; -struct QirBigInt; - -namespace Microsoft -{ -namespace Quantum -{ - struct IQuantumGateSet; -} -} // namespace Microsoft /* Methods from __quantum__qis namespace are specific to the target. When QIR is generated it might limit or extend @@ -49,4 +40,5 @@ extern "C" QirArray*, QirCallable*, QirCallable*); -} \ No newline at end of file + +} // extern "C" \ No newline at end of file diff --git a/src/Qir/Runtime/lib/README.md b/src/Qir/Runtime/lib/README.md new file mode 100644 index 00000000000..caec80fe1cf --- /dev/null +++ b/src/Qir/Runtime/lib/README.md @@ -0,0 +1,31 @@ +# API Dependency + +(See the raw file. Please keep the raw file readable rather than the browser-rendered one) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Solution +**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** + Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. + +## Level 1 +**Simulators** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`, `FullstateSimulator`, `CreateFullstateSimulator()`. + `FullstateSimulator` depends on the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** + +## Level 2 +**QIR** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). + Provides the access to simulators (through the `IRuntimeDriver *` returned by `GlobalContext()->GetDriver()`). + +## Level 3 +**QSharpFoundation** Defines `@__quantum__qis__*()` math funcs and ApplyIf entry points. + Depends on QIR (`quantum__rt__result_{equal,get_zero}()`, `quantum__rt__fail()`, `quantum__rt__string_create()`). + +**QSharpCore** Defines `@__quantum__qis__*()` quantum gate set entry points. + Each API depends on `GlobalContext()`, `IQuantumGateSet`. + Uses `QirArray *` from `public\QirTypes.hpp`. + diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index 77d9ffb860d..589b53283e2 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -1,11 +1,12 @@ set(source_files "FullstateSimulator.cpp" - "setup.cpp" "ToffoliSimulator.cpp" ) set(includes "${public_includes}" + "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src" + "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src/simulator" ) #=============================================================================== diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp index 3172e5372f2..51f0b90b8ca 100644 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp @@ -8,10 +8,16 @@ #include #include #include +#include +#include "capi.hpp" + +#include "QirTypes.hpp" +#include "QirRuntime.hpp" #include "QirRuntimeApi_I.hpp" #include "QSharpSimApi_I.hpp" #include "SimFactory.hpp" +#include "OutputStream.hpp" using namespace std; @@ -41,7 +47,7 @@ QUANTUM_SIMULATOR LoadQuantumSimulator() if (handle == NULL) { throw std::runtime_error( - std::string("Failed to load ") + FULLSTATESIMULATORLIB + + std::string("Failed to load ") + FULLSTATESIMULATORLIB + " (error code: " + std::to_string(GetLastError()) + ")"); } #else @@ -122,6 +128,7 @@ namespace Quantum return ids; } + // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. // use for debugging the simulator void DumpState() { @@ -171,6 +178,7 @@ namespace Quantum } } + // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. void GetState(TGetStateCallback callback) override { typedef bool (*TDump)(unsigned, TGetStateCallback); @@ -183,6 +191,9 @@ namespace Quantum return std::to_string(GetQubitId(q)); } + void DumpMachine(const void* location) override; + void DumpRegister(const void* location, const QirArray* qubits) override; + Qubit AllocateQubit() override { typedef void (*TAllocateQubit)(unsigned, unsigned); @@ -408,7 +419,109 @@ namespace Quantum return (std::abs(actualProbability - probabilityOfZero) < precision); } - }; + + private: + std::ostream& GetOutStream(const void* location, std::ofstream& outFileStream); + void DumpMachineImpl(std::ostream& outStream); + void DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits); + void GetStateTo(TDumpLocation location, TDumpToLocationCallback callback); + bool GetRegisterTo(TDumpLocation location, TDumpToLocationCallback callback, const QirArray* qubits); + + private: + TDumpToLocationCallback const dumpToLocationCallback = [](size_t idx, double re, double im, TDumpLocation location) -> bool + { + std::ostream& outStream = *reinterpret_cast(location); + + if (re != 0 || im != 0) + { + outStream << "|" << std::bitset<8>(idx) << ">: " << re << "+" << im << "i" << std::endl; + } + return true; + }; + }; // class CFullstateSimulator + + void CFullstateSimulator::DumpMachineImpl(std::ostream& outStream) + { + outStream << "*********************" << std::endl; + this->GetStateTo((TDumpLocation)&outStream, dumpToLocationCallback); + outStream << "*********************" << std::endl; + outStream.flush(); + } + + void CFullstateSimulator::DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits) + { + outStream << "*********************" << std::endl; + if(!this->GetRegisterTo((TDumpLocation)&outStream, dumpToLocationCallback, qubits)) + { + outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" << std::endl; + } + outStream << "*********************" << std::endl; + outStream.flush(); + } + + bool CFullstateSimulator::GetRegisterTo(TDumpLocation location, TDumpToLocationCallback callback, const QirArray* qubits) + { + // `qubits->buffer` points to the sequence of qubit ids each of type `void*` (logically `qubits->buffer` is of type `void**`). + // We need to pass to the native simulator the pointer to the sequence of `unsigned`. + std::vector unsQbIds(qubits->count); + size_t i = 0; + for (unsigned& qbId : unsQbIds) + { + qbId = (unsigned)(uintptr_t)(((void**)(qubits->buffer))[i]); + ++i; + } + static TDumpQubitsToLocationAPI dumpQubitsToLocation = + reinterpret_cast(this->GetProc("DumpQubitsToLocation")); + return dumpQubitsToLocation( + this->simulatorId, (unsigned)(qubits->count), unsQbIds.data(), callback, + location); + } + + void CFullstateSimulator::GetStateTo(TDumpLocation location, TDumpToLocationCallback callback) + { + static TDumpToLocationAPI dumpTo = reinterpret_cast(this->GetProc("DumpToLocation")); + + dumpTo(this->simulatorId, callback, location); + } + + std::ostream& CFullstateSimulator::GetOutStream(const void* location, std::ofstream& outFileStream) + { + // If the location is not nullptr and not empty string then dump to a file: + if((location != nullptr) && + (((static_cast(location))->str) != "")) + { + // Open the file for appending: + const std::string& filePath = (static_cast(location))->str; + + outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); + if((outFileStream.rdstate() & std::ofstream::failbit) != 0) + { + QirString qstr {std::string("Failed to open dump file \"") + + filePath + + "\""}; + quantum__rt__fail(&qstr); + } + + return outFileStream; + } + + // Otherwise dump to std::cout: + return OutputStream::Get(); + } + + void CFullstateSimulator::DumpMachine(const void* location) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpMachineImpl(outStream); + } + + void CFullstateSimulator::DumpRegister(const void* location, const QirArray* qubits) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpRegisterImpl(outStream, qubits); + } std::unique_ptr CreateFullstateSimulator() { diff --git a/src/Qir/Runtime/lib/Simulators/README.md b/src/Qir/Runtime/lib/Simulators/README.md new file mode 100644 index 00000000000..c34ecac9322 --- /dev/null +++ b/src/Qir/Runtime/lib/Simulators/README.md @@ -0,0 +1,49 @@ +# API Dependency + +(See the raw file. Please keep the raw file readable rather than the browser-rendered one) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Solution +**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** + Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. + +**src\Simulation\Native\src\simulator\capi.hpp** + Declares the APIs exposed by **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** . + + +## Level 1. External To This Directory +**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + +**public\QirRuntimeApi_I.hpp** + Defines `IRuntimeDriver`. + Depends on **public\CoreTypes.hpp**. + +**public\SimFactory.hpp** Declares `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. + Depends on `IRuntimeDriver`. + +**public\QSharpSimApi_I.hpp** + Defines `IQuantumGateSet`, `IDiagnostics`. + Depends on **public\CoreTypes.hpp**, `QirArray`. + + +## Level 2 +**ToffoliSimulator.cpp** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`. + Depends on `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`, **public\CoreTypes.hpp** + +**FullstateSimulator.cpp** Defines the `FullstateSimulator` - QIR wrapper around the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, `CreateFullstateSimulator()`. + Depends on **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, **src\Simulation\Native\src\simulator\capi.hpp**, + `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`. + `CFullstateSimulator::GetOutStream()` depends on `quantum__rt__fail()` - the only dependency on the `public\QirRuntime.hpp`. + Consider breaking up into **FullstateSimulator.hpp** and **FullstateSimulator.cpp**. diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp index bc3c94c3de4..4ab84bf27af 100644 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp @@ -109,11 +109,22 @@ namespace Quantum return std::abs(actualZeroProbability - probabilityOfZero) < precision; } + // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. void GetState(TGetStateCallback callback) override { throw std::logic_error("operation_not_supported"); } + void DumpMachine(const void* location) override + { + throw std::logic_error("operation_not_supported"); + } + + void DumpRegister(const void* location, const QirArray* qubits) override + { + throw std::logic_error("operation_not_supported"); + } + /// /// Implementation of IQuantumGateSet diff --git a/src/Qir/Runtime/lib/Simulators/setup.cpp b/src/Qir/Runtime/lib/Simulators/setup.cpp deleted file mode 100644 index 3de3f4e545b..00000000000 --- a/src/Qir/Runtime/lib/Simulators/setup.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" - -#ifdef _WIN32 -#define EXPORTAPI extern "C" __declspec(dllexport) -#else -#define EXPORTAPI extern "C" -#endif -EXPORTAPI void SetupQirToRunOnFullStateSimulator() -{ - // Leak the simulator, because the QIR only creates one and it will exist for the duration of the session - InitializeQirContext(Microsoft::Quantum::CreateFullstateSimulator().release(), false /*trackAllocatedObjects*/); -} \ No newline at end of file diff --git a/src/Qir/Runtime/public/OutputStream.hpp b/src/Qir/Runtime/public/OutputStream.hpp new file mode 100644 index 00000000000..7aaa9b286f1 --- /dev/null +++ b/src/Qir/Runtime/public/OutputStream.hpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef OUTPUTSTREAM_HPP +#define OUTPUTSTREAM_HPP + +#include +#include "CoreTypes.hpp" // QIR_SHARED_API + +namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. +{ +namespace Quantum +{ + struct QIR_SHARED_API OutputStream + { + struct QIR_SHARED_API ScopedRedirector + { + ScopedRedirector(std::ostream& newOstream); + ~ScopedRedirector(); + + private: + std::ostream& old; + }; + + static std::ostream& Get(); + static std::ostream& Set(std::ostream & newOStream); + + private: + static std::ostream* currentOutputStream; + }; + +} // namespace Microsoft +} // namespace Quantum + +#endif // #ifndef OUTPUTSTREAM_HPP diff --git a/src/Qir/Runtime/public/QSharpSimApi_I.hpp b/src/Qir/Runtime/public/QSharpSimApi_I.hpp index f99d846a960..364186f3cb6 100644 --- a/src/Qir/Runtime/public/QSharpSimApi_I.hpp +++ b/src/Qir/Runtime/public/QSharpSimApi_I.hpp @@ -6,6 +6,7 @@ #include #include "CoreTypes.hpp" +#include "QirTypes.hpp" namespace Microsoft { @@ -58,8 +59,13 @@ namespace Quantum // The callback should be invoked on each basis vector (in the standard computational basis) in little-endian // order until it returns `false` or the state is fully dumped. typedef bool (*TGetStateCallback)(size_t /*basis vector*/, double /* amplitude Re*/, double /* amplitude Im*/); + + // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. virtual void GetState(TGetStateCallback callback) = 0; + virtual void DumpMachine(const void* location) = 0; + virtual void DumpRegister(const void* location, const QirArray* qubits) = 0; + // Both Assert methods return `true`, if the assert holds, `false` otherwise. virtual bool Assert( long numTargets, diff --git a/src/Qir/Runtime/public/QirContext.hpp b/src/Qir/Runtime/public/QirContext.hpp index 7ed5761f908..f6c191ed83c 100644 --- a/src/Qir/Runtime/public/QirContext.hpp +++ b/src/Qir/Runtime/public/QirContext.hpp @@ -14,30 +14,33 @@ namespace Quantum struct IRuntimeDriver; struct AllocationsTracker; - QIR_SHARED_API void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - QIR_SHARED_API void ReleaseQirContext(); struct QIR_SHARED_API QirExecutionContext { - IRuntimeDriver* driver = nullptr; - bool trackAllocatedObjects = false; - std::unique_ptr allocationsTracker; + static void Init(IRuntimeDriver* simulator, bool trackAllocatedObjects = false); + static void Deinit(); - QirExecutionContext(IRuntimeDriver* sim, bool trackAllocatedObjects); + QirExecutionContext(IRuntimeDriver* simulator, bool trackAllocatedObjects); ~QirExecutionContext(); - }; - extern thread_local std::unique_ptr g_context; - extern QIR_SHARED_API std::unique_ptr& GlobalContext(); - struct QIR_SHARED_API QirContextScope - { - QirContextScope(IRuntimeDriver* sim, bool trackAllocatedObjects = false) - { - InitializeQirContext(sim, trackAllocatedObjects); - } - ~QirContextScope() + void OnAddRef(void* object); + void OnRelease(void* object); + void OnAllocate(void* object); + + IRuntimeDriver* GetDriver() const; + + struct QIR_SHARED_API Scoped { - ReleaseQirContext(); - } + Scoped(IRuntimeDriver* sim, bool trackAllocatedObjects = false); + ~Scoped(); + }; + + private: + IRuntimeDriver* driver = nullptr; + bool trackAllocatedObjects = false; + std::unique_ptr allocationsTracker; }; + + QIR_SHARED_API std::unique_ptr& GlobalContext(); + } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Runtime/public/QirRuntime.hpp b/src/Qir/Runtime/public/QirRuntime.hpp index c4236ff0e1a..39fa659445b 100644 --- a/src/Qir/Runtime/public/QirRuntime.hpp +++ b/src/Qir/Runtime/public/QirRuntime.hpp @@ -5,25 +5,12 @@ #include #include // for va_list -#include #include "CoreTypes.hpp" #include "QirTypes.hpp" -struct QirArray; -struct QirCallable; -struct QirString; -struct QirBigInt; - extern "C" { - struct QirRange - { - int64_t start; - int64_t step; - int64_t end; - }; - // ------------------------------------------------------------------------ // Qubits // ------------------------------------------------------------------------ @@ -66,7 +53,7 @@ extern "C" QIR_SHARED_API char* quantum__rt__memory_allocate(uint64_t size); // NOLINT // Fail the computation with the given error message. - QIR_SHARED_API void quantum__rt__fail(QirString* msg); // NOLINT + [[noreturn]] QIR_SHARED_API void quantum__rt__fail(QirString* msg); // NOLINT // Include the given message in the computation's execution log or equivalent. QIR_SHARED_API void quantum__rt__message(QirString* msg); // NOLINT @@ -162,8 +149,7 @@ extern "C" // Initializes the callable with the provided function table and capture tuple. The capture tuple pointer // should be null if there is no capture. - typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); // NOLINT - QIR_SHARED_API QirCallable* quantum__rt__callable_create(t_CallableEntry*, t_CaptureCallback*, PTuple); // NOLINT + QIR_SHARED_API QirCallable* quantum__rt__callable_create(QirCallable::t_CallableEntry*, QirCallable::t_CaptureCallback*, PTuple); // NOLINT // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference // count becomes 0. The behavior is undefined if the reference count becomes negative. @@ -304,12 +290,3 @@ extern "C" // Returns true if the first big integer is greater than or equal to the second, false otherwise. // TODO QIR_SHARED_API bool quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT } - -// To do: consider extracting to QirRuntimeOut.hpp -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - QIR_SHARED_API std::ostream& SetOutputStream(std::ostream & newOStream); -} // namespace Microsoft -} // namespace Quantum diff --git a/src/Qir/Runtime/public/QirTypes.hpp b/src/Qir/Runtime/public/QirTypes.hpp index 4809def635b..41a6cf035a3 100644 --- a/src/Qir/Runtime/public/QirTypes.hpp +++ b/src/Qir/Runtime/public/QirTypes.hpp @@ -49,6 +49,7 @@ struct QIR_SHARED_API QirString std::string str; QirString(std::string&& str); + QirString(const char* cstr); }; /*====================================================================================================================== @@ -58,7 +59,7 @@ struct QIR_SHARED_API QirString a header that contains the relevant data. The header immediately precedes the tuple's buffer in memory when the tuple is created. ======================================================================================================================*/ -using PTuple = char*; +using PTuple = char*; // To do: consider replacing `char*` with `void*` in order to block the accidental {dereferencing and pointer arithmtic}. struct QIR_SHARED_API QirTupleHeader { int refCount = 0; @@ -120,10 +121,11 @@ static_assert( /*====================================================================================================================== QirCallable ======================================================================================================================*/ -typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); -typedef void (*t_CaptureCallback)(PTuple, int32_t); struct QIR_SHARED_API QirCallable { + typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); + typedef void (*t_CaptureCallback)(PTuple, int32_t); + static int constexpr Adjoint = 1; static int constexpr Controlled = 1 << 1; @@ -172,4 +174,14 @@ struct QIR_SHARED_API QirCallable void ApplyFunctor(int functor); void InvokeCaptureCallback(int32_t index, int32_t parameter); -}; \ No newline at end of file +}; + +struct QIR_SHARED_API QirRange +{ + int64_t start; + int64_t step; + int64_t end; + + QirRange(); + QirRange(int64_t start, int64_t step, int64_t end); +}; diff --git a/src/Qir/Runtime/public/README.md b/src/Qir/Runtime/public/README.md new file mode 100644 index 00000000000..98b44723f33 --- /dev/null +++ b/src/Qir/Runtime/public/README.md @@ -0,0 +1,55 @@ +# API Dependency + +(See the raw file. Please keep the raw file readable rather than the browser-rendered one) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +To do: Consider moving `public\` one level up, or all the rest one level down the directory tree. + +## Level 0. External To This Directory +**..\..\Common\Include\qsharp__foundation_internal.hpp** + Depends on `QIR_SHARED_API` - consider moving below `public\`. + +**..\..\Common\Include\SimulatorStub.hpp** + Depends on `public\` - consider moving below `public\`. + + +## Level +**TracerTypes.hpp** Defines types `OpId`, `Time`, `Duration`, `LayerId`; constants `INVALID`, `REQUESTNEW`. + Does not depend on anything of our code. + Only used by Tracer and Tracer Test. Consider moving from `public` to a location still visible for tests. + +**CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + + +## Level + +**OutputStream.hpp** Defines `OutputStream`, `ScopedRedirector` - the means to redirect the output stream from `std::cout` to string, file, etc. + Used by the tests that verify the output (e.g. `Message()`). + Depends on `QIR_SHARED_API`. + +**QirRuntimeApi_I.hpp** Defines `IRuntimeDriver` that provides the access to the quantum machine simulator. + Depends on `QIR_SHARED_API`, `Qubit`, `Result`. + +**QirContext.hpp** Defines `QirExecutionContext`, `?Scoped`. + Uses `IRuntimeDriver *`, {`AllocationsTracker *` defined in `QIR` - reverse dependency}. + Depends on `QIR_SHARED_API` + +**QSharpSimApi_I.hpp** Defines `IQuantumGateSet`, `IDiagnostics`. + Depends on `QIR_SHARED_API`, `Qubit`, `PauliId`, `Result`, `QirArray`. + +## Level + +**SimFactory.hpp** Defines `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. + Depends on `QIR_SHARED_API`, `IRuntimeDriver` diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp index d76f37725b3..5bfe60ec4d5 100644 --- a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp +++ b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp @@ -78,7 +78,7 @@ struct ResultsReferenceCountingTestQAPI : public SimulatorStub TEST_CASE("Results: comparison and reference counting", "[qir_support]") { std::unique_ptr qapi = std::make_unique(3); - QirContextScope qirctx(qapi.get()); + QirExecutionContext::Scoped qirctx(qapi.get()); Result r1 = qapi->Measure(0, nullptr, 0, nullptr); // we don't need real qubits for this test Result r2 = qapi->Measure(0, nullptr, 0, nullptr); @@ -104,7 +104,6 @@ TEST_CASE("Results: comparison and reference counting", "[qir_support]") 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); @@ -127,8 +126,6 @@ TEST_CASE("Arrays: one dimensional", "[qir_support]") 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); @@ -162,8 +159,6 @@ TEST_CASE("Arrays: multiple dimensions", "[qir_support]") TEST_CASE("Arrays: copy elision", "[qir_support]") { - QirContextScope qirctx(nullptr); - QirArray* copy = quantum__rt__array_copy(nullptr, true /*force*/); CHECK(copy == nullptr); @@ -191,8 +186,6 @@ TEST_CASE("Arrays: copy elision", "[qir_support]") 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); @@ -226,8 +219,6 @@ TEST_CASE("Arrays: empty", "[qir_support]") 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); @@ -269,8 +260,6 @@ TEST_CASE("Arrays: check the slice range", "[qir_support]") 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); @@ -302,7 +291,6 @@ TEST_CASE("Arrays: slice of 1D array", "[qir_support]") 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); @@ -327,8 +315,6 @@ TEST_CASE("Arrays: reversed slice of 1D array", "[qir_support]") 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; @@ -446,8 +432,6 @@ TEST_CASE("Arrays: slice of 3D array", "[qir_support]") 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; @@ -518,8 +502,6 @@ TEST_CASE("Arrays: reversed slice of 3D array", "[qir_support]") 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; @@ -579,8 +561,6 @@ TEST_CASE("Arrays: project of 3D array", "[qir_support]") 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"); @@ -601,8 +581,6 @@ TEST_CASE("Strings: reuse", "[qir_support]") 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"); @@ -624,8 +602,6 @@ TEST_CASE("Strings: concatenate", "[qir_support]") 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)); @@ -681,8 +657,6 @@ TEST_CASE("Strings: conversions from built-in types", "[qir_support]") 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")); @@ -755,7 +729,7 @@ struct QubitTestQAPI : public SimulatorStub TEST_CASE("Qubits: allocate, release, dump", "[qir_support]") { std::unique_ptr qapi = std::make_unique(4); - QirContextScope qirctx(qapi.get()); + QirExecutionContext::Scoped qirctx(qapi.get()); QirString* qstr = nullptr; Qubit q = quantum__rt__qubit_allocate(); @@ -807,7 +781,7 @@ struct ControlledCallablesTestSimulator : public SimulatorStub TEST_CASE("Unpacking input tuples of nested callables (case2)", "[qir_support]") { std::unique_ptr qapi = std::make_unique(); - QirContextScope qirctx(qapi.get()); + QirExecutionContext::Scoped qirctx(qapi.get()); Qubit target = quantum__rt__qubit_allocate(); QirArray* controlsInner = quantum__rt__qubit_allocate_array(3); @@ -845,7 +819,7 @@ TEST_CASE("Unpacking input tuples of nested callables (case2)", "[qir_support]") TEST_CASE("Unpacking input tuples of nested callables (case1)", "[qir_support]") { std::unique_ptr qapi = std::make_unique(); - QirContextScope qirctx(qapi.get()); + QirExecutionContext::Scoped qirctx(qapi.get()); Qubit target = quantum__rt__qubit_allocate(); QirArray* controlsInner = quantum__rt__qubit_allocate_array(3); @@ -891,7 +865,7 @@ TEST_CASE("Unpacking input tuples of nested callables (case1)", "[qir_support]") TEST_CASE("Allocation tracking for arrays", "[qir_support]") { - InitializeQirContext(nullptr /*don't need a simulator*/, true /*track allocations*/); + QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); QirArray* bounce = quantum__rt__array_create_1d(1, 1); quantum__rt__array_update_reference_count(bounce, -1); @@ -902,15 +876,15 @@ TEST_CASE("Allocation tracking for arrays", "[qir_support]") CHECK_THROWS(quantum__rt__array_update_reference_count(releaseTwice, -1)); QirArray* maybeLeaked = quantum__rt__array_create_1d(1, 1); - CHECK_THROWS(ReleaseQirContext()); + CHECK_THROWS(QirExecutionContext::Deinit()); quantum__rt__array_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(ReleaseQirContext()); + CHECK_NOTHROW(QirExecutionContext::Deinit()); } TEST_CASE("Allocation tracking for tuples", "[qir_support]") { - InitializeQirContext(nullptr /*don't need a simulator*/, true /*track allocations*/); + QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); PTuple bounce = quantum__rt__tuple_create(1); quantum__rt__tuple_update_reference_count(bounce, -1); @@ -921,18 +895,19 @@ TEST_CASE("Allocation tracking for tuples", "[qir_support]") CHECK_THROWS(quantum__rt__tuple_update_reference_count(releaseTwice, -1)); PTuple maybeLeaked = quantum__rt__tuple_create(1); - CHECK_THROWS(ReleaseQirContext()); + CHECK_THROWS(QirExecutionContext::Deinit()); quantum__rt__tuple_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(ReleaseQirContext()); + CHECK_NOTHROW(QirExecutionContext::Deinit()); + } static void NoopCallableEntry(PTuple, PTuple, PTuple) {} TEST_CASE("Allocation tracking for callables", "[qir_support]") { - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; + QirCallable::t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - InitializeQirContext(nullptr /*don't need a simulator*/, true /*track allocations*/); + QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); QirCallable* bounce = quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); @@ -946,16 +921,18 @@ TEST_CASE("Allocation tracking for callables", "[qir_support]") QirCallable* maybeLeaked = quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - CHECK_THROWS(ReleaseQirContext()); + //CHECK_THROWS(ReleaseQirContext()); + CHECK_THROWS(QirExecutionContext::Deinit()); quantum__rt__callable_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(ReleaseQirContext()); + //CHECK_NOTHROW(ReleaseQirContext()); + CHECK_NOTHROW(QirExecutionContext::Deinit()); } TEST_CASE("Callables: copy elision", "[qir_support]") { - QirContextScope qirctx(nullptr, true); - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; + QirExecutionContext::Scoped qirctx(nullptr, true); + QirCallable::t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; QirCallable* original = quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); @@ -1049,7 +1026,7 @@ struct AdjointsTestSimulator : public SimulatorStub TEST_CASE("Adjoints of R should use inverse of the angle", "[qir_support]") { std::unique_ptr qapi = std::make_unique(); - QirContextScope qirctx(qapi.get()); + QirExecutionContext::Scoped qirctx(qapi.get()); const double angle = 0.42; @@ -1071,7 +1048,7 @@ TEST_CASE("Adjoints of R should use inverse of the angle", "[qir_support]") TEST_CASE("Adjoints of Exp should use inverse of the angle", "[qir_support]") { std::unique_ptr qapi = std::make_unique(); - QirContextScope qirctx(qapi.get()); + QirExecutionContext::Scoped qirctx(qapi.get()); const double angle = 0.42; diff --git a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp index d5a3e934f82..280fecb8f2c 100644 --- a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp +++ b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp @@ -12,6 +12,7 @@ #include "QirContext.hpp" #include "QirRuntime.hpp" #include "SimFactory.hpp" +#include "OutputStream.hpp" using namespace Microsoft::Quantum; using namespace std; @@ -133,7 +134,7 @@ int main(int argc, char* argv[]) // Initialize simulator. unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(sim.get(), false /*trackAllocatedObjects*/); // Add the --simulation-output options. // N.B. This option should be present in all standalone drivers. @@ -261,7 +262,7 @@ int main(int argc, char* argv[]) if (!simulationOutputFileOpt->empty()) { simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); + Microsoft::Quantum::OutputStream::Set(simulationOutputFileStream); simulatorOutputStream = &simulationOutputFileStream; } diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index 39a168f2ccc..1820e69175c 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -357,7 +357,7 @@ extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__body(); // 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*/); + QirExecutionContext::Scoped 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/Tests/QIR-dynamic/qir-driver.cpp b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp index fc6a6129b8a..247b6505007 100644 --- a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp +++ b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp @@ -2,25 +2,29 @@ // 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" -// 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; -} +#include "QirTypes.hpp" +#include "SimFactory.hpp" +#include "QirContext.hpp" +#include "OutputStream.hpp" -extern "C" void SetupQirToRunOnFullStateSimulator(); extern "C" int64_t Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); // NOLINT + +extern "C" void Microsoft__Quantum__Testing__QIR__DumpMachineTest__body(); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__body(const void*); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__DumpRegisterTest__body(); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__body(const void*); // NOLINT + +using namespace Microsoft::Quantum; + TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") { - SetupQirToRunOnFullStateSimulator(); + QirExecutionContext::Scoped contextReleaser{CreateFullstateSimulator().release()}; const int ret1 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); const int ret2 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__body(); @@ -34,3 +38,124 @@ TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") CHECK(ret1 != ret3); CHECK(ret2 != ret3); } + + +static bool FileExists(const char * filePath) +{ + return std::ifstream(filePath).operator bool(); +} + +TEST_CASE("QIR: DumpMachine", "[qir][DumpMachine]") +{ + QirExecutionContext::Scoped contextReleaser{CreateFullstateSimulator().release()}; + + // Dump to the std::cout: + { + std::ostringstream outStrStream; + { + // Redirect the output from std::cout to outStrStream: + OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); + + Microsoft__Quantum__Testing__QIR__DumpMachineTest__body(); + } // qOStreamRedirector goes out of scope. + + REQUIRE(outStrStream.str().size() != 0); + } + + + // Dump to empty string location (std::cout): + { + std::ostringstream outStrStream; + const QirString qstrEmpty{std::string("")}; + { + // Redirect the output from std::cout to outStrStream: + OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); + + Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__body(&qstrEmpty); + } // qOStreamRedirector goes out of scope. + + REQUIRE(outStrStream.str().size() != 0); + } + + + // Dump to a file: + const char* filePath = "DumpMachineTest.log"; + + // Remove the `filePath`, if exists. + if(FileExists(filePath)) + { + CHECK(0 == remove(filePath)); + } + + REQUIRE(!FileExists(filePath)); + + // Dump the machine state to that `filePath`: + const QirString qstr{std::string(filePath)}; + Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__body(&qstr); + + // Make sure the file has been created. + REQUIRE(FileExists(filePath)); + + // If we got here then the test has succeeded, we don't need the file. + // Otherwise (test fails) we don't get here, and the file is kept for the subsequent analysis. + // Remove the file, ignore the failure: + (void) remove(filePath); +} + + +TEST_CASE("QIR: DumpRegister", "[qir][DumpRegister]") +{ + QirExecutionContext::Scoped contextReleaser{CreateFullstateSimulator().release()}; + + // Dump to the std::cout: + { + std::ostringstream outStrStream; + { + // Redirect the output from std::cout to outStrStream: + OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); + + Microsoft__Quantum__Testing__QIR__DumpRegisterTest__body(); + } // qOStreamRedirector goes out of scope. + + REQUIRE(outStrStream.str().size() != 0); + } + + + // Dump to empty string location (std::cout): + { + std::ostringstream outStrStream; + const QirString qstrEmpty{""}; + { + // Redirect the output from std::cout to outStrStream: + OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); + + Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__body(&qstrEmpty); + } // qOStreamRedirector goes out of scope. + + REQUIRE(outStrStream.str().size() != 0); + } + + + // Dump to a file: + const char* filePath = "DumpRegisterTest.log"; + + // Remove the `filePath` if exists. + if(FileExists(filePath)) + { + CHECK(0 == remove(filePath)); + } + + REQUIRE(!FileExists(filePath)); + + // Dump to that `filePath`: + const QirString qstr{filePath}; + Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__body(&qstr); + + // Make sure the file has been created. + REQUIRE(FileExists(filePath)); + + // If we got here then the test has succeeded, we don't need the file. + // Otherwise (test fails) we don't get here, and the file is kept for the subsequent analysis. + // Remove the file, ignore the failure: + (void) remove(filePath); +} diff --git a/src/Qir/Tests/QIR-dynamic/qsharp/qir-test-dump.qs b/src/Qir/Tests/QIR-dynamic/qsharp/qir-test-dump.qs new file mode 100644 index 00000000000..e8e5c8801b5 --- /dev/null +++ b/src/Qir/Tests/QIR-dynamic/qsharp/qir-test-dump.qs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Testing.QIR +{ + open Microsoft.Quantum.Diagnostics; + + @EntryPoint() + function DumpMachineToFileTest(filePath : String) : Unit { + DumpMachine(filePath); + } + + @EntryPoint() + function DumpMachineTest() : Unit { + DumpMachine(); + } + + @EntryPoint() + operation DumpRegisterTest() : Unit { + use q2 = Qubit[2] { + DumpRegister((), q2); + } + } + + @EntryPoint() + operation DumpRegisterToFileTest(filePath : String) : Unit { + use q2 = Qubit[2] { + DumpRegister(filePath, q2); + } + } +} // namespace Microsoft.Quantum.Testing.QIR diff --git a/src/Qir/Tests/QIR-static/qir-driver.cpp b/src/Qir/Tests/QIR-static/qir-driver.cpp index 5d8180e2206..5f47d64bb9c 100644 --- a/src/Qir/Tests/QIR-static/qir-driver.cpp +++ b/src/Qir/Tests/QIR-static/qir-driver.cpp @@ -26,16 +26,6 @@ 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 @@ -64,7 +54,7 @@ extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop( // NO bool compilerDecoy); TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") { - QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); constexpr int64_t n = 5; int64_t values[n] = {0, 1, 2, 3, 4}; @@ -159,7 +149,7 @@ struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub 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*/); + QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__body()); @@ -187,7 +177,7 @@ TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][ 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*/); + QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); REQUIRE(42 + (2 + 8) / 2 == TestMultidimArrays(42, 2, 4, 8)); REQUIRE(17 + (3 + 7) / 2 == TestMultidimArrays(17, 3, 5, 7)); @@ -200,7 +190,7 @@ TEST_CASE("QIR: multidimensional arrays", "[qir][qir.arrMultid]") 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*/); + QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); bool failed = false; try @@ -220,7 +210,7 @@ TEST_CASE("QIR: Report range in a failure message", "[qir][qir.range]") 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*/); + QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); const int64_t res = Microsoft__Quantum__Testing__QIR__TestPartials__body(42, 17); REQUIRE(res == 42 - 17); @@ -320,7 +310,7 @@ extern "C" void __quantum__qis__k__ctl(QirArray* controls, Qubit q) // NOLINT TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") { unique_ptr qapi = make_unique(); - QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); g_ctrqapi = qapi.get(); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__body()); diff --git a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp index a1269ef83ed..28ceaaa4d0a 100644 --- a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp @@ -119,7 +119,7 @@ TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") { unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); - QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__body()); @@ -134,7 +134,7 @@ TEST_CASE("QIR: ApplyIf with functors", "[qir][qir.conditionals]") { unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); - QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__body()); @@ -149,7 +149,7 @@ TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") { unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); - QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body()); diff --git a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll index 3d08b24f10c..f2675822195 100644 --- a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll +++ b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll @@ -9,7 +9,7 @@ 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) +;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) diff --git a/src/Qir/Tests/QIR-static/qir-test-ouput.cpp b/src/Qir/Tests/QIR-static/qir-test-ouput.cpp index 9331edc0a0f..2f7546b9cc3 100644 --- a/src/Qir/Tests/QIR-static/qir-test-ouput.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-ouput.cpp @@ -1,35 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include #include #include "catch.hpp" #include "QirTypes.hpp" #include "QirRuntime.hpp" +#include "OutputStream.hpp" extern "C" void Microsoft__Quantum__Testing__QIR__Out__MessageTest__body(void*); // NOLINT - -// https://stackoverflow.com/a/5419388/6362941 -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 -struct OstreamRedirectorScoped -{ - OstreamRedirectorScoped(std::ostream& newOstream) - : old(Microsoft::Quantum::SetOutputStream(newOstream)) - {} - - ~OstreamRedirectorScoped() - { - Microsoft::Quantum::SetOutputStream(old); - } - - private: - std::ostream& old; -}; - - TEST_CASE("QIR: Out.Message", "[qir.Out][qir.Out.Message]") { const std::string testStr1 = "Test String 1"; @@ -38,7 +18,8 @@ TEST_CASE("QIR: Out.Message", "[qir.Out][qir.Out.Message]") std::ostringstream outStrStream; { - OstreamRedirectorScoped qOStreamRedirector(outStrStream); // Redirect the output from std::cout to outStrStream. + // Redirect the output from std::cout to outStrStream: + Microsoft::Quantum::OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); // Log something (to the redirected output): QirString qstr{std::string(testStr1)}; diff --git a/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp b/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp index 1914b666ba6..b49404aeda8 100644 --- a/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp +++ b/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp @@ -20,7 +20,7 @@ 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*/); + QirExecutionContext::Scoped qirctx(tr.get(), true /*trackAllocatedObjects*/); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__body()); const vector& layers = tr->UseLayers(); @@ -37,7 +37,7 @@ TEST_CASE("Invoke each intrinsic from Q# core once", "[qir-tracer]") TEST_CASE("Conditional execution on measurement result", "[qir-tracer]") { shared_ptr tr = CreateTracer(1 /*layer duration*/, g_operationNames); - QirContextScope qirctx(tr.get(), true /*trackAllocatedObjects*/); + QirExecutionContext::Scoped qirctx(tr.get(), true /*trackAllocatedObjects*/); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestMeasurements__body()); diff --git a/src/Qir/Tests/TestUtils.cpp b/src/Qir/Tests/TestUtils.cpp new file mode 100644 index 00000000000..045b3730160 --- /dev/null +++ b/src/Qir/Tests/TestUtils.cpp @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include + +// Can manually add calls to DebugLog in the ll files for debugging. +extern "C" void DebugLog(int64_t value) +{ + std::cout << value << std::endl; +} +extern "C" void DebugLogPtr(char* value) +{ + std::cout << (const void*)value << std::endl; +} diff --git a/src/Qir/Tests/TestUtils.hpp b/src/Qir/Tests/TestUtils.hpp new file mode 100644 index 00000000000..c4239f5ce27 --- /dev/null +++ b/src/Qir/Tests/TestUtils.hpp @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#ifndef TESTUTILS_HPP +#define TESTUTILS_HPP + +#include + +extern "C" void DebugLog(int64_t value); +extern "C" void DebugLogPtr(char* value); + +#endif // #ifndef TESTUTILS_HPP diff --git a/src/Qir/build_all.ps1 b/src/Qir/build_all.ps1 new file mode 100644 index 00000000000..ad960794083 --- /dev/null +++ b/src/Qir/build_all.ps1 @@ -0,0 +1,3 @@ +& Runtime\build-qir-runtime.ps1 +& Tests\build-qir-tests.ps1 +& Samples\build-qir-samples.ps1 diff --git a/src/Qir/test_all.ps1 b/src/Qir/test_all.ps1 new file mode 100644 index 00000000000..e50f68baf41 --- /dev/null +++ b/src/Qir/test_all.ps1 @@ -0,0 +1,3 @@ +& Runtime\test-qir-runtime.ps1 +& Tests\test-qir-tests.ps1 +& Samples\test-qir-samples.ps1 diff --git a/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj b/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj index 5772508edfb..89696902458 100644 --- a/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj +++ b/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/Simulation/Native/src/simulator/capi.cpp b/src/Simulation/Native/src/simulator/capi.cpp index 4a5f95356b6..da95a1754d2 100644 --- a/src/Simulation/Native/src/simulator/capi.cpp +++ b/src/Simulation/Native/src/simulator/capi.cpp @@ -205,6 +205,11 @@ extern "C" Microsoft::Quantum::Simulator::get(id)->dump(callback); } + MICROSOFT_QUANTUM_DECL void DumpToLocation(_In_ unsigned id, _In_ TDumpToLocationCallback callback, _In_ TDumpLocation location) + { + Microsoft::Quantum::Simulator::get(id)->dump(callback, location); + } + // dump the wavefunction of the subset of qubits to the given callback returns false MICROSOFT_QUANTUM_DECL bool DumpQubits( _In_ unsigned id, @@ -216,6 +221,18 @@ extern "C" return Microsoft::Quantum::Simulator::get(id)->dumpQubits(qs, callback); } + MICROSOFT_QUANTUM_DECL bool DumpQubitsToLocation( + _In_ unsigned id, + _In_ unsigned n, + _In_reads_(n) unsigned* q, + _In_ TDumpToLocationCallback callback, + _In_ TDumpLocation location) + { + std::vector qs(q, q + n); + return Microsoft::Quantum::Simulator::get(id)->dumpQubits(qs, callback, location); + } + + // dump the list of logical qubit ids to given callback MICROSOFT_QUANTUM_DECL void DumpIds(_In_ unsigned id, _In_ void (*callback)(unsigned)) { diff --git a/src/Simulation/Native/src/simulator/capi.hpp b/src/Simulation/Native/src/simulator/capi.hpp index 75d8779423a..9e8437fc6cd 100644 --- a/src/Simulation/Native/src/simulator/capi.hpp +++ b/src/Simulation/Native/src/simulator/capi.hpp @@ -16,18 +16,41 @@ extern "C" { // non-quantum - MICROSOFT_QUANTUM_DECL unsigned init(); - MICROSOFT_QUANTUM_DECL void destroy(_In_ unsigned sid); - MICROSOFT_QUANTUM_DECL void seed(_In_ unsigned sid, _In_ unsigned s); + MICROSOFT_QUANTUM_DECL unsigned init(); // NOLINT + MICROSOFT_QUANTUM_DECL void destroy(_In_ unsigned sid); // NOLINT + MICROSOFT_QUANTUM_DECL void seed(_In_ unsigned sid, _In_ unsigned s); // NOLINT MICROSOFT_QUANTUM_DECL void Dump(_In_ unsigned sid, _In_ bool (*callback)(size_t, double, double)); MICROSOFT_QUANTUM_DECL bool DumpQubits( _In_ unsigned sid, _In_ unsigned n, _In_reads_(n) unsigned* q, _In_ bool (*callback)(size_t, double, double)); + + typedef void* TDumpLocation; + typedef bool (*TDumpToLocationCallback)(size_t, double, double, TDumpLocation); + // TDumpToLocationAPI is the siugnature of DumpToLocation. The caller needs to cast the address to TDumpToLocationAPI + // to correctly call DumpToLocation from outside of this dynamic library. + typedef void (*TDumpToLocationAPI)(unsigned sid, TDumpToLocationCallback callback, TDumpLocation location); + MICROSOFT_QUANTUM_DECL void DumpToLocation(_In_ unsigned sid, _In_ TDumpToLocationCallback callback, _In_ TDumpLocation location); + + // TDumpQubitsToLocationAPI is the siugnature of DumpQubitsToLocation. The caller needs to cast the address to TDumpQubitsToLocationAPI + // to correctly call DumpQubitsToLocation from outside of this dynamic library. + typedef bool (*TDumpQubitsToLocationAPI)( + unsigned sid, + unsigned n, + unsigned* q, + TDumpToLocationCallback callback, + TDumpLocation location); + MICROSOFT_QUANTUM_DECL bool DumpQubitsToLocation( + _In_ unsigned sid, + _In_ unsigned n, + _In_reads_(n) unsigned* q, + _In_ TDumpToLocationCallback callback, + _In_ TDumpLocation location); + MICROSOFT_QUANTUM_DECL void DumpIds(_In_ unsigned sid, _In_ void (*callback)(unsigned)); - MICROSOFT_QUANTUM_DECL std::size_t random_choice(_In_ unsigned sid, _In_ std::size_t n, _In_reads_(n) double* p); + MICROSOFT_QUANTUM_DECL std::size_t random_choice(_In_ unsigned sid, _In_ std::size_t n, _In_reads_(n) double* p); // NOLINT MICROSOFT_QUANTUM_DECL double JointEnsembleProbability( _In_ unsigned sid, @@ -44,9 +67,9 @@ extern "C" ); // allocate and release - MICROSOFT_QUANTUM_DECL void allocateQubit(_In_ unsigned sid, _In_ unsigned qid); - MICROSOFT_QUANTUM_DECL void release(_In_ unsigned sid, _In_ unsigned q); - MICROSOFT_QUANTUM_DECL unsigned num_qubits(_In_ unsigned sid); + MICROSOFT_QUANTUM_DECL void allocateQubit(_In_ unsigned sid, _In_ unsigned qid); // NOLINT + MICROSOFT_QUANTUM_DECL void release(_In_ unsigned sid, _In_ unsigned q); // NOLINT + MICROSOFT_QUANTUM_DECL unsigned num_qubits(_In_ unsigned sid); // NOLINT // single-qubit gates MICROSOFT_QUANTUM_DECL void X(_In_ unsigned sid, _In_ unsigned q); @@ -110,13 +133,13 @@ extern "C" _In_ unsigned sid, _In_ unsigned n, _In_reads_(n) unsigned* q, - _In_ std::size_t table_size, - _In_reads_(table_size) std::size_t* permutation_table); + _In_ std::size_t table_size, // NOLINT + _In_reads_(table_size) std::size_t* permutation_table); // NOLINT MICROSOFT_QUANTUM_DECL void AdjPermuteBasis( _In_ unsigned sid, _In_ unsigned n, _In_reads_(n) unsigned* q, - _In_ std::size_t table_size, - _In_reads_(table_size) std::size_t* permutation_table); + _In_ std::size_t table_size, // NOLINT + _In_reads_(table_size) std::size_t* permutation_table); // NOLINT } diff --git a/src/Simulation/Native/src/simulator/simulator.hpp b/src/Simulation/Native/src/simulator/simulator.hpp index 9617a4f1ad1..5c1c52f9a86 100644 --- a/src/Simulation/Native/src/simulator/simulator.hpp +++ b/src/Simulation/Native/src/simulator/simulator.hpp @@ -302,6 +302,18 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface } } + void dump(TDumpToLocationCallback callback, TDumpLocation location) override + { + recursive_lock_type l(mutex()); + flush(); + + auto wfn = psi.data(); + for (std::size_t i = 0; i < wfn.size(); i++) + { + if (!callback(i, wfn[i].real(), wfn[i].imag(), location)) return; + } + } + void dumpIds(void (*callback)(logical_qubit_id)) { recursive_lock_type l(mutex()); @@ -314,6 +326,26 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface } } + bool dumpQubits(std::vector const& qs, TDumpToLocationCallback callback, TDumpLocation location) override + { + assert(qs.size() <= num_qubits()); + + WavefunctionStorage wfn(1ull << qs.size()); + + if (subsytemwavefunction(qs, wfn, 1e-10)) + { + for (std::size_t i = 0; i < wfn.size(); i++) + { + if (!callback(i, wfn[i].real(), wfn[i].imag(), location)) break; + } + return true; + } + else + { + return false; + } + } + bool dumpQubits(std::vector const& qs, bool (*callback)(size_t, double, double)) { assert(qs.size() <= num_qubits()); diff --git a/src/Simulation/Native/src/simulator/simulatorinterface.hpp b/src/Simulation/Native/src/simulator/simulatorinterface.hpp index 23bba3fb5ea..6d3bff907b9 100644 --- a/src/Simulation/Native/src/simulator/simulatorinterface.hpp +++ b/src/Simulation/Native/src/simulator/simulatorinterface.hpp @@ -3,6 +3,7 @@ #pragma once +#include "capi.hpp" #include "gates.hpp" #include "types.hpp" #include "util/openmp.hpp" @@ -94,7 +95,16 @@ class SimulatorInterface { assert(false); } - virtual bool dumpQubits(std::vector const& qs, bool (*callback)(size_t, double, double)) + virtual void dump(TDumpToLocationCallback, TDumpLocation) + { + assert(false); + } + virtual bool dumpQubits(std::vector const& qs, bool (*callback)(size_t, double, double)) + { + assert(false); + return false; + } + virtual bool dumpQubits(std::vector const& qs, TDumpToLocationCallback callback, TDumpLocation location) { assert(false); return false; diff --git a/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs b/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs index 1faa7bda1a5..a7c351fad69 100644 --- a/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs +++ b/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs @@ -37,7 +37,7 @@ public StateDumper(QuantumSimulator qsim) public abstract bool Callback(uint idx, double real, double img); /// - /// The QuantumSimautor being reported. + /// The QuantumSimulator being reported. /// public QuantumSimulator Simulator { get; } diff --git a/src/Xunit/Microsoft.Quantum.Xunit.csproj b/src/Xunit/Microsoft.Quantum.Xunit.csproj index bdfba3e2b0c..edf298ffd8e 100644 --- a/src/Xunit/Microsoft.Quantum.Xunit.csproj +++ b/src/Xunit/Microsoft.Quantum.Xunit.csproj @@ -1,4 +1,4 @@ - + From f95241f68b549dd8015cb3fa50cc6d3a82a8c2d5 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 05:31:06 -0700 Subject: [PATCH 02/11] Minor cleanup --- src/Qir/Runtime/unittests/QirRuntimeTests.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp index 5bfe60ec4d5..a1736a3dd21 100644 --- a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp +++ b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp @@ -921,11 +921,9 @@ TEST_CASE("Allocation tracking for callables", "[qir_support]") QirCallable* maybeLeaked = quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - //CHECK_THROWS(ReleaseQirContext()); CHECK_THROWS(QirExecutionContext::Deinit()); quantum__rt__callable_update_reference_count(maybeLeaked, -1); - //CHECK_NOTHROW(ReleaseQirContext()); CHECK_NOTHROW(QirExecutionContext::Deinit()); } From afddac37a85ec12a39db3c40e7ddd77caae72ee3 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 05:37:34 -0700 Subject: [PATCH 03/11] Updated teh SDK version --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index f9fa3ed4ed8..4f282911276 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "Microsoft.Quantum.Sdk": "0.15.210324735-alpha" + "Microsoft.Quantum.Sdk": "0.15.210425472-pull" } } From 24b9055ece340bb46004f041b1d75ef3fde3079a Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 06:49:32 -0700 Subject: [PATCH 04/11] Fixing the build CI break --- src/Simulation/Native/src/simulator/capi.hpp | 2 ++ src/Simulation/Native/src/simulator/simulator.hpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Native/src/simulator/capi.hpp b/src/Simulation/Native/src/simulator/capi.hpp index 9e8437fc6cd..e0d4b25d0c6 100644 --- a/src/Simulation/Native/src/simulator/capi.hpp +++ b/src/Simulation/Native/src/simulator/capi.hpp @@ -8,7 +8,9 @@ // SAL only defined in windows. #ifndef _In_ +// NOLINTNEXTLINE #define _In_ +// NOLINTNEXTLINE #define _In_reads_(n) #endif diff --git a/src/Simulation/Native/src/simulator/simulator.hpp b/src/Simulation/Native/src/simulator/simulator.hpp index 5c1c52f9a86..2a4dc555def 100644 --- a/src/Simulation/Native/src/simulator/simulator.hpp +++ b/src/Simulation/Native/src/simulator/simulator.hpp @@ -304,7 +304,6 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface void dump(TDumpToLocationCallback callback, TDumpLocation location) override { - recursive_lock_type l(mutex()); flush(); auto wfn = psi.data(); From 34040a7abab04ad041ec897bf2399397a93dc674 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 07:00:05 -0700 Subject: [PATCH 05/11] Updated teh SDK package version --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 4f282911276..33c75a1f917 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "Microsoft.Quantum.Sdk": "0.15.210425472-pull" + "Microsoft.Quantum.Sdk": "0.15.210425594-alpha" } } From 7e7f7f5719014b6042a60d32d5555f8418e3bf89 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 18:50:47 -0700 Subject: [PATCH 06/11] CR changes --- src/Qir/Runtime/lib/QIR/OutputStream.cpp | 7 +-- src/Qir/Runtime/lib/QIR/README.md | 7 ++- src/Qir/Runtime/lib/QIR/bridge-rt.ll | 1 - src/Qir/Runtime/lib/QSharpCore/README.md | 9 ++-- .../Runtime/lib/QSharpFoundation/README.md | 8 ++-- .../QSharpFoundation/qsharp-foundation-qis.ll | 8 ++-- src/Qir/Runtime/lib/README.md | 6 ++- .../lib/Simulators/FullstateSimulator.cpp | 43 +++++++++++++------ src/Qir/Runtime/lib/Simulators/README.md | 5 ++- .../lib/Simulators/ToffoliSimulator.cpp | 3 +- src/Qir/Runtime/public/QSharpSimApi_I.hpp | 3 +- src/Qir/Runtime/public/QirTypes.hpp | 2 +- src/Qir/Runtime/public/README.md | 12 +++--- 13 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/Qir/Runtime/lib/QIR/OutputStream.cpp b/src/Qir/Runtime/lib/QIR/OutputStream.cpp index 40d4a485337..bcc739e0c3d 100644 --- a/src/Qir/Runtime/lib/QIR/OutputStream.cpp +++ b/src/Qir/Runtime/lib/QIR/OutputStream.cpp @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// https://stackoverflow.com/a/5419388/6362941 -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 +// https://stackoverflow.com/a/5419388/6362941 redirect std::cout to a string +// Discussion/history and some more info about the output redirection: +// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 +// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 #include #include "OutputStream.hpp" diff --git a/src/Qir/Runtime/lib/QIR/README.md b/src/Qir/Runtime/lib/QIR/README.md index 413f26efbcf..8e3c54ab7a0 100644 --- a/src/Qir/Runtime/lib/QIR/README.md +++ b/src/Qir/Runtime/lib/QIR/README.md @@ -1,6 +1,6 @@ # API Dependency -(See the raw file. Please keep the raw file readable rather than the browser-rendered one) +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) The listed earlier ones provide the functionality to the listed later ones (the listed later ones include and/or call the listed earlier ones, @@ -21,6 +21,7 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 1 + **allocationsTracker.hpp** Defines `Microsoft::Quantum::AllocationsTracker` that tracks the allocations and detects the mem leaks. Does not depend on anything of our code. @@ -32,6 +33,7 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 2 + **allocationsTracker.cpp** Implements the internals of `Microsoft::Quantum::AllocationsTracker`. Depends on `quantum__rt__fail()`, `quantum__rt__string_create()` @@ -40,6 +42,7 @@ Same-level entities are independent of each other (unless specified otherwise). Gets/returns `IRuntimeDriver *`. ## Level 3 + **delegated.cpp** Implements `quantum__rt__result_*()`, `quantum__rt__qubit_{allocate,release,to_string}()`. Each API depends on `Microsoft::Quantum::GlobalContext()[->GetDriver()]`, `quantum__rt__qubit_to_string()` also depends on strings.cpp's `quantum__rt__string_create()`. @@ -50,11 +53,13 @@ Same-level entities are independent of each other (unless specified otherwise). **delegated.cpp**'s `quantum__rt__qubit_allocate()` ## Level 4 + **callables.cpp** Defines the {internals of `QirTupleHeader`, `QirCallable`}, `quantum__rt__tuple_*()`, `quantum__rt__callable_*()` Depends on `QirArray`, `Microsoft::Quantum::GlobalContext()`, `quantum__rt__fail()`, `quantum__rt__string_create()`, `TupleWithControls`, Consider breaking up into **Tuples.cpp** and **Callables.cpp**. ## Level 5 + **bridge-rt.ll** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). The C++ Standard reserves the identifiers starting with double underscores `__`, that is why the definitions of `@__quantum__rt__*` have been put to `.ll` file rather than `.cpp` file. diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/lib/QIR/bridge-rt.ll index a444ecf4d20..f5ae6f08edb 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/lib/QIR/bridge-rt.ll @@ -233,7 +233,6 @@ define dllexport %Array* @__quantum__rt__array_copy(%Array* %.ar, i1 %force) { ; TODO: This bridge isn't cross-platform! ; it works on Windows but on Linux %args ends up not being a valid pointer. -;declare void @DebugLogPtr(i8*) define dllexport %Array* @__quantum__rt__array_create(i32 %item_size, i32 %dim_count, ...) { %args1 = alloca i8*, align 8 %args2 = bitcast i8** %args1 to i8* diff --git a/src/Qir/Runtime/lib/QSharpCore/README.md b/src/Qir/Runtime/lib/QSharpCore/README.md index 87468f7c415..c996c196249 100644 --- a/src/Qir/Runtime/lib/QSharpCore/README.md +++ b/src/Qir/Runtime/lib/QSharpCore/README.md @@ -1,6 +1,6 @@ # API Dependency -(See the raw file. Please keep the raw file readable rather than the browser-rendered one) +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) The listed earlier ones provide the functionality to the listed later ones (the listed later ones include and/or call the listed earlier ones, @@ -10,12 +10,14 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 0. External To This Directory + **public\CoreTypes.hpp** (QUBIT, PauliId, RESULT)[, `public\QirTypes.hpp` (QirArray)]. **public\QirContext.hpp** Declares `GlobalContext()`. **public\QSharpSimApi_I.hpp** Defines `IQuantumGateSet`. -## Level +## Level 1 + **qsharp__core__qis.hpp** Declares `quantum__qis__*()` gate set implementations. Depends on `public\CoreTypes.hpp` (QUBIT, PauliId, RESULT) Uses `QirArray *` from `public\QirTypes.hpp`. @@ -23,7 +25,8 @@ Same-level entities are independent of each other (unless specified otherwise). **intrinsics.cpp** Defines `quantum__qis__*()` gate set implementation. Each API depends on `GlobalContext()`, `IQuantumGateSet`. -## Level +## Level 2 + **qsharp-core-qis.ll** Defines `@__quantum__qis__*()` quantum gate set entry points (to be called by the `.ll` files generated from users' `.qs` files). The C++ Standard reserves the identifiers starting with double underscores `__`, that is why the definitions of `@__quantum__qis__*` have been put to `.ll` file rather than `.cpp` file. diff --git a/src/Qir/Runtime/lib/QSharpFoundation/README.md b/src/Qir/Runtime/lib/QSharpFoundation/README.md index 23e26cf81fc..f7b22c204f9 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/README.md +++ b/src/Qir/Runtime/lib/QSharpFoundation/README.md @@ -1,6 +1,6 @@ # API Dependency -(See the raw file. Please keep the raw file readable rather than the browser-rendered one) +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) The listed earlier ones provide the functionality to the listed later ones (the listed later ones include and/or call the listed earlier ones, @@ -10,6 +10,7 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 0. External To This Directory + **public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. Does not depend on anything of our code. @@ -19,7 +20,8 @@ Same-level entities are independent of each other (unless specified otherwise). **public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. -## Level +## Level 1 + **conditionals.cpp** Defines `quantum__qis__apply*__body()`. Depends on QIR's `quantum__rt__result_{equal,get_zero}()`, declared in **public\QirRuntime.hpp**. @@ -28,7 +30,7 @@ Same-level entities are independent of each other (unless specified otherwise). Depends on `quantum__rt__fail()`, `quantum__rt__string_create()`, declared in **public\QirRuntime.hpp**. -## Level +## Level 2 **qsharp__foundation__qis.hpp** Declares `quantum__qis__*()` math funcs and ApplyIf. diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll index 650657a9766..7c376e13193 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll +++ b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll @@ -31,12 +31,12 @@ %struct.QirString = type opaque %PauliId = type i32 -; To do: remove this declaration after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. +; TODO: remove this declaration after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. declare dllimport void @quantum__rt__message(%"struct.QirString"* %str) ;=============================================================================== ; -; To do: remove this function after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. +; TODO: remove this function after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. define dllexport void @__quantum__qis__message__body(%String* %.str) { %str = bitcast %String* %.str to %struct.QirString* call void @quantum__rt__message(%"struct.QirString"* %str) @@ -48,7 +48,7 @@ define dllexport void @__quantum__qis__message__body(%String* %.str) { ; ; LLVM intrinsics (https://llvm.org/docs/LangRef.html): -; To do: consider calling these directly from the compiler-generated .ll code, rather than throug the QIR. +; TODO: consider calling these directly from the compiler-generated .ll code, rather than through the QIR. #632 declare double @llvm.sqrt.f64(double %.val) declare double @llvm.log.f64(double %Val) declare double @llvm.sin.f64(double %Val) @@ -215,7 +215,7 @@ define dllexport double @__quantum__qis__drawrandomdouble__body(double %min, dou ;=============================================================================== -; quantum.qis conditional functions implementation +; quantum.qis conditional functions/operations implementation ; define dllexport void @__quantum__qis__applyifelseintrinsic__body( %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { diff --git a/src/Qir/Runtime/lib/README.md b/src/Qir/Runtime/lib/README.md index caec80fe1cf..a10d363d9d3 100644 --- a/src/Qir/Runtime/lib/README.md +++ b/src/Qir/Runtime/lib/README.md @@ -1,6 +1,6 @@ # API Dependency -(See the raw file. Please keep the raw file readable rather than the browser-rendered one) +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) The listed earlier ones provide the functionality to the listed later ones (the listed later ones include and/or call the listed earlier ones, @@ -10,18 +10,22 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 0. External To This Solution + **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. ## Level 1 + **Simulators** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`, `FullstateSimulator`, `CreateFullstateSimulator()`. `FullstateSimulator` depends on the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** ## Level 2 + **QIR** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). Provides the access to simulators (through the `IRuntimeDriver *` returned by `GlobalContext()->GetDriver()`). ## Level 3 + **QSharpFoundation** Defines `@__quantum__qis__*()` math funcs and ApplyIf entry points. Depends on QIR (`quantum__rt__result_{equal,get_zero}()`, `quantum__rt__fail()`, `quantum__rt__string_create()`). diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp index 51f0b90b8ca..4a5e1f57653 100644 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp @@ -128,7 +128,8 @@ namespace Quantum return ids; } - // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // Use `DumpMachine()` and `DumpRegister()` instead. // use for debugging the simulator void DumpState() { @@ -178,7 +179,8 @@ namespace Quantum } } - // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // Use `DumpMachine()` and `DumpRegister()` instead. void GetState(TGetStateCallback callback) override { typedef bool (*TDump)(unsigned, TGetStateCallback); @@ -442,20 +444,28 @@ namespace Quantum void CFullstateSimulator::DumpMachineImpl(std::ostream& outStream) { - outStream << "*********************" << std::endl; + outStream << "# wave function for qubits (least to most significant qubit ids):" << std::endl; this->GetStateTo((TDumpLocation)&outStream, dumpToLocationCallback); - outStream << "*********************" << std::endl; outStream.flush(); } void CFullstateSimulator::DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits) { - outStream << "*********************" << std::endl; + outStream << "# wave function for qubits with ids (least to most significant): "; + for(int64_t idx = 0; idx < qubits->count; ++idx) + { + if(idx != 0) + { + outStream << "; "; + } + outStream << (uintptr_t)(((void **)(qubits->buffer))[idx]); + } + outStream << ':' << std::endl; + if(!this->GetRegisterTo((TDumpLocation)&outStream, dumpToLocationCallback, qubits)) { outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" << std::endl; } - outStream << "*********************" << std::endl; outStream.flush(); } @@ -493,13 +503,22 @@ namespace Quantum // Open the file for appending: const std::string& filePath = (static_cast(location))->str; - outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); - if((outFileStream.rdstate() & std::ofstream::failbit) != 0) + bool openException = false; + try + { + outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); + } + catch(const std::ofstream::failure& e) + { + openException = true; + std::cerr << "Exception caught: \"" << e.what() << "\".\n"; + } + + if( ((outFileStream.rdstate() & std::ofstream::failbit) != 0) + || openException) { - QirString qstr {std::string("Failed to open dump file \"") + - filePath + - "\""}; - quantum__rt__fail(&qstr); + std::cerr << "Failed to open dump file \"" + filePath + "\".\n"; + return OutputStream::Get(); // Dump to std::cout. } return outFileStream; diff --git a/src/Qir/Runtime/lib/Simulators/README.md b/src/Qir/Runtime/lib/Simulators/README.md index c34ecac9322..073744c7338 100644 --- a/src/Qir/Runtime/lib/Simulators/README.md +++ b/src/Qir/Runtime/lib/Simulators/README.md @@ -1,6 +1,6 @@ # API Dependency -(See the raw file. Please keep the raw file readable rather than the browser-rendered one) +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) The listed earlier ones provide the functionality to the listed later ones (the listed later ones include and/or call the listed earlier ones, @@ -10,6 +10,7 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 0. External To This Solution + **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. @@ -18,6 +19,7 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 1. External To This Directory + **public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. Does not depend on anything of our code. @@ -39,6 +41,7 @@ Same-level entities are independent of each other (unless specified otherwise). ## Level 2 + **ToffoliSimulator.cpp** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`. Depends on `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`, **public\CoreTypes.hpp** diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp index 4ab84bf27af..c862812fc42 100644 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp @@ -109,7 +109,8 @@ namespace Quantum return std::abs(actualZeroProbability - probabilityOfZero) < precision; } - // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // Use `DumpMachine()` and `DumpRegister()` instead. void GetState(TGetStateCallback callback) override { throw std::logic_error("operation_not_supported"); diff --git a/src/Qir/Runtime/public/QSharpSimApi_I.hpp b/src/Qir/Runtime/public/QSharpSimApi_I.hpp index 364186f3cb6..eed3f0553d4 100644 --- a/src/Qir/Runtime/public/QSharpSimApi_I.hpp +++ b/src/Qir/Runtime/public/QSharpSimApi_I.hpp @@ -60,7 +60,8 @@ namespace Quantum // order until it returns `false` or the state is fully dumped. typedef bool (*TGetStateCallback)(size_t /*basis vector*/, double /* amplitude Re*/, double /* amplitude Im*/); - // To do: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. + // Use `DumpMachine()` and `DumpRegister()` instead. virtual void GetState(TGetStateCallback callback) = 0; virtual void DumpMachine(const void* location) = 0; diff --git a/src/Qir/Runtime/public/QirTypes.hpp b/src/Qir/Runtime/public/QirTypes.hpp index 41a6cf035a3..bd99722fe3c 100644 --- a/src/Qir/Runtime/public/QirTypes.hpp +++ b/src/Qir/Runtime/public/QirTypes.hpp @@ -59,7 +59,7 @@ struct QIR_SHARED_API QirString a header that contains the relevant data. The header immediately precedes the tuple's buffer in memory when the tuple is created. ======================================================================================================================*/ -using PTuple = char*; // To do: consider replacing `char*` with `void*` in order to block the accidental {dereferencing and pointer arithmtic}. +using PTuple = char*; // TODO: consider replacing `char*` with `void*` in order to block the accidental {dereferencing and pointer arithmtic}. struct QIR_SHARED_API QirTupleHeader { int refCount = 0; diff --git a/src/Qir/Runtime/public/README.md b/src/Qir/Runtime/public/README.md index 98b44723f33..658f8716347 100644 --- a/src/Qir/Runtime/public/README.md +++ b/src/Qir/Runtime/public/README.md @@ -1,6 +1,6 @@ # API Dependency -(See the raw file. Please keep the raw file readable rather than the browser-rendered one) +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) The listed earlier ones provide the functionality to the listed later ones (the listed later ones include and/or call the listed earlier ones, @@ -9,9 +9,10 @@ the listed later ones cannot be compiled into an executable without the listed e Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. -To do: Consider moving `public\` one level up, or all the rest one level down the directory tree. +TODO: Consider moving `public\` one level up, or all the rest one level down the directory tree. ## Level 0. External To This Directory + **..\..\Common\Include\qsharp__foundation_internal.hpp** Depends on `QIR_SHARED_API` - consider moving below `public\`. @@ -19,7 +20,8 @@ To do: Consider moving `public\` one level up, or all the rest one level down th Depends on `public\` - consider moving below `public\`. -## Level +## Level 1 + **TracerTypes.hpp** Defines types `OpId`, `Time`, `Duration`, `LayerId`; constants `INVALID`, `REQUESTNEW`. Does not depend on anything of our code. Only used by Tracer and Tracer Test. Consider moving from `public` to a location still visible for tests. @@ -33,7 +35,7 @@ To do: Consider moving `public\` one level up, or all the rest one level down th **QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. -## Level +## Level 2 **OutputStream.hpp** Defines `OutputStream`, `ScopedRedirector` - the means to redirect the output stream from `std::cout` to string, file, etc. Used by the tests that verify the output (e.g. `Message()`). @@ -49,7 +51,7 @@ To do: Consider moving `public\` one level up, or all the rest one level down th **QSharpSimApi_I.hpp** Defines `IQuantumGateSet`, `IDiagnostics`. Depends on `QIR_SHARED_API`, `Qubit`, `PauliId`, `Result`, `QirArray`. -## Level +## Level 3 **SimFactory.hpp** Defines `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. Depends on `QIR_SHARED_API`, `IRuntimeDriver` From ddd9ccf44360ee10ea38c753874282c857c9c853 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 16 Apr 2021 19:09:29 -0700 Subject: [PATCH 07/11] CR changes --- src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp index c862812fc42..72af5544699 100644 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "QirRuntimeApi_I.hpp" #include "QSharpSimApi_I.hpp" @@ -118,12 +119,14 @@ namespace Quantum void DumpMachine(const void* location) override { - throw std::logic_error("operation_not_supported"); + std::cerr << __func__ << " is not yet implemented" << std::endl; + // TODO: How to implement: https://github.com/microsoft/qsharp-runtime/pull/634#discussion_r615183791 } void DumpRegister(const void* location, const QirArray* qubits) override { - throw std::logic_error("operation_not_supported"); + std::cerr << __func__ << " is not yet implemented" << std::endl; + // TODO: How to implement: https://github.com/microsoft/qsharp-runtime/pull/634#discussion_r615183791 } From 9ee7089c6a6de58c9e5ca732d14324e45cab3f70 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Mon, 19 Apr 2021 16:55:16 -0700 Subject: [PATCH 08/11] Reverted the breaking changes. --- src/Qir/Runtime/README.md | 5 +-- src/Qir/Runtime/lib/QIR/OutputStream.cpp | 5 +++ src/Qir/Runtime/lib/QIR/callables.cpp | 4 +-- src/Qir/Runtime/lib/QIR/context.cpp | 34 ++++++++++++++++--- .../lib/Simulators/FullstateSimulator.cpp | 7 ++-- .../lib/Simulators/ToffoliSimulator.cpp | 3 +- src/Qir/Runtime/public/QSharpSimApi_I.hpp | 3 +- src/Qir/Runtime/public/QirContext.hpp | 31 +++++++++++++---- src/Qir/Runtime/public/QirRuntime.hpp | 13 ++++++- src/Qir/Runtime/public/QirTypes.hpp | 5 ++- src/Qir/Runtime/unittests/QirRuntimeTests.cpp | 4 +-- 11 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/Qir/Runtime/README.md b/src/Qir/Runtime/README.md index ed008c6b7f7..98a33407afa 100644 --- a/src/Qir/Runtime/README.md +++ b/src/Qir/Runtime/README.md @@ -145,8 +145,9 @@ There are two ways to compile and run the QIR files against the runtime. QIR's architecture assumes a single target, whether that be hardware or a particular simulator. As a result, there is no provision in the QIR specifications to choose a target dynamically. To connect QIR to the simulators from this runtime, - we provide `QirExecutionContext::Init()` and `QirExecutionContext::Deinit()` methods. Switching contexts while executing QIR isn't - supported and would yield undefined behavior. + we provide `QirExecutionContext::Init()` (earlier `InitializeQirContext`) + and `QirExecutionContext::Deinit()` (earlier `ReleaseQirContext`) methods. + Switching contexts while executing QIR isn't supported and would yield undefined behavior. ### Building from IR files diff --git a/src/Qir/Runtime/lib/QIR/OutputStream.cpp b/src/Qir/Runtime/lib/QIR/OutputStream.cpp index bcc739e0c3d..070f5bb9a81 100644 --- a/src/Qir/Runtime/lib/QIR/OutputStream.cpp +++ b/src/Qir/Runtime/lib/QIR/OutputStream.cpp @@ -36,5 +36,10 @@ namespace Quantum OutputStream::Set(old); } + std::ostream& SetOutputStream(std::ostream & newOStream) + { + return OutputStream::Set(newOStream); + } + } // namespace Quantum } // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp index a425c7251e4..c7db55ea269 100644 --- a/src/Qir/Runtime/lib/QIR/callables.cpp +++ b/src/Qir/Runtime/lib/QIR/callables.cpp @@ -113,8 +113,8 @@ extern "C" } QirCallable* quantum__rt__callable_create( - QirCallable::t_CallableEntry* entries, - QirCallable::t_CaptureCallback* captureCallbacks, + t_CallableEntry* entries, + t_CaptureCallback* captureCallbacks, PTuple capture) { assert(entries != nullptr); diff --git a/src/Qir/Runtime/lib/QIR/context.cpp b/src/Qir/Runtime/lib/QIR/context.cpp index cd0931b1ec4..9502dddb4f9 100644 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ b/src/Qir/Runtime/lib/QIR/context.cpp @@ -13,11 +13,25 @@ namespace Microsoft { namespace Quantum { - std::unique_ptr& GlobalContext() - { - static std::unique_ptr g_context = nullptr; - - return g_context; + static std::unique_ptr g_context = nullptr; + std::unique_ptr& GlobalContext() { return g_context; } + + void InitializeQirContext(IRuntimeDriver* sim, bool trackAllocatedObjects) + { + assert(g_context == nullptr); + g_context = std::make_unique(sim, trackAllocatedObjects); + } + + void ReleaseQirContext() + { + assert(g_context != nullptr); + + if (g_context->trackAllocatedObjects) + { + g_context->allocationsTracker->CheckForLeaks(); + } + + g_context.reset(nullptr); } QirExecutionContext::QirExecutionContext(IRuntimeDriver* simulator, bool trackAllocatedObjects) @@ -94,5 +108,15 @@ namespace Quantum QirExecutionContext::Deinit(); } + QirContextScope::QirContextScope(IRuntimeDriver* sim, bool trackAllocatedObjects /*= false*/) + { + InitializeQirContext(sim, trackAllocatedObjects); + } + + QirContextScope::~QirContextScope() + { + ReleaseQirContext(); + } + } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp index 4a5e1f57653..9d9322459c3 100644 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp @@ -128,9 +128,7 @@ namespace Quantum return ids; } - // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. - // Use `DumpMachine()` and `DumpRegister()` instead. - // use for debugging the simulator + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. void DumpState() { std::cout << "*********************" << std::endl; @@ -179,8 +177,7 @@ namespace Quantum } } - // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. - // Use `DumpMachine()` and `DumpRegister()` instead. + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. void GetState(TGetStateCallback callback) override { typedef bool (*TDump)(unsigned, TGetStateCallback); diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp index 72af5544699..87b71d2ac34 100644 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp @@ -110,8 +110,7 @@ namespace Quantum return std::abs(actualZeroProbability - probabilityOfZero) < precision; } - // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. - // Use `DumpMachine()` and `DumpRegister()` instead. + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. void GetState(TGetStateCallback callback) override { throw std::logic_error("operation_not_supported"); diff --git a/src/Qir/Runtime/public/QSharpSimApi_I.hpp b/src/Qir/Runtime/public/QSharpSimApi_I.hpp index eed3f0553d4..1fa6649a292 100644 --- a/src/Qir/Runtime/public/QSharpSimApi_I.hpp +++ b/src/Qir/Runtime/public/QSharpSimApi_I.hpp @@ -60,8 +60,7 @@ namespace Quantum // order until it returns `false` or the state is fully dumped. typedef bool (*TGetStateCallback)(size_t /*basis vector*/, double /* amplitude Re*/, double /* amplitude Im*/); - // TODO: remove after the `function DumpMachine<'T> (location : 'T) : Unit` is implemented. - // Use `DumpMachine()` and `DumpRegister()` instead. + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. virtual void GetState(TGetStateCallback callback) = 0; virtual void DumpMachine(const void* location) = 0; diff --git a/src/Qir/Runtime/public/QirContext.hpp b/src/Qir/Runtime/public/QirContext.hpp index f6c191ed83c..0eb35941676 100644 --- a/src/Qir/Runtime/public/QirContext.hpp +++ b/src/Qir/Runtime/public/QirContext.hpp @@ -14,12 +14,26 @@ namespace Quantum struct IRuntimeDriver; struct AllocationsTracker; + // Deprecated: Use `QirExecutionContext::Init()` instead. + QIR_SHARED_API void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects = false); + // Deprecated: Use `QirExecutionContext::Deinit()` instead. + QIR_SHARED_API void ReleaseQirContext(); + struct QIR_SHARED_API QirExecutionContext { + // Direct access from outside of `QirExecutionContext` is deprecated: The variables are to become `private`. + // { + // Use `Microsoft::Quantum::GlobalContext()->GetDriver()` instead: + IRuntimeDriver* driver = nullptr; + // Use `QirExecutionContext::{OnAddRef(), OnRelease(), OnAllocate()}`instead of direct access: + bool trackAllocatedObjects = false; + std::unique_ptr allocationsTracker; + // } + static void Init(IRuntimeDriver* simulator, bool trackAllocatedObjects = false); static void Deinit(); - QirExecutionContext(IRuntimeDriver* simulator, bool trackAllocatedObjects); + QirExecutionContext(IRuntimeDriver* sim, bool trackAllocatedObjects); ~QirExecutionContext(); void OnAddRef(void* object); @@ -33,14 +47,17 @@ namespace Quantum Scoped(IRuntimeDriver* sim, bool trackAllocatedObjects = false); ~Scoped(); }; - - private: - IRuntimeDriver* driver = nullptr; - bool trackAllocatedObjects = false; - std::unique_ptr allocationsTracker; }; - QIR_SHARED_API std::unique_ptr& GlobalContext(); + // Direct access is deprecated, use GlobalContext() instead. + extern std::unique_ptr g_context; + extern QIR_SHARED_API std::unique_ptr& GlobalContext(); + // Deprecated, use `QirExecutionContext::Scoped` instead. + struct QIR_SHARED_API QirContextScope + { + QirContextScope(IRuntimeDriver* sim, bool trackAllocatedObjects = false); + ~QirContextScope(); + }; } // namespace Quantum } // namespace Microsoft \ No newline at end of file diff --git a/src/Qir/Runtime/public/QirRuntime.hpp b/src/Qir/Runtime/public/QirRuntime.hpp index 39fa659445b..dfec0d6adac 100644 --- a/src/Qir/Runtime/public/QirRuntime.hpp +++ b/src/Qir/Runtime/public/QirRuntime.hpp @@ -149,7 +149,7 @@ extern "C" // Initializes the callable with the provided function table and capture tuple. The capture tuple pointer // should be null if there is no capture. - QIR_SHARED_API QirCallable* quantum__rt__callable_create(QirCallable::t_CallableEntry*, QirCallable::t_CaptureCallback*, PTuple); // NOLINT + QIR_SHARED_API QirCallable* quantum__rt__callable_create(t_CallableEntry*, t_CaptureCallback*, PTuple); // NOLINT // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference // count becomes 0. The behavior is undefined if the reference count becomes negative. @@ -290,3 +290,14 @@ extern "C" // Returns true if the first big integer is greater than or equal to the second, false otherwise. // TODO QIR_SHARED_API bool quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT } + + +namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. +{ +namespace Quantum +{ + // Deprecated, use `Microsoft::Quantum::OutputStream::Set()` instead. + QIR_SHARED_API std::ostream& SetOutputStream(std::ostream & newOStream); +} // namespace Microsoft +} // namespace Quantum + diff --git a/src/Qir/Runtime/public/QirTypes.hpp b/src/Qir/Runtime/public/QirTypes.hpp index bd99722fe3c..f0c52be4dec 100644 --- a/src/Qir/Runtime/public/QirTypes.hpp +++ b/src/Qir/Runtime/public/QirTypes.hpp @@ -121,11 +121,10 @@ static_assert( /*====================================================================================================================== QirCallable ======================================================================================================================*/ +typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); // TODO: Move to `QirCallable::t_CallableEntry`. +typedef void (*t_CaptureCallback)(PTuple, int32_t); // TODO: Move to `QirCallable::t_CaptureCallback`. struct QIR_SHARED_API QirCallable { - typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); - typedef void (*t_CaptureCallback)(PTuple, int32_t); - static int constexpr Adjoint = 1; static int constexpr Controlled = 1 << 1; diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp index a1736a3dd21..66127fb69ae 100644 --- a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp +++ b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp @@ -905,7 +905,7 @@ TEST_CASE("Allocation tracking for tuples", "[qir_support]") static void NoopCallableEntry(PTuple, PTuple, PTuple) {} TEST_CASE("Allocation tracking for callables", "[qir_support]") { - QirCallable::t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; + t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); @@ -930,7 +930,7 @@ TEST_CASE("Allocation tracking for callables", "[qir_support]") TEST_CASE("Callables: copy elision", "[qir_support]") { QirExecutionContext::Scoped qirctx(nullptr, true); - QirCallable::t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; + t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; QirCallable* original = quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); From ecae5566d3bdecd699e88cbe999a4904fef154a6 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Mon, 19 Apr 2021 18:17:24 -0700 Subject: [PATCH 09/11] Build break fix --- src/Qir/Runtime/lib/QIR/context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/Runtime/lib/QIR/context.cpp b/src/Qir/Runtime/lib/QIR/context.cpp index 9502dddb4f9..c46e555e862 100644 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ b/src/Qir/Runtime/lib/QIR/context.cpp @@ -13,7 +13,7 @@ namespace Microsoft { namespace Quantum { - static std::unique_ptr g_context = nullptr; + std::unique_ptr g_context = nullptr; std::unique_ptr& GlobalContext() { return g_context; } void InitializeQirContext(IRuntimeDriver* sim, bool trackAllocatedObjects) From 3687527ce85a5f263a499ef29d4813df352df022 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Tue, 20 Apr 2021 12:50:00 -0700 Subject: [PATCH 10/11] CR changes --- src/Qir/Runtime/lib/QIR/bridge-rt.ll | 1 - src/Qir/Runtime/lib/QIR/context.cpp | 20 +++++++++---------- .../QSharpFoundation/qsharp-foundation-qis.ll | 12 ----------- .../lib/Simulators/ToffoliSimulator.cpp | 6 ++---- src/Qir/Runtime/public/QirContext.hpp | 8 ++++---- src/Qir/Tests/QIR-static/qir-test-noqsharp.ll | 1 - 6 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/lib/QIR/bridge-rt.ll index f5ae6f08edb..c328a845edf 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/lib/QIR/bridge-rt.ll @@ -238,7 +238,6 @@ define dllexport %Array* @__quantum__rt__array_create(i32 %item_size, i32 %dim_c %args2 = bitcast i8** %args1 to i8* call void @llvm.va_start(i8* %args2) %args = load i8*, i8** %args1, align 8 - ;call void @DebugLogPtr(i8* %args) %ar = call %"struct.QirArray"* @quantum__rt__array_create_nonvariadic(i32 %item_size, i32 %dim_count, i8* %args) call void @llvm.va_end(i8* %args2) %.ar = bitcast %"struct.QirArray"* %ar to %Array* diff --git a/src/Qir/Runtime/lib/QIR/context.cpp b/src/Qir/Runtime/lib/QIR/context.cpp index c46e555e862..abe04acf692 100644 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ b/src/Qir/Runtime/lib/QIR/context.cpp @@ -16,10 +16,10 @@ namespace Quantum std::unique_ptr g_context = nullptr; std::unique_ptr& GlobalContext() { return g_context; } - void InitializeQirContext(IRuntimeDriver* sim, bool trackAllocatedObjects) + void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects) { assert(g_context == nullptr); - g_context = std::make_unique(sim, trackAllocatedObjects); + g_context = std::make_unique(driver, trackAllocatedObjects); } void ReleaseQirContext() @@ -34,8 +34,8 @@ namespace Quantum g_context.reset(nullptr); } - QirExecutionContext::QirExecutionContext(IRuntimeDriver* simulator, bool trackAllocatedObjects) - : driver(simulator) + QirExecutionContext::QirExecutionContext(IRuntimeDriver* drv, bool trackAllocatedObjects) + : driver(drv) , trackAllocatedObjects(trackAllocatedObjects) { if (this->trackAllocatedObjects) @@ -51,10 +51,10 @@ namespace Quantum // (we'll have to move `allocationsTracker.hpp` to `public/`). QirExecutionContext::~QirExecutionContext() = default; - void QirExecutionContext::Init(IRuntimeDriver* simulator, bool trackAllocatedObjects /*= false*/) + void QirExecutionContext::Init(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) { assert(GlobalContext() == nullptr); - GlobalContext() = std::make_unique(simulator, trackAllocatedObjects); + GlobalContext() = std::make_unique(driver, trackAllocatedObjects); } void QirExecutionContext::Deinit() @@ -98,9 +98,9 @@ namespace Quantum return this->driver; } - QirExecutionContext::Scoped::Scoped(IRuntimeDriver* sim, bool trackAllocatedObjects /*= false*/) + QirExecutionContext::Scoped::Scoped(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) { - QirExecutionContext::Init(sim, trackAllocatedObjects); + QirExecutionContext::Init(driver, trackAllocatedObjects); } QirExecutionContext::Scoped::~Scoped() @@ -108,9 +108,9 @@ namespace Quantum QirExecutionContext::Deinit(); } - QirContextScope::QirContextScope(IRuntimeDriver* sim, bool trackAllocatedObjects /*= false*/) + QirContextScope::QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) { - InitializeQirContext(sim, trackAllocatedObjects); + InitializeQirContext(driver, trackAllocatedObjects); } QirContextScope::~QirContextScope() diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll index 7c376e13193..e7a51d2937b 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll +++ b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll @@ -31,18 +31,6 @@ %struct.QirString = type opaque %PauliId = type i32 -; TODO: remove this declaration after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. -declare dllimport void @quantum__rt__message(%"struct.QirString"* %str) - -;=============================================================================== -; -; TODO: remove this function after the https://github.com/microsoft/qsharp-runtime/issues/578 is resolved. -define dllexport void @__quantum__qis__message__body(%String* %.str) { - %str = bitcast %String* %.str to %struct.QirString* - call void @quantum__rt__message(%"struct.QirString"* %str) - ret void -} - ;=============================================================================== ; quantum.qis math functions declarations ; diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp index 87b71d2ac34..f02742fcfd1 100644 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp @@ -118,14 +118,12 @@ namespace Quantum void DumpMachine(const void* location) override { - std::cerr << __func__ << " is not yet implemented" << std::endl; - // TODO: How to implement: https://github.com/microsoft/qsharp-runtime/pull/634#discussion_r615183791 + std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 } void DumpRegister(const void* location, const QirArray* qubits) override { - std::cerr << __func__ << " is not yet implemented" << std::endl; - // TODO: How to implement: https://github.com/microsoft/qsharp-runtime/pull/634#discussion_r615183791 + std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 } diff --git a/src/Qir/Runtime/public/QirContext.hpp b/src/Qir/Runtime/public/QirContext.hpp index 0eb35941676..354d1781f4b 100644 --- a/src/Qir/Runtime/public/QirContext.hpp +++ b/src/Qir/Runtime/public/QirContext.hpp @@ -30,10 +30,10 @@ namespace Quantum std::unique_ptr allocationsTracker; // } - static void Init(IRuntimeDriver* simulator, bool trackAllocatedObjects = false); + static void Init(IRuntimeDriver* driver, bool trackAllocatedObjects = false); static void Deinit(); - QirExecutionContext(IRuntimeDriver* sim, bool trackAllocatedObjects); + QirExecutionContext(IRuntimeDriver* driver, bool trackAllocatedObjects); ~QirExecutionContext(); void OnAddRef(void* object); @@ -44,7 +44,7 @@ namespace Quantum struct QIR_SHARED_API Scoped { - Scoped(IRuntimeDriver* sim, bool trackAllocatedObjects = false); + Scoped(IRuntimeDriver* driver, bool trackAllocatedObjects = false); ~Scoped(); }; }; @@ -56,7 +56,7 @@ namespace Quantum // Deprecated, use `QirExecutionContext::Scoped` instead. struct QIR_SHARED_API QirContextScope { - QirContextScope(IRuntimeDriver* sim, bool trackAllocatedObjects = false); + QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObjects = false); ~QirContextScope(); }; } // namespace Quantum diff --git a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll index f2675822195..35105d109b6 100644 --- a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll +++ b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll @@ -9,7 +9,6 @@ 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) From 05bf23c12764a7d4e62b64a841c9acdffe8e4ba8 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Tue, 20 Apr 2021 17:07:03 -0700 Subject: [PATCH 11/11] Build break fix. --- src/Qir/Runtime/lib/QIR/OutputStream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Qir/Runtime/lib/QIR/OutputStream.cpp b/src/Qir/Runtime/lib/QIR/OutputStream.cpp index 070f5bb9a81..c0cda545d21 100644 --- a/src/Qir/Runtime/lib/QIR/OutputStream.cpp +++ b/src/Qir/Runtime/lib/QIR/OutputStream.cpp @@ -7,6 +7,7 @@ // https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 #include +#include "QirRuntime.hpp" #include "OutputStream.hpp" namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17.