From 3f01d984ef91a4cde94e123c927885c1442b6a99 Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Thu, 18 Feb 2021 17:22:16 -0800 Subject: [PATCH 1/5] QirRuntime: support ApplyIf* and ApplyConditionally* intrinsics Also enabled conditional rewrites for the QIR-static tests. --- src/QirRuntime/lib/QIR/CMakeLists.txt | 5 +- src/QirRuntime/lib/QIR/bridge-qis.ll | 189 +++++++++++++++++- src/QirRuntime/lib/QIR/conditionals.cpp | 114 +++++++++++ src/QirRuntime/lib/QIR/context.cpp | 2 + src/QirRuntime/lib/QIR/delegated.cpp | 4 +- src/QirRuntime/lib/QIR/quantum__qis.hpp | 32 ++- src/QirRuntime/lib/QIR/quantum__rt.hpp | 4 + src/QirRuntime/public/QirContext.hpp | 1 + src/QirRuntime/test/QIR-static/qir-driver.cpp | 96 ++++++++- src/QirRuntime/test/QIR-static/qsharp/Math.qs | 3 + src/QirRuntime/test/QIR-static/qsharp/main.cs | 13 ++ .../test/QIR-static/qsharp/qir-gen.csproj | 2 + .../test/QIR-static/qsharp/qir-test-arrays.qs | 6 +- .../qsharp/qir-test-conditionals.qs | 58 ++++++ .../QIR-static/qsharp/qir-test-functors.qs | 46 ++++- 15 files changed, 555 insertions(+), 20 deletions(-) create mode 100644 src/QirRuntime/lib/QIR/conditionals.cpp create mode 100644 src/QirRuntime/test/QIR-static/qsharp/main.cs create mode 100644 src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs diff --git a/src/QirRuntime/lib/QIR/CMakeLists.txt b/src/QirRuntime/lib/QIR/CMakeLists.txt index 48d8e755119..16d1634b935 100644 --- a/src/QirRuntime/lib/QIR/CMakeLists.txt +++ b/src/QirRuntime/lib/QIR/CMakeLists.txt @@ -49,8 +49,9 @@ compile_from_qir(bridge-qis ${bridge_qis_target}) # create qir-qis-support lib from the C++ sources # set(qis_sup_source_files - "intrinsics.cpp" - "intrinsicsMath.cpp" + conditionals.cpp + intrinsics.cpp + intrinsicsMath.cpp intrinsicsOut.cpp ) diff --git a/src/QirRuntime/lib/QIR/bridge-qis.ll b/src/QirRuntime/lib/QIR/bridge-qis.ll index 6566d652351..4880865b553 100644 --- a/src/QirRuntime/lib/QIR/bridge-qis.ll +++ b/src/QirRuntime/lib/QIR/bridge-qis.ll @@ -60,7 +60,7 @@ 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*) -declare void @quantum__qis__message__body(%"struct.QirString"* %str) +declare void @quantum__qis__message__body(%struct.QirString* %str) ;=============================================================================== ; quantum.qis namespace implementations @@ -284,8 +284,8 @@ define void @__quantum__qis__z__ctl(%Array* %.ctls, %Qubit* %.q) { ;=============================================================================== ; define void @__quantum__qis__message__body(%String* %.str) { - %str = bitcast %String* %.str to %"struct.QirString"* - call void @quantum__qis__message__body(%"struct.QirString"* %str) + %str = bitcast %String* %.str to %struct.QirString* + call void @quantum__qis__message__body(%struct.QirString* %str) ret void } @@ -437,3 +437,186 @@ define i64 @__quantum__qis__drawrandomint__body(i64 %min, i64 %max) { %result = call i64 @quantum__qis__drawrandomint__body(i64 %min, i64 %max) ret i64 %result } + +;=============================================================================== +; quantum.qis conditional functions +; +declare void @unexpected_conditional() +declare void @quantum__qis__applyifelseintrinsic__body(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyconditionallyintrinsicca__body( + %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyconditionallyintrinsicca__adj( + %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyconditionallyintrinsicca__ctl( + %struct.QirArray*, %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyconditionallyintrinsicca__ctladj( + %struct.QirArray*, %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) + +; applyif +define void @__quantum__qis__applyifelseintrinsic__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + %r = bitcast %Result* %.r to %class.RESULT* + %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* + %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* + call void @quantum__qis__applyifelseintrinsic__body( + %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) + ret void +} + +define void @__quantum__qis__applyifelseintrinsica__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + call void @__quantum__qis__applyifelseintrinsic__body(%Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) + ret void +} +define void @__quantum__qis__applyifelseintrinsica__adj(%Result, %Callable*, %Callable*) { + call void @unexpected_conditional() + ret void +} + +define void @__quantum__qis__applyifelseintrinsicc__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + call void @__quantum__qis__applyifelseintrinsic__body(%Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) + ret void +} +define void @__quantum__qis__applyifelseintrinsicc__ctl(%Array*, %Result, %Callable*, %Callable*) { + call void @unexpected_conditional() + ret void +} + +define void @__quantum__qis__applyifelseintrinsicca__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + call void @__quantum__qis__applyifelseintrinsic__body(%Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) + ret void +} +define void @__quantum__qis__applyifelseintrinsicca__ctl(%Array*, %Result, %Callable*, %Callable*) { + call void @unexpected_conditional() + ret void +} +define void @__quantum__qis__applyifelseintrinsicca__adj(%Result, %Callable*, %Callable*) { + call void @unexpected_conditional() + ret void +} +define void @__quantum__qis__applyifelseintrinsicca__ctladj(%Array*, %Result, %Callable*, %Callable*) { + call void @unexpected_conditional() + ret void +} + +; applyconditionally +define void @__quantum__qis__applyconditionallyintrinsicca__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { + + %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* + %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* + %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* + %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* + call void @quantum__qis__applyconditionallyintrinsicca__body( + %struct.QirArray* %rs1, %struct.QirArray* %rs2, + %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) + ret void +} +define void @__quantum__qis__applyconditionallyintrinsicca__ctl( + %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) { + + %.prs1 = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 0 + %.rs1 = load %Array*, %Array** %.prs1 + + %.prs2 = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 1 + %.rs2 = load %Array*, %Array** %.prs2 + + %.pclb_on_equal = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 2 + %.clb_on_equal = load %Callable*, %Callable** %.pclb_on_equal + + %.pclb_on_different = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 3 + %.clb_on_different = load %Callable*, %Callable** %.pclb_on_different + + %ctls = bitcast %Array* %.ctls to %struct.QirArray* + %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* + %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* + %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* + %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* + call void @quantum__qis__applyconditionallyintrinsicca__ctl( + %struct.QirArray* %ctls, + %struct.QirArray* %rs1, %struct.QirArray* %rs2, + %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) + ret void +} +define void @__quantum__qis__applyconditionallyintrinsicca__adj( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { + + %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* + %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* + %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* + %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* + call void @quantum__qis__applyconditionallyintrinsicca__adj( + %struct.QirArray* %rs1, %struct.QirArray* %rs2, + %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) + ret void +} +define void @__quantum__qis__applyconditionallyintrinsicca__ctladj( + %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) { + + %.prs1 = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 0 + %.rs1 = load %Array*, %Array** %.prs1 + + %.prs2 = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 1 + %.rs2 = load %Array*, %Array** %.prs2 + + %.pclb_on_equal = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 2 + %.clb_on_equal = load %Callable*, %Callable** %.pclb_on_equal + + %.pclb_on_different = getelementptr inbounds + {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 3 + %.clb_on_different = load %Callable*, %Callable** %.pclb_on_different + + %ctls = bitcast %Array* %.ctls to %struct.QirArray* + %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* + %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* + %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* + %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* + call void @quantum__qis__applyconditionallyintrinsicca__ctladj( + %struct.QirArray* %ctls, + %struct.QirArray* %rs1, %struct.QirArray* %rs2, + %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) + ret void +} + +define void @__quantum__qis__applyconditionallyintrinsic__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { + call void @__quantum__qis__applyconditionallyintrinsicca__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) + ret void +} + +define void @__quantum__qis__applyconditionallyintrinsica__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { + call void @__quantum__qis__applyconditionallyintrinsicca__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) + ret void +} +define void @__quantum__qis__applyconditionallyintrinsica__adj( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { + call void @__quantum__qis__applyconditionallyintrinsicca__adj( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) + ret void +} + +define void @__quantum__qis__applyconditionallyintrinsicc__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { + call void @__quantum__qis__applyconditionallyintrinsicca__body( + %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) + ret void +} +define void @__quantum__qis__applyconditionallyintrinsicc__ctl( + %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) { + call void @__quantum__qis__applyconditionallyintrinsicca__ctl( + %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) + ret void +} + diff --git a/src/QirRuntime/lib/QIR/conditionals.cpp b/src/QirRuntime/lib/QIR/conditionals.cpp new file mode 100644 index 00000000000..3d997113b0d --- /dev/null +++ b/src/QirRuntime/lib/QIR/conditionals.cpp @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include + +#include "quantum__qis.hpp" + +#include "QirTypes.hpp" +#include "quantum__rt.hpp" + +static void ApplyWithFunctor(bool isControlled, bool isAdjoint, QirArray* ctls, QirCallable* clb) +{ + auto tupleSize = isControlled ? sizeof(void*) : 0; + PTuple argsTuple = quantum__rt__tuple_create(tupleSize); + + QirCallable* clbFunc = + (isAdjoint || isControlled) ? quantum__rt__callable_copy(clb, true /*force new instance*/) : clb; + + if (isAdjoint) + { + quantum__rt__callable_make_adjoint(clbFunc); + } + if (isControlled) + { + quantum__rt__callable_make_controlled(clbFunc); + *reinterpret_cast(argsTuple) = ctls; + } + + quantum__rt__callable_invoke(clbFunc, argsTuple /*args*/, nullptr /*result*/); + + if (clb != clbFunc) + { + quantum__rt__callable_update_reference_count(clbFunc, -1); + } + quantum__rt__tuple_update_reference_count(argsTuple, -1); +} + +static bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) +{ + assert(rs1 != nullptr && rs2 != nullptr && rs1->count == rs2->count); + assert(rs1->itemSizeInBytes == sizeof(void*)); // the array should contain pointers to RESULT + assert(rs2->itemSizeInBytes == sizeof(void*)); // the array should contain pointers to RESULT + + RESULT** results1 = reinterpret_cast(rs1->buffer); + RESULT** results2 = reinterpret_cast(rs2->buffer); + for (int64_t i = 0; i < rs1->count; i++) + { + if (!quantum__rt__result_equal(results1[i], results2[i])) + { + return false; + } + } + return true; +} + +extern "C" +{ + void unexpected_conditional() + { + throw std::logic_error( + "This conditional callback should have been lowered to a corresponding __body call in QIR"); + } + + void quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) + { + QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; + PTuple argsTuple = quantum__rt__tuple_create(0); + quantum__rt__callable_invoke(clbApply, argsTuple /*args*/, nullptr /*result*/); + quantum__rt__tuple_update_reference_count(argsTuple, -1); + } + + void quantum__qis__applyconditionallyintrinsicca__body( + QirArray* rs1, + QirArray* rs2, + QirCallable* clbOnAllEqual, + QirCallable* clbOnSomeDifferent) + { + QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; + ApplyWithFunctor(false /*C*/, false /*A*/, nullptr, clbApply); + } + + void quantum__qis__applyconditionallyintrinsicca__adj( + QirArray* rs1, + QirArray* rs2, + QirCallable* clbOnAllEqual, + QirCallable* clbOnSomeDifferent) + { + QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; + ApplyWithFunctor(false /*C*/, true /*A*/, nullptr, clbApply); + } + + void quantum__qis__applyconditionallyintrinsicca__ctl( + QirArray* ctls, + QirArray* rs1, + QirArray* rs2, + QirCallable* clbOnAllEqual, + QirCallable* clbOnSomeDifferent) + { + QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; + ApplyWithFunctor(true /*C*/, false /*A*/, ctls, clbApply); + } + + void quantum__qis__applyconditionallyintrinsicca__ctladj( + QirArray* ctls, + QirArray* rs1, + QirArray* rs2, + QirCallable* clbOnAllEqual, + QirCallable* clbOnSomeDifferent) + { + QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; + ApplyWithFunctor(true /*C*/, true /*A*/, ctls, clbApply); + } +} \ No newline at end of file diff --git a/src/QirRuntime/lib/QIR/context.cpp b/src/QirRuntime/lib/QIR/context.cpp index 5456b112fa6..11c7fcdcc38 100644 --- a/src/QirRuntime/lib/QIR/context.cpp +++ b/src/QirRuntime/lib/QIR/context.cpp @@ -15,8 +15,10 @@ #define QIR_SHARED_API #endif +// 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 diff --git a/src/QirRuntime/lib/QIR/delegated.cpp b/src/QirRuntime/lib/QIR/delegated.cpp index 129e6a615ee..6530297b227 100644 --- a/src/QirRuntime/lib/QIR/delegated.cpp +++ b/src/QirRuntime/lib/QIR/delegated.cpp @@ -27,12 +27,12 @@ std::unordered_map& AllocatedResults() extern "C" { - Result UseZero() + Result quantum__rt__result_zero() { return Microsoft::Quantum::g_context->simulator->UseZero(); } - Result UseOne() + Result quantum__rt__result_one() { return Microsoft::Quantum::g_context->simulator->UseOne(); } diff --git a/src/QirRuntime/lib/QIR/quantum__qis.hpp b/src/QirRuntime/lib/QIR/quantum__qis.hpp index e22e1937f96..dadae7ee56d 100644 --- a/src/QirRuntime/lib/QIR/quantum__qis.hpp +++ b/src/QirRuntime/lib/QIR/quantum__qis.hpp @@ -60,7 +60,7 @@ extern "C" QIR_SHARED_API void quantum__qis__z__body(QUBIT*); // NOLINT QIR_SHARED_API void quantum__qis__z__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void quantum__qis__message__body(QirString* qstr); // NOLINT + QIR_SHARED_API void quantum__qis__message__body(QirString* qstr); // NOLINT // Q# Math: QIR_SHARED_API bool quantum__qis__isnan__body(double d); // NOLINT @@ -75,5 +75,35 @@ extern "C" QIR_SHARED_API double quantum__qis__ieeeremainder__body(double x, double y); // NOLINT QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT + QIR_SHARED_API bool quantum__qis__isnan__body(double d); // NOLINT + QIR_SHARED_API double quantum__qis__infinity__body(); // NOLINT + QIR_SHARED_API bool quantum__qis__isinf__body(double d); // NOLINT + QIR_SHARED_API double quantum__qis__arctan2__body(double y, double x); // NOLINT + QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT + // Q# ApplyIf: + QIR_SHARED_API void unexpected_conditional(); // NOLINT + QIR_SHARED_API void quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__body( // NOLINT + QirArray*, + QirArray*, + QirCallable*, + QirCallable*); + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__adj( // NOLINT + QirArray*, + QirArray*, + QirCallable*, + QirCallable*); + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__ctl( // NOLINT + QirArray*, + QirArray*, + QirArray*, + QirCallable*, + QirCallable*); + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__ctladj( // NOLINT + QirArray*, + QirArray*, + QirArray*, + QirCallable*, + QirCallable*); } \ No newline at end of file diff --git a/src/QirRuntime/lib/QIR/quantum__rt.hpp b/src/QirRuntime/lib/QIR/quantum__rt.hpp index f0d24001f19..de3c64876a3 100644 --- a/src/QirRuntime/lib/QIR/quantum__rt.hpp +++ b/src/QirRuntime/lib/QIR/quantum__rt.hpp @@ -84,6 +84,10 @@ extern "C" // becomes 0. The behavior is undefined if the reference count becomes negative. QIR_SHARED_API void quantum__rt__result_update_reference_count(RESULT*, int32_t); // NOLINT + // Not in the QIR spec right now + QIR_SHARED_API RESULT* quantum__rt__result_one(); // NOLINT + QIR_SHARED_API RESULT* quantum__rt__result_zero(); // NOLINT + // ------------------------------------------------------------------------ // Tuples // ------------------------------------------------------------------------ diff --git a/src/QirRuntime/public/QirContext.hpp b/src/QirRuntime/public/QirContext.hpp index 672eef42660..11baa2f148c 100644 --- a/src/QirRuntime/public/QirContext.hpp +++ b/src/QirRuntime/public/QirContext.hpp @@ -4,6 +4,7 @@ #pragma once #include +#include "CoreTypes.hpp" namespace Microsoft { diff --git a/src/QirRuntime/test/QIR-static/qir-driver.cpp b/src/QirRuntime/test/QIR-static/qir-driver.cpp index 3015db659bb..00b794625d9 100644 --- a/src/QirRuntime/test/QIR-static/qir-driver.cpp +++ b/src/QirRuntime/test/QIR-static/qir-driver.cpp @@ -1,19 +1,19 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include #include +#include #include #include #include #include #include "CoreTypes.hpp" +#include "QirContext.hpp" +#include "QirTypes.hpp" #include "QuantumApi_I.hpp" #include "SimFactory.hpp" #include "SimulatorStub.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" #include "quantum__rt.hpp" #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file @@ -223,8 +223,8 @@ TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") } #endif -// The Microsoft__Quantum__Testing__QIR__TestControlled__body tests needs proper semantics of X and M, and nothing else. -// The validation is done inside the test and it would return an error code in case of failure. +// The Microsoft__Quantum__Testing__QIR__TestFunctors__body tests needs proper semantics of X and M, and nothing else. +// The validation is done inside the test and it would throw in case of failure. struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub { std::vector qubits; @@ -299,13 +299,18 @@ struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub } }; FunctorsTestSimulator* g_ctrqapi = nullptr; -extern "C" void Microsoft__Quantum__Testing__QIR__TestControlled__body(); // NOLINT -extern "C" void __quantum__qis__k__body(Qubit q) // NOLINT +static int g_cKCalls = 0; +static int g_cKCallsControlled = 0; +extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__body(); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__body(); // NOLINT +extern "C" void __quantum__qis__k__body(Qubit q) // NOLINT { + g_cKCalls++; g_ctrqapi->X(q); } extern "C" void __quantum__qis__k__ctl(QirArray* controls, Qubit q) // NOLINT { + g_cKCallsControlled++; g_ctrqapi->ControlledX(controls->count, reinterpret_cast(controls->buffer), q); } TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") @@ -314,7 +319,82 @@ TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); g_ctrqapi = qapi.get(); - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestControlled__body()); + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__body()); + + const int cKCalls = g_cKCalls; + const int cKCallsControlled = g_cKCallsControlled; + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__body()); + CHECK(g_cKCalls - cKCalls == 3); + CHECK(g_cKCallsControlled - cKCallsControlled == 5); g_ctrqapi = nullptr; } + +// TestConditionalOnResult() is authored in a way that the expected path through the function only applies X operator +// for the chosen sequence of measurement results, and all other paths apply Y. Thus, the correct execution must get the +// expected maximum number of X and ControlledX callback. +struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub +{ + int cX = 0; + int ccX = 0; + vector mockMeasurements; + + explicit ConditionalsTestSimulator(vector&& results) + : mockMeasurements(results) + { + } + + Qubit AllocateQubit() override + { + return nullptr; + } + void ReleaseQubit(Qubit qubit) override {} + + void X(Qubit) override + { + this->cX++; + } + void ControlledX(long numControls, Qubit controls[], Qubit qubit) override + { + this->ccX++; + } + void Y(Qubit) override {} + + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + static int callNumber = -1; + callNumber++; + assert(callNumber < this->mockMeasurements.size() && "ConditionalsTestSimulator isn't set up correctly"); + + return (this->mockMeasurements[callNumber] == Result_Zero) ? UseZero() : UseOne(); + } + + bool AreEqualResults(Result r1, Result r2) override + { + // those are bogus pointers but it's ok to compare them _as pointers_ + return (r1 == r2); + } + + void ReleaseResult(Result result) override {} // the results aren't allocated by this test simulator + + Result UseZero() override + { + return reinterpret_cast(0); + } + + Result UseOne() override + { + return reinterpret_cast(1); + } +}; +extern "C" void Microsoft__Quantum__Testing__QIR__TestConditionalOnResult__body(); // NOLINT +TEST_CASE("QIR: conditionals on measurement results", "[qir][qir.conditionals]") +{ + unique_ptr qapi = + make_unique(vector{Result_Zero, Result_One}); + QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestConditionalOnResult__body()); + CHECK(qapi->cX == 14); + CHECK(qapi->ccX == 9); +} \ No newline at end of file diff --git a/src/QirRuntime/test/QIR-static/qsharp/Math.qs b/src/QirRuntime/test/QIR-static/qsharp/Math.qs index 23d81bcbe09..6edba1a7860 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/Math.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/Math.qs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + namespace Microsoft.Quantum.Intrinsic { open Microsoft.Quantum.Targeting; diff --git a/src/QirRuntime/test/QIR-static/qsharp/main.cs b/src/QirRuntime/test/QIR-static/qsharp/main.cs new file mode 100644 index 00000000000..b2ebeb1c3ad --- /dev/null +++ b/src/QirRuntime/test/QIR-static/qsharp/main.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Currently, compiling to QIR has to suppress C# generation but then we need to provide Main function ourselves. +namespace CompilerWorkaround +{ + class Program + { + static void Main(string[] args) + { + } + } +} \ No newline at end of file diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj b/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj index 16a4473fd18..48b15aa3665 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj @@ -3,7 +3,9 @@ Exe netcoreapp3.1 + false True + honeywell.qpu diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs index 0adc1cb96e1..694d0159787 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs @@ -32,7 +32,8 @@ namespace Microsoft.Quantum.Testing.QIR { // The purpose of this block is to keep the Q# compiler from optimizing away other tests when generating QIR if (compilerDecoy) { - let res1 = TestControlled(); + let res11 = TestFunctors(); + let res12 = TestFunctorsNoArgs(); let res2 = TestPartials(17, 42); TestQubitResultManagement(); @@ -53,6 +54,9 @@ namespace Microsoft.Quantum.Testing.QIR { let res17 = ArcCosTest(); let res18 = ArcTanTest(); MessageTest("Test"); + + // Conditionals: + TestConditionalOnResult(); } return sum; } diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs new file mode 100644 index 00000000000..88ed0fd32a0 --- /dev/null +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace Microsoft.Quantum.Testing.QIR { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + + operation TestConditionalOnResult() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + // Zero branch + let r1 = M(q1); + ApplyIfElseRCA(r1, (X, q1), (Y, q1)); + Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); + Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + + // One branch + let r2 = M(q1); + ApplyIfElseRCA(r2, (Y, q1), (X, q1)); + + // Multiple ops, guarded by the condition + if r1 == Zero { + X(q1); + X(q2); + } + else { + Y(q1); + } + + // Multi-result comparison + Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); + Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally( + [r1, One], [Zero, r2], (X, q1), (Y, q1)); + + // Other variants + ApplyIfElseR(r1, (X, q1), (Y, q1)); + Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); + Controlled ApplyIfElseRC([q2], (r1, (X, q1), (Y, q1))); + + ApplyIfOne(r2, (X, q1)); + Adjoint ApplyIfOneA(r2, (X, q1)); + Controlled ApplyIfOneC([q2], (r2, (X, q1))); + Adjoint Controlled ApplyIfOneCA([q2], (r2, (X, q1))); + + ApplyIfZero(r1, (X, q1)); + Adjoint ApplyIfZeroA(r1, (X, q1)); + Controlled ApplyIfZeroC([q2], (r1, (X, q1))); + Adjoint Controlled ApplyIfZeroCA([q2], (r1, (X, q1))); + + Adjoint Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyA( + [r1], [r2], (Y, q1), (X, q1)); + Controlled Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyC( + [q2], ([r1], [r2], (Y, q1), (X, q1))); + Adjoint Controlled Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyCA( + [q2], ([r1], [r2], (Y, q1), (X, q1))); + } +} \ No newline at end of file diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-functors.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-functors.qs index aa6a91f200b..0d8c283dc87 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-functors.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-functors.qs @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. // For the test to pass implement K to be the same as X. We need it for the test, because the standard bridge doesn't // support multi-controlled X. @@ -18,11 +20,11 @@ namespace Microsoft.Quantum.Testing.QIR { operation Qop(q : Qubit, n : Int) : Unit is Adj+Ctl { body (...) { - if (n%2 == 1) { K(q); } + if n%2 == 1 { K(q); } } adjoint self; controlled (ctrls, ...) { - if (n%2 == 1) { Controlled K(ctrls, q); } + if n%2 == 1 { Controlled K(ctrls, q); } } } @@ -30,7 +32,7 @@ namespace Microsoft.Quantum.Testing.QIR { // BasicMeasurementFeedback, which in turn doesn't allow updating mutables inside measurement conditionals. // this means, we cannot easily get detailed failure information back from Q#, but the test driver can mock // the simulator to track the point of failure. - operation TestControlled() : Unit { + operation TestFunctors() : Unit { let qop = Qop(_, 1); let adj_qop = Adjoint qop; let ctl_qop = Controlled qop; @@ -63,4 +65,42 @@ namespace Microsoft.Quantum.Testing.QIR { } } } + + // The operation is not sensical but in tests we can mock K operator to check that it actually executes + operation NoArgs() : Unit + is Adj+Ctl { + body (...) { + use q = Qubit(); + K(q); + } + adjoint self; + controlled (ctrls, ...) { + use q = Qubit(); + Controlled K(ctrls, q); + } + } + + operation TestFunctorsNoArgs() : Unit { + NoArgs(); + let qop = NoArgs; + let adj_qop = Adjoint qop; + let ctl_qop = Controlled qop; + let adj_ctl_qop = Adjoint Controlled qop; + let ctl_ctl_qop = Controlled ctl_qop; + + use (q1, q2, q3) = (Qubit(), Qubit(), Qubit()) { + X(q1); + X(q2); + X(q3); + + qop(); + adj_qop(); + ctl_qop([q1], ()); + adj_ctl_qop([q1], ()); + ctl_ctl_qop([q1], ([q2], ())); + + Controlled qop([q1, q2], ()); + Adjoint Controlled ctl_ctl_qop([q1], ([q2], ([q3], ()))); + } + } } \ No newline at end of file From 2fb1ef19b8763ebef10a74743063f5b6e8bce64f Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Tue, 23 Feb 2021 12:59:37 -0800 Subject: [PATCH 2/5] cleanup after merge and split tests --- src/QirRuntime/lib/QIR/conditionals.cpp | 4 +- src/QirRuntime/lib/QIR/quantum__qis.hpp | 5 - src/QirRuntime/public/QirContext.hpp | 1 - src/QirRuntime/test/QIR-static/CMakeLists.txt | 1 + src/QirRuntime/test/QIR-static/qir-driver.cpp | 69 ----------- .../test/QIR-static/qir-test-conditionals.cpp | 111 ++++++++++++++++++ .../test/QIR-static/qsharp/qir-test-arrays.qs | 8 +- .../qsharp/qir-test-conditionals.qs | 46 +++++--- 8 files changed, 148 insertions(+), 97 deletions(-) create mode 100644 src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp diff --git a/src/QirRuntime/lib/QIR/conditionals.cpp b/src/QirRuntime/lib/QIR/conditionals.cpp index 3d997113b0d..154fe491ce6 100644 --- a/src/QirRuntime/lib/QIR/conditionals.cpp +++ b/src/QirRuntime/lib/QIR/conditionals.cpp @@ -65,9 +65,7 @@ extern "C" void quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) { QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; - PTuple argsTuple = quantum__rt__tuple_create(0); - quantum__rt__callable_invoke(clbApply, argsTuple /*args*/, nullptr /*result*/); - quantum__rt__tuple_update_reference_count(argsTuple, -1); + ApplyWithFunctor(false /*C*/, false /*A*/, nullptr, clbApply); } void quantum__qis__applyconditionallyintrinsicca__body( diff --git a/src/QirRuntime/lib/QIR/quantum__qis.hpp b/src/QirRuntime/lib/QIR/quantum__qis.hpp index dadae7ee56d..7fa55c4860f 100644 --- a/src/QirRuntime/lib/QIR/quantum__qis.hpp +++ b/src/QirRuntime/lib/QIR/quantum__qis.hpp @@ -75,11 +75,6 @@ extern "C" QIR_SHARED_API double quantum__qis__ieeeremainder__body(double x, double y); // NOLINT QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT - QIR_SHARED_API bool quantum__qis__isnan__body(double d); // NOLINT - QIR_SHARED_API double quantum__qis__infinity__body(); // NOLINT - QIR_SHARED_API bool quantum__qis__isinf__body(double d); // NOLINT - QIR_SHARED_API double quantum__qis__arctan2__body(double y, double x); // NOLINT - QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT // Q# ApplyIf: QIR_SHARED_API void unexpected_conditional(); // NOLINT diff --git a/src/QirRuntime/public/QirContext.hpp b/src/QirRuntime/public/QirContext.hpp index 11baa2f148c..672eef42660 100644 --- a/src/QirRuntime/public/QirContext.hpp +++ b/src/QirRuntime/public/QirContext.hpp @@ -4,7 +4,6 @@ #pragma once #include -#include "CoreTypes.hpp" namespace Microsoft { diff --git a/src/QirRuntime/test/QIR-static/CMakeLists.txt b/src/QirRuntime/test/QIR-static/CMakeLists.txt index a9754805783..9fc202a931a 100644 --- a/src/QirRuntime/test/QIR-static/CMakeLists.txt +++ b/src/QirRuntime/test/QIR-static/CMakeLists.txt @@ -17,6 +17,7 @@ add_custom_target(qir_static_test_lib DEPENDS ${QIR_TESTS_LIBS}) # add_executable(qir-static-tests qir-driver.cpp + qir-test-conditionals.cpp qir-test-math.cpp qir-test-strings.cpp qir-test-ouput.cpp diff --git a/src/QirRuntime/test/QIR-static/qir-driver.cpp b/src/QirRuntime/test/QIR-static/qir-driver.cpp index 00b794625d9..c9157c42fcf 100644 --- a/src/QirRuntime/test/QIR-static/qir-driver.cpp +++ b/src/QirRuntime/test/QIR-static/qir-driver.cpp @@ -329,72 +329,3 @@ TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") g_ctrqapi = nullptr; } - -// TestConditionalOnResult() is authored in a way that the expected path through the function only applies X operator -// for the chosen sequence of measurement results, and all other paths apply Y. Thus, the correct execution must get the -// expected maximum number of X and ControlledX callback. -struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - int cX = 0; - int ccX = 0; - vector mockMeasurements; - - explicit ConditionalsTestSimulator(vector&& results) - : mockMeasurements(results) - { - } - - Qubit AllocateQubit() override - { - return nullptr; - } - void ReleaseQubit(Qubit qubit) override {} - - void X(Qubit) override - { - this->cX++; - } - void ControlledX(long numControls, Qubit controls[], Qubit qubit) override - { - this->ccX++; - } - void Y(Qubit) override {} - - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override - { - static int callNumber = -1; - callNumber++; - assert(callNumber < this->mockMeasurements.size() && "ConditionalsTestSimulator isn't set up correctly"); - - return (this->mockMeasurements[callNumber] == Result_Zero) ? UseZero() : UseOne(); - } - - bool AreEqualResults(Result r1, Result r2) override - { - // those are bogus pointers but it's ok to compare them _as pointers_ - return (r1 == r2); - } - - void ReleaseResult(Result result) override {} // the results aren't allocated by this test simulator - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; -extern "C" void Microsoft__Quantum__Testing__QIR__TestConditionalOnResult__body(); // NOLINT -TEST_CASE("QIR: conditionals on measurement results", "[qir][qir.conditionals]") -{ - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestConditionalOnResult__body()); - CHECK(qapi->cX == 14); - CHECK(qapi->ccX == 9); -} \ No newline at end of file diff --git a/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp new file mode 100644 index 00000000000..f311bc28719 --- /dev/null +++ b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include +#include +#include + +#include "catch.hpp" + +#include "CoreTypes.hpp" +#include "QirContext.hpp" +#include "QirTypes.hpp" +#include "QuantumApi_I.hpp" +#include "SimulatorStub.hpp" + +using namespace std; +using namespace Microsoft::Quantum; + +// TestConditionalOnResult() is authored in a way that the expected path through the function only applies X operator +// for the chosen sequence of measurement results, and all other paths apply Y. Thus, the correct execution must get the +// expected maximum number of X and ControlledX callbacks. +struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub +{ + int cX = 0; + int ccX = 0; + + vector mockMeasurements; + int nMeasure = -1; + + explicit ConditionalsTestSimulator(vector&& results) + : mockMeasurements(results) + { + } + + Qubit AllocateQubit() override + { + return nullptr; + } + void ReleaseQubit(Qubit qubit) override {} + + void X(Qubit) override + { + this->cX++; + } + void ControlledX(long numControls, Qubit controls[], Qubit qubit) override + { + this->ccX++; + } + void Y(Qubit) override {} + + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + this->nMeasure++; + assert(this->nMeasure < this->mockMeasurements.size() && "ConditionalsTestSimulator isn't set up correctly"); + + return (this->mockMeasurements[this->nMeasure] == Result_Zero) ? UseZero() : UseOne(); + } + + bool AreEqualResults(Result r1, Result r2) override + { + // those are bogus pointers but it's ok to compare them _as pointers_ + return (r1 == r2); + } + + void ReleaseResult(Result result) override {} // the results aren't allocated by this test simulator + + Result UseZero() override + { + return reinterpret_cast(0); + } + + Result UseOne() override + { + return reinterpret_cast(1); + } +}; + +extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIf__body(); // NOLINT +TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") +{ + unique_ptr qapi = + make_unique(vector{Result_Zero, Result_One}); + QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__body()); + CHECK(qapi->cX == 10); + CHECK(qapi->ccX == 7); +} + +extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body(); // NOLINT +TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") +{ + unique_ptr qapi = + make_unique(vector{Result_Zero, Result_One}); + QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body()); + CHECK(qapi->cX == 4); + CHECK(qapi->ccX == 2); +} + +extern "C" void Microsoft__Quantum__Testing__QIR__TestConditionalRewrite__body(); // NOLINT +TEST_CASE("QIR: conditionals on measurement results", "[qir][qir.conditionals]") +{ + unique_ptr qapi = + make_unique(vector{Result_Zero, Result_One}); + QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestConditionalRewrite__body()); + CHECK(qapi->cX == 1); + CHECK(qapi->ccX == 1); +} \ No newline at end of file diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs index 694d0159787..4c0bcd85ee6 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs @@ -32,8 +32,8 @@ namespace Microsoft.Quantum.Testing.QIR { // The purpose of this block is to keep the Q# compiler from optimizing away other tests when generating QIR if (compilerDecoy) { - let res11 = TestFunctors(); - let res12 = TestFunctorsNoArgs(); + let res1_1 = TestFunctors(); + let res1_2 = TestFunctorsNoArgs(); let res2 = TestPartials(17, 42); TestQubitResultManagement(); @@ -56,7 +56,9 @@ namespace Microsoft.Quantum.Testing.QIR { MessageTest("Test"); // Conditionals: - TestConditionalOnResult(); + TestApplyIf(); + TestApplyConditionally(); + TestConditionalRewrite(); } return sum; } diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs index 88ed0fd32a0..662ee2e8b47 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs @@ -4,7 +4,7 @@ namespace Microsoft.Quantum.Testing.QIR { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - operation TestConditionalOnResult() : Unit { + operation TestApplyIf() : Unit { use q1 = Qubit(); use q2 = Qubit(); @@ -13,26 +13,13 @@ namespace Microsoft.Quantum.Testing.QIR { ApplyIfElseRCA(r1, (X, q1), (Y, q1)); Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + X(q2); Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); // One branch - let r2 = M(q1); + let r2 = M(q2); ApplyIfElseRCA(r2, (Y, q1), (X, q1)); - // Multiple ops, guarded by the condition - if r1 == Zero { - X(q1); - X(q2); - } - else { - Y(q1); - } - - // Multi-result comparison - Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); - Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally( - [r1, One], [Zero, r2], (X, q1), (Y, q1)); - // Other variants ApplyIfElseR(r1, (X, q1), (Y, q1)); Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); @@ -47,7 +34,21 @@ namespace Microsoft.Quantum.Testing.QIR { Adjoint ApplyIfZeroA(r1, (X, q1)); Controlled ApplyIfZeroC([q2], (r1, (X, q1))); Adjoint Controlled ApplyIfZeroCA([q2], (r1, (X, q1))); + } + + operation TestApplyConditionally() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + let r1 = M(q1); + X(q2); + let r2 = M(q2); + Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); + Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally( + [r1, One], [Zero, r2], (X, q1), (Y, q1)); + + // Other variants Adjoint Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyA( [r1], [r2], (Y, q1), (X, q1)); Controlled Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyC( @@ -55,4 +56,17 @@ namespace Microsoft.Quantum.Testing.QIR { Adjoint Controlled Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyCA( [q2], ([r1], [r2], (Y, q1), (X, q1))); } + + operation TestConditionalRewrite() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + if M(q1) == Zero { + X(q1); + Controlled X([q2], q1); + } + else { + Y(q1); + } + } } \ No newline at end of file From d291e013b5b5fa54005c46b9cf0996072927f2e0 Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Wed, 24 Feb 2021 09:00:16 -0800 Subject: [PATCH 3/5] Remove conditional rewrite; other PR feedback --- .../test/QIR-static/qir-test-conditionals.cpp | 35 ++++++----- .../test/QIR-static/qsharp/qir-gen.csproj | 1 - .../test/QIR-static/qsharp/qir-test-arrays.qs | 2 +- .../qsharp/qir-test-conditionals.qs | 63 ++++++++----------- 4 files changed, 46 insertions(+), 55 deletions(-) diff --git a/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp index f311bc28719..1941194c6ef 100644 --- a/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp +++ b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp @@ -24,7 +24,7 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub int ccX = 0; vector mockMeasurements; - int nMeasure = -1; + int nextMeasureResult = 0; explicit ConditionalsTestSimulator(vector&& results) : mockMeasurements(results) @@ -49,10 +49,11 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override { - this->nMeasure++; - assert(this->nMeasure < this->mockMeasurements.size() && "ConditionalsTestSimulator isn't set up correctly"); + assert(this->nextMeasureResult < this->mockMeasurements.size() + && "ConditionalsTestSimulator isn't set up correctly"); - return (this->mockMeasurements[this->nMeasure] == Result_Zero) ? UseZero() : UseOne(); + return (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); + this->nextMeasureResult++; } bool AreEqualResults(Result r1, Result r2) override @@ -82,30 +83,30 @@ TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__body()); - CHECK(qapi->cX == 10); - CHECK(qapi->ccX == 7); + CHECK(qapi->cX == 8); + CHECK(qapi->ccX == 0); } -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body(); // NOLINT -TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") +extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__body(); // NOLINT +TEST_CASE("QIR: ApplyIf with functors", "[qir][qir.conditionals][skip]") { unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body()); - CHECK(qapi->cX == 4); - CHECK(qapi->ccX == 2); + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__body()); + CHECK(qapi->cX == 5); + CHECK(qapi->ccX == 7); } -extern "C" void Microsoft__Quantum__Testing__QIR__TestConditionalRewrite__body(); // NOLINT -TEST_CASE("QIR: conditionals on measurement results", "[qir][qir.conditionals]") +extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body(); // NOLINT +TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") { unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestConditionalRewrite__body()); - CHECK(qapi->cX == 1); - CHECK(qapi->ccX == 1); -} \ No newline at end of file + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body()); + CHECK(qapi->cX == 4); + CHECK(qapi->ccX == 2); +} diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj b/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj index 48b15aa3665..8413527711b 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj @@ -5,7 +5,6 @@ netcoreapp3.1 false True - honeywell.qpu diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs index 4c0bcd85ee6..58ced577cd5 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs @@ -57,8 +57,8 @@ namespace Microsoft.Quantum.Testing.QIR { // Conditionals: TestApplyIf(); + TestApplyIfWithFunctors(); TestApplyConditionally(); - TestConditionalRewrite(); } return sum; } diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs index 662ee2e8b47..e86a30e42ee 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs @@ -2,35 +2,43 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.Testing.QIR { open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Simulation.QuantumProcessor.Extensions; operation TestApplyIf() : Unit { use q1 = Qubit(); use q2 = Qubit(); - // Zero branch let r1 = M(q1); - ApplyIfElseRCA(r1, (X, q1), (Y, q1)); - Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); - Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); X(q2); - Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); - - // One branch let r2 = M(q2); - ApplyIfElseRCA(r2, (Y, q1), (X, q1)); - // Other variants ApplyIfElseR(r1, (X, q1), (Y, q1)); - Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); - Controlled ApplyIfElseRC([q2], (r1, (X, q1), (Y, q1))); + ApplyIfElseR(r2, (Y, q1), (X, q1)); + // Other variants + ApplyIfElseRA(r1, (X, q1), (Y, q1)); + ApplyIfElseRC(r1, (X, q1), (Y, q1)); + ApplyIfElseRCA(r1, (X, q1), (Y, q1)); ApplyIfOne(r2, (X, q1)); + ApplyIfZero(r1, (X, q1)); + } + + operation TestApplyIfWithFunctors() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + let r1 = M(q1); + X(q2); + let r2 = M(q2); + + Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); + Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); + Controlled ApplyIfElseRC([q2], (r1, (X, q1), (Y, q1))); Adjoint ApplyIfOneA(r2, (X, q1)); Controlled ApplyIfOneC([q2], (r2, (X, q1))); Adjoint Controlled ApplyIfOneCA([q2], (r2, (X, q1))); - - ApplyIfZero(r1, (X, q1)); Adjoint ApplyIfZeroA(r1, (X, q1)); Controlled ApplyIfZeroC([q2], (r1, (X, q1))); Adjoint Controlled ApplyIfZeroCA([q2], (r1, (X, q1))); @@ -44,29 +52,12 @@ namespace Microsoft.Quantum.Testing.QIR { X(q2); let r2 = M(q2); - Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); - Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionally( - [r1, One], [Zero, r2], (X, q1), (Y, q1)); + ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); + ApplyConditionally([r1, One], [Zero, r2], (X, q1), (Y, q1)); - // Other variants - Adjoint Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyA( - [r1], [r2], (Y, q1), (X, q1)); - Controlled Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyC( - [q2], ([r1], [r2], (Y, q1), (X, q1))); - Adjoint Controlled Microsoft.Quantum.Simulation.QuantumProcessor.Extensions.ApplyConditionallyCA( - [q2], ([r1], [r2], (Y, q1), (X, q1))); + Adjoint ApplyConditionallyA([r1], [r2], (Y, q1), (X, q1)); + Controlled ApplyConditionallyC([q2], ([r1], [r2], (Y, q1), (X, q1))); + Adjoint Controlled ApplyConditionallyCA([q2], ([r1], [r2], (Y, q1), (X, q1))); } - operation TestConditionalRewrite() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - if M(q1) == Zero { - X(q1); - Controlled X([q2], q1); - } - else { - Y(q1); - } - } } \ No newline at end of file From fd155fcb6ebb6f13327db4c0d423f0ae6f866b7c Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Wed, 24 Feb 2021 12:43:35 -0800 Subject: [PATCH 4/5] Implement remaining ApplyIf* variants and add more diagnostics to the tests --- src/QirRuntime/lib/QIR/bridge-qis.ll | 100 ++++++++++++++---- src/QirRuntime/lib/QIR/conditionals.cpp | 30 +++++- src/QirRuntime/lib/QIR/quantum__qis.hpp | 38 ++++--- .../test/QIR-static/qir-test-conditionals.cpp | 78 +++++++++++--- .../qsharp/qir-test-conditionals.qs | 4 +- 5 files changed, 192 insertions(+), 58 deletions(-) diff --git a/src/QirRuntime/lib/QIR/bridge-qis.ll b/src/QirRuntime/lib/QIR/bridge-qis.ll index 4880865b553..e9896b30c3b 100644 --- a/src/QirRuntime/lib/QIR/bridge-qis.ll +++ b/src/QirRuntime/lib/QIR/bridge-qis.ll @@ -441,8 +441,13 @@ define i64 @__quantum__qis__drawrandomint__body(i64 %min, i64 %max) { ;=============================================================================== ; quantum.qis conditional functions ; -declare void @unexpected_conditional() -declare void @quantum__qis__applyifelseintrinsic__body(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyifelseintrinsicca__body(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyifelseintrinsicca__adj(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyifelseintrinsicca__ctl( + %struct.QirArray*, %class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) +declare void @quantum__qis__applyifelseintrinsicca__ctladj( + %struct.QirArray*, %class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) + declare void @quantum__qis__applyconditionallyintrinsicca__body( %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) declare void @quantum__qis__applyconditionallyintrinsicca__adj( @@ -453,51 +458,102 @@ declare void @quantum__qis__applyconditionallyintrinsicca__ctladj( %struct.QirArray*, %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) ; applyif -define void @__quantum__qis__applyifelseintrinsic__body( + +define void @__quantum__qis__applyifelseintrinsicca__body( %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + %r = bitcast %Result* %.r to %class.RESULT* %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* - call void @quantum__qis__applyifelseintrinsic__body( + call void @quantum__qis__applyifelseintrinsicca__body( %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) ret void } +define void @__quantum__qis__applyifelseintrinsicca__ctl(%Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) { + %.pr = getelementptr inbounds + {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 0 + %.r = load %Result*, %Result** %.pr + + %.pclb_on_zero = getelementptr inbounds + {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 1 + %.clb_on_zero = load %Callable*, %Callable** %.pclb_on_zero + + %.pclb_on_one = getelementptr inbounds + {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 2 + %.clb_on_one = load %Callable*, %Callable** %.pclb_on_one + + %ctls = bitcast %Array* %.ctls to %struct.QirArray* + %r = bitcast %Result* %.r to %class.RESULT* + %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* + %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* + call void @quantum__qis__applyifelseintrinsicca__ctl( + %struct.QirArray* %ctls, %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) -define void @__quantum__qis__applyifelseintrinsica__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsic__body(%Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsica__adj(%Result, %Callable*, %Callable*) { - call void @unexpected_conditional() +define void @__quantum__qis__applyifelseintrinsicca__adj( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + + %r = bitcast %Result* %.r to %class.RESULT* + %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* + %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* + call void @quantum__qis__applyifelseintrinsicca__adj( + %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) ret void } +define void @__quantum__qis__applyifelseintrinsicca__ctladj( + %Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) { + + %.pr = getelementptr inbounds + {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 0 + %.r = load %Result*, %Result** %.pr + + %.pclb_on_zero = getelementptr inbounds + {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 1 + %.clb_on_zero = load %Callable*, %Callable** %.pclb_on_zero + + %.pclb_on_one = getelementptr inbounds + {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 2 + %.clb_on_one = load %Callable*, %Callable** %.pclb_on_one + + %ctls = bitcast %Array* %.ctls to %struct.QirArray* + %r = bitcast %Result* %.r to %class.RESULT* + %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* + %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* + call void @quantum__qis__applyifelseintrinsicca__ctladj( + %struct.QirArray* %ctls, %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) -define void @__quantum__qis__applyifelseintrinsicc__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsic__body(%Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsicc__ctl(%Array*, %Result, %Callable*, %Callable*) { - call void @unexpected_conditional() + +define void @__quantum__qis__applyifelseintrinsic__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + call void @__quantum__qis__applyifelseintrinsicca__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsicca__body( +define void @__quantum__qis__applyifelseintrinsica__body( %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsic__body(%Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) + call void @__quantum__qis__applyifelseintrinsicca__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsicca__ctl(%Array*, %Result, %Callable*, %Callable*) { - call void @unexpected_conditional() +define void @__quantum__qis__applyifelseintrinsica__adj( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + call void @__quantum__qis__applyifelseintrinsicca__adj( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsicca__adj(%Result, %Callable*, %Callable*) { - call void @unexpected_conditional() + +define void @__quantum__qis__applyifelseintrinsicc__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { + call void @__quantum__qis__applyifelseintrinsicca__body( + %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsicca__ctladj(%Array*, %Result, %Callable*, %Callable*) { - call void @unexpected_conditional() +define void @__quantum__qis__applyifelseintrinsicc__ctl(%Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) { + call void @__quantum__qis__applyifelseintrinsicca__ctl(%Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) ret void } diff --git a/src/QirRuntime/lib/QIR/conditionals.cpp b/src/QirRuntime/lib/QIR/conditionals.cpp index 154fe491ce6..7b88b458cf0 100644 --- a/src/QirRuntime/lib/QIR/conditionals.cpp +++ b/src/QirRuntime/lib/QIR/conditionals.cpp @@ -56,16 +56,36 @@ static bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) extern "C" { - void unexpected_conditional() + void quantum__qis__applyifelseintrinsicca__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) { - throw std::logic_error( - "This conditional callback should have been lowered to a corresponding __body call in QIR"); + QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; + ApplyWithFunctor(false /*C*/, false /*A*/, nullptr, clbApply); } - void quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) + void quantum__qis__applyifelseintrinsicca__adj(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) { QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; - ApplyWithFunctor(false /*C*/, false /*A*/, nullptr, clbApply); + ApplyWithFunctor(false /*C*/, true /*A*/, nullptr, clbApply); + } + + void quantum__qis__applyifelseintrinsicca__ctl( + QirArray* ctls, + RESULT* r, + QirCallable* clbOnZero, + QirCallable* clbOnOne) + { + QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; + ApplyWithFunctor(true /*C*/, false /*A*/, ctls, clbApply); + } + + void quantum__qis__applyifelseintrinsicca__ctladj( + QirArray* ctls, + RESULT* r, + QirCallable* clbOnZero, + QirCallable* clbOnOne) + { + QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; + ApplyWithFunctor(true /*C*/, true /*A*/, ctls, clbApply); } void quantum__qis__applyconditionallyintrinsicca__body( diff --git a/src/QirRuntime/lib/QIR/quantum__qis.hpp b/src/QirRuntime/lib/QIR/quantum__qis.hpp index 7fa55c4860f..ba560aeab96 100644 --- a/src/QirRuntime/lib/QIR/quantum__qis.hpp +++ b/src/QirRuntime/lib/QIR/quantum__qis.hpp @@ -63,23 +63,33 @@ extern "C" QIR_SHARED_API void quantum__qis__message__body(QirString* qstr); // NOLINT // Q# Math: - QIR_SHARED_API bool quantum__qis__isnan__body(double d); // NOLINT - QIR_SHARED_API double quantum__qis__infinity__body(); // NOLINT - QIR_SHARED_API bool quantum__qis__isinf__body(double d); // NOLINT - QIR_SHARED_API double quantum__qis__arctan2__body(double y, double x); // NOLINT - QIR_SHARED_API double quantum__qis__sinh__body(double theta); // NOLINT - QIR_SHARED_API double quantum__qis__cosh__body(double theta); // NOLINT - QIR_SHARED_API double quantum__qis__arcsin__body(double theta); // NOLINT - QIR_SHARED_API double quantum__qis__arccos__body(double theta); // NOLINT - QIR_SHARED_API double quantum__qis__arctan__body(double theta); // NOLINT + QIR_SHARED_API bool quantum__qis__isnan__body(double d); // NOLINT + QIR_SHARED_API double quantum__qis__infinity__body(); // NOLINT + QIR_SHARED_API bool quantum__qis__isinf__body(double d); // NOLINT + QIR_SHARED_API double quantum__qis__arctan2__body(double y, double x); // NOLINT + QIR_SHARED_API double quantum__qis__sinh__body(double theta); // NOLINT + QIR_SHARED_API double quantum__qis__cosh__body(double theta); // NOLINT + QIR_SHARED_API double quantum__qis__arcsin__body(double theta); // NOLINT + QIR_SHARED_API double quantum__qis__arccos__body(double theta); // NOLINT + QIR_SHARED_API double quantum__qis__arctan__body(double theta); // NOLINT - QIR_SHARED_API double quantum__qis__ieeeremainder__body(double x, double y); // NOLINT - QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT + QIR_SHARED_API double quantum__qis__ieeeremainder__body(double x, double y); // NOLINT + QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT // Q# ApplyIf: - QIR_SHARED_API void unexpected_conditional(); // NOLINT - QIR_SHARED_API void quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT - QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__body( // NOLINT + QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__body(RESULT*, QirCallable*, QirCallable*); // NOLINT + QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__adj(RESULT*, QirCallable*, QirCallable*); // NOLINT + QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__ctl( // NOLINT + QirArray*, + RESULT*, + QirCallable*, + QirCallable*); + QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__ctladj( // NOLINT + QirArray*, + RESULT*, + QirCallable*, + QirCallable*); + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__body( // NOLINT QirArray*, QirArray*, QirCallable*, diff --git a/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp index 1941194c6ef..556f63d12d6 100644 --- a/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp +++ b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include #include +#include #include #include "catch.hpp" @@ -20,8 +21,10 @@ using namespace Microsoft::Quantum; // expected maximum number of X and ControlledX callbacks. struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub { - int cX = 0; - int ccX = 0; + int nGateCallback = 0; + vector xCallbacks; + vector cxCallbacks; + vector otherCallbacks; vector mockMeasurements; int nextMeasureResult = 0; @@ -31,6 +34,29 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub { } + std::string GetHistory() + { + std::stringstream out; + out << "X: "; + for (int i : this->xCallbacks) + { + out << i << ","; + } + + out << std::endl << "CX: "; + for (int i : this->cxCallbacks) + { + out << i << ","; + } + + out << std::endl << "Other: "; + for (int i : this->otherCallbacks) + { + out << i << ","; + } + return out.str(); + } + Qubit AllocateQubit() override { return nullptr; @@ -39,21 +65,34 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub void X(Qubit) override { - this->cX++; + this->xCallbacks.push_back(this->nGateCallback); + this->nGateCallback++; } void ControlledX(long numControls, Qubit controls[], Qubit qubit) override { - this->ccX++; + this->cxCallbacks.push_back(this->nGateCallback); + this->nGateCallback++; + } + void Y(Qubit) override + { + this->otherCallbacks.push_back(this->nGateCallback); + this->nGateCallback++; + } + void ControlledY(long numControls, Qubit controls[], Qubit qubit) override + { + this->otherCallbacks.push_back(this->nGateCallback); + this->nGateCallback++; } - void Y(Qubit) override {} Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override { - assert(this->nextMeasureResult < this->mockMeasurements.size() - && "ConditionalsTestSimulator isn't set up correctly"); + assert( + this->nextMeasureResult < this->mockMeasurements.size() && + "ConditionalsTestSimulator isn't set up correctly"); - return (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); + Result r = (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); this->nextMeasureResult++; + return r; } bool AreEqualResults(Result r1, Result r2) override @@ -83,20 +122,26 @@ TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__body()); - CHECK(qapi->cX == 8); - CHECK(qapi->ccX == 0); + + INFO(qapi->GetHistory()); + CHECK(qapi->xCallbacks.size() == 8); + CHECK(qapi->cxCallbacks.size() == 0); + CHECK(qapi->otherCallbacks.size() == 0); } extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__body(); // NOLINT -TEST_CASE("QIR: ApplyIf with functors", "[qir][qir.conditionals][skip]") +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*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__body()); - CHECK(qapi->cX == 5); - CHECK(qapi->ccX == 7); + + INFO(qapi->GetHistory()); + CHECK(qapi->xCallbacks.size() == 5); + CHECK(qapi->cxCallbacks.size() == 7); + CHECK(qapi->otherCallbacks.size() == 0); } extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body(); // NOLINT @@ -107,6 +152,9 @@ TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__body()); - CHECK(qapi->cX == 4); - CHECK(qapi->ccX == 2); + + INFO(qapi->GetHistory()); + CHECK(qapi->xCallbacks.size() == 4); + CHECK(qapi->cxCallbacks.size() == 2); + CHECK(qapi->otherCallbacks.size() == 0); } diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs index e86a30e42ee..28a1b755a64 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs @@ -8,9 +8,9 @@ namespace Microsoft.Quantum.Testing.QIR { use q1 = Qubit(); use q2 = Qubit(); - let r1 = M(q1); + let r1 = M(q1); // expected: r1 = Zero X(q2); - let r2 = M(q2); + let r2 = M(q2); // expected: r2 = One ApplyIfElseR(r1, (X, q1), (Y, q1)); ApplyIfElseR(r2, (Y, q1), (X, q1)); From b5fd2dd76faf0642f5564439346b5a4f281f786e Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Wed, 24 Feb 2021 16:14:05 -0800 Subject: [PATCH 5/5] Update to reflect changes in ApplyIf* consolidation on the Q# side --- src/QirRuntime/lib/QIR/bridge-qis.ll | 219 +----------------------- src/QirRuntime/lib/QIR/conditionals.cpp | 92 +--------- src/QirRuntime/lib/QIR/quantum__qis.hpp | 32 +--- 3 files changed, 15 insertions(+), 328 deletions(-) diff --git a/src/QirRuntime/lib/QIR/bridge-qis.ll b/src/QirRuntime/lib/QIR/bridge-qis.ll index e9896b30c3b..e841bb18dd2 100644 --- a/src/QirRuntime/lib/QIR/bridge-qis.ll +++ b/src/QirRuntime/lib/QIR/bridge-qis.ll @@ -441,238 +441,31 @@ define i64 @__quantum__qis__drawrandomint__body(i64 %min, i64 %max) { ;=============================================================================== ; quantum.qis conditional functions ; -declare void @quantum__qis__applyifelseintrinsicca__body(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) -declare void @quantum__qis__applyifelseintrinsicca__adj(%class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) -declare void @quantum__qis__applyifelseintrinsicca__ctl( - %struct.QirArray*, %class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) -declare void @quantum__qis__applyifelseintrinsicca__ctladj( - %struct.QirArray*, %class.RESULT*, %struct.QirCallable*, %struct.QirCallable*) - -declare void @quantum__qis__applyconditionallyintrinsicca__body( +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*) -declare void @quantum__qis__applyconditionallyintrinsicca__adj( - %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) -declare void @quantum__qis__applyconditionallyintrinsicca__ctl( - %struct.QirArray*, %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) -declare void @quantum__qis__applyconditionallyintrinsicca__ctladj( - %struct.QirArray*, %struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*) - -; applyif - -define void @__quantum__qis__applyifelseintrinsicca__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - - %r = bitcast %Result* %.r to %class.RESULT* - %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* - %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* - call void @quantum__qis__applyifelseintrinsicca__body( - %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) - ret void -} -define void @__quantum__qis__applyifelseintrinsicca__ctl(%Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) { - %.pr = getelementptr inbounds - {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 0 - %.r = load %Result*, %Result** %.pr - %.pclb_on_zero = getelementptr inbounds - {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 1 - %.clb_on_zero = load %Callable*, %Callable** %.pclb_on_zero - - %.pclb_on_one = getelementptr inbounds - {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 2 - %.clb_on_one = load %Callable*, %Callable** %.pclb_on_one - - %ctls = bitcast %Array* %.ctls to %struct.QirArray* - %r = bitcast %Result* %.r to %class.RESULT* - %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* - %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* - call void @quantum__qis__applyifelseintrinsicca__ctl( - %struct.QirArray* %ctls, %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) - - ret void -} -define void @__quantum__qis__applyifelseintrinsicca__adj( +define void @__quantum__qis__applyifelseintrinsic__body( %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { %r = bitcast %Result* %.r to %class.RESULT* %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* - call void @quantum__qis__applyifelseintrinsicca__adj( + call void @quantum__qis__applyifelseintrinsic__body( %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) ret void } -define void @__quantum__qis__applyifelseintrinsicca__ctladj( - %Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) { - - %.pr = getelementptr inbounds - {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 0 - %.r = load %Result*, %Result** %.pr - - %.pclb_on_zero = getelementptr inbounds - {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 1 - %.clb_on_zero = load %Callable*, %Callable** %.pclb_on_zero - - %.pclb_on_one = getelementptr inbounds - {%Result*, %Callable*, %Callable*}, {%Result*, %Callable*, %Callable*}* %.args, i32 0, i32 2 - %.clb_on_one = load %Callable*, %Callable** %.pclb_on_one - - %ctls = bitcast %Array* %.ctls to %struct.QirArray* - %r = bitcast %Result* %.r to %class.RESULT* - %clb_on_zero = bitcast %Callable* %.clb_on_zero to %struct.QirCallable* - %clb_on_one = bitcast %Callable* %.clb_on_one to %struct.QirCallable* - call void @quantum__qis__applyifelseintrinsicca__ctladj( - %struct.QirArray* %ctls, %class.RESULT* %r, %struct.QirCallable* %clb_on_zero, %struct.QirCallable* %clb_on_one) - - ret void -} - -define void @__quantum__qis__applyifelseintrinsic__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsicca__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) - ret void -} -define void @__quantum__qis__applyifelseintrinsica__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsicca__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) - ret void -} -define void @__quantum__qis__applyifelseintrinsica__adj( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsicca__adj( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) - ret void -} - -define void @__quantum__qis__applyifelseintrinsicc__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) { - call void @__quantum__qis__applyifelseintrinsicca__body( - %Result* %.r, %Callable* %.clb_on_zero, %Callable* %.clb_on_one) - ret void -} -define void @__quantum__qis__applyifelseintrinsicc__ctl(%Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) { - call void @__quantum__qis__applyifelseintrinsicca__ctl(%Array* %.ctls, { %Result*, %Callable*, %Callable* }* %.args) - ret void -} - -; applyconditionally -define void @__quantum__qis__applyconditionallyintrinsicca__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { - - %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* - %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* - %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* - %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* - call void @quantum__qis__applyconditionallyintrinsicca__body( - %struct.QirArray* %rs1, %struct.QirArray* %rs2, - %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) - ret void -} -define void @__quantum__qis__applyconditionallyintrinsicca__ctl( - %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) { - - %.prs1 = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 0 - %.rs1 = load %Array*, %Array** %.prs1 - - %.prs2 = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 1 - %.rs2 = load %Array*, %Array** %.prs2 - - %.pclb_on_equal = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 2 - %.clb_on_equal = load %Callable*, %Callable** %.pclb_on_equal - - %.pclb_on_different = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 3 - %.clb_on_different = load %Callable*, %Callable** %.pclb_on_different - - %ctls = bitcast %Array* %.ctls to %struct.QirArray* - %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* - %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* - %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* - %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* - call void @quantum__qis__applyconditionallyintrinsicca__ctl( - %struct.QirArray* %ctls, - %struct.QirArray* %rs1, %struct.QirArray* %rs2, - %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) - ret void -} -define void @__quantum__qis__applyconditionallyintrinsicca__adj( +define void @__quantum__qis__applyconditionallyintrinsic__body( %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* - call void @quantum__qis__applyconditionallyintrinsicca__adj( + call void @quantum__qis__applyconditionallyintrinsic__body( %struct.QirArray* %rs1, %struct.QirArray* %rs2, %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) ret void } -define void @__quantum__qis__applyconditionallyintrinsicca__ctladj( - %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) { - - %.prs1 = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 0 - %.rs1 = load %Array*, %Array** %.prs1 - - %.prs2 = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 1 - %.rs2 = load %Array*, %Array** %.prs2 - - %.pclb_on_equal = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 2 - %.clb_on_equal = load %Callable*, %Callable** %.pclb_on_equal - - %.pclb_on_different = getelementptr inbounds - {%Array*, %Array*, %Callable*, %Callable*}, {%Array*, %Array*, %Callable*, %Callable*}* %.args, i32 0, i32 3 - %.clb_on_different = load %Callable*, %Callable** %.pclb_on_different - - %ctls = bitcast %Array* %.ctls to %struct.QirArray* - %rs1 = bitcast %Array* %.rs1 to %struct.QirArray* - %rs2 = bitcast %Array* %.rs2 to %struct.QirArray* - %clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable* - %clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable* - call void @quantum__qis__applyconditionallyintrinsicca__ctladj( - %struct.QirArray* %ctls, - %struct.QirArray* %rs1, %struct.QirArray* %rs2, - %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) - ret void -} - -define void @__quantum__qis__applyconditionallyintrinsic__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { - call void @__quantum__qis__applyconditionallyintrinsicca__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) - ret void -} - -define void @__quantum__qis__applyconditionallyintrinsica__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { - call void @__quantum__qis__applyconditionallyintrinsicca__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) - ret void -} -define void @__quantum__qis__applyconditionallyintrinsica__adj( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { - call void @__quantum__qis__applyconditionallyintrinsicca__adj( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) - ret void -} - -define void @__quantum__qis__applyconditionallyintrinsicc__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) { - call void @__quantum__qis__applyconditionallyintrinsicca__body( - %Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) - ret void -} -define void @__quantum__qis__applyconditionallyintrinsicc__ctl( - %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) { - call void @__quantum__qis__applyconditionallyintrinsicca__ctl( - %Array* %.ctls, { %Array*, %Array*, %Callable*, %Callable* }* %.args) - ret void -} diff --git a/src/QirRuntime/lib/QIR/conditionals.cpp b/src/QirRuntime/lib/QIR/conditionals.cpp index 7b88b458cf0..72c46e21db6 100644 --- a/src/QirRuntime/lib/QIR/conditionals.cpp +++ b/src/QirRuntime/lib/QIR/conditionals.cpp @@ -9,30 +9,10 @@ #include "QirTypes.hpp" #include "quantum__rt.hpp" -static void ApplyWithFunctor(bool isControlled, bool isAdjoint, QirArray* ctls, QirCallable* clb) +static void Apply(QirCallable* clb) { - auto tupleSize = isControlled ? sizeof(void*) : 0; - PTuple argsTuple = quantum__rt__tuple_create(tupleSize); - - QirCallable* clbFunc = - (isAdjoint || isControlled) ? quantum__rt__callable_copy(clb, true /*force new instance*/) : clb; - - if (isAdjoint) - { - quantum__rt__callable_make_adjoint(clbFunc); - } - if (isControlled) - { - quantum__rt__callable_make_controlled(clbFunc); - *reinterpret_cast(argsTuple) = ctls; - } - - quantum__rt__callable_invoke(clbFunc, argsTuple /*args*/, nullptr /*result*/); - - if (clb != clbFunc) - { - quantum__rt__callable_update_reference_count(clbFunc, -1); - } + PTuple argsTuple = quantum__rt__tuple_create(0); + quantum__rt__callable_invoke(clb, argsTuple /*args*/, nullptr /*result*/); quantum__rt__tuple_update_reference_count(argsTuple, -1); } @@ -56,77 +36,19 @@ static bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) extern "C" { - void quantum__qis__applyifelseintrinsicca__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) + void quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) { QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; - ApplyWithFunctor(false /*C*/, false /*A*/, nullptr, clbApply); - } - - void quantum__qis__applyifelseintrinsicca__adj(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) - { - QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; - ApplyWithFunctor(false /*C*/, true /*A*/, nullptr, clbApply); - } - - void quantum__qis__applyifelseintrinsicca__ctl( - QirArray* ctls, - RESULT* r, - QirCallable* clbOnZero, - QirCallable* clbOnOne) - { - QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; - ApplyWithFunctor(true /*C*/, false /*A*/, ctls, clbApply); - } - - void quantum__qis__applyifelseintrinsicca__ctladj( - QirArray* ctls, - RESULT* r, - QirCallable* clbOnZero, - QirCallable* clbOnOne) - { - QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; - ApplyWithFunctor(true /*C*/, true /*A*/, ctls, clbApply); - } - - void quantum__qis__applyconditionallyintrinsicca__body( - QirArray* rs1, - QirArray* rs2, - QirCallable* clbOnAllEqual, - QirCallable* clbOnSomeDifferent) - { - QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; - ApplyWithFunctor(false /*C*/, false /*A*/, nullptr, clbApply); - } - - void quantum__qis__applyconditionallyintrinsicca__adj( - QirArray* rs1, - QirArray* rs2, - QirCallable* clbOnAllEqual, - QirCallable* clbOnSomeDifferent) - { - QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; - ApplyWithFunctor(false /*C*/, true /*A*/, nullptr, clbApply); - } - - void quantum__qis__applyconditionallyintrinsicca__ctl( - QirArray* ctls, - QirArray* rs1, - QirArray* rs2, - QirCallable* clbOnAllEqual, - QirCallable* clbOnSomeDifferent) - { - QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; - ApplyWithFunctor(true /*C*/, false /*A*/, ctls, clbApply); + Apply(clbApply); } - void quantum__qis__applyconditionallyintrinsicca__ctladj( - QirArray* ctls, + void quantum__qis__applyconditionallyintrinsic__body( QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, QirCallable* clbOnSomeDifferent) { QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; - ApplyWithFunctor(true /*C*/, true /*A*/, ctls, clbApply); + Apply(clbApply); } } \ No newline at end of file diff --git a/src/QirRuntime/lib/QIR/quantum__qis.hpp b/src/QirRuntime/lib/QIR/quantum__qis.hpp index ba560aeab96..c7f5a108ea8 100644 --- a/src/QirRuntime/lib/QIR/quantum__qis.hpp +++ b/src/QirRuntime/lib/QIR/quantum__qis.hpp @@ -77,36 +77,8 @@ extern "C" QIR_SHARED_API int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT // Q# ApplyIf: - QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__body(RESULT*, QirCallable*, QirCallable*); // NOLINT - QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__adj(RESULT*, QirCallable*, QirCallable*); // NOLINT - QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__ctl( // NOLINT - QirArray*, - RESULT*, - QirCallable*, - QirCallable*); - QIR_SHARED_API void quantum__qis__applyifelseintrinsicca__ctladj( // NOLINT - QirArray*, - RESULT*, - QirCallable*, - QirCallable*); - QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__body( // NOLINT - QirArray*, - QirArray*, - QirCallable*, - QirCallable*); - QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__adj( // NOLINT - QirArray*, - QirArray*, - QirCallable*, - QirCallable*); - QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__ctl( // NOLINT - QirArray*, - QirArray*, - QirArray*, - QirCallable*, - QirCallable*); - QIR_SHARED_API void quantum__qis__applyconditionallyintrinsicca__ctladj( // NOLINT - QirArray*, + QIR_SHARED_API void quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsic__body( // NOLINT QirArray*, QirArray*, QirCallable*,