diff --git a/global.json b/global.json index f9fa3ed4ed8..33c75a1f917 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.210425594-alpha" } } 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..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 `InitializeQirContext` and `ReleaseQirContext` 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 @@ -166,8 +167,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..c0cda545d21 --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/OutputStream.cpp @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// https://stackoverflow.com/a/5419388/6362941 redirect std::cout to a string +// Discussion/history and some more info about the output redirection: +// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 +// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 + +#include +#include "QirRuntime.hpp" +#include "OutputStream.hpp" + +namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. +{ +namespace Quantum +{ + std::ostream* OutputStream::currentOutputStream = &std::cout; // Output to std::cout by default. + + std::ostream& OutputStream::Get() + { + return *currentOutputStream; + } + + std::ostream& OutputStream::Set(std::ostream& newOStream) + { + std::ostream& oldOStream = *currentOutputStream; + currentOutputStream = &newOStream; + return oldOStream; + } + + OutputStream::ScopedRedirector::ScopedRedirector(std::ostream& newOstream) + : old(OutputStream::Set(newOstream)) + {} + + OutputStream::ScopedRedirector::~ScopedRedirector() + { + OutputStream::Set(old); + } + + std::ostream& SetOutputStream(std::ostream & newOStream) + { + return OutputStream::Set(newOStream); + } + +} // namespace Quantum +} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/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..8e3c54ab7a0 --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/README.md @@ -0,0 +1,66 @@ +# API Dependency + +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Directory + +**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + + +## Level 1 + +**allocationsTracker.hpp** Defines `Microsoft::Quantum::AllocationsTracker` that tracks the allocations and detects the mem leaks. + Does not depend on anything of our code. + +**utils.cpp** Implements `quantum__rt__fail()`, `quantum__rt__memory_allocate()`, `quantum__rt__heap_{alloc,free}()`. + `quantum__rt__heap_alloc()` calls **strings.cpp**'s `quantum__rt__string_create()` - cyclical dependency. + +**strings.cpp** Implements `QirString`, `quantum__rt__string_*()`, `quantum__rt___to_string()` (except `qubit_to_string` and `result_to_string`). + Depends on **utils.cpp**'s `quantum__rt__fail()` - cyclical dependency. + + +## Level 2 + +**allocationsTracker.cpp** Implements the internals of `Microsoft::Quantum::AllocationsTracker`. + Depends on `quantum__rt__fail()`, `quantum__rt__string_create()` + +**context.cpp** Implements the internals of `Microsoft::Quantum::QirExecutionContext`, + Depends on **allocationsTracker.hpp**'s `Microsoft::Quantum::AllocationsTracker`. + Gets/returns `IRuntimeDriver *`. + +## Level 3 + +**delegated.cpp** Implements `quantum__rt__result_*()`, `quantum__rt__qubit_{allocate,release,to_string}()`. + Each API depends on `Microsoft::Quantum::GlobalContext()[->GetDriver()]`, + `quantum__rt__qubit_to_string()` also depends on strings.cpp's `quantum__rt__string_create()`. + `quantum__rt__result_to_string()` also depends on strings.cpp's `quantum__rt__string_create()`. + +**arrays.cpp** Implements {the internals of `QirArray`} and `quantum__rt__*array*`. + Depends on `Microsoft::Quantum::GlobalContext()`, `quantum__rt__fail()`, `quantum__rt__string_create()`, + **delegated.cpp**'s `quantum__rt__qubit_allocate()` + +## Level 4 + +**callables.cpp** Defines the {internals of `QirTupleHeader`, `QirCallable`}, `quantum__rt__tuple_*()`, `quantum__rt__callable_*()` + Depends on `QirArray`, `Microsoft::Quantum::GlobalContext()`, `quantum__rt__fail()`, `quantum__rt__string_create()`, `TupleWithControls`, + Consider breaking up into **Tuples.cpp** and **Callables.cpp**. + +## 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..c328a845edf 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/lib/QIR/bridge-rt.ll @@ -233,13 +233,11 @@ 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* 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/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp index f63bc44dc03..c7db55ea269 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; @@ -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..abe04acf692 100644 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ b/src/Qir/Runtime/lib/QIR/context.cpp @@ -9,32 +9,17 @@ #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 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); - - if (g_context->driver != nullptr) - { - ResultOne = g_context->driver->UseOne(); - ResultZero = g_context->driver->UseZero(); - } - else - { - ResultOne = nullptr; - ResultZero = nullptr; - } + g_context = std::make_unique(driver, trackAllocatedObjects); } void ReleaseQirContext() @@ -46,13 +31,11 @@ namespace Quantum g_context->allocationsTracker->CheckForLeaks(); } - ResultOne = nullptr; - ResultZero = nullptr; g_context.reset(nullptr); } - QirExecutionContext::QirExecutionContext(IRuntimeDriver* sim, bool trackAllocatedObjects) - : driver(sim) + QirExecutionContext::QirExecutionContext(IRuntimeDriver* drv, bool trackAllocatedObjects) + : driver(drv) , trackAllocatedObjects(trackAllocatedObjects) { if (this->trackAllocatedObjects) @@ -60,7 +43,80 @@ namespace Quantum this->allocationsTracker = std::make_unique(); } } + + // If we just remove this user-declared-and-defined dtor + // then it will be automatically defined together with the class definition, + // which will require the `AllocationsTracker` to be a complete type + // everywhere where `public/QirContext.hpp` is included + // (we'll have to move `allocationsTracker.hpp` to `public/`). QirExecutionContext::~QirExecutionContext() = default; + void QirExecutionContext::Init(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) + { + assert(GlobalContext() == nullptr); + GlobalContext() = std::make_unique(driver, trackAllocatedObjects); + } + + void QirExecutionContext::Deinit() + { + assert(GlobalContext() != nullptr); + + if (GlobalContext()->trackAllocatedObjects) + { + GlobalContext()->allocationsTracker->CheckForLeaks(); + } + + GlobalContext().reset(nullptr); + } + + void QirExecutionContext::OnAddRef(void* object) + { + if(trackAllocatedObjects) + { + this->allocationsTracker->OnAddRef(object); + } + } + + void QirExecutionContext::OnRelease(void* object) + { + if(this->trackAllocatedObjects) + { + this->allocationsTracker->OnRelease(object); + } + } + + void QirExecutionContext::OnAllocate(void* object) + { + if(this->trackAllocatedObjects) + { + this->allocationsTracker->OnAllocate(object); + } + } + + IRuntimeDriver* QirExecutionContext::GetDriver() const + { + return this->driver; + } + + QirExecutionContext::Scoped::Scoped(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) + { + QirExecutionContext::Init(driver, trackAllocatedObjects); + } + + QirExecutionContext::Scoped::~Scoped() + { + QirExecutionContext::Deinit(); + } + + QirContextScope::QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) + { + InitializeQirContext(driver, trackAllocatedObjects); + } + + QirContextScope::~QirContextScope() + { + ReleaseQirContext(); + } + } // 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..c996c196249 --- /dev/null +++ b/src/Qir/Runtime/lib/QSharpCore/README.md @@ -0,0 +1,33 @@ +# API Dependency + +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Directory + +**public\CoreTypes.hpp** (QUBIT, PauliId, RESULT)[, `public\QirTypes.hpp` (QirArray)]. +**public\QirContext.hpp** Declares `GlobalContext()`. +**public\QSharpSimApi_I.hpp** + Defines `IQuantumGateSet`. + +## Level 1 + +**qsharp__core__qis.hpp** Declares `quantum__qis__*()` gate set implementations. + Depends on `public\CoreTypes.hpp` (QUBIT, PauliId, RESULT) + Uses `QirArray *` from `public\QirTypes.hpp`. + +**intrinsics.cpp** Defines `quantum__qis__*()` gate set implementation. + Each API depends on `GlobalContext()`, `IQuantumGateSet`. + +## 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. + 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..f7b22c204f9 --- /dev/null +++ b/src/Qir/Runtime/lib/QSharpFoundation/README.md @@ -0,0 +1,41 @@ +# API Dependency + +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Directory + +**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + + +## Level 1 + +**conditionals.cpp** Defines `quantum__qis__apply*__body()`. + Depends on QIR's `quantum__rt__result_{equal,get_zero}()`, declared in **public\QirRuntime.hpp**. + +**intrinsicsMath.cpp** Defines `quantum__qis__*` math funcs implementations, + `Quantum::Qis::Internal::{excStrDrawRandomVal,RandomizeSeed,GetLastGeneratedRandomI64,GetLastGeneratedRandomDouble}`. + Depends on `quantum__rt__fail()`, `quantum__rt__string_create()`, declared in **public\QirRuntime.hpp**. + + +## Level 2 + +**qsharp__foundation__qis.hpp** + Declares `quantum__qis__*()` math funcs and ApplyIf. + Depends on **public\CoreTypes.hpp**, **public\QirTypes.hpp**. + +**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..e7a51d2937b 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll +++ b/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll @@ -31,22 +31,12 @@ %struct.QirString = type opaque %PauliId = type i32 -declare void @quantum__rt__message(%"struct.QirString"* %str) - -;=============================================================================== -; -; To do: 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 +; quantum.qis math functions declarations ; ; LLVM intrinsics (https://llvm.org/docs/LangRef.html): +; 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) @@ -66,6 +56,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 +203,8 @@ define dllexport double @__quantum__qis__drawrandomdouble__body(double %min, dou ;=============================================================================== -; quantum.qis conditional functions +; quantum.qis conditional functions/operations 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 +228,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..a10d363d9d3 --- /dev/null +++ b/src/Qir/Runtime/lib/README.md @@ -0,0 +1,35 @@ +# API Dependency + +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Solution + +**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** + Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. + +## Level 1 + +**Simulators** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`, `FullstateSimulator`, `CreateFullstateSimulator()`. + `FullstateSimulator` depends on the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** + +## Level 2 + +**QIR** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). + Provides the access to simulators (through the `IRuntimeDriver *` returned by `GlobalContext()->GetDriver()`). + +## Level 3 + +**QSharpFoundation** Defines `@__quantum__qis__*()` math funcs and ApplyIf entry points. + Depends on QIR (`quantum__rt__result_{equal,get_zero}()`, `quantum__rt__fail()`, `quantum__rt__string_create()`). + +**QSharpCore** Defines `@__quantum__qis__*()` quantum gate set entry points. + Each API depends on `GlobalContext()`, `IQuantumGateSet`. + Uses `QirArray *` from `public\QirTypes.hpp`. + diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt 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..9d9322459c3 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,7 +128,7 @@ namespace Quantum return ids; } - // use for debugging the simulator + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. void DumpState() { std::cout << "*********************" << std::endl; @@ -171,6 +177,7 @@ namespace Quantum } } + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. void GetState(TGetStateCallback callback) override { typedef bool (*TDump)(unsigned, TGetStateCallback); @@ -183,6 +190,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 +418,126 @@ 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 << "# wave function for qubits (least to most significant qubit ids):" << std::endl; + this->GetStateTo((TDumpLocation)&outStream, dumpToLocationCallback); + outStream.flush(); + } + + void CFullstateSimulator::DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits) + { + outStream << "# wave function for qubits with ids (least to most significant): "; + for(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.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; + + bool openException = false; + try + { + outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); + } + catch(const std::ofstream::failure& e) + { + openException = true; + std::cerr << "Exception caught: \"" << e.what() << "\".\n"; + } + + if( ((outFileStream.rdstate() & std::ofstream::failbit) != 0) + || openException) + { + std::cerr << "Failed to open dump file \"" + filePath + "\".\n"; + return OutputStream::Get(); // Dump to std::cout. + } + + return outFileStream; + } + + // Otherwise dump to std::cout: + return OutputStream::Get(); + } + + void CFullstateSimulator::DumpMachine(const void* location) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpMachineImpl(outStream); + } + + void CFullstateSimulator::DumpRegister(const void* location, const QirArray* qubits) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpRegisterImpl(outStream, qubits); + } std::unique_ptr CreateFullstateSimulator() { diff --git a/src/Qir/Runtime/lib/Simulators/README.md b/src/Qir/Runtime/lib/Simulators/README.md new file mode 100644 index 00000000000..073744c7338 --- /dev/null +++ b/src/Qir/Runtime/lib/Simulators/README.md @@ -0,0 +1,52 @@ +# API Dependency + +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +## Level 0. External To This Solution + +**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** + Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. + +**src\Simulation\Native\src\simulator\capi.hpp** + Declares the APIs exposed by **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** . + + +## Level 1. External To This Directory + +**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + +**public\QirRuntimeApi_I.hpp** + Defines `IRuntimeDriver`. + Depends on **public\CoreTypes.hpp**. + +**public\SimFactory.hpp** Declares `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. + Depends on `IRuntimeDriver`. + +**public\QSharpSimApi_I.hpp** + Defines `IQuantumGateSet`, `IDiagnostics`. + Depends on **public\CoreTypes.hpp**, `QirArray`. + + +## Level 2 + +**ToffoliSimulator.cpp** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`. + Depends on `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`, **public\CoreTypes.hpp** + +**FullstateSimulator.cpp** Defines the `FullstateSimulator` - QIR wrapper around the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, `CreateFullstateSimulator()`. + Depends on **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, **src\Simulation\Native\src\simulator\capi.hpp**, + `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`. + `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..f02742fcfd1 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" @@ -109,11 +110,22 @@ namespace Quantum return std::abs(actualZeroProbability - probabilityOfZero) < precision; } + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. void GetState(TGetStateCallback callback) override { throw std::logic_error("operation_not_supported"); } + void DumpMachine(const void* location) override + { + std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 + } + + void DumpRegister(const void* location, const QirArray* qubits) override + { + std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 + } + /// /// Implementation of IQuantumGateSet 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..1fa6649a292 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*/); + + // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. virtual void GetState(TGetStateCallback callback) = 0; + virtual void DumpMachine(const void* location) = 0; + virtual void DumpRegister(const void* location, const QirArray* qubits) = 0; + // Both Assert methods return `true`, if the assert holds, `false` otherwise. virtual bool Assert( long numTargets, diff --git a/src/Qir/Runtime/public/QirContext.hpp b/src/Qir/Runtime/public/QirContext.hpp index 7ed5761f908..354d1781f4b 100644 --- a/src/Qir/Runtime/public/QirContext.hpp +++ b/src/Qir/Runtime/public/QirContext.hpp @@ -14,30 +14,50 @@ namespace Quantum struct IRuntimeDriver; struct AllocationsTracker; + // Deprecated: Use `QirExecutionContext::Init()` instead. QIR_SHARED_API void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects = false); + // Deprecated: Use `QirExecutionContext::Deinit()` instead. QIR_SHARED_API void ReleaseQirContext(); + struct QIR_SHARED_API QirExecutionContext { + // Direct access from outside of `QirExecutionContext` is deprecated: The variables are to become `private`. + // { + // Use `Microsoft::Quantum::GlobalContext()->GetDriver()` instead: IRuntimeDriver* driver = nullptr; + // Use `QirExecutionContext::{OnAddRef(), OnRelease(), OnAllocate()}`instead of direct access: bool trackAllocatedObjects = false; std::unique_ptr allocationsTracker; + // } + + static void Init(IRuntimeDriver* driver, bool trackAllocatedObjects = false); + static void Deinit(); - QirExecutionContext(IRuntimeDriver* sim, bool trackAllocatedObjects); + QirExecutionContext(IRuntimeDriver* driver, bool trackAllocatedObjects); ~QirExecutionContext(); + + void OnAddRef(void* object); + void OnRelease(void* object); + void OnAllocate(void* object); + + IRuntimeDriver* GetDriver() const; + + struct QIR_SHARED_API Scoped + { + Scoped(IRuntimeDriver* driver, bool trackAllocatedObjects = false); + ~Scoped(); + }; }; - extern thread_local std::unique_ptr g_context; + + // 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) - { - InitializeQirContext(sim, trackAllocatedObjects); - } - ~QirContextScope() - { - ReleaseQirContext(); - } + QirContextScope(IRuntimeDriver* driver, 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 c4236ff0e1a..dfec0d6adac 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,7 +149,6 @@ 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 // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference @@ -305,11 +291,13 @@ extern "C" // 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 { + // 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 4809def635b..f0c52be4dec 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*; // TODO: consider replacing `char*` with `void*` in order to block the accidental {dereferencing and pointer arithmtic}. struct QIR_SHARED_API QirTupleHeader { int refCount = 0; @@ -120,8 +121,8 @@ static_assert( /*====================================================================================================================== QirCallable ======================================================================================================================*/ -typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); -typedef void (*t_CaptureCallback)(PTuple, int32_t); +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 { static int constexpr Adjoint = 1; @@ -172,4 +173,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..658f8716347 --- /dev/null +++ b/src/Qir/Runtime/public/README.md @@ -0,0 +1,57 @@ +# API Dependency + +(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) + +The listed earlier ones provide the functionality to the listed later ones +(the listed later ones include and/or call the listed earlier ones, +the listed later ones cannot be compiled into an executable without the listed earlier ones). + +Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. + + +TODO: Consider moving `public\` one level up, or all the rest one level down the directory tree. + +## Level 0. External To This Directory + +**..\..\Common\Include\qsharp__foundation_internal.hpp** + Depends on `QIR_SHARED_API` - consider moving below `public\`. + +**..\..\Common\Include\SimulatorStub.hpp** + Depends on `public\` - consider moving below `public\`. + + +## Level 1 + +**TracerTypes.hpp** Defines types `OpId`, `Time`, `Duration`, `LayerId`; constants `INVALID`, `REQUESTNEW`. + Does not depend on anything of our code. + Only used by Tracer and Tracer Test. Consider moving from `public` to a location still visible for tests. + +**CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. + Does not depend on anything of our code. + +**QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. + Depends on the listed earlier ones. + +**QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. + + +## Level 2 + +**OutputStream.hpp** Defines `OutputStream`, `ScopedRedirector` - the means to redirect the output stream from `std::cout` to string, file, etc. + Used by the tests that verify the output (e.g. `Message()`). + Depends on `QIR_SHARED_API`. + +**QirRuntimeApi_I.hpp** Defines `IRuntimeDriver` that provides the access to the quantum machine simulator. + Depends on `QIR_SHARED_API`, `Qubit`, `Result`. + +**QirContext.hpp** Defines `QirExecutionContext`, `?Scoped`. + Uses `IRuntimeDriver *`, {`AllocationsTracker *` defined in `QIR` - reverse dependency}. + Depends on `QIR_SHARED_API` + +**QSharpSimApi_I.hpp** Defines `IQuantumGateSet`, `IDiagnostics`. + Depends on `QIR_SHARED_API`, `Qubit`, `PauliId`, `Result`, `QirArray`. + +## Level 3 + +**SimFactory.hpp** Defines `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. + Depends on `QIR_SHARED_API`, `IRuntimeDriver` diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp index d76f37725b3..66127fb69ae 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,10 +895,11 @@ 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) {} @@ -932,7 +907,7 @@ TEST_CASE("Allocation tracking for callables", "[qir_support]") { t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - InitializeQirContext(nullptr /*don't need a simulator*/, true /*track allocations*/); + 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,15 +921,15 @@ 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()); } TEST_CASE("Callables: copy elision", "[qir_support]") { - QirContextScope qirctx(nullptr, true); + QirExecutionContext::Scoped qirctx(nullptr, true); t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; QirCallable* original = @@ -1049,7 +1024,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 +1046,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..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) 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..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 @@ -16,18 +18,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 +69,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 +135,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..2a4dc555def 100644 --- a/src/Simulation/Native/src/simulator/simulator.hpp +++ b/src/Simulation/Native/src/simulator/simulator.hpp @@ -302,6 +302,17 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface } } + void dump(TDumpToLocationCallback callback, TDumpLocation location) override + { + 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 +325,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 @@ - +