diff --git a/src/QirRuntime/lib/QIR/callables.cpp b/src/QirRuntime/lib/QIR/callables.cpp index 503ae8c1bf6..f63bc44dc03 100644 --- a/src/QirRuntime/lib/QIR/callables.cpp +++ b/src/QirRuntime/lib/QIR/callables.cpp @@ -181,13 +181,13 @@ int QirTupleHeader::Release() } assert(this->refCount > 0); // doesn't guarantee we catch double releases but better than nothing - --this->refCount; + int retVal = --this->refCount; if (this->refCount == 0) { char* buffer = reinterpret_cast(this); delete[] buffer; } - return this->refCount; + return retVal; } QirTupleHeader* QirTupleHeader::Create(int size) diff --git a/src/QirRuntime/lib/QSharpFoundation/intrinsicsMath.cpp b/src/QirRuntime/lib/QSharpFoundation/intrinsicsMath.cpp index cc22595ebe3..4a0a206d58a 100644 --- a/src/QirRuntime/lib/QSharpFoundation/intrinsicsMath.cpp +++ b/src/QirRuntime/lib/QSharpFoundation/intrinsicsMath.cpp @@ -12,7 +12,8 @@ namespace // Visible in this translation unit only. { extern thread_local bool randomizeSeed; -extern int64_t lastGeneratedRndNum; +extern int64_t lastGeneratedRndI64; +extern double lastGeneratedRndDouble; } // Implementation: @@ -74,7 +75,7 @@ int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum) { if(minimum > maximum) { - quantum__rt__fail(quantum__rt__string_create(Quantum::Qis::Internal::excStrDrawRandomInt)); + quantum__rt__fail(quantum__rt__string_create(Quantum::Qis::Internal::excStrDrawRandomVal)); } // https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution @@ -83,8 +84,30 @@ int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum) ? std::random_device()() : // Default 0); // For test purposes only. - lastGeneratedRndNum = std::uniform_int_distribution(minimum, maximum)(gen); - return lastGeneratedRndNum; + lastGeneratedRndI64 = std::uniform_int_distribution(minimum, maximum)(gen); + return lastGeneratedRndI64; +} + +double quantum__qis__drawrandomdouble__body(double minimum, double maximum) +{ + if(minimum > maximum) + { + quantum__rt__fail(quantum__rt__string_create(Quantum::Qis::Internal::excStrDrawRandomVal)); + } + + // For testing purposes we need separate generators for Int and Double: + // https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution + // https://en.cppreference.com/w/cpp/numeric/random + thread_local static std::mt19937_64 gen(randomizeSeed + ? std::random_device()() : // Default + 0); // For test purposes only. + + // https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution + lastGeneratedRndDouble = std::uniform_real_distribution( + minimum, + std::nextafter(maximum, std::numeric_limits::max())) // "Notes" section. + (gen); + return lastGeneratedRndDouble; } } // extern "C" @@ -92,7 +115,8 @@ int64_t quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum) namespace // Visible in this translation unit only. { thread_local bool randomizeSeed = true; -int64_t lastGeneratedRndNum = 0; +int64_t lastGeneratedRndI64 = 0; +double lastGeneratedRndDouble = 0.0; } // For test purposes only: @@ -102,17 +126,23 @@ namespace Qis { namespace Internal { - char const excStrDrawRandomInt[] = "Invalid Argument: minimum > maximum for DrawRandomInt()"; + char const excStrDrawRandomVal[] = "Invalid Argument: minimum > maximum"; void RandomizeSeed(bool randomize) { randomizeSeed = randomize; } - int64_t GetLastGeneratedRandomNumber() + int64_t GetLastGeneratedRandomI64() { - return lastGeneratedRndNum; + return lastGeneratedRndI64; } + + double GetLastGeneratedRandomDouble() + { + return lastGeneratedRndDouble; + } + } // namespace Internal } // namespace Qis } // namespace Quantum diff --git a/src/QirRuntime/lib/QSharpFoundation/qsharp-foundation-qis.ll b/src/QirRuntime/lib/QSharpFoundation/qsharp-foundation-qis.ll index 71b45b7c692..ac63ad53302 100644 --- a/src/QirRuntime/lib/QSharpFoundation/qsharp-foundation-qis.ll +++ b/src/QirRuntime/lib/QSharpFoundation/qsharp-foundation-qis.ll @@ -31,7 +31,7 @@ %struct.QirString = type opaque %PauliId = type i32 -declare dllimport void @quantum__rt__message(%"struct.QirString"* %str) +declare void @quantum__rt__message(%"struct.QirString"* %str) ;=============================================================================== ; @@ -64,6 +64,7 @@ declare double @quantum__qis__arccos__body(double %theta) declare double @quantum__qis__arctan__body(double %theta) declare double @quantum__qis__ieeeremainder__body(double %y, double %x) declare i64 @quantum__qis__drawrandomint__body(i64 %min, i64 %max) +declare double @quantum__qis__drawrandomdouble__body(double %min, double %max) ; API for the user code: define dllexport double @__quantum__qis__nan__body() { ; Q#: function NAN() : Double http://www.cplusplus.com/reference/cmath/nan-function/ @@ -191,6 +192,15 @@ define dllexport i64 @__quantum__qis__drawrandomint__body(i64 %min, i64 %max) { ret i64 %result } +; operation DrawRandomDouble (min : Double, max : Double) : Double +; https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.random.drawrandomdouble +define double @__quantum__qis__drawrandomdouble__body(double %min, double %max) { + %result = call double @quantum__qis__drawrandomdouble__body(double %min, double %max) + ret double %result +} + + + ;=============================================================================== ; quantum.qis conditional functions ; diff --git a/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation__qis.hpp b/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation__qis.hpp index 5e64f5f0751..aa234b36170 100644 --- a/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation__qis.hpp +++ b/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation__qis.hpp @@ -40,6 +40,7 @@ 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 double quantum__qis__drawrandomdouble__body(double minimum, double maximum); // NOLINT // Q# ApplyIf: QIR_SHARED_API void quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT diff --git a/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation_internal.hpp b/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation_internal.hpp index 6e923191efe..92ecfeb23f3 100644 --- a/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation_internal.hpp +++ b/src/QirRuntime/lib/QSharpFoundation/qsharp__foundation_internal.hpp @@ -13,10 +13,11 @@ namespace Qis { namespace Internal { - extern char const excStrDrawRandomInt[]; + extern char const excStrDrawRandomVal[]; void RandomizeSeed(bool randomize); - int64_t GetLastGeneratedRandomNumber(); + int64_t GetLastGeneratedRandomI64(); + double GetLastGeneratedRandomDouble(); } // namespace Internal } // namespace Qis } // namespace Quantum diff --git a/src/QirRuntime/public/QirTypes.hpp b/src/QirRuntime/public/QirTypes.hpp index 02b1d686f68..4809def635b 100644 --- a/src/QirRuntime/public/QirTypes.hpp +++ b/src/QirRuntime/public/QirTypes.hpp @@ -61,7 +61,7 @@ struct QIR_SHARED_API QirString using PTuple = char*; struct QIR_SHARED_API QirTupleHeader { - int32_t refCount = 0; + int refCount = 0; int32_t aliasCount = 0; // used to enable copy elision, see the QIR specifications for details int32_t tupleSize = 0; // when creating the tuple, must be set to the size of the tuple's data buffer (in bytes) diff --git a/src/QirRuntime/test/QIR-static/qir-test-math.cpp b/src/QirRuntime/test/QIR-static/qir-test-math.cpp index b46b2ed4a1e..b544ce5c6a1 100644 --- a/src/QirRuntime/test/QIR-static/qir-test-math.cpp +++ b/src/QirRuntime/test/QIR-static/qir-test-math.cpp @@ -22,6 +22,7 @@ extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__CoshTest__body(); extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__TanhTest__body(); // NOLINT extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__IeeeRemainderTest__body(); // NOLINT extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__body(int64_t min, int64_t max); // NOLINT +extern "C" double Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__body(double min, double max); // NOLINT TEST_CASE("QIR: Math.Sqrt", "[qir.math][qir.Math.Sqrt]") { @@ -97,7 +98,7 @@ TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") const uint64_t qsRndNum = Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__body(std::numeric_limits::min(), std::numeric_limits::max()); - const uint64_t cppRndNum = Quantum::Qis::Internal::GetLastGeneratedRandomNumber(); // This call must be done + const uint64_t cppRndNum = Quantum::Qis::Internal::GetLastGeneratedRandomI64(); // This call must be done // _after_ the Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__body(). REQUIRE(qsRndNum == cppRndNum); } @@ -112,7 +113,7 @@ TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") } catch (std::runtime_error const& exc) { - REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomInt)); + REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); } // There is a strong difference in the opinions about how the random number generator must be tested. @@ -201,3 +202,31 @@ TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") // } } // TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") + +TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") +{ + // Test that the Q# random number generator is a wrapper around the C++ generator: + size_t times = 1000; + while(--times) + { + const double qsRndNum = + Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__body(std::numeric_limits::min(), + std::numeric_limits::max()); + const double cppRndNum = Quantum::Qis::Internal::GetLastGeneratedRandomDouble(); // This call must be done + // _after_ the Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__body(). + REQUIRE(qsRndNum == cppRndNum); + } + + // Make sure the correct exception is thrown if min > max: + REQUIRE_THROWS_AS(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__body(10.0, 5.0), std::runtime_error); + + // Check the exception string: + try + { + (void)Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__body(10.0, 5.0); + } + catch (std::runtime_error const& exc) + { + REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); + } +} // TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") 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 efe2e706db7..67b966a3cd9 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-arrays.qs @@ -57,6 +57,7 @@ namespace Microsoft.Quantum.Testing.QIR { let res19 = ParityTest(); let res20 = PauliArrayAsIntTest(); let res21 = PauliArrayAsIntFailTest(); + let res22 = TestDrawRandomDouble(0.0, 1.0); MessageTest("Test"); // Conditionals: diff --git a/src/QirRuntime/test/QIR-static/qsharp/qir-test-math.qs b/src/QirRuntime/test/QIR-static/qsharp/qir-test-math.qs index 137718d76cd..1556fe939ba 100644 --- a/src/QirRuntime/test/QIR-static/qsharp/qir-test-math.qs +++ b/src/QirRuntime/test/QIR-static/qsharp/qir-test-math.qs @@ -66,6 +66,10 @@ namespace Microsoft.Quantum.Testing.QIR.Math { return DrawRandomInt(min, max); } + operation TestDrawRandomDouble(min : Double, max : Double) : Double { + return DrawRandomDouble(min, max); + } + function Close(expected : Double, actual : Double) : Bool { let neighbourhood = 0.0000001; // On x86-64 + Win the error is in 16th digit after the decimal point. // E.g. enstead of 0.0 there can be 0.00000000000000012.