Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Added some math funcs implementation #500

Merged
merged 9 commits into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/QirRuntime/lib/QIR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ add_dependencies(qir-rt ${bridge_rt_target})
#
set(qis_sup_source_files
"intrinsics.cpp"
"intrinsicsMath.cpp"
)

add_library(qir-qis-support ${qis_sup_source_files})
Expand Down
57 changes: 57 additions & 0 deletions src/QirRuntime/lib/QIR/bridge-qis.ll
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,60 @@ define void @__quantum__qis__z__ctl(%Array* %.ctls, %Qubit* %.q) {
}


;===============================================================================
; quantum.qis math functions
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
;

; LLVM intrinsics (https://llvm.org/docs/LangRef.html):
declare double @llvm.sqrt.f64(double %.val)
declare double @llvm.log.f64(double %Val)

; Native implementations:
declare i1 @quantum__qis__isnan__body(double %d)
declare double @quantum__qis__infinity__body()
declare i1 @quantum__qis__isinf__body(double %d)
declare double @quantum__qis__arctan2__body(double %y, double %x)

; API for the user code:
define double @__quantum__qis__nan__body() { ; Q#: function NAN() : Double http://www.cplusplus.com/reference/cmath/nan-function/
%result = call double @llvm.sqrt.f64(double -1.0) ; sqrt(<negative>) -> NaN
ret double %result
}

define i1 @__quantum__qis__isnan__body(double %d) { ; http://www.cplusplus.com/reference/cmath/isnan/
%result = call i1 @quantum__qis__isnan__body(double %d)
ret i1 %result
}

define double @__quantum__qis__infinity__body() { ; https://en.cppreference.com/w/c/numeric/math/INFINITY
%result = call double @quantum__qis__infinity__body()
ret double %result
}

define i1 @__quantum__qis__isinf__body(double %d) { ; https://en.cppreference.com/w/cpp/numeric/math/isinf
%result = call i1 @quantum__qis__isinf__body(double %d)
ret i1 %result
}

define double @__quantum__qis__sqrt__body(double %d) { ; https://en.cppreference.com/w/cpp/numeric/math/sqrt
%result = call double @llvm.sqrt.f64(double %d)
ret double %result
}

define double @__quantum__qis__log__body(double %d) { ; https://en.cppreference.com/w/cpp/numeric/math/log
%result = call double @llvm.log.f64(double %d)
ret double %result
}

define i1 @__quantum__qis__isnegativeinfinity__body(double %d) { ; Q#: function IsNegativeInfinity(d : Double) : Bool
; https://en.cppreference.com/w/cpp/numeric/math/log https://llvm.org/docs/LangRef.html#llvm-log-intrinsic
%negInf = call double @llvm.log.f64(double 0.0) ; ln(0) -> (-infinity)
%result = fcmp oeq double %negInf, %d ; %result = (%negInf == %d)
ret i1 %result
}

define double @__quantum__qis__arctan2__body(double %y, double %x) { ; Q#: function ArcTan2 (y : Double, x : Double) : Double
; https://en.cppreference.com/w/cpp/numeric/math/atan2
%result = call double @quantum__qis__arctan2__body(double %y, double %x)
ret double %result
}
31 changes: 31 additions & 0 deletions src/QirRuntime/lib/QIR/intrinsicsMath.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include <cmath>
#include "quantum__qis.hpp"

extern "C"
{

// Implementations:
bool quantum__qis__isnan__body(double d)
{
return std::isnan(d); // https://en.cppreference.com/w/cpp/numeric/math/isnan
}

double quantum__qis__infinity__body()
{
return INFINITY; // https://en.cppreference.com/w/c/numeric/math/INFINITY
}

bool quantum__qis__isinf__body(double d)
{
return std::isinf(d); // https://en.cppreference.com/w/cpp/numeric/math/isinf
}

double quantum__qis__arctan2__body(double y, double x)
{
return std::atan2(y, x); // https://en.cppreference.com/w/cpp/numeric/math/atan2
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
}

} // extern "C"
6 changes: 6 additions & 0 deletions src/QirRuntime/lib/QIR/quantum__qis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,10 @@ extern "C"
QIR_SHARED_API void quantum__qis__y__ctl(QirArray*, QUBIT*); // NOLINT
QIR_SHARED_API void quantum__qis__z__body(QUBIT*); // NOLINT
QIR_SHARED_API void quantum__qis__z__ctl(QirArray*, QUBIT*); // 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

}
4 changes: 3 additions & 1 deletion src/QirRuntime/test/QIR-static/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ add_custom_target(qir_static_test_lib DEPENDS ${QIR_TESTS_LIBS})
# The executable target for QIR tests triggers the custom actions to compile ll files
#
add_executable(qir-static-tests
qir-driver.cpp)
qir-driver.cpp
qir-test-math.cpp
)

target_link_libraries(qir-static-tests PUBLIC
${QIR_TESTS_LIBS}
Expand Down
37 changes: 37 additions & 0 deletions src/QirRuntime/test/QIR-static/compiler/Constants.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// these are all the static methods and const fields form System.Math class of .NET CLR
// that are not exposed as language operators and are relevant within type System.
// If there are two versions of the function for Int and Double types, the corresponding
// functions have suffix I or D. ExpD also has a suffix to avoid name clash with Primitives.Exp.

namespace Microsoft.Quantum.Math {
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved

/// # Summary
/// Returns the natural logarithmic base to double-precision.
///
/// # Output
/// A double-precision approximation of the natural logarithic base,
/// $e \approx 2.7182818284590452354$.
///
/// # See Also
/// - Microsoft.Quantum.Math.PI
function E() : Double {
return 2.7182818284590452354;
}

/// # Summary
/// Represents the ratio of the circumference of a circle to its diameter.
///
/// # Ouptut
/// A double-precision approximation of the the circumference of a circle
/// to its diameter, $\pi \approx 3.14159265358979323846$.
///
/// # See Also
/// - Microsoft.Quantum.Math.E
function PI() : Double {
return 3.14159265358979323846;
}

}
41 changes: 41 additions & 0 deletions src/QirRuntime/test/QIR-static/compiler/QirTarget.qs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,47 @@ namespace Microsoft.Quantum.Intrinsic {

open Microsoft.Quantum.Targeting;

@Inline()
function NAN() : Double {
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
body intrinsic;
}

@Inline()
function IsNan(d: Double) : Bool {
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
body intrinsic;
}

@Inline()
function INFINITY() : Double {
body intrinsic;
}

@Inline()
function IsInf(d: Double) : Bool {
body intrinsic;
}

@Inline()
function IsNegativeInfinity(d : Double) : Bool {
body intrinsic;
}

@Inline()
function Sqrt(d : Double) : Double {
body intrinsic;
}

@Inline()
function Log(d : Double) : Double {
body intrinsic;
}

@Inline()
function ArcTan2(y : Double, x : Double) : Double {
body intrinsic;
}


operation X(qb : Qubit) : Unit
is Adj + Ctl {
body intrinsic;
Expand Down
2 changes: 1 addition & 1 deletion src/QirRuntime/test/QIR-static/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def log(message):
output_file = file_name

command = (qsc + " build --qir s --build-exe --input " + files_to_process +
" compiler\\qircore.qs compiler\\qirtarget.qs --proj " + output_file)
" compiler\\qircore.qs compiler\\qirtarget.qs compiler\\Constants.qs --proj " + output_file)
log("Executing: " + command)
subprocess.run(command, shell = True)

12 changes: 6 additions & 6 deletions src/QirRuntime/test/QIR-static/qir-driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays( // NOLINT
int64_t index,
int64_t val,
bool dummy);
TEST_CASE("QIR: Using 1D arrays", "[qir]")
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]")
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
{
// re-enable tracking when https://github.com/microsoft/qsharp-compiler/issues/844 is fixed
QirContextScope qirctx(nullptr, false /*trackAllocatedObjects*/);
Expand Down Expand Up @@ -152,7 +152,7 @@ struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub
return reinterpret_cast<Result>(1);
}
};
TEST_CASE("QIR: allocating and releasing qubits and results", "[qir]")
TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][qir.result]")
{
unique_ptr<QubitsResultsTestSimulator> sim = make_unique<QubitsResultsTestSimulator>();
QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/);
Expand Down Expand Up @@ -182,7 +182,7 @@ TEST_CASE("QIR: allocating and releasing qubits and results", "[qir]")
// that is written to the original array at [1,1,1] and then retrieved from [1,1].
// Thus, all three dimensions must be at least 2.
extern "C" int64_t TestMultidimArrays(char value, int64_t dim0, int64_t dim1, int64_t dim2);
TEST_CASE("QIR: multidimensional arrays", "[qir]")
TEST_CASE("QIR: multidimensional arrays", "[qir][qir.arrMultid]")
{
QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/);

Expand All @@ -195,7 +195,7 @@ TEST_CASE("QIR: multidimensional arrays", "[qir]")

// Manually authored QIR to test dumping range [0..2..6] into string and then raising a failure with it
extern "C" void TestFailWithRangeString(int64_t start, int64_t step, int64_t end);
TEST_CASE("QIR: Report range in a failure message", "[qir]")
TEST_CASE("QIR: Report range in a failure message", "[qir][qir.range]")
{
QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/);

Expand All @@ -215,7 +215,7 @@ TEST_CASE("QIR: Report range in a failure message", "[qir]")
#if 0 // TODO: Q# compiler crashes generating QIR for TestPartials
// TestPartials subtracts the second argument from the first and returns the result.
extern "C" int64_t Microsoft__Quantum__Testing__QIR__TestPartials__body(int64_t, int64_t); // NOLINT
TEST_CASE("QIR: Partial application of a callable", "[qir]")
TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]")
{
QirContextScope qirctx(nullptr, true /*trackAllocatedObjects*/);

Expand Down Expand Up @@ -309,7 +309,7 @@ extern "C" void __quantum__qis__k__ctl(QirArray* controls, Qubit q) // NOLINT
{
g_ctrqapi->ControlledX(controls->count, reinterpret_cast<Qubit*>(controls->buffer), q);
}
TEST_CASE("QIR: application of nested controlled functor", "[qir]")
TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]")
{
unique_ptr<FunctorsTestSimulator> qapi = make_unique<FunctorsTestSimulator>();
QirContextScope qirctx(qapi.get(), true /*trackAllocatedObjects*/);
Expand Down
10 changes: 8 additions & 2 deletions src/QirRuntime/test/QIR-static/qir-test-arrays.qs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace Microsoft.Quantum.Testing.QIR
{
open Microsoft.Quantum.Testing.QIR.Math;

@EntryPoint()
operation Test_Arrays(array : Int[], index : Int, val : Int, dummy : Bool) : Int
{
Expand Down Expand Up @@ -33,9 +35,13 @@ namespace Microsoft.Quantum.Testing.QIR
if (dummy)
{
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
let res1 = TestControlled();
//Q# compiler crashes if both TestControlled and TestPartials are enabled
//let res2 = TestPartials(17, 42);
let res2 = TestPartials(17, 42);
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
let res3 = Test_Qubit_Result_Management();

// Math tests:
let res4 = SqrtTest();
let res5 = LogTest();
let res6 = ArcTan2Test();
}

return sum;
Expand Down
25 changes: 25 additions & 0 deletions src/QirRuntime/test/QIR-static/qir-test-math.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include <cstdint>

#include "catch.hpp"

extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__SqrtTest__body(); // NOLINT
extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__LogTest__body(); // NOLINT
extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__ArcTan2Test__body(); // NOLINT

TEST_CASE("QIR: Math.Sqrt", "[qir.math][qir.Math.Sqrt]")
{
REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Math__SqrtTest__body());
}

TEST_CASE("QIR: Math.Log", "[qir.math][qir.Math.Log]")
{
REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Math__LogTest__body());
}

TEST_CASE("QIR: Math.ArcTan2", "[qir.math][qir.Math.ArcTan2]")
{
REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Math__ArcTan2Test__body());
}

64 changes: 64 additions & 0 deletions src/QirRuntime/test/QIR-static/qir-test-math.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Testing.QIR.Math
{
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math; // E()

function SqrtTest() : Int {
if( 2.0 != Sqrt( 4.0) ) { return 1; }
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
if( 3.0 != Sqrt( 9.0) ) { return 2; }
if(10.0 != Sqrt(100.0) ) { return 3; }

if( not IsNan(Sqrt(-5.0))) { return 4; }
if( not IsNan(Sqrt(NAN()))) { return 5; }
if( not IsInf(Sqrt(INFINITY()))) { return 6; }

return 0;
}

function LogTest() : Int {
if( 1.0 != Log(E()) ) { return 1; } // ln(e) -> 1
if( 2.0 != Log(E() * E()) ) { return 2; } // ln(e^2) -> 2

if( not IsNegativeInfinity(Log(0.0))) { return 3; } // ln(0) -> (-nfinity)
if( not IsNan(Log(-5.0))) { return 4; } // ln(<negaive>) -> NaN
if( not IsNan(Log(NAN()))) { return 5; } // ln(NaN) -> NaN
if( not IsInf(Log(INFINITY()))) { return 6; } // ln(+infinity) -> +infinity

return 0;
}

function ArcTan2Test() : Int {

// function ArcTan2(y : Double, x : Double) : Double

if( 0.0 != ArcTan2( 0.0, 1.0 ) ) { return 1; }
if( PI() != ArcTan2( 0.0, -1.0 ) ) { return 2; }
if( PI()/2.0 != ArcTan2( 1.0, 0.0 ) ) { return 3; }
if( -PI()/2.0 != ArcTan2(-1.0, 0.0 ) ) { return 4; }

if( PI()/4.0 != ArcTan2( 1.0, 1.0 ) ) { return 5; }
if( PI()*3.0/4.0 != ArcTan2( 1.0, -1.0 ) ) { return 6; }
if( -PI()*3.0/4.0 != ArcTan2(-1.0, -1.0 ) ) { return 7; }
if( -PI()/4.0 != ArcTan2(-1.0, 1.0 ) ) { return 8; }

if( 0.0 != ArcTan2( 0.0, 0.0 ) ) { return 9; }

// Fails because of lack of precision:
// if( PI()/6.0 != ArcTan2( 1.0, Sqrt(3.0) ) ) { return 10; } // tg(Pi/6) = sin(Pi/6) / cos(Pi/6) = (1/2) / (Sqrt(3)/2) = 1/Sqrt(3) = y/x. ArcTan2(1.0, Sqrt(3)) -> Pi/6

if( not IsNan(ArcTan2( NAN(), 0.0) )) { return 11; }
if( not IsNan(ArcTan2( 0.0, NAN()) )) { return 12; }
if( not IsNan(ArcTan2( NAN(), NAN()) )) { return 13; }

// The infinity cases show discrepancy between
// https://docs.microsoft.com/en-us/dotnet/api/system.math.atan2?view=net-5.0
kuzminrobin marked this conversation as resolved.
Show resolved Hide resolved
// and https://en.cppreference.com/w/cpp/numeric/math/atan2 .

return 0;
}

}

Loading