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..e841bb18dd2 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,35 @@ 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 @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 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__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__applyconditionallyintrinsic__body( + %struct.QirArray* %rs1, %struct.QirArray* %rs2, + %struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different) + ret void +} + diff --git a/src/QirRuntime/lib/QIR/conditionals.cpp b/src/QirRuntime/lib/QIR/conditionals.cpp new file mode 100644 index 00000000000..72c46e21db6 --- /dev/null +++ b/src/QirRuntime/lib/QIR/conditionals.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include + +#include "quantum__qis.hpp" + +#include "QirTypes.hpp" +#include "quantum__rt.hpp" + +static void Apply(QirCallable* clb) +{ + PTuple argsTuple = quantum__rt__tuple_create(0); + quantum__rt__callable_invoke(clb, argsTuple /*args*/, nullptr /*result*/); + 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 quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) + { + QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne; + Apply(clbApply); + } + + void quantum__qis__applyconditionallyintrinsic__body( + QirArray* rs1, + QirArray* rs2, + QirCallable* clbOnAllEqual, + QirCallable* clbOnSomeDifferent) + { + QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; + Apply(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..c7f5a108ea8 100644 --- a/src/QirRuntime/lib/QIR/quantum__qis.hpp +++ b/src/QirRuntime/lib/QIR/quantum__qis.hpp @@ -60,20 +60,27 @@ 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 - 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 quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT + QIR_SHARED_API void quantum__qis__applyconditionallyintrinsic__body( // NOLINT + 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/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 3015db659bb..c9157c42fcf 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,13 @@ 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; } 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..556f63d12d6 --- /dev/null +++ b/src/QirRuntime/test/QIR-static/qir-test-conditionals.cpp @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include +#include +#include +#include + +#include "catch.hpp" + +#include "CoreTypes.hpp" +#include "QirContext.hpp" +#include "QirTypes.hpp" +#include "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 nGateCallback = 0; + vector xCallbacks; + vector cxCallbacks; + vector otherCallbacks; + + vector mockMeasurements; + int nextMeasureResult = 0; + + explicit ConditionalsTestSimulator(vector&& results) + : mockMeasurements(results) + { + } + + std::string GetHistory() + { + std::stringstream out; + out << "X: "; + for (int i : this->xCallbacks) + { + out << i << ","; + } + + out << std::endl << "CX: "; + for (int i : this->cxCallbacks) + { + out << i << ","; + } + + out << std::endl << "Other: "; + for (int i : this->otherCallbacks) + { + out << i << ","; + } + return out.str(); + } + + Qubit AllocateQubit() override + { + return nullptr; + } + void ReleaseQubit(Qubit qubit) override {} + + void X(Qubit) override + { + this->xCallbacks.push_back(this->nGateCallback); + this->nGateCallback++; + } + void ControlledX(long numControls, Qubit controls[], Qubit qubit) override + { + 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++; + } + + Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + { + assert( + this->nextMeasureResult < this->mockMeasurements.size() && + "ConditionalsTestSimulator isn't set up correctly"); + + Result r = (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); + this->nextMeasureResult++; + return r; + } + + bool AreEqualResults(Result r1, Result r2) override + { + // those are bogus pointers but it's ok to compare them _as pointers_ + return (r1 == r2); + } + + void ReleaseResult(Result result) override {} // the results aren't allocated by this test simulator + + Result UseZero() override + { + return reinterpret_cast(0); + } + + Result UseOne() override + { + return reinterpret_cast(1); + } +}; + +extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIf__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()); + + 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]") +{ + unique_ptr qapi = + make_unique(vector{Result_Zero, Result_One}); + QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/); + + CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__body()); + + 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 +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()); + + 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/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/qir-gen.csproj b/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj index cafd2d3365a..dc0df356f9f 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-gen.csproj @@ -3,6 +3,7 @@ Exe netcoreapp3.1 + false True false false 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..58ced577cd5 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 res1_1 = TestFunctors(); + let res1_2 = TestFunctorsNoArgs(); let res2 = TestPartials(17, 42); TestQubitResultManagement(); @@ -53,6 +54,11 @@ namespace Microsoft.Quantum.Testing.QIR { let res17 = ArcCosTest(); let res18 = ArcTanTest(); MessageTest("Test"); + + // Conditionals: + TestApplyIf(); + TestApplyIfWithFunctors(); + TestApplyConditionally(); } 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..28a1b755a64 --- /dev/null +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-conditionals.qs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace Microsoft.Quantum.Testing.QIR { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Simulation.QuantumProcessor.Extensions; + + operation TestApplyIf() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + let r1 = M(q1); // expected: r1 = Zero + X(q2); + let r2 = M(q2); // expected: r2 = One + + ApplyIfElseR(r1, (X, q1), (Y, q1)); + ApplyIfElseR(r2, (Y, q1), (X, q1)); + + // Other variants + ApplyIfElseRA(r1, (X, q1), (Y, q1)); + ApplyIfElseRC(r1, (X, q1), (Y, q1)); + ApplyIfElseRCA(r1, (X, q1), (Y, q1)); + ApplyIfOne(r2, (X, q1)); + ApplyIfZero(r1, (X, q1)); + } + + operation TestApplyIfWithFunctors() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + let r1 = M(q1); + X(q2); + let r2 = M(q2); + + Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); + Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); + Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); + Controlled ApplyIfElseRC([q2], (r1, (X, q1), (Y, q1))); + Adjoint ApplyIfOneA(r2, (X, q1)); + Controlled ApplyIfOneC([q2], (r2, (X, q1))); + Adjoint Controlled ApplyIfOneCA([q2], (r2, (X, q1))); + Adjoint ApplyIfZeroA(r1, (X, q1)); + Controlled ApplyIfZeroC([q2], (r1, (X, q1))); + Adjoint Controlled ApplyIfZeroCA([q2], (r1, (X, q1))); + } + + operation TestApplyConditionally() : Unit { + use q1 = Qubit(); + use q2 = Qubit(); + + let r1 = M(q1); + X(q2); + let r2 = M(q2); + + ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); + ApplyConditionally([r1, One], [Zero, r2], (X, q1), (Y, q1)); + + Adjoint ApplyConditionallyA([r1], [r2], (Y, q1), (X, q1)); + Controlled ApplyConditionallyC([q2], ([r1], [r2], (Y, q1), (X, q1))); + Adjoint Controlled ApplyConditionallyCA([q2], ([r1], [r2], (Y, q1), (X, q1))); + } + +} \ 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