Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ install(TARGETS qir-input-reference-standalone RUNTIME DESTINATION "${CMAKE_BINA
include(CTest)
add_test(
NAME qir-input-reference-standalone
COMMAND qir-input-reference-standalone --int-value 1 --integer-array 1 2 3 4 5 --double-value 0.5 --double-array 0.1 0.2 0.3 0.4 0.5 --bool-value true --bool-array true TRUE false fALSe 1 --pauli-value PauliX --pauli-array PauliI paulix PAULIY PAulIZ --range-value 1 2 10 --range-array 1 2 10 5 5 50 10 1 20 --string-value ASampleString --result-value one --result-array one ONE true TRUE 1 zero ZERO false FALSE 0
COMMAND qir-input-reference-standalone --int-value 1 --integer-array 1 2 3 4 5 --double-value 0.5 --double-array 0.1 0.2 0.3 0.4 0.5 --bool-value true --bool-array true TRUE false fALSe 1 --pauli-value PauliI --pauli-array PauliI paulix PAULIY PAulIZ --range-value 1 2 10 --range-array 1 2 10 5 5 50 10 1 20 --string-value ASampleString --result-value one --result-array one ONE 1 0 zero ZERO --string-array StringA StringB StringC
)

# set the environment path for loading shared libs the tests are using
Expand Down
239 changes: 132 additions & 107 deletions src/QirRuntime/samples/StandaloneInputReference/qir-driver.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include <cstring> // for memcpy
#include <fstream>
#include <iostream>
#include <map>
Expand All @@ -10,90 +9,122 @@

#include "CLI11.hpp"

#include "CoreTypes.hpp"
#include "QirContext.hpp"
#include "QirTypes.hpp"
#include "QirRuntimeApi_I.hpp"
#include "SimFactory.hpp"
#include "QirRuntime.hpp"
#include "SimFactory.hpp"

using namespace Microsoft::Quantum;
using namespace std;

struct InteropArray
{
int64_t Size;
void* Data;

InteropArray(int64_t size, void* data) :
Size(size),
Data(data){}
};

using RangeTuple = tuple<int64_t, int64_t, int64_t>;
struct InteropRange
{
int64_t Start;
int64_t Step;
int64_t End;

InteropRange() :
Start(0),
Step(0),
End(0){}

InteropRange(RangeTuple rangeTuple) :
Start(get<0>(rangeTuple)),
Step(get<1>(rangeTuple)),
End(get<2>(rangeTuple)){}
};

// This is the function corresponding to the QIR entry-point.
extern "C" int64_t Quantum__StandaloneSupportedInputs__ExerciseInputs__body( // NOLINT
extern "C" void Quantum__StandaloneSupportedInputs__ExerciseInputs( // NOLINT
int64_t intValue,
InteropArray* integerArray,
double doubleValue,
Result resultValue,
QirString* stringValue);

const char FalseAsChar = 0x0;
const char TrueAsChar = 0x1;
InteropArray* doubleArray,
char boolValue,
InteropArray* boolArray,
char pauliValue,
InteropArray* pauliArray,
InteropRange* rangeValue,
char resultValue,
InteropArray* resultArray,
const char* stringValue);

const char InteropFalseAsChar = 0x0;
const char InteropTrueAsChar = 0x1;
map<string, bool> BoolAsCharMap{
{"0", FalseAsChar},
{"false", FalseAsChar},
{"1", TrueAsChar},
{"true", TrueAsChar}};
{"0", InteropFalseAsChar},
{"false", InteropFalseAsChar},
{"1", InteropTrueAsChar},
{"true", InteropTrueAsChar}};

map<string, PauliId> PauliMap{
{"PauliI", PauliId::PauliId_I},
{"PauliX", PauliId::PauliId_X},
{"PauliY", PauliId::PauliId_Y},
{"PauliZ", PauliId::PauliId_Z}};

const char InteropResultZeroAsChar = 0x0;
const char InteropResultOneAsChar = 0x1;
map<string, char> ResultAsCharMap{
{"0", FalseAsChar},
{"Zero", FalseAsChar},
{"false", FalseAsChar},
{"1", TrueAsChar},
{"One", TrueAsChar},
{"true", TrueAsChar}};
{"0", InteropResultZeroAsChar},
{"Zero", InteropResultZeroAsChar},
{"1", InteropResultOneAsChar},
{"One", InteropResultOneAsChar}
};

template<typename T>
QirArray* CreateQirArray(T* dataBuffer, int64_t itemCount)
unique_ptr<InteropArray> CreateInteropArray(vector<T>& v)
{
unique_ptr<InteropArray> array(new InteropArray(v.size(), v.data()));
return array;
}

unique_ptr<InteropRange> CreateInteropRange(RangeTuple rangeTuple)
{
int32_t typeSize = sizeof(T); // NOLINT
QirArray* qirArray = quantum__rt__array_create_1d(typeSize, itemCount);
memcpy(qirArray->buffer, dataBuffer, typeSize * itemCount);
return qirArray;
unique_ptr<InteropRange> range(new InteropRange(rangeTuple));
return range;
}

template<typename D, typename S>
unique_ptr<D[]> TranslateVectorToBuffer(vector<S>sourceVector, function<D(S)> translationFunction)
template<typename T>
void FreePointerVector(vector<T*>& v)
{
unique_ptr<D[]> buffer (new D[sourceVector.size()]);
for (int index = 0; index < sourceVector.size(); index++)
for (auto p : v)
{
buffer[index] = translationFunction(sourceVector[index]);
delete p;
}

return buffer;
}

using RangeTuple = tuple<int64_t, int64_t, int64_t>;
QirRange TranslateRangeTupleToQirRange(RangeTuple rangeTuple)
char TranslatePauliToChar(PauliId& pauli)
{
QirRange qirRange = {
get<0>(rangeTuple), // Start
get<1>(rangeTuple), // Step
get<2>(rangeTuple) // End
};
return static_cast<char>(pauli);
}

return qirRange;
template<typename S, typename D>
void TranslateVector(vector<S>& sourceVector, vector<D>& destinationVector, function<D(S&)> translationFunction)
{
destinationVector.resize(sourceVector.size());
transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction);
}

bool TranslateCharToBool(char boolAsChar)
InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple)
{
return (boolAsChar != FalseAsChar);
InteropRange* range = new InteropRange(rangeTuple);
return range;
}

// Result Zero and One are opaque types defined by the runtime. They are declared here and initialized before executing
// the simulation.
Result RuntimeResultZero = nullptr;
Result RuntimeResultOne = nullptr;
Result TranslateCharToResult(char resultAsChar)
const char* TranslateStringToCharBuffer(string& s)
{
return resultAsChar == FalseAsChar ? RuntimeResultZero : RuntimeResultOne;
return s.c_str();
}

int main(int argc, char* argv[])
Expand All @@ -103,20 +134,14 @@ int main(int argc, char* argv[])
// Initialize simulator.
unique_ptr<IRuntimeDriver> sim = CreateFullstateSimulator();
QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/);
RuntimeResultZero = sim->UseZero();
RuntimeResultOne = sim->UseOne();

// Add the --simulation-output and --operation-output options.
// N.B. These options should be present in all standalone drivers.
// Add the --simulation-output options.
// N.B. This option should be present in all standalone drivers.
string simulationOutputFile;
CLI::Option* simulationOutputFileOpt = app.add_option(
"-s,--simulation-output", simulationOutputFile,
"File where the output produced during the simulation is written");

string operationOutputFile;
CLI::Option* operationOutputFileOpt = app.add_option(
"-o,--operation-output", operationOutputFile, "File where the output of the Q# operation is written");

// Add the options that correspond to the parameters that the QIR entry-point needs.
// Option for a Q# Int type.
int64_t intValue = 0;
Expand All @@ -135,8 +160,10 @@ int main(int argc, char* argv[])
app.add_option("--double-array", doubleVector, "A double array")->required();

// Option for a Q# Bool type.
bool boolValue = false;
app.add_option("--bool-value", boolValue, "A bool value")->required();
char boolAsCharValue = InteropFalseAsChar;
app.add_option("--bool-value", boolAsCharValue, "A bool value")
->required()
->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case));

// Option for a Q# Array<Bool> type.
// N.B. For command line parsing, a char vector is used because vector<bool> is a specialized version of vector not
Expand All @@ -161,8 +188,8 @@ int main(int argc, char* argv[])
// Option for Q# Range type.
// N.B. RangeTuple type is used here instead of QirRange because CLI11 supports tuple parsing which is leveraged and
// the tuple is later translated to QirRange.
RangeTuple rangeValue(0, 0, 0);
app.add_option("--range-value", rangeValue, "A Range value (start, step, end)")->required();
RangeTuple rangeTuple(0, 0, 0);
app.add_option("--range-value", rangeTuple, "A Range value (start, step, end)")->required();

// Option for a Q# Array<Range> type.
vector<RangeTuple> rangeTupleVector;
Expand All @@ -171,7 +198,7 @@ int main(int argc, char* argv[])
// Option for Q# Result type.
// N.B. This is implemented as a char rather than a boolean to be consistent with the way an array of results has to
// be implemented.
char resultAsCharValue = FalseAsChar;
char resultAsCharValue = InteropResultZeroAsChar;
app.add_option("--result-value", resultAsCharValue, "A Result value")
->required()
->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case));
Expand All @@ -188,41 +215,45 @@ int main(int argc, char* argv[])
string stringValue;
app.add_option("--string-value", stringValue, "A String value")->required();

// Option for a Q# Array<String> type.
vector<string> stringVector;
app.add_option("--string-array", stringVector, "A String array")->required();


// With all the options added, parse arguments from the command line.
CLI11_PARSE(app, argc, argv);

// Translate values to its final form after parsing.
// Create a QirArray of integer values.
QirArray* qirIntegerArray = CreateQirArray(integerVector.data(), integerVector.size());

// Create a QirArray of double values.
QirArray* qirDoubleArray = CreateQirArray(doubleVector.data(), doubleVector.size());

// Create a QirArray of bool values.
unique_ptr<bool[]> boolArray = TranslateVectorToBuffer<bool, char>(boolAsCharVector, TranslateCharToBool);
QirArray* qirboolArray = CreateQirArray(boolArray.get(), boolAsCharVector.size());
// Create an interop array of integer values.
unique_ptr<InteropArray> integerArray = CreateInteropArray(integerVector);

// Create a QirArray of Pauli values.
QirArray* qirPauliArray = CreateQirArray(pauliVector.data(), pauliVector.size());
// Create an interop array of double values.
unique_ptr<InteropArray> doubleArray = CreateInteropArray(doubleVector);

// Create a QirRange.
QirRange qirRange = TranslateRangeTupleToQirRange(rangeValue);
// Create an interop array of bool values.
unique_ptr<InteropArray> boolArray = CreateInteropArray(boolAsCharVector);

// Create a QirArray of Range values.
unique_ptr<QirRange[]> rangeArray = TranslateVectorToBuffer<QirRange, RangeTuple>(
rangeTupleVector, TranslateRangeTupleToQirRange);
// Translate a PauliID value to its char representation.
char pauliAsCharValue = TranslatePauliToChar(pauliValue);

QirArray* qirRangeArray = CreateQirArray(rangeArray.get(), rangeTupleVector.size());
// Create an interop array of Pauli values represented as chars.
vector<char> pauliAsCharVector;
TranslateVector<PauliId, char>(pauliVector, pauliAsCharVector, TranslatePauliToChar);
unique_ptr<InteropArray> pauliArray = CreateInteropArray(pauliAsCharVector);

// Create a Result.
Result result = TranslateCharToResult(resultAsCharValue);
// Create an interop range.
unique_ptr<InteropRange> rangeValue = CreateInteropRange(rangeTuple);
vector<InteropRange*> rangeVector;
TranslateVector<RangeTuple, InteropRange*>(rangeTupleVector, rangeVector, TranslateRangeTupleToInteropRangePointer);
unique_ptr<InteropArray> rangeArray = CreateInteropArray(rangeVector);

// Create a QirArray of Result values.
unique_ptr<Result[]> resultArray = TranslateVectorToBuffer<Result, char>(resultAsCharVector, TranslateCharToResult);
QirArray* qirResultArray = CreateQirArray(resultArray.get(), resultAsCharVector.size());
// Create an interop array of Result values.
unique_ptr<InteropArray> resultArray = CreateInteropArray(resultAsCharVector);

// Create a QirString.
QirString* qirString = quantum__rt__string_create(stringValue.c_str());
// Create an interop array of String values.
vector<const char *> stringBufferVector;
TranslateVector<string, const char*>(stringVector, stringBufferVector, TranslateStringToCharBuffer);
unique_ptr<InteropArray> stringArray = CreateInteropArray(stringBufferVector);

// Redirect the simulator output from std::cout if the --simulation-output option is present.
ostream* simulatorOutputStream = &cout;
Expand All @@ -234,29 +265,23 @@ int main(int argc, char* argv[])
simulatorOutputStream = &simulationOutputFileStream;
}

// Redirect the Q# operation output from std::cout if the --operation-output option is present.
ostream* operationOutputStream = &cout;
ofstream operationOutputFileStream;
if (!operationOutputFileOpt->empty())
{
operationOutputFileStream.open(operationOutputFile);
operationOutputStream = &operationOutputFileStream;
}

// Run simulation and write the output of the operation to the corresponding stream.
int64_t operationOutput = Quantum__StandaloneSupportedInputs__ExerciseInputs__body(
intValue, doubleValue, result, qirString);

Quantum__StandaloneSupportedInputs__ExerciseInputs(
intValue,
integerArray.get(),
doubleValue,
doubleArray.get(),
boolAsCharValue,
boolArray.get(),
pauliAsCharValue,
pauliArray.get(),
rangeValue.get(),
resultAsCharValue,
resultArray.get(),
stringValue.c_str());

FreePointerVector(rangeVector);
simulatorOutputStream->flush();
(*operationOutputStream) << operationOutput << endl;
operationOutputStream->flush();

// Close opened file buffers;
if (operationOutputFileStream.is_open())
{
operationOutputFileStream.close();
}

if (simulationOutputFileStream.is_open())
{
simulationOutputFileStream.close();
Expand Down
Loading