Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions cudaq/lib/Frontend/nvqpp/ConvertExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,21 @@ bool QuakeBridgeVisitor::visitMathLibFunc(clang::CallExpr *x,
(funcName == "tan" || funcName == "tanf"))
return floatOperator(math::TanOp{}, "tan");

// Handle std::asin
if ((isInNamespace(func, "std") || isNotInANamespace(func)) &&
(funcName == "asin" || funcName == "asinf"))
return floatOperator(math::AsinOp{}, "asin");

// Handle std::acos
if ((isInNamespace(func, "std") || isNotInANamespace(func)) &&
(funcName == "acos" || funcName == "acosf"))
return floatOperator(math::AcosOp{}, "acos");

// Handle std::atan
if ((isInNamespace(func, "std") || isNotInANamespace(func)) &&
(funcName == "atan" || funcName == "atanf"))
return floatOperator(math::AtanOp{}, "atan");
Comment thread
LakshikkaNathan marked this conversation as resolved.

// Handle std::exp
if ((isInNamespace(func, "std") || isNotInANamespace(func)) &&
(funcName == "exp" || funcName == "expf"))
Expand Down
70 changes: 70 additions & 0 deletions cudaq/test/AST-Quake/math_functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates. *
* All rights reserved. *
* *
* This source code and the accompanying materials are made available under *
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/

// RUN: cudaq-quake %s | cudaq-opt | FileCheck %s

#include <cudaq.h>
#include <cmath>

// Standard math functions are usable inside kernels and lower to the matching
// MLIR math dialect ops. The angle is a runtime parameter so the calls are not
// constant-folded away.

struct MathFunctions {
void operator()(double x) __qpu__ {
cudaq::qubit q;
rx(std::sin(x), q);
rx(std::cos(x), q);
rx(std::tan(x), q);
rx(std::asin(x), q);
rx(std::acos(x), q);
rx(std::atan(x), q);
rx(std::sqrt(x), q);
rx(std::exp(x), q);
rx(std::log(x), q);
}
};

struct MathFunctionsFloat {
void operator()(float x) __qpu__ {
cudaq::qubit q;
rx(sinf(x), q);
rx(cosf(x), q);
rx(tanf(x), q);
rx(asinf(x), q);
rx(acosf(x), q);
rx(atanf(x), q);
rx(sqrtf(x), q);
rx(expf(x), q);
rx(logf(x), q);
}
};

// CHECK-LABEL: func.func @__nvqpp__mlirgen__MathFunctions
// CHECK-DAG: math.sin
// CHECK-DAG: math.cos
// CHECK-DAG: math.tan
// CHECK-DAG: math.asin
// CHECK-DAG: math.acos
// CHECK-DAG: math.atan
// CHECK-DAG: math.sqrt
// CHECK-DAG: math.exp
// CHECK-DAG: math.log
// CHECK: quake.rx

// CHECK-LABEL: func.func @__nvqpp__mlirgen__MathFunctionsFloat
// CHECK-DAG: math.sin
// CHECK-DAG: math.cos
// CHECK-DAG: math.tan
// CHECK-DAG: math.asin
// CHECK-DAG: math.acos
// CHECK-DAG: math.atan
// CHECK-DAG: math.sqrt
// CHECK-DAG: math.exp
// CHECK-DAG: math.log
// CHECK: quake.rx
6 changes: 6 additions & 0 deletions docs/sphinx/specification/cudaq/kernels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ Kernels can be composed of the following:
* Classical control flow constructs from the classical language (:code:`if`, :code:`for`, :code:`while`, etc.)
* Stack variable declarations for supported types.
* Arithmetic operations on integer and floating point stack variables
* Calls to a defined set of standard mathematical functions on floating-point
operands: the real-valued, angle-producing functions :code:`sin`,
:code:`cos`, :code:`tan`, :code:`asin`, :code:`acos`, :code:`atan`,
:code:`sqrt`, :code:`exp`, and :code:`log` (in C++, the corresponding
:code:`std::` functions; in Python, the corresponding :code:`numpy`
functions)
* Coherent conditional execution - :code:`if ( boolExprFromQubitMeasurement ) { x (another_qubit); }`
* Syntax for common quantum programming patterns (e.g. compute-action-uncompute).

Expand Down
45 changes: 44 additions & 1 deletion python/cudaq/kernel/ast_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,10 @@ def isArithmeticType(self, type):
type) or F32Type.isinstance(type) or ComplexType.isinstance(type)

def __isSupportedNumpyFunction(self, id):
return id in ['sin', 'cos', 'sqrt', 'ceil', 'exp']
return id in [
'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'arcsin', 'arccos',
'arctan', 'sqrt', 'ceil', 'exp', 'log'
]

def __isSupportedVectorFunction(self, id):
return id in ['front', 'back', 'append']
Expand Down Expand Up @@ -3535,6 +3538,46 @@ def bodyBuilder(iterVar):
return
self.pushValue(math.CeilOp(value).result)
return
if node.func.attr == 'tan':
if ComplexType.isinstance(value.type):
self.emitFatalError(
f"numpy call ({node.func.attr}) is not "
f"supported for complex numbers", node)
return
self.pushValue(math.TanOp(value).result)
return
if node.func.attr in ('asin', 'arcsin'):
if ComplexType.isinstance(value.type):
self.emitFatalError(
f"numpy call ({node.func.attr}) is not "
f"supported for complex numbers", node)
return
self.pushValue(math.AsinOp(value).result)
return
if node.func.attr in ('acos', 'arccos'):
if ComplexType.isinstance(value.type):
self.emitFatalError(
f"numpy call ({node.func.attr}) is not "
f"supported for complex numbers", node)
return
self.pushValue(math.AcosOp(value).result)
return
if node.func.attr in ('atan', 'arctan'):
if ComplexType.isinstance(value.type):
self.emitFatalError(
f"numpy call ({node.func.attr}) is not "
f"supported for complex numbers", node)
return
self.pushValue(math.AtanOp(value).result)
return
if node.func.attr == 'log':
if ComplexType.isinstance(value.type):
self.emitFatalError(
f"numpy call ({node.func.attr}) is not "
f"supported for complex numbers", node)
return
self.pushValue(math.LogOp(value).result)
return

self.emitFatalError(
f"unsupported NumPy call ({node.func.attr})", node)
Expand Down
130 changes: 130 additions & 0 deletions python/tests/kernel/test_kernel_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,133 @@ def check(c: any):
check([np.float32(np.pi / 2), 0])
check([1, 0])
check([np.pi / 2, 0, True])


def test_math_functions_float64():
"""Test that math functions can be used inside kernels and match host numpy (float64)."""

@cudaq.kernel
def f_sin() -> np.float64:
return np.sin(np.float64(0.5))

assert is_close(np.sin(0.5), f_sin())

@cudaq.kernel
def f_cos() -> np.float64:
return np.cos(np.float64(0.5))

assert is_close(np.cos(0.5), f_cos())

@cudaq.kernel
def f_tan() -> np.float64:
return np.tan(np.float64(0.5))

assert is_close(np.tan(0.5), f_tan())

@cudaq.kernel
def f_arcsin() -> np.float64:
return np.arcsin(np.float64(0.5))

assert is_close(np.arcsin(0.5), f_arcsin())

@cudaq.kernel
def f_arccos() -> np.float64:
return np.arccos(np.float64(0.5))

assert is_close(np.arccos(0.5), f_arccos())

@cudaq.kernel
def f_arctan() -> np.float64:
return np.arctan(np.float64(0.5))

assert is_close(np.arctan(0.5), f_arctan())

@cudaq.kernel
def f_sqrt() -> np.float64:
return np.sqrt(np.float64(0.5))

assert is_close(np.sqrt(0.5), f_sqrt())

@cudaq.kernel
def f_exp() -> np.float64:
return np.exp(np.float64(0.5))

assert is_close(np.exp(0.5), f_exp())

@cudaq.kernel
def f_log() -> np.float64:
return np.log(np.float64(0.5))

assert is_close(np.log(0.5), f_log())


def test_math_functions_float32():
"""Test that math functions can be used inside kernels and match host numpy (float32)."""

@cudaq.kernel
def f_sin() -> np.float32:
return np.sin(np.float32(0.5))

assert is_close(np.sin(np.float32(0.5)), f_sin())

@cudaq.kernel
def f_cos() -> np.float32:
return np.cos(np.float32(0.5))

assert is_close(np.cos(np.float32(0.5)), f_cos())

@cudaq.kernel
def f_tan() -> np.float32:
return np.tan(np.float32(0.5))

assert is_close(np.tan(np.float32(0.5)), f_tan())

@cudaq.kernel
def f_arcsin() -> np.float32:
return np.arcsin(np.float32(0.5))

assert is_close(np.arcsin(np.float32(0.5)), f_arcsin())

@cudaq.kernel
def f_arccos() -> np.float32:
return np.arccos(np.float32(0.5))

assert is_close(np.arccos(np.float32(0.5)), f_arccos())

@cudaq.kernel
def f_arctan() -> np.float32:
return np.arctan(np.float32(0.5))

assert is_close(np.arctan(np.float32(0.5)), f_arctan())

@cudaq.kernel
def f_sqrt() -> np.float32:
return np.sqrt(np.float32(0.5))

assert is_close(np.sqrt(np.float32(0.5)), f_sqrt())

@cudaq.kernel
def f_exp() -> np.float32:
return np.exp(np.float32(0.5))

assert is_close(np.exp(np.float32(0.5)), f_exp())

@cudaq.kernel
def f_log() -> np.float32:
return np.log(np.float32(0.5))

assert is_close(np.log(np.float32(0.5)), f_log())


def test_math_function_as_gate_parameter():
"""Test that a math function result can be used as a gate parameter."""

@cudaq.kernel
def prep(p: float):
q = cudaq.qubit()
ry(2.0 * np.asin(np.sqrt(p)), q)

cudaq.set_random_seed(13)
p = 0.3
counts = cudaq.sample(prep, p, shots_count=20000)
assert abs(counts.probability('1') - p) < 0.02
Loading