Skip to content

Commit

Permalink
Merge branch 'main' into py-cpp-interop
Browse files Browse the repository at this point in the history
  • Loading branch information
khalatepradnya authored Oct 8, 2024
2 parents a38ee71 + e21a32b commit e9734fb
Show file tree
Hide file tree
Showing 51 changed files with 2,049 additions and 470 deletions.
14 changes: 11 additions & 3 deletions docs/sphinx/api/languages/cpp_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Common
.. doxygenclass:: cudaq::async_result
:members:

.. doxygentypedef:: async_sample_result


.. doxygenstruct:: cudaq::ExecutionResult
:members:
Expand Down Expand Up @@ -115,6 +117,8 @@ Noise Modeling
.. doxygenclass:: cudaq::noise_model
:members:

.. doxygenenum:: cudaq::noise_model_type

Kernel Builder
===============

Expand Down Expand Up @@ -166,7 +170,9 @@ Platform

.. doxygenclass:: cudaq::BaseRemoteSimulatorQPU

.. doxygenclass:: cudaq::BaseNvcfSimulatorQPU
.. doxygenclass:: cudaq::BaseNvcfSimulatorQPU

.. doxygenclass:: cudaq::OrcaRemoteRESTQPU

.. doxygenclass:: cudaq::quantum_platform
:members:
Expand Down Expand Up @@ -229,5 +235,7 @@ Namespaces
.. doxygennamespace:: cudaq::orca
:desc-only:

.. doxygenfunction:: cudaq::orca::sample(std::vector<std::size_t> &input_state, std::vector<std::size_t> &loop_lengths, std::vector<double> &bs_angles, int n_samples = 10000)
.. doxygenfunction:: cudaq::orca::sample(std::vector<std::size_t> &input_state, std::vector<std::size_t> &loop_lengths, std::vector<double> &bs_angles, std::vector<double> &ps_angles, int n_samples = 10000)
.. doxygenfunction:: cudaq::orca::sample(std::vector<std::size_t> &input_state, std::vector<std::size_t> &loop_lengths, std::vector<double> &bs_angles, int n_samples = 10000, std::size_t qpu_id = 0)
.. doxygenfunction:: cudaq::orca::sample(std::vector<std::size_t> &input_state, std::vector<std::size_t> &loop_lengths, std::vector<double> &bs_angles, std::vector<double> &ps_angles, int n_samples = 10000, std::size_t qpu_id = 0)
.. doxygenfunction:: cudaq::orca::sample_async(std::vector<std::size_t> &input_state, std::vector<std::size_t> &loop_lengths, std::vector<double> &bs_angles, int n_samples = 10000, std::size_t qpu_id = 0)
.. doxygenfunction:: cudaq::orca::sample_async(std::vector<std::size_t> &input_state, std::vector<std::size_t> &loop_lengths, std::vector<double> &bs_angles, std::vector<double> &ps_angles, int n_samples = 10000, std::size_t qpu_id = 0)
39 changes: 35 additions & 4 deletions docs/sphinx/examples/cpp/providers/orca.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
#include "cudaq/orca.h"
#include "cudaq.h"

#include <fstream>
#include <iostream>

#include <chrono>
#include <thread>

// define helper function to generate linear spaced vectors
template <typename T>
void linear_spaced_vector(std::vector<T> &xs, T min, T max, std::size_t N) {
Expand All @@ -20,6 +26,8 @@ void linear_spaced_vector(std::vector<T> &xs, T min, T max, std::size_t N) {
}

int main() {
using namespace std::this_thread; // sleep_for, sleep_until
using namespace std::chrono_literals; // `ns`, `us`, `ms`, `s`, `h`, etc.

// A time-bin boson sampling experiment: An input state of 4 indistinguishable
// photons mixed with 4 vacuum states across 8 time bins (modes) enter the
Expand Down Expand Up @@ -60,11 +68,15 @@ int main() {
// we can also set number of requested samples
int n_samples{10000};

// Submit to ORCA synchronously (e.g., wait for the job result to be returned
// before proceeding with the rest of the execution).
// Submit to ORCA synchronously (e.g., wait for the job result to be
// returned before proceeding with the rest of the execution).
std::cout << "Submitting to ORCA Server synchronously" << std::endl;
auto counts =
cudaq::orca::sample(input_state, loop_lengths, bs_angles, n_samples);

// Print the results
counts.dump();

// If the system includes phase shifters, the phase shifter angles can be
// included in the call

Expand All @@ -73,8 +85,27 @@ int main() {
// ps_angles, n_samples);
// ```

// Print the results
counts.dump();
// Alternatively we can submit to ORCA asynchronously (e.g., continue
// executing code in the file until the job has been returned).
std::cout << "Submitting to ORCA Server asynchronously" << std::endl;
auto async_results = cudaq::orca::sample_async(input_state, loop_lengths,
bs_angles, n_samples);

// Can write the future to file:
{
std::ofstream out("saveMe.json");
out << async_results;
}

// Then come back and read it in later.
cudaq::async_result<cudaq::sample_result> readIn;
std::ifstream in("saveMe.json");
in >> readIn;

sleep_for(200ms); // wait for the job to be processed
// Get the results of the read in future.
auto async_counts = readIn.get();
async_counts.dump();

return 0;
}
32 changes: 32 additions & 0 deletions docs/sphinx/examples/python/providers/orca.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cudaq
import time

import numpy as np
import os
Expand Down Expand Up @@ -45,9 +46,11 @@
# we can also set number of requested samples
n_samples = 10000

# Option A:
# By using the synchronous `cudaq.orca.sample`, the execution of
# any remaining classical code in the file will occur only
# after the job has been returned from ORCA Server.
print("Submitting to ORCA Server synchronously")
counts = cudaq.orca.sample(input_state, loop_lengths, bs_angles, n_samples)

# If the system includes phase shifters, the phase shifter angles can be
Expand All @@ -59,3 +62,32 @@

# Print the results
print(counts)

# Option B:
# By using the asynchronous `cudaq.orca.sample_async`, the remaining
# classical code will be executed while the job is being handled
# by Orca. This is ideal when submitting via a queue over
# the cloud.
print("Submitting to ORCA Server asynchronously")
async_results = cudaq.orca.sample_async(input_state, loop_lengths, bs_angles,
n_samples)
# ... more classical code to run ...

# We can either retrieve the results later in the program with
# ```
# async_counts = async_results.get()
# ```
# or we can also write the job reference (`async_results`) to
# a file and load it later or from a different process.
file = open("future.txt", "w")
file.write(str(async_results))
file.close()

# We can later read the file content and retrieve the job
# information and results.
time.sleep(0.2) # wait for the job to be processed
same_file = open("future.txt", "r")
retrieved_async_results = cudaq.AsyncSampleResult(str(same_file.read()))

counts = retrieved_async_results.get()
print(counts)
10 changes: 10 additions & 0 deletions docs/sphinx/using/backends/hardware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ configuration.
export ORCA_ACCESS_URL="https://<ORCA API Server>"
Sometimes the requests to the PT-1 require an authentication token. This token can be set as an
environment variable named ``ORCA_AUTH_TOKEN``. For example, if the token is :code:`AbCdEf123456`,
you can set the environment variable as follows:

.. code:: bash
export ORCA_AUTH_TOKEN="AbCdEf123456"
Submission from C++
`````````````````````````

Expand Down
1 change: 1 addition & 0 deletions include/cudaq/Frontend/nvqpp/ASTBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class QuakeBridgeVisitor
DataRecursionQueue *q = nullptr);
bool VisitCXXConstructExpr(clang::CXXConstructExpr *x);
bool VisitCXXOperatorCallExpr(clang::CXXOperatorCallExpr *x);
bool VisitCXXParenListInitExpr(clang::CXXParenListInitExpr *x);
bool WalkUpFromCXXOperatorCallExpr(clang::CXXOperatorCallExpr *x);
bool TraverseDeclRefExpr(clang::DeclRefExpr *x,
DataRecursionQueue *q = nullptr);
Expand Down
1 change: 1 addition & 0 deletions include/cudaq/Optimizer/CodeGen/QIRFunctionNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static constexpr const char QIRMeasureToRegister[] =

static constexpr const char QIRCnot[] = "__quantum__qis__cnot";
static constexpr const char QIRCphase[] = "__quantum__qis__cphase";
static constexpr const char QIRCZ[] = "__quantum__qis__cz";
static constexpr const char QIRReadResultBody[] =
"__quantum__qis__read_result__body";

Expand Down
48 changes: 48 additions & 0 deletions include/cudaq/Optimizer/Dialect/Quake/QuakeOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,54 @@ def quake_ReturnWireOp : QuakeOp<"return_wire"> {
let assemblyFormat = "$target `:` type(operands) attr-dict";
}

//===----------------------------------------------------------------------===//
// Struq handling
//===----------------------------------------------------------------------===//

def quake_MakeStruqOp : QuakeOp<"make_struq", [Pure]> {
let summary = "create a quantum struct from a set of quantum references";
let description = [{
Given a list of values of quantum reference type, creates a new quantum
product reference type. This is a logical grouping and does not imply any
new quantum references are created.

This operation can be useful for grouping a number of values of type `veq`
into a logical product type, which may be passed to a pure device kernel
as a single unit, for example. These product types may always be erased into
a vector of the quantum references used to compose them via a make_struq op.
}];

let arguments = (ins Variadic<NonStruqRefType>:$veqs);
let results = (outs StruqType);
let hasVerifier = 1;

let assemblyFormat = [{
$veqs `:` functional-type(operands, results) attr-dict
}];
}

def quake_GetMemberOp : QuakeOp<"get_member", [Pure]> {
let summary = "extract quantum references from a quantum struct";
let description = [{
The get_member operation can be used to extract a set of quantum references
from a quantum struct (product) type. The fields in the quantum struct are
indexed from 0 to $n-1$ where $n$ is the number of fields. An index outside
of this range will produce a verification error.
}];

let arguments = (ins
StruqType:$struq,
I32Attr:$index
);
let results = (outs NonStruqRefType);
let hasCanonicalizer = 1;
let hasVerifier = 1;

let assemblyFormat = [{
$struq `[` $index `]` `:` functional-type(operands, results) attr-dict
}];
}

//===----------------------------------------------------------------------===//
// ToControl, FromControl pair
//===----------------------------------------------------------------------===//
Expand Down
10 changes: 8 additions & 2 deletions include/cudaq/Optimizer/Dialect/Quake/QuakeTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace quake {
inline bool isQuantumType(mlir::Type ty) {
// NB: this intentionally excludes MeasureType.
return mlir::isa<quake::RefType, quake::VeqType, quake::WireType,
quake::ControlType>(ty);
quake::ControlType, quake::StruqType>(ty);
}

/// \returns true if \p `ty` is a Quake type.
Expand All @@ -34,10 +34,16 @@ inline bool isQuakeType(mlir::Type ty) {
return isQuantumType(ty) || mlir::isa<quake::MeasureType>(ty);
}

inline bool isQuantumReferenceType(mlir::Type ty) {
/// \returns true if \p ty is a quantum reference type, excluding `struq`.
inline bool isNonStruqReferenceType(mlir::Type ty) {
return mlir::isa<quake::RefType, quake::VeqType>(ty);
}

/// \returns true if \p ty is a quantum reference type.
inline bool isQuantumReferenceType(mlir::Type ty) {
return isNonStruqReferenceType(ty) || mlir::isa<quake::StruqType>(ty);
}

/// A quake wire type is a linear type.
inline bool isLinearType(mlir::Type ty) {
return mlir::isa<quake::WireType>(ty);
Expand Down
44 changes: 42 additions & 2 deletions include/cudaq/Optimizer/Dialect/Quake/QuakeTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,41 @@ def VeqType : QuakeType<"Veq", "veq"> {
}];
}

//===----------------------------------------------------------------------===//
// StruqType: quantum reference type; product of veq and ref types.
//===----------------------------------------------------------------------===//

def StruqType : QuakeType<"Struq", "struq"> {
let summary = "a product type of quantum references";
let description = [{
This type allows one to group veqs of quantum references together in a
single product type.

To support Python, a struq type can be assigned a name. This allows the
python bridge to perform dictionary based lookups on member field names.
}];

let parameters = (ins
"mlir::StringAttr":$name,
ArrayRefParameter<"mlir::Type">:$members
);
let hasCustomAssemblyFormat = 1;

let extraClassDeclaration = [{
std::size_t getNumMembers() const { return getMembers().size(); }
}];

let builders = [
TypeBuilder<(ins CArg<"llvm::ArrayRef<mlir::Type>">:$members), [{
return $_get($_ctxt, mlir::StringAttr{}, members);
}]>,
TypeBuilder<(ins CArg<"llvm::StringRef">:$name,
CArg<"llvm::ArrayRef<mlir::Type>">:$members), [{
return $_get($_ctxt, mlir::StringAttr::get($_ctxt, name), members);
}]>
];
}

//===----------------------------------------------------------------------===//
// MeasureType: classical data type
//===----------------------------------------------------------------------===//
Expand All @@ -183,14 +218,19 @@ def MeasureType : QuakeType<"Measure", "measure"> {
}

def AnyQTypeLike : TypeConstraint<Or<[WireType.predicate, VeqType.predicate,
ControlType.predicate, RefType.predicate]>, "quake quantum types">;
ControlType.predicate, RefType.predicate, StruqType.predicate]>,
"quake quantum types">;
def AnyQType : Type<AnyQTypeLike.predicate, "quantum type">;
def AnyQTargetTypeLike : TypeConstraint<Or<[WireType.predicate,
VeqType.predicate, RefType.predicate]>, "quake quantum target types">;
def AnyQTargetType : Type<AnyQTargetTypeLike.predicate, "quantum target type">;
def AnyRefTypeLike : TypeConstraint<Or<[VeqType.predicate,
def AnyRefTypeLike : TypeConstraint<Or<[VeqType.predicate, StruqType.predicate,
RefType.predicate]>, "quake quantum reference types">;
def AnyRefType : Type<AnyRefTypeLike.predicate, "quantum reference type">;
def NonStruqRefTypeLike : TypeConstraint<Or<[VeqType.predicate,
RefType.predicate]>, "non-struct quake quantum reference types">;
def NonStruqRefType : Type<NonStruqRefTypeLike.predicate,
"non-struct quantum reference type">;
def AnyQValueTypeLike : TypeConstraint<Or<[WireType.predicate,
ControlType.predicate]>, "quake quantum value types">;
def AnyQValueType : Type<AnyQValueTypeLike.predicate, "quantum value type">;
Expand Down
22 changes: 17 additions & 5 deletions lib/Frontend/nvqpp/ConvertDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ void QuakeBridgeVisitor::addArgumentSymbols(
auto parmTy = entryBlock->getArgument(index).getType();
if (isa<FunctionType, cc::CallableType, cc::IndirectCallableType,
cc::PointerType, cc::SpanLikeType, LLVM::LLVMStructType,
quake::ControlType, quake::RefType, quake::VeqType,
quake::WireType>(parmTy)) {
quake::ControlType, quake::RefType, quake::StruqType,
quake::VeqType, quake::WireType>(parmTy)) {
symbolTable.insert(name, entryBlock->getArgument(index));
} else {
auto stackSlot = builder.create<cc::AllocaOp>(loc, parmTy);
Expand Down Expand Up @@ -658,9 +658,8 @@ bool QuakeBridgeVisitor::VisitVarDecl(clang::VarDecl *x) {
if (auto qType = dyn_cast<quake::RefType>(type)) {
// Variable is of !quake.ref type.
if (x->hasInit() && !valueStack.empty()) {
auto val = popValue();
symbolTable.insert(name, val);
return pushValue(val);
symbolTable.insert(name, peekValue());
return true;
}
auto zero = builder.create<mlir::arith::ConstantIntOp>(
loc, 0, builder.getIntegerType(64));
Expand All @@ -672,6 +671,13 @@ bool QuakeBridgeVisitor::VisitVarDecl(clang::VarDecl *x) {
return pushValue(addressTheQubit);
}

if (isa<quake::StruqType>(type)) {
// A pure quantum struct is just passed along by value. It cannot be stored
// to a variable.
symbolTable.insert(name, peekValue());
return true;
}

// Here we maybe have something like auto var = mz(qreg)
if (auto vecType = dyn_cast<cc::StdvecType>(type)) {
// Variable is of !cc.stdvec type.
Expand Down Expand Up @@ -805,6 +811,12 @@ bool QuakeBridgeVisitor::VisitVarDecl(clang::VarDecl *x) {
return pushValue(cast);
}

// Don't allocate memory for a quantum or value-semantic struct.
if (auto insertValOp = initValue.getDefiningOp<cc::InsertValueOp>()) {
symbolTable.insert(x->getName(), initValue);
return pushValue(initValue);
}

// Initialization expression resulted in a value. Create a variable and save
// that value to the variable's memory address.
Value alloca = builder.create<cc::AllocaOp>(loc, type);
Expand Down
Loading

0 comments on commit e9734fb

Please sign in to comment.