diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs new file mode 100644 index 00000000000..f4f92e66a4e --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.IO; +using System.Text; + +using Xunit; + +using Microsoft.Quantum.Qir.Tools.Driver; +using Microsoft.Quantum.QsCompiler.BondSchemas.Execution; + +namespace Tests.Microsoft.Quantum.Qir.Tools +{ + public class QirDriverGeneratorTests + { + private static string TestArtifactsDirectory = Path.Combine("TestArtifacts", "FullStateDriverGenerator"); + private static string TestCasesDirectory = Path.Combine("TestCases", "FullStateDriverGenerator"); + private static IDictionary TestCases = + new Dictionary + { + { + "UseNoArgs", + new EntryPointOperation{Name = "UseNoArgs"} + }, + { + "UseBoolArg", + new EntryPointOperation + { + Name = "UseBoolArg", + Parameters = new List{new Parameter{ Name = "BoolArg", Type = DataType.BoolType}} + } + }, + { + "UseBoolArrayArg", + new EntryPointOperation + { + Name = "UseBoolArrayArg", + Parameters = new List{new Parameter{ Name = "BoolArrayArg", Type = DataType.ArrayType, ArrayType = DataType.BoolType}} + } + }, + { + "UseDoubleArg", + new EntryPointOperation + { + Name = "UseDoublArg", + Parameters = new List{new Parameter{ Name = "DoubleArg", Type = DataType.DoubleType}} + } + }, + { + "UseDoubleArrayArg", + new EntryPointOperation + { + Name = "UseDoubleArrayArg", + Parameters = new List{new Parameter{ Name = "DoubleArrayArg", Type = DataType.ArrayType, ArrayType = DataType.DoubleType}} + } + }, + { + "UseIntegerArg", + new EntryPointOperation + { + Name = "UseIntegerArg", + Parameters = new List{new Parameter{ Name = "IntegerArg", Type = DataType.IntegerType}} + } + }, + { + "UseIntegerArrayArg", + new EntryPointOperation + { + Name = "UseIntegerArrayArg", + Parameters = new List{new Parameter{ Name = "IntegerArrayArg", Type = DataType.ArrayType, ArrayType = DataType.IntegerType}} + } + }, + { + "UsePauliArg", + new EntryPointOperation + { + Name = "UsePauliArg", + Parameters = new List{new Parameter{ Name = "PauliArg", Type = DataType.PauliType}} + } + }, + { + "UsePauliArrayArg", + new EntryPointOperation + { + Name = "UsePauliArrayArg", + Parameters = new List{new Parameter{ Name = "PauliArrayArg", Type = DataType.ArrayType, ArrayType = DataType.PauliType}} + } + }, + { + "UseRangeArg", + new EntryPointOperation + { + Name = "UseRangeArg", + Parameters = new List{new Parameter{ Name = "RangeArg", Type = DataType.RangeType}} + } + }, + { + "UseRangeArrayArg", + new EntryPointOperation + { + Name = "UseRangeArrayArg", + Parameters = new List{new Parameter{ Name = "RangeArrayArg", Type = DataType.ArrayType, ArrayType = DataType.RangeType}} + } + }, + { + "UseResultArg", + new EntryPointOperation + { + Name = "UseResultArg", + Parameters = new List{new Parameter{ Name = "ResultArg", Type = DataType.ResultType}} + } + }, + { + "UseResultArrayArg", + new EntryPointOperation + { + Name = "UseResultArrayArg", + Parameters = new List{new Parameter{ Name = "ResultArrayArg", Type = DataType.ArrayType, ArrayType = DataType.ResultType}} + } + }, + { + "UseStringArg", + new EntryPointOperation + { + Name = "UseStringArg", + Parameters = new List{new Parameter{ Name = "StringArg", Type = DataType.StringType}} + } + }, + { + "UseMiscArgs", + new EntryPointOperation + { + Name = "UseMiscArgs", + Parameters = new List{ + new Parameter{ Name = "BoolArg", Type = DataType.BoolType}, + new Parameter{ Name = "IntegerArrayArg", Position = 1, Type = DataType.ArrayType, ArrayType = DataType.IntegerType}, + new Parameter{ Name = "RangeArg", Position = 2, Type = DataType.RangeType}, + new Parameter{ Name = "StringArg", Position = 3, Type = DataType.StringType} + } + } + } + }; + + [Theory] + [InlineData("UseNoArgs")] + [InlineData("UseBoolArg")] + [InlineData("UseBoolArrayArg")] + [InlineData("UseDoubleArg")] + [InlineData("UseDoubleArrayArg")] + [InlineData("UseIntegerArg")] + [InlineData("UseIntegerArrayArg")] + [InlineData("UsePauliArg")] + [InlineData("UsePauliArrayArg")] + [InlineData("UseRangeArg")] + [InlineData("UseRangeArrayArg")] + [InlineData("UseResultArg")] + [InlineData("UseResultArrayArg")] + [InlineData("UseStringArg")] + [InlineData("UseMiscArgs")] + public void GenerateFullStateSimulatorDriver(string testCase) + { + var entryPointOperation = TestCases[testCase]; + var driverGenerator = new QirFullStateDriverGenerator(); + var driverFileName = $"{testCase}.cpp"; + var verificationCppSourceCode = File.ReadAllText(Path.Combine(TestCasesDirectory, driverFileName)); + if (!Directory.Exists(TestArtifactsDirectory)) + { + Directory.CreateDirectory(TestArtifactsDirectory); + } + + var generatedStream = File.Create(Path.Combine(TestArtifactsDirectory, driverFileName)); + driverGenerator.GenerateAsync(entryPointOperation, generatedStream).Wait(); + var generatedStreamReader = new StreamReader(generatedStream, Encoding.UTF8); + var generatedCppSourceCode = generatedStreamReader.ReadToEnd(); + Assert.Equal(verificationCppSourceCode, generatedCppSourceCode); + generatedStream.Close(); + + } + } +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp new file mode 100644 index 00000000000..ac2d8510044 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map BoolAsCharMap{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; + +extern "C" void UseBoolArg( + char BoolArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + char BoolArgCli; + BoolArgCli = InteropFalseAsChar; + app.add_option("--BoolArg", BoolArgCli, "Option to provide a value for the BoolArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char BoolArgInterop = BoolArgCli; + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseBoolArg( + BoolArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp new file mode 100644 index 00000000000..49ba88538d7 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp @@ -0,0 +1,112 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map BoolAsCharMap{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; + +extern "C" void UseBoolArrayArg( + InteropArray* BoolArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + vector BoolArrayArgCli; + app.add_option("--BoolArrayArg", BoolArrayArgCli, "Option to provide a value for the BoolArrayArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr BoolArrayArgUniquePtr = CreateInteropArray(BoolArrayArgCli); + InteropArray* BoolArrayArgInterop = BoolArrayArgUniquePtr.get(); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseBoolArrayArg( + BoolArrayArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp new file mode 100644 index 00000000000..c07687f1e4e --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +extern "C" void UseDoublArg( + double_t DoubleArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + double_t DoubleArgCli; + DoubleArgCli = 0.0; + app.add_option("--DoubleArg", DoubleArgCli, "Option to provide a value for the DoubleArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + double_t DoubleArgInterop = DoubleArgCli; + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseDoublArg( + DoubleArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp new file mode 100644 index 00000000000..707c2095bc9 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +extern "C" void UseDoubleArrayArg( + InteropArray* DoubleArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + vector DoubleArrayArgCli; + app.add_option("--DoubleArrayArg", DoubleArrayArgCli, "Option to provide a value for the DoubleArrayArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr DoubleArrayArgUniquePtr = CreateInteropArray(DoubleArrayArgCli); + InteropArray* DoubleArrayArgInterop = DoubleArrayArgUniquePtr.get(); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseDoubleArrayArg( + DoubleArrayArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp new file mode 100644 index 00000000000..6d4d4614f9a --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +extern "C" void UseIntegerArg( + int64_t IntegerArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + int64_t IntegerArgCli; + IntegerArgCli = 0; + app.add_option("--IntegerArg", IntegerArgCli, "Option to provide a value for the IntegerArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + int64_t IntegerArgInterop = IntegerArgCli; + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseIntegerArg( + IntegerArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp new file mode 100644 index 00000000000..e3cc2f8bf60 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +extern "C" void UseIntegerArrayArg( + InteropArray* IntegerArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + vector IntegerArrayArgCli; + app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); + InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseIntegerArrayArg( + IntegerArrayArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp new file mode 100644 index 00000000000..28373859dbf --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp @@ -0,0 +1,174 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +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)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map BoolAsCharMap{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; + +// Auxiliary functions for interop with Q# String type. +const char* TranslateStringToCharBuffer(string& s) +{ + return s.c_str(); +} + +extern "C" void UseMiscArgs( + char BoolArg, + InteropArray* IntegerArrayArg, + InteropRange* RangeArg, + const char* StringArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + char BoolArgCli; + BoolArgCli = InteropFalseAsChar; + app.add_option("--BoolArg", BoolArgCli, "Option to provide a value for the BoolArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); + + vector IntegerArrayArgCli; + app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") + ->required(); + + RangeTuple RangeArgCli; + app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") + ->required(); + + string StringArgCli; + app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char BoolArgInterop = BoolArgCli; + + unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); + InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); + + InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); + + const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseMiscArgs( + BoolArgInterop, + IntegerArrayArgInterop, + RangeArgInterop, + StringArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp new file mode 100644 index 00000000000..eb6597a7f4f --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +extern "C" void UseNoArgs( +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseNoArgs( + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp new file mode 100644 index 00000000000..134db56ea44 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Pauli type. +map PauliMap{ + {"PauliI", PauliId::PauliId_I}, + {"PauliX", PauliId::PauliId_X}, + {"PauliY", PauliId::PauliId_Y}, + {"PauliZ", PauliId::PauliId_Z} +}; + +char TranslatePauliToChar(PauliId& pauli) +{ + return static_cast(pauli); +} + +extern "C" void UsePauliArg( + char PauliArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + PauliId PauliArgCli; + PauliArgCli = PauliId::PauliId_I; + app.add_option("--PauliArg", PauliArgCli, "Option to provide a value for the PauliArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char PauliArgInterop = TranslatePauliToChar(PauliArgCli); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UsePauliArg( + PauliArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp new file mode 100644 index 00000000000..22b27411fe6 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Pauli type. +map PauliMap{ + {"PauliI", PauliId::PauliId_I}, + {"PauliX", PauliId::PauliId_X}, + {"PauliY", PauliId::PauliId_Y}, + {"PauliZ", PauliId::PauliId_Z} +}; + +char TranslatePauliToChar(PauliId& pauli) +{ + return static_cast(pauli); +} + +extern "C" void UsePauliArrayArg( + InteropArray* PauliArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + vector PauliArrayArgCli; + app.add_option("--PauliArrayArg", PauliArrayArgCli, "Option to provide a value for the PauliArrayArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + vector PauliArrayArgIntermediate; + TranslateVector(PauliArrayArgCli, PauliArrayArgIntermediate, TranslatePauliToChar); + unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgIntermediate); + InteropArray* PauliArrayArgInterop = PauliArrayArgUniquePtr.get(); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UsePauliArrayArg( + PauliArrayArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp new file mode 100644 index 00000000000..3b89eb0ab7c --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +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)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} + +extern "C" void UseRangeArg( + InteropRange* RangeArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + RangeTuple RangeArgCli; + app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseRangeArg( + RangeArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp new file mode 100644 index 00000000000..354999a5251 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +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)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} + +// Auxiliary functions for interop with Q# Range[] type +template +void FreePointerVector(vector& v) +{ + for (auto p : v) + { + delete p; + } +} + +extern "C" void UseRangeArrayArg( + InteropArray* RangeArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + vector RangeArrayArgCli; + app.add_option("--RangeArrayArg", RangeArrayArgCli, "Option to provide a value for the RangeArrayArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + vector RangeArrayArgIntermediate; + TranslateVector(RangeArrayArgCli, RangeArrayArgIntermediate, TranslateRangeTupleToInteropRangePointer); + unique_ptr RangeArrayArgUniquePtr = CreateInteropArray(RangeArrayArgIntermediate); + InteropArray* RangeArrayArgInterop = RangeArrayArgUniquePtr.get(); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseRangeArrayArg( + RangeArrayArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp new file mode 100644 index 00000000000..516888b83b0 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Result type. +const char InteropResultZeroAsChar = 0x0; +const char InteropResultOneAsChar = 0x1; +map ResultAsCharMap{ + {"0", InteropResultZeroAsChar}, + {"Zero", InteropResultZeroAsChar}, + {"1", InteropResultOneAsChar}, + {"One", InteropResultOneAsChar} +}; + +extern "C" void UseResultArg( + char ResultArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + char ResultArgCli; + ResultArgCli = InteropResultZeroAsChar; + app.add_option("--ResultArg", ResultArgCli, "Option to provide a value for the ResultArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char ResultArgInterop = ResultArgCli; + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseResultArg( + ResultArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp new file mode 100644 index 00000000000..2f5f36c09e1 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp @@ -0,0 +1,112 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Result type. +const char InteropResultZeroAsChar = 0x0; +const char InteropResultOneAsChar = 0x1; +map ResultAsCharMap{ + {"0", InteropResultZeroAsChar}, + {"Zero", InteropResultZeroAsChar}, + {"1", InteropResultOneAsChar}, + {"One", InteropResultOneAsChar} +}; + +extern "C" void UseResultArrayArg( + InteropArray* ResultArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + vector ResultArrayArgCli; + app.add_option("--ResultArrayArg", ResultArrayArgCli, "Option to provide a value for the ResultArrayArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr ResultArrayArgUniquePtr = CreateInteropArray(ResultArrayArgCli); + InteropArray* ResultArrayArgInterop = ResultArrayArgUniquePtr.get(); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseResultArrayArg( + ResultArrayArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp new file mode 100644 index 00000000000..bdfc37c878b --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +#include "SimFactory.hpp" + +using namespace Microsoft::Quantum; +using namespace std; + +// Auxiliary functions for interop with Q# String type. +const char* TranslateStringToCharBuffer(string& s) +{ + return s.c_str(); +} + +extern "C" void UseStringArg( + const char* StringArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. + unique_ptr sim = CreateFullstateSimulator(); + QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + + // Add a command line option for each entry-point parameter. + string StringArgCli; + app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); + + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + UseStringArg( + StringArgInterop + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Tests.Microsoft.Quantum.Qir.Tools.csproj b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Tests.Microsoft.Quantum.Qir.Tools.csproj index 55ffaa29bd4..cd9a3d60202 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Tests.Microsoft.Quantum.Qir.Tools.csproj +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Tests.Microsoft.Quantum.Qir.Tools.csproj @@ -17,4 +17,52 @@ + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + diff --git a/src/Qir/Execution/Tools/Driver/IQirSimulatorInitializer.cs b/src/Qir/Execution/Tools/Driver/IQirSimulatorInitializer.cs new file mode 100644 index 00000000000..1ed263c814d --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/IQirSimulatorInitializer.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + public interface IQirSimulatorInitializer + { + public string Generate(); + + public IList Headers { get; } + + public IList LinkLibraries { get; } + } +} diff --git a/src/Qir/Execution/Tools/Driver/QirCppDriver.cs b/src/Qir/Execution/Tools/Driver/QirCppDriver.cs new file mode 100644 index 00000000000..807f959095b --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppDriver.cs @@ -0,0 +1,576 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 16.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + using System.IO; + using Microsoft.Quantum.QsCompiler.BondSchemas.Execution; + using System; + + /// + /// Class to produce the template output + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + public partial class QirCppDriver : QirCppDriverBase + { + /// + /// Create the template output + /// + public virtual string TransformText() + { + this.Write(@"//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include ""CLI11.hpp"" + +#include ""QirRuntime.hpp"" +#include ""QirContext.hpp"" + +"); + foreach (var header in SimulatorInitializer.Headers) { + this.Write("#include \""); + this.Write(this.ToStringHelper.ToStringWithCulture(header)); + this.Write("\"\r\n"); + } + this.Write("\r\nusing namespace Microsoft::Quantum;\r\nusing namespace std;\r\n"); + if (EntryPoint.ContainsArgumentType(DataType.ArrayType)) { + this.Write(@" +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} +"); + } + if (EntryPoint.ContainsArgumentType(DataType.RangeType) || EntryPoint.ContainsArrayType(DataType.RangeType)) { + this.Write(@" +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +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)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} +"); + } + if (EntryPoint.ContainsArrayType(DataType.RangeType)) { + this.Write("\r\n// Auxiliary functions for interop with Q# Range[] type\r\ntemplate\r\n" + + "void FreePointerVector(vector& v)\r\n{\r\n for (auto p : v)\r\n {\r\n d" + + "elete p;\r\n }\r\n}\r\n"); + } + if (EntryPoint.ContainsArgumentType(DataType.BoolType) || EntryPoint.ContainsArrayType(DataType.BoolType)) { + this.Write("\r\n// Auxiliary functions for interop with Q# Bool type.\r\nconst char InteropFalseA" + + "sChar = 0x0;\r\nconst char InteropTrueAsChar = 0x1;\r\nmap "); + this.Write(this.ToStringHelper.ToStringWithCulture(QirCppInterop.CliOptionTransformerMapName(DataType.BoolType))); + this.Write("{\r\n {\"0\", InteropFalseAsChar},\r\n {\"false\", InteropFalseAsChar},\r\n {\"1\", " + + "InteropTrueAsChar},\r\n {\"true\", InteropTrueAsChar}\r\n};\r\n"); + } + if (EntryPoint.ContainsArgumentType(DataType.PauliType) || EntryPoint.ContainsArrayType(DataType.PauliType)) { + this.Write("\r\n// Auxiliary functions for interop with Q# Pauli type.\r\nmap "); + this.Write(this.ToStringHelper.ToStringWithCulture(QirCppInterop.CliOptionTransformerMapName(DataType.PauliType))); + this.Write("{\r\n {\"PauliI\", PauliId::PauliId_I},\r\n {\"PauliX\", PauliId::PauliId_X},\r\n " + + "{\"PauliY\", PauliId::PauliId_Y},\r\n {\"PauliZ\", PauliId::PauliId_Z}\r\n};\r\n\r\nchar " + + "TranslatePauliToChar(PauliId& pauli)\r\n{\r\n return static_cast(pauli);\r\n}" + + "\r\n"); + } + if (EntryPoint.ContainsArgumentType(DataType.ResultType) || EntryPoint.ContainsArrayType(DataType.ResultType)) { + this.Write("\r\n// Auxiliary functions for interop with Q# Result type.\r\nconst char InteropResu" + + "ltZeroAsChar = 0x0;\r\nconst char InteropResultOneAsChar = 0x1;\r\nmap" + + " "); + this.Write(this.ToStringHelper.ToStringWithCulture(QirCppInterop.CliOptionTransformerMapName(DataType.ResultType))); + this.Write("{\r\n {\"0\", InteropResultZeroAsChar},\r\n {\"Zero\", InteropResultZeroAsChar},\r\n " + + " {\"1\", InteropResultOneAsChar},\r\n {\"One\", InteropResultOneAsChar}\r\n};\r\n"); + } + if (EntryPoint.ContainsArgumentType(DataType.StringType) || EntryPoint.ContainsArrayType(DataType.StringType)) { + this.Write("\r\n// Auxiliary functions for interop with Q# String type.\r\nconst char* TranslateS" + + "tringToCharBuffer(string& s)\r\n{\r\n return s.c_str();\r\n}\r\n"); + } + this.Write("\r\nextern \"C\" void "); + this.Write(this.ToStringHelper.ToStringWithCulture(EntryPoint.Name)); + this.Write("(\r\n"); + for (int i = 0; i < EntryPoint.Parameters.Count; i++) { + var arg = EntryPoint.Parameters[i]; + var isLastArg = i == (EntryPoint.Parameters.Count-1); + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropType())); + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.Name)); + this.Write(this.ToStringHelper.ToStringWithCulture((isLastArg) ? "" : ",")); + this.Write("\r\n"); + } + this.Write("); // QIR interop function.\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n CLI::App " + + "app(\"QIR Standalone Entry Point\");\r\n\r\n // Initialize simulator.\r\n"); + var initializerReader = new StringReader(SimulatorInitializer.Generate()); + string line; + while((line = initializerReader.ReadLine()) != null) { + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(line)); + this.Write("\r\n"); + } + this.Write(@" + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + ""--simulation-output"", + simulationOutputFile, + ""File where the output produced during the simulation is written""); + +"); + if (EntryPoint.Parameters.Count > 0) { + this.Write(" // Add a command line option for each entry-point parameter.\r\n"); + } + foreach (var arg in EntryPoint.Parameters) { + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionType())); + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(";\r\n"); + if (arg.CliOptionVariableDefaultValue() != null) { + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(" = "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableDefaultValue())); + this.Write(";\r\n"); + } + this.Write(" app.add_option(\""); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionName())); + this.Write("\", "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(", \""); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionDescription())); + this.Write("\")\r\n ->required()"); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionTransformerMapName() != null ? "" : ";")); + this.Write("\r\n"); + if (arg.CliOptionTransformerMapName() != null) { + this.Write(" ->transform(CLI::CheckedTransformer("); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionTransformerMapName())); + this.Write(", CLI::ignore_case));\r\n"); + } + this.Write("\r\n"); + } + this.Write(" // After all the options have been added, parse arguments from the command li" + + "ne.\r\n CLI11_PARSE(app, argc, argv);\r\n\r\n"); + if (EntryPoint.Parameters.Count > 0) { + this.Write(" // Cast parsed arguments to its interop types.\r\n"); + } + foreach (var arg in EntryPoint.Parameters) { + if (arg.Type == DataType.ArrayType) { + var arrayInteropTranslator = QirCppInterop.CliOptionTypeToInteropTypeTranslator(arg.ArrayType); + if (arrayInteropTranslator == null) { + this.Write(" unique_ptr "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.UniquePtrVariableName())); + this.Write(" = CreateInteropArray("); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(");\r\n"); + } + else { + var arrayCliOptionType = QirCppInterop.CliOptionType(arg.ArrayType); + var arrayInteropType = QirCppInterop.InteropType(arg.ArrayType); + this.Write(" vector<"); + this.Write(this.ToStringHelper.ToStringWithCulture(arrayInteropType)); + this.Write("> "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.IntermediateVariableName())); + this.Write(";\r\n TranslateVector<"); + this.Write(this.ToStringHelper.ToStringWithCulture(arrayCliOptionType)); + this.Write(", "); + this.Write(this.ToStringHelper.ToStringWithCulture(arrayInteropType)); + this.Write(">("); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(", "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.IntermediateVariableName())); + this.Write(", "); + this.Write(this.ToStringHelper.ToStringWithCulture(arrayInteropTranslator)); + this.Write(");\r\n unique_ptr "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.UniquePtrVariableName())); + this.Write(" = CreateInteropArray("); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.IntermediateVariableName())); + this.Write(");\r\n"); + } + this.Write(" InteropArray* "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropVariableName())); + this.Write(" = "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.UniquePtrVariableName())); + this.Write(".get();\r\n"); + } + else { + var interopTranslator = QirCppInterop.CliOptionTypeToInteropTypeTranslator(arg.Type); + if (interopTranslator == null) { + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropType())); + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropVariableName())); + this.Write(" = "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(";\r\n"); + } + else { + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropType())); + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropVariableName())); + this.Write(" = "); + this.Write(this.ToStringHelper.ToStringWithCulture(interopTranslator)); + this.Write("("); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.CliOptionVariableName())); + this.Write(");\r\n"); + } + } + this.Write("\r\n"); + } + this.Write(@" // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + "); + this.Write(this.ToStringHelper.ToStringWithCulture(EntryPoint.Name)); + this.Write("(\r\n"); + for (int i = 0; i < EntryPoint.Parameters.Count; i++) { + var arg = EntryPoint.Parameters[i]; + var isLastArg = i == (EntryPoint.Parameters.Count-1); + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(arg.InteropVariableName())); + this.Write(this.ToStringHelper.ToStringWithCulture((isLastArg) ? "" : ",")); + this.Write("\r\n"); + } + this.Write(" );\r\n\r\n // Flush the output of the simulation.\r\n simulatorOutputStream->" + + "flush();\r\n if (simulationOutputFileStream.is_open())\r\n {\r\n simulati" + + "onOutputFileStream.close();\r\n }\r\n\r\n return 0;\r\n}\r\n"); + return this.GenerationEnvironment.ToString(); + } + } + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + public class QirCppDriverBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/Qir/Execution/Tools/Driver/QirCppDriver.tt b/src/Qir/Execution/Tools/Driver/QirCppDriver.tt new file mode 100644 index 00000000000..a99fa9716e7 --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppDriver.tt @@ -0,0 +1,246 @@ +<#@ template language="C#" linePragmas="false" #> +<#@ import namespace="System.IO" #> +<#@ import namespace="Microsoft.Quantum.QsCompiler.BondSchemas.Execution" #> +//------------------------------------------------------------------------------ +// This code was generated by a tool. +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +#include "QirRuntime.hpp" +#include "QirContext.hpp" + +<# foreach (var header in SimulatorInitializer.Headers) { #> +#include "<#= header #>" +<# } #> + +using namespace Microsoft::Quantum; +using namespace std; +<# if (EntryPoint.ContainsArgumentType(DataType.ArrayType)) { #> + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} +<# } #> +<# if (EntryPoint.ContainsArgumentType(DataType.RangeType) || EntryPoint.ContainsArrayType(DataType.RangeType)) { #> + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +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)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} +<# } #> +<# if (EntryPoint.ContainsArrayType(DataType.RangeType)) { #> + +// Auxiliary functions for interop with Q# Range[] type +template +void FreePointerVector(vector& v) +{ + for (auto p : v) + { + delete p; + } +} +<# } #> +<# if (EntryPoint.ContainsArgumentType(DataType.BoolType) || EntryPoint.ContainsArrayType(DataType.BoolType)) { #> + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map <#= QirCppInterop.CliOptionTransformerMapName(DataType.BoolType) #>{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; +<# } #> +<# if (EntryPoint.ContainsArgumentType(DataType.PauliType) || EntryPoint.ContainsArrayType(DataType.PauliType)) { #> + +// Auxiliary functions for interop with Q# Pauli type. +map <#= QirCppInterop.CliOptionTransformerMapName(DataType.PauliType) #>{ + {"PauliI", PauliId::PauliId_I}, + {"PauliX", PauliId::PauliId_X}, + {"PauliY", PauliId::PauliId_Y}, + {"PauliZ", PauliId::PauliId_Z} +}; + +char TranslatePauliToChar(PauliId& pauli) +{ + return static_cast(pauli); +} +<# } #> +<# if (EntryPoint.ContainsArgumentType(DataType.ResultType) || EntryPoint.ContainsArrayType(DataType.ResultType)) { #> + +// Auxiliary functions for interop with Q# Result type. +const char InteropResultZeroAsChar = 0x0; +const char InteropResultOneAsChar = 0x1; +map <#= QirCppInterop.CliOptionTransformerMapName(DataType.ResultType) #>{ + {"0", InteropResultZeroAsChar}, + {"Zero", InteropResultZeroAsChar}, + {"1", InteropResultOneAsChar}, + {"One", InteropResultOneAsChar} +}; +<# } #> +<# if (EntryPoint.ContainsArgumentType(DataType.StringType) || EntryPoint.ContainsArrayType(DataType.StringType)) { #> + +// Auxiliary functions for interop with Q# String type. +const char* TranslateStringToCharBuffer(string& s) +{ + return s.c_str(); +} +<# } #> + +extern "C" void <#= EntryPoint.Name #>( +<# for (int i = 0; i < EntryPoint.Parameters.Count; i++) { + var arg = EntryPoint.Parameters[i]; + var isLastArg = i == (EntryPoint.Parameters.Count-1); #> + <#= arg.InteropType() #> <#= arg.Name #><#= (isLastArg) ? "" : "," #> +<# } #> +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize simulator. +<# var initializerReader = new StringReader(SimulatorInitializer.Generate()); + string line; + while((line = initializerReader.ReadLine()) != null) { #> + <#= line #> +<# } #> + + // Add the --simulation-output option. + string simulationOutputFile; + CLI::Option* simulationOutputFileOpt = app.add_option( + "--simulation-output", + simulationOutputFile, + "File where the output produced during the simulation is written"); + +<# if (EntryPoint.Parameters.Count > 0) { #> + // Add a command line option for each entry-point parameter. +<# } #> +<# foreach (var arg in EntryPoint.Parameters) { #> + <#= arg.CliOptionType() #> <#= arg.CliOptionVariableName() #>; +<# if (arg.CliOptionVariableDefaultValue() != null) { #> + <#= arg.CliOptionVariableName() #> = <#= arg.CliOptionVariableDefaultValue() #>; +<# } #> + app.add_option("<#= arg.CliOptionName() #>", <#= arg.CliOptionVariableName() #>, "<#= arg.CliOptionDescription() #>") + ->required()<#= arg.CliOptionTransformerMapName() != null ? "" : ";" #> +<# if (arg.CliOptionTransformerMapName() != null) { #> + ->transform(CLI::CheckedTransformer(<#= arg.CliOptionTransformerMapName() #>, CLI::ignore_case)); +<# } #> + +<# } #> + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + +<# if (EntryPoint.Parameters.Count > 0) { #> + // Cast parsed arguments to its interop types. +<# } #> +<# foreach (var arg in EntryPoint.Parameters) { #> +<# if (arg.Type == DataType.ArrayType) { + var arrayInteropTranslator = QirCppInterop.CliOptionTypeToInteropTypeTranslator(arg.ArrayType); #> +<# if (arrayInteropTranslator == null) { #> + unique_ptr <#= arg.UniquePtrVariableName() #> = CreateInteropArray(<#= arg.CliOptionVariableName() #>); +<# } #> +<# else { + var arrayCliOptionType = QirCppInterop.CliOptionType(arg.ArrayType); + var arrayInteropType = QirCppInterop.InteropType(arg.ArrayType); #> + vector<<#= arrayInteropType #>> <#= arg.IntermediateVariableName() #>; + TranslateVector<<#= arrayCliOptionType #>, <#= arrayInteropType #>>(<#= arg.CliOptionVariableName() #>, <#= arg.IntermediateVariableName() #>, <#= arrayInteropTranslator #>); + unique_ptr <#= arg.UniquePtrVariableName() #> = CreateInteropArray(<#= arg.IntermediateVariableName() #>); +<# } #> + InteropArray* <#= arg.InteropVariableName() #> = <#= arg.UniquePtrVariableName() #>.get(); +<# } #> +<# else { + var interopTranslator = QirCppInterop.CliOptionTypeToInteropTypeTranslator(arg.Type); #> +<# if (interopTranslator == null) { #> + <#= arg.InteropType() #> <#= arg.InteropVariableName() #> = <#= arg.CliOptionVariableName() #>; +<# } #> +<# else { #> + <#= arg.InteropType() #> <#= arg.InteropVariableName() #> = <#= interopTranslator #>(<#= arg.CliOptionVariableName() #>); +<# } #> +<# } #> + +<# } #> + // Redirect the simulator output from std::cout if the --simulation-output option is present. + ostream* simulatorOutputStream = &cout; + ofstream simulationOutputFileStream; + if (!simulationOutputFileOpt->empty()) + { + simulationOutputFileStream.open(simulationOutputFile); + SetOutputStream(simulationOutputFileStream); + simulatorOutputStream = &simulationOutputFileStream; + } + + // Execute the entry point operation. + <#= EntryPoint.Name #>( +<# for (int i = 0; i < EntryPoint.Parameters.Count; i++) { + var arg = EntryPoint.Parameters[i]; + var isLastArg = i == (EntryPoint.Parameters.Count-1); #> + <#= arg.InteropVariableName() #><#= (isLastArg) ? "" : "," #> +<# } #> + ); + + // Flush the output of the simulation. + simulatorOutputStream->flush(); + if (simulationOutputFileStream.is_open()) + { + simulationOutputFileStream.close(); + } + + return 0; +} diff --git a/src/Qir/Execution/Tools/Driver/QirCppDriverEx.cs b/src/Qir/Execution/Tools/Driver/QirCppDriverEx.cs new file mode 100644 index 00000000000..7c05fba2b3e --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppDriverEx.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Quantum.QsCompiler.BondSchemas.Execution; + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + public partial class QirCppDriver + { + public readonly EntryPointOperation EntryPoint; + + public readonly IQirSimulatorInitializer SimulatorInitializer; + + public QirCppDriver(EntryPointOperation entryPoint, IQirSimulatorInitializer simulatorInitializer) + { + EntryPoint = entryPoint; + SimulatorInitializer = simulatorInitializer; + } + } +} diff --git a/src/Qir/Execution/Tools/Driver/QirCppDriverGenerator.cs b/src/Qir/Execution/Tools/Driver/QirCppDriverGenerator.cs new file mode 100644 index 00000000000..a9a8233464b --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppDriverGenerator.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Quantum.QsCompiler.BondSchemas.Execution; + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + public class QirCppDriverGenerator : IQirDriverGenerator + { + private readonly IQirSimulatorInitializer SimulatorInitalizer; + + private delegate string StringConversion(ArgumentValue value); + + public QirCppDriverGenerator(IQirSimulatorInitializer simulatorInitializer) + { + SimulatorInitalizer = simulatorInitializer; + } + + public async Task GenerateAsync(EntryPointOperation entryPointOperation, Stream stream) + { + var qirCppDriver = new QirCppDriver(entryPointOperation, SimulatorInitalizer); + var cppSource = qirCppDriver.TransformText(); + await stream.WriteAsync(Encoding.UTF8.GetBytes(cppSource)); + await stream.FlushAsync(); + stream.Position = 0; + } + + public string GetCommandLineArguments(ExecutionInformation executionInformation) + { + // Sort arguments by position. + var sortedArguments = executionInformation.EntryPoint.Parameters.OrderBy(arg => arg.Position); + var argumentBuilder = new StringBuilder(); + foreach (var arg in sortedArguments) + { + if (argumentBuilder.Length != 0) + { + argumentBuilder.Append(' '); + } + + argumentBuilder.Append($"--{arg.Name}").Append(' ').Append(GetArgumentValueString(arg, executionInformation.ArgumentValues[arg.Name])); + } + + return argumentBuilder.ToString(); + } + + private static string GetArgumentValueString(Parameter argument, ArgumentValue argumentValue) + { + // Today, only the first argument value in the array will be used. + return argument.Type switch + { + DataType.BoolType => GetBoolValueString(argumentValue), + DataType.DoubleType => GetDoubleValueString(argumentValue), + DataType.IntegerType => GetIntegerValueString(argumentValue), + DataType.PauliType => GetPauliValueString(argumentValue), + DataType.RangeType => GetRangeValueString(argumentValue), + DataType.ResultType => GetResultValueString(argumentValue), + DataType.StringType => GetStringValueString(argumentValue), + DataType.ArrayType => GetArrayValueString(argument.ArrayType, argumentValue.Array), + _ => throw new ArgumentException($"Unsupported data type {argument.Type}") + }; + } + + private static string GetArrayValueString(DataType? arrayType, IList arrayValue) + { + static string ConvertArray(IList list, StringConversion conversion) => + string.Join(' ', list.Select(item => conversion.Invoke(item))); + + return arrayType switch + { + DataType.BoolType => ConvertArray(arrayValue, GetBoolValueString), + DataType.DoubleType => ConvertArray(arrayValue, GetDoubleValueString), + DataType.IntegerType => ConvertArray(arrayValue, GetIntegerValueString), + DataType.PauliType => ConvertArray(arrayValue, GetPauliValueString), + DataType.RangeType => ConvertArray(arrayValue, GetRangeValueString), + DataType.ResultType => ConvertArray(arrayValue, GetResultValueString), + DataType.StringType => ConvertArray(arrayValue, GetStringValueString), + _ => throw new ArgumentException($"Unsupported array data type {arrayType}") + }; + } + + private static string GetResultValueString(ArgumentValue value) + { + if (value?.Result == null) + { + throw new ArgumentNullException("Cannot convert null result value to string."); + } + return value.Result == ResultValue.One ? "1" : "0"; + } + + private static string GetRangeValueString(ArgumentValue value) + { + if (value?.Range == null) + { + throw new ArgumentNullException("Cannot convert null range value to string."); + } + var rangeValue = value.Range; + return $"{rangeValue.Start} {rangeValue.Step} {rangeValue.End}"; + } + + private static string GetStringValueString(ArgumentValue value) + { + if (value?.String == null) + { + throw new ArgumentNullException("Cannot convert null string value to string."); + } + + return $"\"{value.String}\""; + } + + private static string GetBoolValueString(ArgumentValue value) + { + if (value?.Bool == null) + { + throw new ArgumentNullException("Cannot convert null bool value to string."); + } + + return value.Bool.ToString().ToLower(); + } + + private static string GetPauliValueString(ArgumentValue value) + { + if (value?.Pauli == null) + { + throw new ArgumentNullException("Cannot convert null pauli value to string."); + } + + return value.Pauli.ToString().ToLower(); + } + + private static string GetDoubleValueString(ArgumentValue value) + { + if (value?.Double == null) + { + throw new ArgumentNullException("Cannot convert null double value to string."); + } + + return value.Double.ToString().ToLower(); + } + + private static string GetIntegerValueString(ArgumentValue value) + { + if (value?.Integer == null) + { + throw new ArgumentNullException("Cannot convert null integer value to string."); + } + + return value.Integer.ToString().ToLower(); + } + } +} diff --git a/src/Qir/Execution/Tools/Driver/QirCppFullStateSimulatorInitializer.cs b/src/Qir/Execution/Tools/Driver/QirCppFullStateSimulatorInitializer.cs new file mode 100644 index 00000000000..44939338006 --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppFullStateSimulatorInitializer.cs @@ -0,0 +1,302 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 16.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + using System; + + /// + /// Class to produce the template output + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + public partial class QirCppFullStateSimulatorInitializer : QirCppFullStateSimulatorInitializerBase + { + /// + /// Create the template output + /// + public virtual string TransformText() + { + this.Write("unique_ptr sim = CreateFullstateSimulator();\r\nQirContextScope qir" + + "ctx(sim.get(), false /*trackAllocatedObjects*/);"); + return this.GenerationEnvironment.ToString(); + } + } + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + public class QirCppFullStateSimulatorInitializerBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/Qir/Execution/Tools/Driver/QirCppFullStateSimulatorInitializer.tt b/src/Qir/Execution/Tools/Driver/QirCppFullStateSimulatorInitializer.tt new file mode 100644 index 00000000000..275994cf4af --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppFullStateSimulatorInitializer.tt @@ -0,0 +1,3 @@ +<#@ template language="C#" linePragmas="false" #> +unique_ptr sim = CreateFullstateSimulator(); +QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); \ No newline at end of file diff --git a/src/Qir/Execution/Tools/Driver/QirCppInterop.cs b/src/Qir/Execution/Tools/Driver/QirCppInterop.cs new file mode 100644 index 00000000000..b4da38b9676 --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirCppInterop.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Quantum.QsCompiler.BondSchemas.Execution; + +#nullable enable + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + internal static class QirCppInterop + { + public static string? CliOptionTransformerMapName(DataType? dataType) => + dataType switch + { + DataType.BoolType => "BoolAsCharMap", + DataType.IntegerType => null, + DataType.DoubleType => null, + DataType.PauliType => "PauliMap", + DataType.RangeType => null, + DataType.ResultType => "ResultAsCharMap", + DataType.StringType => null, + DataType.ArrayType => null, + _ => throw new ArgumentException($"Invalid data type: {dataType}") + }; + + public static string CliOptionType(DataType? dataType) => + dataType switch + { + DataType.BoolType => "char", + DataType.IntegerType => "int64_t", + DataType.DoubleType => "double_t", + DataType.PauliType => "PauliId", + DataType.RangeType => "RangeTuple", + DataType.ResultType => "char", + DataType.StringType => "string", + DataType.ArrayType => throw new NotSupportedException($"{DataType.ArrayType} does not match to a specific CLI option variable type"), + _ => throw new ArgumentException($"Invalid data type: {dataType}") + }; + + public static string? CliOptionTypeToInteropTypeTranslator(DataType? dataType) => + dataType switch + { + DataType.BoolType => null, + DataType.IntegerType => null, + DataType.DoubleType => null, + DataType.PauliType => "TranslatePauliToChar", + DataType.RangeType => "TranslateRangeTupleToInteropRangePointer", + DataType.ResultType => null, + DataType.StringType => "TranslateStringToCharBuffer", + DataType.ArrayType => throw new NotSupportedException($"{DataType.ArrayType} does not match to a specific CLI option variable type"), + _ => throw new ArgumentException($"Invalid data type: {dataType}") + }; + + public static string? CliOptionVariableDefaultValue(DataType? dataType) => + dataType switch + { + DataType.BoolType => "InteropFalseAsChar", + DataType.IntegerType => "0", + DataType.DoubleType => "0.0", + DataType.PauliType => "PauliId::PauliId_I", + DataType.RangeType => null, + DataType.ResultType => "InteropResultZeroAsChar", + DataType.StringType => null, + DataType.ArrayType => null, + _ => throw new ArgumentException($"Invalid data type: {dataType}") + }; + + public static string InteropType(DataType? dataType) => + dataType switch + { + DataType.BoolType => "char", + DataType.IntegerType => "int64_t", + DataType.DoubleType => "double_t", + DataType.PauliType => "char", + DataType.RangeType => "InteropRange*", + DataType.ResultType => "char", + DataType.StringType => "const char*", + DataType.ArrayType => "InteropArray*", + _ => throw new ArgumentException($"Invalid data type: {dataType}") + }; + } + + internal static class ArgumentCppExtensions + { + public static string CliOptionDescription(this Parameter @this) => + $"Option to provide a value for the {@this.Name} parameter"; + + public static string CliOptionName(this Parameter @this) + { + if (String.IsNullOrEmpty(@this.Name)) + { + throw new InvalidOperationException($"Invalid parameter name '{@this.Name}'"); + } + + return @this.Name.Length == 1 ? $"-{@this.Name}" : $"--{@this.Name}"; + } + + public static string? CliOptionTransformerMapName(this Parameter @this) => + @this.Type switch + { + DataType.ArrayType => QirCppInterop.CliOptionTransformerMapName(@this.ArrayType), + _ => QirCppInterop.CliOptionTransformerMapName(@this.Type) + }; + + public static string CliOptionType(this Parameter @this) => + @this.Type switch + { + DataType.ArrayType => $"vector<{QirCppInterop.CliOptionType(@this.ArrayType)}>", + _ => QirCppInterop.CliOptionType(@this.Type) + }; + + public static string CliOptionVariableName(this Parameter @this) => $"{@this.Name}Cli"; + + public static string? CliOptionVariableDefaultValue(this Parameter @this) => + QirCppInterop.CliOptionVariableDefaultValue(@this.Type); + + public static string IntermediateVariableName(this Parameter @this) => $"{@this.Name}Intermediate"; + + public static string InteropVariableName(this Parameter @this) => $"{@this.Name}Interop"; + + public static string InteropType(this Parameter @this) => QirCppInterop.InteropType(@this.Type); + + public static string UniquePtrVariableName(this Parameter @this) => $"{@this.Name}UniquePtr"; + } + + internal static class EntryPointOperationCppExtension + { + public static bool ContainsArgumentType(this EntryPointOperation @this, DataType type) => + @this.Parameters.Where(arg => arg.Type == type).Any(); + + public static bool ContainsArrayType(this EntryPointOperation @this, DataType type) => + @this.Parameters.Where(arg => arg.ArrayType == type).Any(); + } +} diff --git a/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs b/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs index 20ff9bb5491..8f98b4a80f9 100644 --- a/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs +++ b/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs @@ -10,14 +10,16 @@ namespace Microsoft.Quantum.Qir.Tools.Driver { public class QirFullStateDriverGenerator: IQirDriverGenerator { - public async Task GenerateAsync(EntryPointOperation entryPoint, Stream stream) + private readonly QirCppDriverGenerator DriverGenerator; + public QirFullStateDriverGenerator() { - await Task.Run(() => QirDriverGeneration.GenerateQirDriverCpp(entryPoint, stream)); + DriverGenerator = new QirCppDriverGenerator(new QirFullStateSimulatorInitializer()); } - public string GetCommandLineArguments(ExecutionInformation executionInformation) - { - return QirDriverGeneration.GenerateCommandLineArguments(executionInformation); - } + public async Task GenerateAsync(EntryPointOperation entryPoint, Stream stream) => + await DriverGenerator.GenerateAsync(entryPoint, stream); + + public string GetCommandLineArguments(ExecutionInformation executionInformation) => + DriverGenerator.GetCommandLineArguments(executionInformation); } } diff --git a/src/Qir/Execution/Tools/Driver/QirFullStateSimulatorInitializer.cs b/src/Qir/Execution/Tools/Driver/QirFullStateSimulatorInitializer.cs new file mode 100644 index 00000000000..12bea984368 --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirFullStateSimulatorInitializer.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + public class QirFullStateSimulatorInitializer : IQirSimulatorInitializer + { + public string Generate() => new QirCppFullStateSimulatorInitializer().TransformText(); + + public IList Headers => new List { + "SimFactory.hpp" + }; + + public IList LinkLibraries => new List(); + } +} diff --git a/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj b/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj index f89615e8dc7..323a5fa3a1b 100644 --- a/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj +++ b/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj @@ -13,5 +13,33 @@ - + + + + TextTemplatingFilePreprocessor + QirCppDriver.cs + + + TextTemplatingFilePreprocessor + QirCppFullStateSimulatorInitializer.cs + + + + + + + + + + True + True + QirCppDriver.tt + + + False + True + QirCppFullStateSimulatorInitializer.tt + + +