From 54ee70060535db7616cc0c23faa776d797d3580d Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Mon, 19 Apr 2021 17:46:49 -0700 Subject: [PATCH 01/11] WIP --- Simulation.sln | 19 +++ src/Qir/Controller/Controller.cs | 29 ++--- src/Qir/Controller/Program.cs | 7 +- src/Qir/Controller/QirController.csproj | 4 + .../LoggerTests.cs | 93 ++++++++++++++ .../QirDriverGeneratorTests.cs | 63 +++++++++ .../QirExecutableGeneratorTests.cs | 120 ++++++++++++++++++ .../Tests.Microsoft.Quantum.Qir.Tools.csproj | 21 +++ .../UnitTest1.cs | 14 ++ .../Tests.Microsoft.Quantum.Qir.Tools/Util.cs | 30 +++++ .../Tools}/Executable/ClangClient.cs | 20 ++- .../Tools}/Executable/IClangClient.cs | 2 +- .../Executable/IQirExecutableGenerator.cs | 6 +- .../Executable/IQuantumExecutableRunner.cs | 6 +- .../Executable/QirExecutableGenerator.cs | 12 +- .../Executable/QuantumExecutableRunner.cs | 32 +++-- .../Tools/Microsoft.Quantum.Qir.Tools.csproj | 2 +- src/Qir/Execution/Tools/QirExecutable.cs | 50 +++++--- .../Execution/Tools/QirFullStateExecutable.cs | 17 ++- .../IQirSourceFileGenerator.cs | 2 +- .../QirSourceFileGenerator.cs | 17 +-- .../Tools}/Utility/Clock.cs | 0 .../Tools}/Utility/IClock.cs | 0 .../Tools}/Utility/ILogger.cs | 0 .../Tools}/Utility/Logger.cs | 0 25 files changed, 473 insertions(+), 93 deletions(-) create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Tests.Microsoft.Quantum.Qir.Tools.csproj create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs rename src/Qir/{Controller => Execution/Tools}/Executable/ClangClient.cs (64%) rename src/Qir/{Controller => Execution/Tools}/Executable/IClangClient.cs (88%) rename src/Qir/{Controller => Execution/Tools}/Executable/IQirExecutableGenerator.cs (80%) rename src/Qir/{Controller => Execution/Tools}/Executable/IQuantumExecutableRunner.cs (68%) rename src/Qir/{Controller => Execution/Tools}/Executable/QirExecutableGenerator.cs (83%) rename src/Qir/{Controller => Execution/Tools}/Executable/QuantumExecutableRunner.cs (50%) rename src/Qir/{Controller/Driver => Execution/Tools/SourceGeneration}/IQirSourceFileGenerator.cs (93%) rename src/Qir/{Controller/Driver => Execution/Tools/SourceGeneration}/QirSourceFileGenerator.cs (76%) rename src/Qir/{Controller => Execution/Tools}/Utility/Clock.cs (100%) rename src/Qir/{Controller => Execution/Tools}/Utility/IClock.cs (100%) rename src/Qir/{Controller => Execution/Tools}/Utility/ILogger.cs (100%) rename src/Qir/{Controller => Execution/Tools}/Utility/Logger.cs (100%) diff --git a/Simulation.sln b/Simulation.sln index 8bf6718fb86..1f28b42176e 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -123,6 +123,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Execution", "Execution", "{ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Quantum.Qir.Tools", "src\Qir\Execution\Tools\Microsoft.Quantum.Qir.Tools.csproj", "{C60226E3-98DE-4E92-AED4-B4A93D4CA063}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Microsoft.Quantum.Qir.Tools", "src\Qir\Execution\Tests.Microsoft.Quantum.Qir.Tools\Tests.Microsoft.Quantum.Qir.Tools.csproj", "{4794FC80-4594-403F-AFEC-4889EFE87EA0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -807,6 +809,22 @@ Global {C60226E3-98DE-4E92-AED4-B4A93D4CA063}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {C60226E3-98DE-4E92-AED4-B4A93D4CA063}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {C60226E3-98DE-4E92-AED4-B4A93D4CA063}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Debug|x64.ActiveCfg = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Debug|x64.Build.0 = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Release|Any CPU.Build.0 = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Release|x64.ActiveCfg = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.Release|x64.Build.0 = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -865,6 +883,7 @@ Global {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68} = {4E07F247-ED93-4497-8B58-022314308E67} {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {C60226E3-98DE-4E92-AED4-B4A93D4CA063} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} + {4794FC80-4594-403F-AFEC-4889EFE87EA0} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821} diff --git a/src/Qir/Controller/Controller.cs b/src/Qir/Controller/Controller.cs index 70378632128..8280624d592 100644 --- a/src/Qir/Controller/Controller.cs +++ b/src/Qir/Controller/Controller.cs @@ -3,11 +3,11 @@ using System; using System.IO; +using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Driver; -using Microsoft.Quantum.Qir.Executable; using Microsoft.Quantum.Qir.Model; +using Microsoft.Quantum.Qir.Tools; using Microsoft.Quantum.Qir.Utility; using QirExecutionWrapperSerialization = Microsoft.Quantum.QsCompiler.BondSchemas.QirExecutionWrapper.Protocols; @@ -15,7 +15,6 @@ namespace Microsoft.Quantum.Qir { public static class Controller { - private const string SourceDirectoryPath = "src"; private const string BinaryDirectoryPath = "bin"; private const string ExecutableName = "simulation.exe"; @@ -25,10 +24,7 @@ public static async Task ExecuteAsync( DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, FileInfo errorFile, - IQirSourceFileGenerator driverGenerator, - IQirExecutableGenerator executableGenerator, - IQuantumExecutableRunner executableRunner, - ILogger logger) + ILogger logger) { try { @@ -37,20 +33,15 @@ public static async Task ExecuteAsync( using var inputFileStream = inputFile.OpenRead(); var input = QirExecutionWrapperSerialization.DeserializeFromFastBinary(inputFileStream); - // Step 2: Create driver. - logger.LogInfo("Creating driver file."); - var sourceDirectory = new DirectoryInfo(SourceDirectoryPath); - await driverGenerator.GenerateQirSourceFilesAsync(sourceDirectory, input.EntryPoint, input.QirBytecode); - - // Step 3: Create executable. - logger.LogInfo("Compiling and linking executable."); - var binaryDirectory = new DirectoryInfo(BinaryDirectoryPath); + // Step 2: Create executable. + logger.LogInfo("Creating executable."); + var bytecodeArray = input.QirBytecode.Array.Skip(input.QirBytecode.Offset).Take(input.QirBytecode.Count).ToArray(); + var executable = new QirFullStateExecutable(input.EntryPoint, bytecodeArray); var executableFile = new FileInfo(Path.Combine(BinaryDirectoryPath, ExecutableName)); - await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory); + await executable.BuildAsync(libraryDirectory, includeDirectory, executableFile); - // Step 4: Run executable. - logger.LogInfo("Running executable."); - await executableRunner.RunExecutableAsync(executableFile, input.EntryPoint, outputFile); + // Step 3: Run executable. + await executable.RunAsync(executableFile, outputFile); } catch (Exception e) { diff --git a/src/Qir/Controller/Program.cs b/src/Qir/Controller/Program.cs index 6def5f9d223..df24e0632f9 100644 --- a/src/Qir/Controller/Program.cs +++ b/src/Qir/Controller/Program.cs @@ -4,8 +4,6 @@ using System.CommandLine; using System.CommandLine.Invocation; using System.IO; -using Microsoft.Quantum.Qir.Driver; -using Microsoft.Quantum.Qir.Executable; using Microsoft.Quantum.Qir.Utility; namespace Microsoft.Quantum.Qir @@ -15,9 +13,6 @@ class Program static void Main(string[] args) { var logger = new Logger(new Clock()); - var execGenerator = new QirExecutableGenerator(new ClangClient(logger), logger); - var driverGenerator = new QirSourceFileGenerator(logger); - var execRunner = new QuantumExecutableRunner(logger); logger.LogInfo("QIR controller beginning."); var rootCommand = new RootCommand( @@ -69,7 +64,7 @@ static void Main(string[] args) // Bind to a handler and invoke. rootCommand.Handler = CommandHandler.Create( async (input, output, libraryDirectory, includeDirectory, error) => - await Controller.ExecuteAsync(input, output, libraryDirectory, includeDirectory, error, driverGenerator, execGenerator, execRunner, logger)); + await Controller.ExecuteAsync(input, output, libraryDirectory, includeDirectory, error, logger)); rootCommand.Invoke(args); } } diff --git a/src/Qir/Controller/QirController.csproj b/src/Qir/Controller/QirController.csproj index e9574233c00..c4398261366 100644 --- a/src/Qir/Controller/QirController.csproj +++ b/src/Qir/Controller/QirController.csproj @@ -26,6 +26,10 @@ + + + + True diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs new file mode 100644 index 00000000000..ee7adfee70a --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using Microsoft.Quantum.Qir.Utility; +using Moq; +using Xunit; + +namespace Tests.QirController +{ + public class LoggerTests + { + private readonly Mock clockMock; + private readonly Logger logger; + + public LoggerTests() + { + clockMock = new Mock(); + logger = new Logger(clockMock.Object); + } + + [Fact] + public void TestLogInfo() + { + using var consoleOutput = new StringWriter(); + var message = "some message"; + var time = DateTimeOffset.MinValue; + clockMock.SetupGet(obj => obj.Now).Returns(time); + var expectedLog = $"{time} [INFO]: some message" + Environment.NewLine; + Console.SetOut(consoleOutput); + logger.LogInfo(message); + var actualLog = consoleOutput.ToString(); + Assert.Equal(expectedLog, actualLog); + } + + [Fact] + public void TestLogError() + { + using var consoleOutput = new StringWriter(); + var message = "some message"; + var time = DateTimeOffset.MinValue; + clockMock.SetupGet(obj => obj.Now).Returns(time); + var expectedLog = $"{time} [ERROR]: some message" + Environment.NewLine; + Console.SetOut(consoleOutput); + logger.LogError(message); + var actualLog = consoleOutput.ToString(); + Assert.Equal(expectedLog, actualLog); + } + + [Fact] + public void TestLogExceptionWithoutStackTrace() + { + using var consoleOutput = new StringWriter(); + var time = DateTimeOffset.MinValue; + clockMock.SetupGet(obj => obj.Now).Returns(time); + var exception = new InvalidOperationException(); + var expectedLog = $"{time} [ERROR]: " + + "Exception encountered: System.InvalidOperationException: " + + exception.Message + Environment.NewLine + exception.StackTrace + Environment.NewLine; + Console.SetOut(consoleOutput); + logger.LogException(exception); + var actualLog = consoleOutput.ToString(); + Assert.Equal(expectedLog, actualLog); + } + + [Fact] + public void TestLogExceptionWithStackTrace() + { + using var consoleOutput = new StringWriter(); + var time = DateTimeOffset.MinValue; + clockMock.SetupGet(obj => obj.Now).Returns(time); + Exception exception; + try + { + throw new InvalidOperationException(); + } + // Throw exception to generate stack trace. + catch (Exception thrownException) + { + exception = thrownException; + } + + var expectedLog = $"{time} [ERROR]: " + + "Exception encountered: System.InvalidOperationException: " + + exception.Message + Environment.NewLine + exception.StackTrace + Environment.NewLine; + Console.SetOut(consoleOutput); + logger.LogException(exception); + var actualLog = consoleOutput.ToString(); + Assert.Equal(expectedLog, actualLog); + } + } +} 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..da40aa5d912 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Tools.SourceGeneration; +using Microsoft.Quantum.Qir.Utility; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; +using Moq; +using Xunit; + +namespace Tests.QirController +{ + public class QirDriverGeneratorTests : IDisposable + { + private readonly Mock driverGeneratorMock; + private readonly DirectoryInfo sourceDirectory; + + public QirDriverGeneratorTests() + { + driverGeneratorMock = new Mock(Mock.Of()) { CallBase = true }; + driverGeneratorMock.Setup(obj => obj.GenerateQirDriverCppHelper(It.IsAny(), It.IsAny())); + sourceDirectory = new DirectoryInfo($"{Guid.NewGuid()}-source"); + } + + public void Dispose() + { + Util.DeleteDirectory(sourceDirectory); + } + + [Fact] + public async Task TestGenerateDriver() + { + var entryPoint = new EntryPointOperation() + { + Arguments = new List + { + new Argument() + { + Position = 0, + Name = "argname", + Values = new List { new ArgumentValue { String = "argvalue" } }, + } + } + }; + byte[] bytes = { 1, 2, 3, 4, 5 }; + var bytecode = new ArraySegment(bytes, 1, 3); + await driverGeneratorMock.Object.GenerateQirSourceFilesAsync(sourceDirectory, entryPoint, bytecode); + driverGeneratorMock.Verify(obj => obj.GenerateQirDriverCppHelper(It.Is(actualEntryPoint => Util.EntryPointsAreEqual(entryPoint, actualEntryPoint)), It.IsAny())); + + // Verify that the "bytecode" file was created correctly. + var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); + using var bytecodeFileStream = bytecodeFilePath.OpenRead(); + Assert.Equal(bytecodeFileStream.Length, bytecode.Count); + for (var i = 0; i < bytecodeFileStream.Length; ++i) + { + Assert.Equal(bytecode[i], bytecodeFileStream.ReadByte()); + } + } + } +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs new file mode 100644 index 00000000000..3d9da400687 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Executable; +using Microsoft.Quantum.Qir.Utility; +using Moq; +using Xunit; + +namespace Tests.QirController +{ + public class QirExecutableGeneratorTests : IDisposable + { + private readonly Mock clangClientMock; + private readonly QirExecutableGenerator executableGenerator; + private readonly DirectoryInfo sourceDirectory; + private readonly DirectoryInfo includeDirectory; + private readonly DirectoryInfo libraryDirectory; + private readonly DirectoryInfo binDirectory; + private readonly IList libraryFiles; + private readonly IList sourceFiles; + + public QirExecutableGeneratorTests() + { + clangClientMock = new Mock(); + executableGenerator = new QirExecutableGenerator(clangClientMock.Object, Mock.Of()); + + // Set up files. + var prefix = Guid.NewGuid().ToString(); + binDirectory = new DirectoryInfo($"{prefix}-bin"); + binDirectory.Create(); + libraryDirectory = new DirectoryInfo($"{prefix}-library"); + libraryDirectory.Create(); + libraryFiles = new List() + { + CreateFile("lib1", libraryDirectory, "lib1 contents"), + CreateFile("lib2", libraryDirectory, "lib2 contents"), + }; + includeDirectory = new DirectoryInfo($"{prefix}-include"); + includeDirectory.Create(); + sourceDirectory = new DirectoryInfo($"{prefix}-source"); + sourceDirectory.Create(); + sourceFiles = new List() + { + CreateFile("src1.cpp", sourceDirectory, "src1 contents"), + CreateFile("src2.bc", sourceDirectory, "src2 contents"), + }; + } + + public void Dispose() + { + Util.DeleteDirectory(sourceDirectory); + Util.DeleteDirectory(includeDirectory); + Util.DeleteDirectory(libraryDirectory); + Util.DeleteDirectory(binDirectory); + } + + [Fact] + public async Task TestGenerateExecutable() + { + string[] expectedLibraries = { + "Microsoft.Quantum.Qir.Runtime", + "Microsoft.Quantum.Qir.QSharp.Foundation", + "Microsoft.Quantum.Qir.QSharp.Core" + }; + + var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); + await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory); + + // Verify invocation of clang. + clangClientMock.Verify(obj => obj.CreateExecutableAsync( + It.Is(s => s.SequenceEqual(sourceFiles.Select(fileInfo => fileInfo.FullName))), + It.Is(s => s.SequenceEqual(expectedLibraries)), + libraryDirectory.FullName, + includeDirectory.FullName, + executableFile.FullName)); + + // Verify files were copied. + Assert.True(FilesWereCopied(libraryFiles.ToArray(), binDirectory)); + } + + private static FileInfo CreateFile(string fileName, DirectoryInfo directory, string contents) + { + var filePath = Path.Combine(directory.FullName, fileName); + var fileInfo = new FileInfo(filePath); + using var fileStream = fileInfo.OpenWrite(); + using var streamWriter = new StreamWriter(fileStream); + streamWriter.Write(contents); + return fileInfo; + } + + private static bool FilesWereCopied(FileInfo[] files, DirectoryInfo destinationDirectory) + { + var destinationDirectoryFiles = destinationDirectory.GetFiles(); + foreach (var file in files) + { + var copiedFile = destinationDirectoryFiles.FirstOrDefault(fileInfo => fileInfo.Name == file.Name); + if (copiedFile == null) + { + return false; + } + + using var originalFileReader = file.OpenText(); + var originalFileContents = originalFileReader.ReadToEnd(); + using var copiedFileReader = copiedFile.OpenText(); + var copiedFileContents = copiedFileReader.ReadToEnd(); + if (originalFileContents != copiedFileContents) + { + return false; + } + } + + return true; + } + } +} 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 new file mode 100644 index 00000000000..e4f0be4e5f2 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Tests.Microsoft.Quantum.Qir.Tools.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs new file mode 100644 index 00000000000..e4a276fe6c7 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Tests.Microsoft.Quantum.Qir.Tools +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs new file mode 100644 index 00000000000..476848705ea --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Reflection; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Tests.QirController +{ + public static class Util + { + public static void DeleteDirectory(DirectoryInfo directory) + { + foreach (var file in directory.GetFiles()) + { + file.Delete(); + } + directory.Delete(); + } + + + public static bool EntryPointsAreEqual(EntryPointOperation entryPointA, EntryPointOperation entryPointB) + { + var method = typeof(Extensions) + .GetMethod("ValueEquals", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(EntryPointOperation), typeof(EntryPointOperation) }, null); + object[] parameters = { entryPointA, entryPointB }; + return (bool)method.Invoke(null, parameters); + } + } +} diff --git a/src/Qir/Controller/Executable/ClangClient.cs b/src/Qir/Execution/Tools/Executable/ClangClient.cs similarity index 64% rename from src/Qir/Controller/Executable/ClangClient.cs rename to src/Qir/Execution/Tools/Executable/ClangClient.cs index 81bd435aff4..479469dceb6 100644 --- a/src/Qir/Controller/Executable/ClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/ClangClient.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.CommandLine.Invocation; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; -namespace Microsoft.Quantum.Qir.Executable +namespace Microsoft.Quantum.Qir.Tools.Executable { public class ClangClient : IClangClient { @@ -25,11 +25,17 @@ public async Task CreateExecutableAsync(string[] inputFiles, string[] libraries, var librariesArg = $"{LinkFlag} {string.Join(LinkFlag, libraries)}"; var arguments = $"-v {inputsArg} -I {includePath} -L {libraryPath} {librariesArg} -o {outputPath}"; logger.LogInfo($"Invoking clang with the following arguments: {arguments}"); - var result = await Process.ExecuteAsync( - "clang", - arguments, - stdOut: s => { logger.LogInfo("clang: " + s); }, - stdErr: s => { logger.LogError("clang: " + s); }); + var taskCompletionSource = new TaskCompletionSource(); + using var process = new Process(); + process.StartInfo = new ProcessStartInfo + { + FileName = "clang", + Arguments = arguments, + }; + process.EnableRaisingEvents = true; + process.Exited += (sender, args) => { taskCompletionSource.SetResult(true); }; + process.Start(); + await taskCompletionSource.Task; } } } diff --git a/src/Qir/Controller/Executable/IClangClient.cs b/src/Qir/Execution/Tools/Executable/IClangClient.cs similarity index 88% rename from src/Qir/Controller/Executable/IClangClient.cs rename to src/Qir/Execution/Tools/Executable/IClangClient.cs index 7e7a8e1c95d..e7eacd96db1 100644 --- a/src/Qir/Controller/Executable/IClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/IClangClient.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; -namespace Microsoft.Quantum.Qir.Executable +namespace Microsoft.Quantum.Qir.Tools.Executable { /// /// Wraps the 'clang' tool used for compilation. diff --git a/src/Qir/Controller/Executable/IQirExecutableGenerator.cs b/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs similarity index 80% rename from src/Qir/Controller/Executable/IQirExecutableGenerator.cs rename to src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs index e8ce44f7b7a..88328b0a85c 100644 --- a/src/Qir/Controller/Executable/IQirExecutableGenerator.cs +++ b/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -namespace Microsoft.Quantum.Qir.Executable +namespace Microsoft.Quantum.Qir.Tools.Executable { public interface IQirExecutableGenerator { @@ -15,7 +16,8 @@ public interface IQirExecutableGenerator /// Location of the source files. /// Location of the libraries that must be linked. /// Location of the headers that must be included. + /// Libraries to link. /// - public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory); + public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, IList linkLibraries); } } diff --git a/src/Qir/Controller/Executable/IQuantumExecutableRunner.cs b/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs similarity index 68% rename from src/Qir/Controller/Executable/IQuantumExecutableRunner.cs rename to src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs index 135fbbbfb4f..9db39ba6e16 100644 --- a/src/Qir/Controller/Executable/IQuantumExecutableRunner.cs +++ b/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -namespace Microsoft.Quantum.Qir.Executable +namespace Microsoft.Quantum.Qir.Tools.Executable { public interface IQuantumExecutableRunner { @@ -13,9 +13,9 @@ public interface IQuantumExecutableRunner /// Runs a quantum program executable with the given arguments. /// /// Location of the executable to run. - /// Entry point and arguments to pass. /// Location to write program output. + /// Arguments to supply the program with. /// - Task RunExecutableAsync(FileInfo executableFile, EntryPointOperation entryPointOperation, FileInfo outputFile); + Task RunExecutableAsync(FileInfo executableFile, FileInfo outputFile, string arguments); } } diff --git a/src/Qir/Controller/Executable/QirExecutableGenerator.cs b/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs similarity index 83% rename from src/Qir/Controller/Executable/QirExecutableGenerator.cs rename to src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs index 9445f300115..b99d471e7c1 100644 --- a/src/Qir/Controller/Executable/QirExecutableGenerator.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs @@ -1,20 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; -namespace Microsoft.Quantum.Qir.Executable +namespace Microsoft.Quantum.Qir.Tools.Executable { public class QirExecutableGenerator : IQirExecutableGenerator { - private static readonly string[] LibrariesToLink = { - "Microsoft.Quantum.Qir.Runtime", - "Microsoft.Quantum.Qir.QSharp.Foundation", - "Microsoft.Quantum.Qir.QSharp.Core" - }; private readonly IClangClient clangClient; private readonly ILogger logger; @@ -24,7 +20,7 @@ public QirExecutableGenerator(IClangClient clangClient, ILogger logger) this.logger = logger; } - public async Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory) + public async Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, IList linkLibraries) { // Wrap in a Task.Run because FileInfo methods are not asynchronous. await Task.Run(async () => @@ -42,7 +38,7 @@ await Task.Run(async () => } var inputFiles = sourceDirectory.GetFiles().Select(fileInfo => fileInfo.FullName).ToArray(); - await clangClient.CreateExecutableAsync(inputFiles, LibrariesToLink, libraryDirectory.FullName, includeDirectory.FullName, executableFile.FullName); + await clangClient.CreateExecutableAsync(inputFiles, linkLibraries.ToArray(), libraryDirectory.FullName, includeDirectory.FullName, executableFile.FullName); }); } diff --git a/src/Qir/Controller/Executable/QuantumExecutableRunner.cs b/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs similarity index 50% rename from src/Qir/Controller/Executable/QuantumExecutableRunner.cs rename to src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs index 8ce2bcd41d7..6fd378f3755 100644 --- a/src/Qir/Controller/Executable/QuantumExecutableRunner.cs +++ b/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs @@ -1,14 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.CommandLine.Invocation; +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -namespace Microsoft.Quantum.Qir.Executable +namespace Microsoft.Quantum.Qir.Tools.Executable { public class QuantumExecutableRunner : IQuantumExecutableRunner { @@ -19,23 +17,31 @@ public QuantumExecutableRunner(ILogger logger) this.logger = logger; } - public async Task RunExecutableAsync(FileInfo executableFile, EntryPointOperation entryPointOperation, FileInfo outputFile) + public async Task RunExecutableAsync(FileInfo executableFile, FileInfo outputFile, string arguments) { - var arguments = QirDriverGeneration.GenerateCommandLineArguments(entryPointOperation.Arguments); logger.LogInfo($"Invoking executable {executableFile.FullName} with the following arguments: {arguments}"); - arguments = $"--simulation-output {outputFile.FullName} {arguments}"; if (outputFile.Exists) { outputFile.Delete(); } outputFile.Create().Dispose(); - var result = await Process.ExecuteAsync( - executableFile.FullName, - arguments, - stdOut: s => { logger.LogInfo("executable: " + s); }, - stdErr: s => { logger.LogError("executable: " + s); }); - logger.LogInfo($"Executable has finished running. Result code: {result}"); + using var process = new Process(); + process.StartInfo = new ProcessStartInfo + { + FileName = executableFile.FullName, + Arguments = arguments, + RedirectStandardOutput = true, + }; + + process.EnableRaisingEvents = true; + process.Start(); + var output = await process.StandardOutput.ReadToEndAsync(); + process.WaitForExit(); + using var outputFileStream = outputFile.OpenWrite(); + using var streamWriter = new StreamWriter(outputFileStream); + await streamWriter.WriteAsync(output); + logger.LogInfo($"Executable has finished running. Result code: {process.ExitCode}"); } } } diff --git a/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj b/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj index 5ad95e9541e..9cad908d26d 100644 --- a/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj +++ b/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Qir/Execution/Tools/QirExecutable.cs b/src/Qir/Execution/Tools/QirExecutable.cs index de641f4b174..b2ef6d03c61 100644 --- a/src/Qir/Execution/Tools/QirExecutable.cs +++ b/src/Qir/Execution/Tools/QirExecutable.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Quantum.Qir.Tools.Executable; +using Microsoft.Quantum.Qir.Tools.SourceGeneration; +using Microsoft.Quantum.Qir.Utility; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -14,8 +16,13 @@ namespace Microsoft.Quantum.Qir.Tools /// public abstract class QirExecutable { - private readonly EntryPointOperation EntryPointOperation; - private readonly byte[] QirBytecode; + protected EntryPointOperation EntryPointOperation { get; } + private readonly byte[] qirBytecode; + private const string SourceDirectoryPath = "src"; + private readonly ILogger logger; + private readonly IQuantumExecutableRunner runner; + private readonly IQirSourceFileGenerator sourceGenerator; + private readonly IQirExecutableGenerator executableGenerator; /// /// Constructor for the QirExecutable class. @@ -24,8 +31,22 @@ public abstract class QirExecutable /// QIR bytecode used to build the executable. public QirExecutable(EntryPointOperation entryPoint, byte[] qirBytecode) { - this.EntryPointOperation = entryPoint; - this.QirBytecode = qirBytecode; + EntryPointOperation = entryPoint; + this.qirBytecode = qirBytecode; + logger = new Logger(new Clock()); + runner = new QuantumExecutableRunner(logger); + sourceGenerator = new QirSourceFileGenerator(logger, GenerateDriverAsync); + var clangClient = new ClangClient(logger); + executableGenerator = new QirExecutableGenerator(clangClient, logger); + } + + internal QirExecutable(EntryPointOperation entryPoint, byte[] qirBytecode, ILogger logger, IQirSourceFileGenerator sourceGenerator, IQuantumExecutableRunner runner) + { + EntryPointOperation = entryPoint; + this.qirBytecode = qirBytecode; + this.logger = logger; + this.runner = runner; + this.sourceGenerator = sourceGenerator; } /// @@ -34,25 +55,24 @@ public QirExecutable(EntryPointOperation entryPoint, byte[] qirBytecode) /// Directory where the libraries to link to are located. /// Directory where the headers needed for compilation are located. /// File to write the executable to. - public Task BuildAsync(DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, FileInfo executable) + public async Task BuildAsync(DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, FileInfo executable) { - throw new NotImplementedException(); + var linkLibraries = GetLinkLibraries(); + var sourceDirectory = new DirectoryInfo(SourceDirectoryPath); + await sourceGenerator.GenerateQirSourceFilesAsync(sourceDirectory, EntryPointOperation, qirBytecode); + await executableGenerator.GenerateExecutableAsync(executable, sourceDirectory, libraryDirectory, includeDirectory, linkLibraries); } - // TODO: How arguments are passed to this API will change. - public Task RunAsync(FileInfo executable, DirectoryInfo librariesDirectory, IList arguments) + public async Task RunAsync(FileInfo executable, FileInfo outputFile) { - throw new NotImplementedException(); + var stringArguments = GetCommandLineArguments(EntryPointOperation.Arguments); + await runner.RunExecutableAsync(executable, outputFile, stringArguments); } - // TODO: To be used by BuildAsync. protected abstract Task GenerateDriverAsync(Stream driver); - // TODO: To be used by RunAsync. - // TODO: How arguments are passed to this API will change. - protected abstract string GetCommandLineArguments(IList arguments); + protected abstract string GetCommandLineArguments(IList arguments); - // TODO: To be used by BuildAsync. protected abstract IList GetLinkLibraries(); } } diff --git a/src/Qir/Execution/Tools/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/QirFullStateExecutable.cs index 882572810bc..0a9aab85546 100644 --- a/src/Qir/Execution/Tools/QirFullStateExecutable.cs +++ b/src/Qir/Execution/Tools/QirFullStateExecutable.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Quantum.QsCompiler; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -20,20 +20,23 @@ public QirFullStateExecutable(EntryPointOperation entryPoint, byte[] qirBytecode { } - protected override Task GenerateDriverAsync(Stream driver) + protected override async Task GenerateDriverAsync(Stream driver) { - throw new NotImplementedException(); + await Task.Run(() => QirDriverGeneration.GenerateQirDriverCpp(EntryPointOperation, driver)); } - // TODO: How arguments are passed to this API will change. - protected override string GetCommandLineArguments(IList arguments) + protected override string GetCommandLineArguments(IList arguments) { - throw new NotImplementedException(); + return QirDriverGeneration.GenerateCommandLineArguments(arguments); } protected override IList GetLinkLibraries() { - throw new NotImplementedException(); + return new List { + "Microsoft.Quantum.Qir.Runtime", + "Microsoft.Quantum.Qir.QSharp.Foundation", + "Microsoft.Quantum.Qir.QSharp.Core" + }; } } } diff --git a/src/Qir/Controller/Driver/IQirSourceFileGenerator.cs b/src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs similarity index 93% rename from src/Qir/Controller/Driver/IQirSourceFileGenerator.cs rename to src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs index 92e0449c77a..e2f4668c49a 100644 --- a/src/Qir/Controller/Driver/IQirSourceFileGenerator.cs +++ b/src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -namespace Microsoft.Quantum.Qir.Driver +namespace Microsoft.Quantum.Qir.Tools.SourceGeneration { public interface IQirSourceFileGenerator { diff --git a/src/Qir/Controller/Driver/QirSourceFileGenerator.cs b/src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs similarity index 76% rename from src/Qir/Controller/Driver/QirSourceFileGenerator.cs rename to src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs index 7a7cfe891a2..7d8af3c0915 100644 --- a/src/Qir/Controller/Driver/QirSourceFileGenerator.cs +++ b/src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs @@ -5,20 +5,23 @@ using System.IO; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -namespace Microsoft.Quantum.Qir.Driver +namespace Microsoft.Quantum.Qir.Tools.SourceGeneration { public class QirSourceFileGenerator : IQirSourceFileGenerator { + public delegate Task DriverGenerationMethod(Stream stream); + private const string BytecodeFileName = "qir.bc"; private const string DriverFileName = "driver.cpp"; private readonly ILogger logger; + private readonly DriverGenerationMethod driverGenerationMethod; - public QirSourceFileGenerator(ILogger logger) + public QirSourceFileGenerator(ILogger logger, DriverGenerationMethod driverGenerationMethod) { this.logger = logger; + this.driverGenerationMethod = driverGenerationMethod; } public async Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode) @@ -34,7 +37,7 @@ public async Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, Ent // Create driver. var driverFile = new FileInfo(Path.Combine(sourceDirectory.FullName, DriverFileName)); using var driverFileStream = driverFile.OpenWrite(); - GenerateQirDriverCppHelper(entryPointOperation, driverFileStream); + await driverGenerationMethod.Invoke(driverFileStream); logger.LogInfo($"Created driver file at {driverFile.FullName}."); // Create bytecode file. @@ -43,11 +46,5 @@ public async Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, Ent await bytecodeFileStream.WriteAsync(bytecode.Array, bytecode.Offset, bytecode.Count); logger.LogInfo($"Created bytecode file at {bytecodeFile.FullName}."); } - - // Virtual method wrapper is to enable mocking in unit tests. - public virtual void GenerateQirDriverCppHelper(EntryPointOperation entryPointOperation, Stream stream) - { - QirDriverGeneration.GenerateQirDriverCpp(entryPointOperation, stream); - } } } diff --git a/src/Qir/Controller/Utility/Clock.cs b/src/Qir/Execution/Tools/Utility/Clock.cs similarity index 100% rename from src/Qir/Controller/Utility/Clock.cs rename to src/Qir/Execution/Tools/Utility/Clock.cs diff --git a/src/Qir/Controller/Utility/IClock.cs b/src/Qir/Execution/Tools/Utility/IClock.cs similarity index 100% rename from src/Qir/Controller/Utility/IClock.cs rename to src/Qir/Execution/Tools/Utility/IClock.cs diff --git a/src/Qir/Controller/Utility/ILogger.cs b/src/Qir/Execution/Tools/Utility/ILogger.cs similarity index 100% rename from src/Qir/Controller/Utility/ILogger.cs rename to src/Qir/Execution/Tools/Utility/ILogger.cs diff --git a/src/Qir/Controller/Utility/Logger.cs b/src/Qir/Execution/Tools/Utility/Logger.cs similarity index 100% rename from src/Qir/Controller/Utility/Logger.cs rename to src/Qir/Execution/Tools/Utility/Logger.cs From 6b1642ee3162048ae8c5f15db81114cf0d880563 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Mon, 19 Apr 2021 17:48:58 -0700 Subject: [PATCH 02/11] Remove controller unit tests --- .../Tests.QirController/ControllerTests.cs | 204 ------------------ .../Tests.QirController/LoggerTests.cs | 93 -------- .../QirDriverGeneratorTests.cs | 63 ------ .../QirExecutableGeneratorTests.cs | 120 ----------- .../Tests.QirController.csproj | 20 -- .../Controller/Tests.QirController/Util.cs | 30 --- 6 files changed, 530 deletions(-) delete mode 100644 src/Qir/Controller/Tests.QirController/ControllerTests.cs delete mode 100644 src/Qir/Controller/Tests.QirController/LoggerTests.cs delete mode 100644 src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs delete mode 100644 src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs delete mode 100644 src/Qir/Controller/Tests.QirController/Tests.QirController.csproj delete mode 100644 src/Qir/Controller/Tests.QirController/Util.cs diff --git a/src/Qir/Controller/Tests.QirController/ControllerTests.cs b/src/Qir/Controller/Tests.QirController/ControllerTests.cs deleted file mode 100644 index 6e85b07976b..00000000000 --- a/src/Qir/Controller/Tests.QirController/ControllerTests.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir; -using Microsoft.Quantum.Qir.Driver; -using Microsoft.Quantum.Qir.Executable; -using Microsoft.Quantum.Qir.Model; -using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using Microsoft.Quantum.QsCompiler.BondSchemas.QirExecutionWrapper; -using Moq; -using Xunit; -using QirExecutionWrapperSerialization = Microsoft.Quantum.QsCompiler.BondSchemas.QirExecutionWrapper.Protocols; - -namespace Tests.QirController -{ - public class ControllerTests : IDisposable - { - private Mock driverGeneratorMock; - private Mock executableGeneratorMock; - private Mock executableRunnerMock; - private Mock loggerMock; - private FileInfo inputFile; - private FileInfo bytecodeFile; - private FileInfo errorFile; - private FileInfo outputFile; - private QirExecutionWrapper input; - - public ControllerTests() - { - driverGeneratorMock = new Mock(); - executableGeneratorMock = new Mock(); - executableRunnerMock = new Mock(); - inputFile = new FileInfo($"{Guid.NewGuid()}-input"); - bytecodeFile = new FileInfo($"{Guid.NewGuid()}-bytecode"); - errorFile = new FileInfo($"{Guid.NewGuid()}-error"); - outputFile = new FileInfo($"{Guid.NewGuid()}-output"); - loggerMock = new Mock(); - - // Create a QirExecutableWrapper to be used by the tests. - byte[] bytecode = { 1, 2, 3, 4, 5 }; - input = new QirExecutionWrapper() - { - EntryPoint = new EntryPointOperation() - { - Arguments = new List - { - new Argument() - { - Position = 0, - Name = "argname", - Values = new List { new ArgumentValue { String = "argvalue" } }, - } - } - }, - QirBytecode = new ArraySegment(bytecode, 1, 3), - }; - using var fileStream = inputFile.OpenWrite(); - QirExecutionWrapperSerialization.SerializeToFastBinary(input, fileStream); - } - - public void Dispose() - { - inputFile.Delete(); - bytecodeFile.Delete(); - errorFile.Delete(); - outputFile.Delete(); - } - - [Fact] - public async Task TestExecute() - { - var libraryDirectory = new DirectoryInfo("libraries"); - var includeDirectory = new DirectoryInfo("includes"); - FileInfo actualExecutableFile = null; - Action generateExecutableCallback = async (executableFile, srcDir, libDir, inclDir) => - { - actualExecutableFile = executableFile; - await Task.CompletedTask; - }; - executableGeneratorMock.Setup(obj => obj.GenerateExecutableAsync( - It.IsAny(), - It.IsAny(), - It.Is(actualLibraryDirectory => actualLibraryDirectory.FullName == libraryDirectory.FullName), - It.Is(actualIncludeDirectory => actualIncludeDirectory.FullName == includeDirectory.FullName))).Callback(generateExecutableCallback); - - await Controller.ExecuteAsync( - inputFile, - outputFile, - libraryDirectory, - includeDirectory, - errorFile, - driverGeneratorMock.Object, - executableGeneratorMock.Object, - executableRunnerMock.Object, - loggerMock.Object); - - // Verify driver was created. - driverGeneratorMock.Verify(obj => obj.GenerateQirSourceFilesAsync( - It.IsAny(), - It.Is(entryPoint => Util.EntryPointsAreEqual(entryPoint, input.EntryPoint)), - It.Is>(bytecode => BytecodesAreEqual(bytecode, input.QirBytecode)))); - - // Verify executable was generated. - executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync( - It.IsAny(), - It.IsAny(), - It.Is(actualLibraryDirectory => actualLibraryDirectory.FullName == libraryDirectory.FullName), - It.Is(actualIncludeDirectory => actualIncludeDirectory.FullName == includeDirectory.FullName))); - Assert.NotNull(actualExecutableFile); - - // Verify executable was run. - executableRunnerMock.Verify(obj => obj.RunExecutableAsync( - It.Is(executableFile => actualExecutableFile.FullName == executableFile.FullName), - It.Is(entryPoint => Util.EntryPointsAreEqual(entryPoint, input.EntryPoint)), - It.Is(actualOutputFile => actualOutputFile.FullName == outputFile.FullName))); - } - - [Fact] - public async Task TestExecuteEncountersGenericException() - { - var libraryDirectory = new DirectoryInfo("libraries"); - var includeDirectory = new DirectoryInfo("includes"); - executableGeneratorMock.Setup(obj => obj.GenerateExecutableAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ThrowsAsync(new Exception("exception message")); - - // Execute controller. - await Controller.ExecuteAsync( - inputFile, - outputFile, - libraryDirectory, - includeDirectory, - errorFile, - driverGeneratorMock.Object, - executableGeneratorMock.Object, - executableRunnerMock.Object, - loggerMock.Object); - - // Verify error file was created and contains the error. - Assert.True(errorFile.Exists); - using var errorFileStream = errorFile.OpenRead(); - using var streamReader = new StreamReader(errorFileStream); - var errorFileContents = await streamReader.ReadToEndAsync(); - var error = JsonSerializer.Deserialize(errorFileContents); - Assert.Equal(ErrorMessages.InternalError, error.Message); - Assert.Equal(Constant.ErrorCode.InternalError, error.Code); - } - - [Fact] - public async Task TestExecuteEncountersControllerException() - { - var exceptionMessage = "exception message"; - var errorCode = "error code"; - var libraryDirectory = new DirectoryInfo("libraries"); - var includeDirectory = new DirectoryInfo("includes"); - executableGeneratorMock.Setup(obj => obj.GenerateExecutableAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ThrowsAsync(new ControllerException(exceptionMessage, errorCode)); - - // Execute controller. - await Controller.ExecuteAsync( - inputFile, - outputFile, - libraryDirectory, - includeDirectory, - errorFile, - driverGeneratorMock.Object, - executableGeneratorMock.Object, - executableRunnerMock.Object, - loggerMock.Object); - - // Verify error file was created and contains the error. - Assert.True(errorFile.Exists); - using var errorFileStream = errorFile.OpenRead(); - using var streamReader = new StreamReader(errorFileStream); - var errorFileContents = await streamReader.ReadToEndAsync(); - var error = JsonSerializer.Deserialize(errorFileContents); - Assert.Equal(exceptionMessage, error.Message); - Assert.Equal(errorCode, error.Code); - } - - private bool BytecodesAreEqual(ArraySegment bytecodeA, ArraySegment bytecodeB) - { - if (bytecodeA.Count != bytecodeB.Count) - { - return false; - } - - for (var i = 0; i < bytecodeA.Count; ++i) - { - if (bytecodeA[i] != bytecodeB[i]) - { - return false; - } - } - - return true; - } - } -} diff --git a/src/Qir/Controller/Tests.QirController/LoggerTests.cs b/src/Qir/Controller/Tests.QirController/LoggerTests.cs deleted file mode 100644 index ee7adfee70a..00000000000 --- a/src/Qir/Controller/Tests.QirController/LoggerTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using Microsoft.Quantum.Qir.Utility; -using Moq; -using Xunit; - -namespace Tests.QirController -{ - public class LoggerTests - { - private readonly Mock clockMock; - private readonly Logger logger; - - public LoggerTests() - { - clockMock = new Mock(); - logger = new Logger(clockMock.Object); - } - - [Fact] - public void TestLogInfo() - { - using var consoleOutput = new StringWriter(); - var message = "some message"; - var time = DateTimeOffset.MinValue; - clockMock.SetupGet(obj => obj.Now).Returns(time); - var expectedLog = $"{time} [INFO]: some message" + Environment.NewLine; - Console.SetOut(consoleOutput); - logger.LogInfo(message); - var actualLog = consoleOutput.ToString(); - Assert.Equal(expectedLog, actualLog); - } - - [Fact] - public void TestLogError() - { - using var consoleOutput = new StringWriter(); - var message = "some message"; - var time = DateTimeOffset.MinValue; - clockMock.SetupGet(obj => obj.Now).Returns(time); - var expectedLog = $"{time} [ERROR]: some message" + Environment.NewLine; - Console.SetOut(consoleOutput); - logger.LogError(message); - var actualLog = consoleOutput.ToString(); - Assert.Equal(expectedLog, actualLog); - } - - [Fact] - public void TestLogExceptionWithoutStackTrace() - { - using var consoleOutput = new StringWriter(); - var time = DateTimeOffset.MinValue; - clockMock.SetupGet(obj => obj.Now).Returns(time); - var exception = new InvalidOperationException(); - var expectedLog = $"{time} [ERROR]: " + - "Exception encountered: System.InvalidOperationException: " + - exception.Message + Environment.NewLine + exception.StackTrace + Environment.NewLine; - Console.SetOut(consoleOutput); - logger.LogException(exception); - var actualLog = consoleOutput.ToString(); - Assert.Equal(expectedLog, actualLog); - } - - [Fact] - public void TestLogExceptionWithStackTrace() - { - using var consoleOutput = new StringWriter(); - var time = DateTimeOffset.MinValue; - clockMock.SetupGet(obj => obj.Now).Returns(time); - Exception exception; - try - { - throw new InvalidOperationException(); - } - // Throw exception to generate stack trace. - catch (Exception thrownException) - { - exception = thrownException; - } - - var expectedLog = $"{time} [ERROR]: " + - "Exception encountered: System.InvalidOperationException: " + - exception.Message + Environment.NewLine + exception.StackTrace + Environment.NewLine; - Console.SetOut(consoleOutput); - logger.LogException(exception); - var actualLog = consoleOutput.ToString(); - Assert.Equal(expectedLog, actualLog); - } - } -} diff --git a/src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs b/src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs deleted file mode 100644 index e93837e62ea..00000000000 --- a/src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Driver; -using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using Moq; -using Xunit; - -namespace Tests.QirController -{ - public class QirDriverGeneratorTests : IDisposable - { - private readonly Mock driverGeneratorMock; - private readonly DirectoryInfo sourceDirectory; - - public QirDriverGeneratorTests() - { - driverGeneratorMock = new Mock(Mock.Of()) { CallBase = true }; - driverGeneratorMock.Setup(obj => obj.GenerateQirDriverCppHelper(It.IsAny(), It.IsAny())); - sourceDirectory = new DirectoryInfo($"{Guid.NewGuid()}-source"); - } - - public void Dispose() - { - Util.DeleteDirectory(sourceDirectory); - } - - [Fact] - public async Task TestGenerateDriver() - { - var entryPoint = new EntryPointOperation() - { - Arguments = new List - { - new Argument() - { - Position = 0, - Name = "argname", - Values = new List { new ArgumentValue { String = "argvalue" } }, - } - } - }; - byte[] bytes = { 1, 2, 3, 4, 5 }; - var bytecode = new ArraySegment(bytes, 1, 3); - await driverGeneratorMock.Object.GenerateQirSourceFilesAsync(sourceDirectory, entryPoint, bytecode); - driverGeneratorMock.Verify(obj => obj.GenerateQirDriverCppHelper(It.Is(actualEntryPoint => Util.EntryPointsAreEqual(entryPoint, actualEntryPoint)), It.IsAny())); - - // Verify that the "bytecode" file was created correctly. - var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); - using var bytecodeFileStream = bytecodeFilePath.OpenRead(); - Assert.Equal(bytecodeFileStream.Length, bytecode.Count); - for (var i = 0; i < bytecodeFileStream.Length; ++i) - { - Assert.Equal(bytecode[i], bytecodeFileStream.ReadByte()); - } - } - } -} diff --git a/src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs b/src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs deleted file mode 100644 index 3d9da400687..00000000000 --- a/src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Executable; -using Microsoft.Quantum.Qir.Utility; -using Moq; -using Xunit; - -namespace Tests.QirController -{ - public class QirExecutableGeneratorTests : IDisposable - { - private readonly Mock clangClientMock; - private readonly QirExecutableGenerator executableGenerator; - private readonly DirectoryInfo sourceDirectory; - private readonly DirectoryInfo includeDirectory; - private readonly DirectoryInfo libraryDirectory; - private readonly DirectoryInfo binDirectory; - private readonly IList libraryFiles; - private readonly IList sourceFiles; - - public QirExecutableGeneratorTests() - { - clangClientMock = new Mock(); - executableGenerator = new QirExecutableGenerator(clangClientMock.Object, Mock.Of()); - - // Set up files. - var prefix = Guid.NewGuid().ToString(); - binDirectory = new DirectoryInfo($"{prefix}-bin"); - binDirectory.Create(); - libraryDirectory = new DirectoryInfo($"{prefix}-library"); - libraryDirectory.Create(); - libraryFiles = new List() - { - CreateFile("lib1", libraryDirectory, "lib1 contents"), - CreateFile("lib2", libraryDirectory, "lib2 contents"), - }; - includeDirectory = new DirectoryInfo($"{prefix}-include"); - includeDirectory.Create(); - sourceDirectory = new DirectoryInfo($"{prefix}-source"); - sourceDirectory.Create(); - sourceFiles = new List() - { - CreateFile("src1.cpp", sourceDirectory, "src1 contents"), - CreateFile("src2.bc", sourceDirectory, "src2 contents"), - }; - } - - public void Dispose() - { - Util.DeleteDirectory(sourceDirectory); - Util.DeleteDirectory(includeDirectory); - Util.DeleteDirectory(libraryDirectory); - Util.DeleteDirectory(binDirectory); - } - - [Fact] - public async Task TestGenerateExecutable() - { - string[] expectedLibraries = { - "Microsoft.Quantum.Qir.Runtime", - "Microsoft.Quantum.Qir.QSharp.Foundation", - "Microsoft.Quantum.Qir.QSharp.Core" - }; - - var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); - await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory); - - // Verify invocation of clang. - clangClientMock.Verify(obj => obj.CreateExecutableAsync( - It.Is(s => s.SequenceEqual(sourceFiles.Select(fileInfo => fileInfo.FullName))), - It.Is(s => s.SequenceEqual(expectedLibraries)), - libraryDirectory.FullName, - includeDirectory.FullName, - executableFile.FullName)); - - // Verify files were copied. - Assert.True(FilesWereCopied(libraryFiles.ToArray(), binDirectory)); - } - - private static FileInfo CreateFile(string fileName, DirectoryInfo directory, string contents) - { - var filePath = Path.Combine(directory.FullName, fileName); - var fileInfo = new FileInfo(filePath); - using var fileStream = fileInfo.OpenWrite(); - using var streamWriter = new StreamWriter(fileStream); - streamWriter.Write(contents); - return fileInfo; - } - - private static bool FilesWereCopied(FileInfo[] files, DirectoryInfo destinationDirectory) - { - var destinationDirectoryFiles = destinationDirectory.GetFiles(); - foreach (var file in files) - { - var copiedFile = destinationDirectoryFiles.FirstOrDefault(fileInfo => fileInfo.Name == file.Name); - if (copiedFile == null) - { - return false; - } - - using var originalFileReader = file.OpenText(); - var originalFileContents = originalFileReader.ReadToEnd(); - using var copiedFileReader = copiedFile.OpenText(); - var copiedFileContents = copiedFileReader.ReadToEnd(); - if (originalFileContents != copiedFileContents) - { - return false; - } - } - - return true; - } - } -} diff --git a/src/Qir/Controller/Tests.QirController/Tests.QirController.csproj b/src/Qir/Controller/Tests.QirController/Tests.QirController.csproj deleted file mode 100644 index ba5183ca742..00000000000 --- a/src/Qir/Controller/Tests.QirController/Tests.QirController.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp3.1 - false - - - - - - - - - - - - - - - diff --git a/src/Qir/Controller/Tests.QirController/Util.cs b/src/Qir/Controller/Tests.QirController/Util.cs deleted file mode 100644 index 476848705ea..00000000000 --- a/src/Qir/Controller/Tests.QirController/Util.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.IO; -using System.Reflection; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; - -namespace Tests.QirController -{ - public static class Util - { - public static void DeleteDirectory(DirectoryInfo directory) - { - foreach (var file in directory.GetFiles()) - { - file.Delete(); - } - directory.Delete(); - } - - - public static bool EntryPointsAreEqual(EntryPointOperation entryPointA, EntryPointOperation entryPointB) - { - var method = typeof(Extensions) - .GetMethod("ValueEquals", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(EntryPointOperation), typeof(EntryPointOperation) }, null); - object[] parameters = { entryPointA, entryPointB }; - return (bool)method.Invoke(null, parameters); - } - } -} From 29c6020d8a70728734e0f4df096ecd7a59668074 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Wed, 21 Apr 2021 11:10:53 -0700 Subject: [PATCH 03/11] WIP --- Simulation.sln | 19 --- src/Qir/Controller/Controller.cs | 17 ++- .../QirDriverGeneratorTests.cs | 63 ---------- .../QirExecutableGeneratorTests.cs | 14 +-- .../QirExecutableTests.cs | 111 ++++++++++++++++++ .../Tools/Driver/IQirDriverGenerator.cs | 16 +++ .../Driver/QirFullStateDriverGenerator.cs | 23 ++++ .../Execution/Tools/Executable/ClangClient.cs | 2 +- .../Tools/Executable/IQirExecutable.cs | 16 +++ .../Executable/IQuantumExecutableRunner.cs | 5 +- .../Tools/Executable/QirExecutable.cs | 72 ++++++++++++ .../Executable/QirFullStateExecutable.cs | 37 ++++++ .../Executable/QuantumExecutableRunner.cs | 11 +- src/Qir/Execution/Tools/QirExecutable.cs | 78 ------------ .../Execution/Tools/QirFullStateExecutable.cs | 42 ------- .../IQirSourceFileGenerator.cs | 22 ---- .../QirSourceFileGenerator.cs | 50 -------- 17 files changed, 296 insertions(+), 302 deletions(-) delete mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs create mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs create mode 100644 src/Qir/Execution/Tools/Driver/IQirDriverGenerator.cs create mode 100644 src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs create mode 100644 src/Qir/Execution/Tools/Executable/IQirExecutable.cs create mode 100644 src/Qir/Execution/Tools/Executable/QirExecutable.cs create mode 100644 src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs delete mode 100644 src/Qir/Execution/Tools/QirExecutable.cs delete mode 100644 src/Qir/Execution/Tools/QirFullStateExecutable.cs delete mode 100644 src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs delete mode 100644 src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs diff --git a/Simulation.sln b/Simulation.sln index 1f28b42176e..1a4a4abbd59 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -117,8 +117,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controller", "Controller", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QirController", "src\Qir\Controller\QirController.csproj", "{A77E6661-D143-4E3E-BCD1-8E321A966829}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.QirController", "src\Qir\Controller\Tests.QirController\Tests.QirController.csproj", "{2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Execution", "Execution", "{442E66C8-F69F-44E9-9CD9-1F52C37EA41B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Quantum.Qir.Tools", "src\Qir\Execution\Tools\Microsoft.Quantum.Qir.Tools.csproj", "{C60226E3-98DE-4E92-AED4-B4A93D4CA063}" @@ -777,22 +775,6 @@ Global {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {A77E6661-D143-4E3E-BCD1-8E321A966829}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Debug|x64.ActiveCfg = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Debug|x64.Build.0 = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Release|Any CPU.Build.0 = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Release|x64.ActiveCfg = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.Release|x64.Build.0 = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {C60226E3-98DE-4E92-AED4-B4A93D4CA063}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C60226E3-98DE-4E92-AED4-B4A93D4CA063}.Debug|Any CPU.Build.0 = Debug|Any CPU {C60226E3-98DE-4E92-AED4-B4A93D4CA063}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -880,7 +862,6 @@ Global {D7D34736-A719-4B45-A33F-2723F59EC29D} = {A7DB7367-9FD6-4164-8263-A05077BE54AB} {4E07F247-ED93-4497-8B58-022314308E67} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {A77E6661-D143-4E3E-BCD1-8E321A966829} = {4E07F247-ED93-4497-8B58-022314308E67} - {2E4B9604-A5CD-4B49-B1D4-A7AC8ABAEF68} = {4E07F247-ED93-4497-8B58-022314308E67} {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {C60226E3-98DE-4E92-AED4-B4A93D4CA063} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} {4794FC80-4594-403F-AFEC-4889EFE87EA0} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} diff --git a/src/Qir/Controller/Controller.cs b/src/Qir/Controller/Controller.cs index 8280624d592..013b0f62901 100644 --- a/src/Qir/Controller/Controller.cs +++ b/src/Qir/Controller/Controller.cs @@ -7,7 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Model; -using Microsoft.Quantum.Qir.Tools; +using Microsoft.Quantum.Qir.Tools.Executable; using Microsoft.Quantum.Qir.Utility; using QirExecutionWrapperSerialization = Microsoft.Quantum.QsCompiler.BondSchemas.QirExecutionWrapper.Protocols; @@ -24,7 +24,7 @@ public static async Task ExecuteAsync( DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, FileInfo errorFile, - ILogger logger) + ILogger logger) { try { @@ -35,13 +35,18 @@ public static async Task ExecuteAsync( // Step 2: Create executable. logger.LogInfo("Creating executable."); - var bytecodeArray = input.QirBytecode.Array.Skip(input.QirBytecode.Offset).Take(input.QirBytecode.Count).ToArray(); - var executable = new QirFullStateExecutable(input.EntryPoint, bytecodeArray); + var bytecodeArray = input.QirBytecode.Array.Skip(input.QirBytecode.Offset).Take(input.QirBytecode.Count).ToList().ToArray(); var executableFile = new FileInfo(Path.Combine(BinaryDirectoryPath, ExecutableName)); - await executable.BuildAsync(libraryDirectory, includeDirectory, executableFile); + var executable = QirFullStateExecutable.CreateInstance(executableFile, bytecodeArray, logger); + await executable.BuildAsync(input.EntryPoint, libraryDirectory, includeDirectory); // Step 3: Run executable. - await executable.RunAsync(executableFile, outputFile); + if (outputFile.Exists) + { + outputFile.Delete(); + } + using var outputStream = outputFile.OpenWrite(); + await executable.RunAsync(input.EntryPoint, outputStream); } catch (Exception e) { diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs deleted file mode 100644 index da40aa5d912..00000000000 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirDriverGeneratorTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Tools.SourceGeneration; -using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using Moq; -using Xunit; - -namespace Tests.QirController -{ - public class QirDriverGeneratorTests : IDisposable - { - private readonly Mock driverGeneratorMock; - private readonly DirectoryInfo sourceDirectory; - - public QirDriverGeneratorTests() - { - driverGeneratorMock = new Mock(Mock.Of()) { CallBase = true }; - driverGeneratorMock.Setup(obj => obj.GenerateQirDriverCppHelper(It.IsAny(), It.IsAny())); - sourceDirectory = new DirectoryInfo($"{Guid.NewGuid()}-source"); - } - - public void Dispose() - { - Util.DeleteDirectory(sourceDirectory); - } - - [Fact] - public async Task TestGenerateDriver() - { - var entryPoint = new EntryPointOperation() - { - Arguments = new List - { - new Argument() - { - Position = 0, - Name = "argname", - Values = new List { new ArgumentValue { String = "argvalue" } }, - } - } - }; - byte[] bytes = { 1, 2, 3, 4, 5 }; - var bytecode = new ArraySegment(bytes, 1, 3); - await driverGeneratorMock.Object.GenerateQirSourceFilesAsync(sourceDirectory, entryPoint, bytecode); - driverGeneratorMock.Verify(obj => obj.GenerateQirDriverCppHelper(It.Is(actualEntryPoint => Util.EntryPointsAreEqual(entryPoint, actualEntryPoint)), It.IsAny())); - - // Verify that the "bytecode" file was created correctly. - var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); - using var bytecodeFileStream = bytecodeFilePath.OpenRead(); - Assert.Equal(bytecodeFileStream.Length, bytecode.Count); - for (var i = 0; i < bytecodeFileStream.Length; ++i) - { - Assert.Equal(bytecode[i], bytecodeFileStream.ReadByte()); - } - } - } -} diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs index 3d9da400687..a84a13c03b5 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs @@ -6,7 +6,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Executable; +using Microsoft.Quantum.Qir.Tools.Executable; using Microsoft.Quantum.Qir.Utility; using Moq; using Xunit; @@ -23,6 +23,7 @@ public class QirExecutableGeneratorTests : IDisposable private readonly DirectoryInfo binDirectory; private readonly IList libraryFiles; private readonly IList sourceFiles; + private IList linkLibraries; public QirExecutableGeneratorTests() { @@ -49,6 +50,7 @@ public QirExecutableGeneratorTests() CreateFile("src1.cpp", sourceDirectory, "src1 contents"), CreateFile("src2.bc", sourceDirectory, "src2 contents"), }; + linkLibraries = new List { "lib1", "lib2" }; } public void Dispose() @@ -62,19 +64,13 @@ public void Dispose() [Fact] public async Task TestGenerateExecutable() { - string[] expectedLibraries = { - "Microsoft.Quantum.Qir.Runtime", - "Microsoft.Quantum.Qir.QSharp.Foundation", - "Microsoft.Quantum.Qir.QSharp.Core" - }; - var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); - await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory); + await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory, linkLibraries); // Verify invocation of clang. clangClientMock.Verify(obj => obj.CreateExecutableAsync( It.Is(s => s.SequenceEqual(sourceFiles.Select(fileInfo => fileInfo.FullName))), - It.Is(s => s.SequenceEqual(expectedLibraries)), + It.Is(s => s.SequenceEqual(linkLibraries)), libraryDirectory.FullName, includeDirectory.FullName, executableFile.FullName)); diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs new file mode 100644 index 00000000000..bddd00965d7 --- /dev/null +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Tools.Driver; +using Microsoft.Quantum.Qir.Tools.Executable; +using Microsoft.Quantum.Qir.Utility; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; +using Moq; +using Xunit; + +namespace Tests.QirController +{ + public class QirExecutableTests : IDisposable + { + private readonly DirectoryInfo sourceDirectory; + private readonly DirectoryInfo includeDirectory; + private readonly DirectoryInfo libraryDirectory; + private readonly DirectoryInfo binDirectory; + private readonly Mock driverGeneratorMock; + private readonly Mock executableGeneratorMock; + private readonly Mock runnerMock; + private readonly Mock qirExecutable; + private readonly FileInfo executableFile; + private readonly byte[] qirBytecode = { 1, 2, 3, 4, 5 }; + private readonly IList linkLibraries; + + public QirExecutableTests() + { + // Set up files. + var prefix = Guid.NewGuid().ToString(); + binDirectory = new DirectoryInfo($"{prefix}-bin"); + binDirectory.Create(); + libraryDirectory = new DirectoryInfo($"{prefix}-library"); + libraryDirectory.Create(); + includeDirectory = new DirectoryInfo($"{prefix}-include"); + includeDirectory.Create(); + sourceDirectory = new DirectoryInfo($"{prefix}-source"); + sourceDirectory.Create(); + executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executable")); + driverGeneratorMock = new Mock(); + executableGeneratorMock = new Mock(); + runnerMock = new Mock(); + qirExecutable = new Mock(executableFile, qirBytecode, Mock.Of(), driverGeneratorMock.Object, executableGeneratorMock.Object, runnerMock.Object) { CallBase = true }; + linkLibraries = new List { "lib1", "lib2" }; + qirExecutable.SetupGet(obj => obj.LinkLibraries).Returns(linkLibraries); + qirExecutable.SetupGet(obj => obj.SourceDirectoryPath).Returns(sourceDirectory.FullName); + } + + public void Dispose() + { + Util.DeleteDirectory(sourceDirectory); + Util.DeleteDirectory(includeDirectory); + Util.DeleteDirectory(libraryDirectory); + Util.DeleteDirectory(binDirectory); + } + + [Fact] + public async Task TestBuild() + { + // Set up. + var entryPoint = new EntryPointOperation(); + var driverFileContents = "driver file contents"; + driverGeneratorMock.Setup(obj => obj.GenerateAsync(entryPoint, It.IsAny())).Callback((entryPoint, stream) => + { + using var streamWriter = new StreamWriter(stream); + streamWriter.Write(driverFileContents); + }); + + // Build the executable. + await qirExecutable.Object.BuildAsync(entryPoint, libraryDirectory, includeDirectory); + + // Verify that the "bytecode" file was created correctly. + var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); + using var bytecodeFileStream = bytecodeFilePath.OpenRead(); + Assert.Equal(bytecodeFileStream.Length, qirBytecode.Length); + for (var i = 0; i < bytecodeFileStream.Length; ++i) + { + Assert.Equal(qirBytecode[i], bytecodeFileStream.ReadByte()); + } + + // Verify that the driver was written to the correct file. + var driver = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.driver")); + using var driverStreamReader = driver.OpenText(); + var actualDriverContents = driverStreamReader.ReadToEnd(); + Assert.Equal(driverFileContents, actualDriverContents); + + // Verify that the executable was generated. + executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync(executableFile, It.Is(arg => arg.FullName == sourceDirectory.FullName), libraryDirectory, includeDirectory, linkLibraries)); + } + + [Fact] + public async Task TestRun() + { + // Set up. + using var outputStream = new MemoryStream(); + var entryPoint = new EntryPointOperation(); + var arguments = "arguments"; + driverGeneratorMock.Setup(obj => obj.GetCommandLineArguments(entryPoint)).Returns(arguments); + + // Run executable. + await qirExecutable.Object.RunAsync(entryPoint, outputStream); + + // Verify runner was invoked properly. + runnerMock.Verify(obj => obj.RunExecutableAsync(executableFile, outputStream, arguments)); + } + } +} diff --git a/src/Qir/Execution/Tools/Driver/IQirDriverGenerator.cs b/src/Qir/Execution/Tools/Driver/IQirDriverGenerator.cs new file mode 100644 index 00000000000..5c995bd7d0a --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/IQirDriverGenerator.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + public interface IQirDriverGenerator + { + Task GenerateAsync(EntryPointOperation entryPoint, Stream stream); + + string GetCommandLineArguments(EntryPointOperation entryPoint); + } +} diff --git a/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs b/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs new file mode 100644 index 00000000000..e09ac1196c1 --- /dev/null +++ b/src/Qir/Execution/Tools/Driver/QirFullStateDriverGenerator.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.QsCompiler; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Microsoft.Quantum.Qir.Tools.Driver +{ + public class QirFullStateDriverGenerator: IQirDriverGenerator + { + public async Task GenerateAsync(EntryPointOperation entryPoint, Stream stream) + { + await Task.Run(() => QirDriverGeneration.GenerateQirDriverCpp(entryPoint, stream)); + } + + public string GetCommandLineArguments(EntryPointOperation entryPoint) + { + return QirDriverGeneration.GenerateCommandLineArguments(entryPoint.Arguments); + } + } +} diff --git a/src/Qir/Execution/Tools/Executable/ClangClient.cs b/src/Qir/Execution/Tools/Executable/ClangClient.cs index 479469dceb6..036483c16c8 100644 --- a/src/Qir/Execution/Tools/Executable/ClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/ClangClient.cs @@ -23,7 +23,7 @@ public async Task CreateExecutableAsync(string[] inputFiles, string[] libraries, // string.Join does not automatically prepend the delimiter, so it is included again in the string here. var librariesArg = $"{LinkFlag} {string.Join(LinkFlag, libraries)}"; - var arguments = $"-v {inputsArg} -I {includePath} -L {libraryPath} {librariesArg} -o {outputPath}"; + var arguments = $"{inputsArg} -I {includePath} -L {libraryPath} {librariesArg} -o {outputPath}"; logger.LogInfo($"Invoking clang with the following arguments: {arguments}"); var taskCompletionSource = new TaskCompletionSource(); using var process = new Process(); diff --git a/src/Qir/Execution/Tools/Executable/IQirExecutable.cs b/src/Qir/Execution/Tools/Executable/IQirExecutable.cs new file mode 100644 index 00000000000..7caaa8328b8 --- /dev/null +++ b/src/Qir/Execution/Tools/Executable/IQirExecutable.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Microsoft.Quantum.Qir.Tools.Executable +{ + public interface IQirExecutable + { + Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory); + + Task RunAsync(EntryPointOperation entryPoint, Stream output); + } +} diff --git a/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs b/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs index 9db39ba6e16..0ac0edf4fa5 100644 --- a/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs +++ b/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; namespace Microsoft.Quantum.Qir.Tools.Executable { @@ -13,9 +12,9 @@ public interface IQuantumExecutableRunner /// Runs a quantum program executable with the given arguments. /// /// Location of the executable to run. - /// Location to write program output. + /// Stream to write program output. /// Arguments to supply the program with. /// - Task RunExecutableAsync(FileInfo executableFile, FileInfo outputFile, string arguments); + Task RunExecutableAsync(FileInfo executableFile, Stream stream, string arguments); } } diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs new file mode 100644 index 00000000000..ccc88ff2568 --- /dev/null +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Tools.Driver; +using Microsoft.Quantum.Qir.Utility; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Microsoft.Quantum.Qir.Tools.Executable +{ + /// + /// Base for creating and running QIR-based executables. + /// + public abstract class QirExecutable : IQirExecutable + { + private const string DriverFileName = "qir.driver"; + private const string BytecodeFileName = "qir.bc"; + protected FileInfo ExecutableFile { get; } + private readonly byte[] qirBytecode; + public virtual string SourceDirectoryPath => "src"; + private readonly ILogger logger; + private readonly IQuantumExecutableRunner runner; + private readonly IQirDriverGenerator driverGenerator; + private readonly IQirExecutableGenerator executableGenerator; + + public QirExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) + { + ExecutableFile = executableFile; + this.qirBytecode = qirBytecode; + this.logger = logger; + this.runner = runner; + this.driverGenerator = driverGenerator; + this.executableGenerator = executableGenerator; + } + + public async Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory) + { + var sourceDirectory = new DirectoryInfo(SourceDirectoryPath); + if (sourceDirectory.Exists) + { + sourceDirectory.Delete(true); + } + + sourceDirectory.Create(); + logger.LogInfo($"Created source directory at {sourceDirectory.FullName}."); + + // Create driver. + var driverFile = new FileInfo(Path.Combine(sourceDirectory.FullName, DriverFileName)); + using var driverFileStream = driverFile.OpenWrite(); + await driverGenerator.GenerateAsync(entryPoint, driverFileStream); + logger.LogInfo($"Created driver file at {driverFile.FullName}."); + + // Create bytecode file. + var bytecodeFile = new FileInfo(Path.Combine(sourceDirectory.FullName, BytecodeFileName)); + using var bytecodeFileStream = bytecodeFile.OpenWrite(); + await bytecodeFileStream.WriteAsync(qirBytecode); + logger.LogInfo($"Created bytecode file at {bytecodeFile.FullName}."); + + await executableGenerator.GenerateExecutableAsync(ExecutableFile, sourceDirectory, libraryDirectory, includeDirectory, LinkLibraries); + } + + public async Task RunAsync(EntryPointOperation entryPoint, Stream output) + { + var stringArguments = driverGenerator.GetCommandLineArguments(entryPoint); + await runner.RunExecutableAsync(ExecutableFile, output, stringArguments); + } + + public abstract IList LinkLibraries { get; } + } +} diff --git a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs new file mode 100644 index 00000000000..e70bba806e6 --- /dev/null +++ b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.IO; +using Microsoft.Quantum.Qir.Tools.Driver; +using Microsoft.Quantum.Qir.Utility; + +namespace Microsoft.Quantum.Qir.Tools.Executable +{ + /// + /// Class to create and run QIR-based executables that use the full-state simulator. + /// + public class QirFullStateExecutable : QirExecutable + { + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) + : base(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner) + { + + } + + public static IQirExecutable CreateInstance(FileInfo executableFile, byte[] qirBytecode, ILogger logger) + { + var driverGenerator = new QirFullStateDriverGenerator(); + var clangClient = new ClangClient(logger); + var executableGenerator = new QirExecutableGenerator(clangClient, logger); + var runner = new QuantumExecutableRunner(logger); + return new QirFullStateExecutable(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner) as IQirExecutable; + } + + public override IList LinkLibraries => new List { + "Microsoft.Quantum.Qir.Runtime", + "Microsoft.Quantum.Qir.QSharp.Foundation", + "Microsoft.Quantum.Qir.QSharp.Core" + }; + } +} diff --git a/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs b/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs index 6fd378f3755..9cf123c31ab 100644 --- a/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs +++ b/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs @@ -17,15 +17,9 @@ public QuantumExecutableRunner(ILogger logger) this.logger = logger; } - public async Task RunExecutableAsync(FileInfo executableFile, FileInfo outputFile, string arguments) + public async Task RunExecutableAsync(FileInfo executableFile, Stream stream, string arguments) { logger.LogInfo($"Invoking executable {executableFile.FullName} with the following arguments: {arguments}"); - if (outputFile.Exists) - { - outputFile.Delete(); - } - - outputFile.Create().Dispose(); using var process = new Process(); process.StartInfo = new ProcessStartInfo { @@ -38,8 +32,7 @@ public async Task RunExecutableAsync(FileInfo executableFile, FileInfo outputFil process.Start(); var output = await process.StandardOutput.ReadToEndAsync(); process.WaitForExit(); - using var outputFileStream = outputFile.OpenWrite(); - using var streamWriter = new StreamWriter(outputFileStream); + using var streamWriter = new StreamWriter(stream); await streamWriter.WriteAsync(output); logger.LogInfo($"Executable has finished running. Result code: {process.ExitCode}"); } diff --git a/src/Qir/Execution/Tools/QirExecutable.cs b/src/Qir/Execution/Tools/QirExecutable.cs deleted file mode 100644 index b2ef6d03c61..00000000000 --- a/src/Qir/Execution/Tools/QirExecutable.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.Quantum.Qir.Tools.Executable; -using Microsoft.Quantum.Qir.Tools.SourceGeneration; -using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Microsoft.Quantum.Qir.Tools -{ - /// - /// Base for creating and running QIR-based executables. - /// - public abstract class QirExecutable - { - protected EntryPointOperation EntryPointOperation { get; } - private readonly byte[] qirBytecode; - private const string SourceDirectoryPath = "src"; - private readonly ILogger logger; - private readonly IQuantumExecutableRunner runner; - private readonly IQirSourceFileGenerator sourceGenerator; - private readonly IQirExecutableGenerator executableGenerator; - - /// - /// Constructor for the QirExecutable class. - /// - /// Object that provides data to specify which entry-point to use for building and running a QIR-based executable. - /// QIR bytecode used to build the executable. - public QirExecutable(EntryPointOperation entryPoint, byte[] qirBytecode) - { - EntryPointOperation = entryPoint; - this.qirBytecode = qirBytecode; - logger = new Logger(new Clock()); - runner = new QuantumExecutableRunner(logger); - sourceGenerator = new QirSourceFileGenerator(logger, GenerateDriverAsync); - var clangClient = new ClangClient(logger); - executableGenerator = new QirExecutableGenerator(clangClient, logger); - } - - internal QirExecutable(EntryPointOperation entryPoint, byte[] qirBytecode, ILogger logger, IQirSourceFileGenerator sourceGenerator, IQuantumExecutableRunner runner) - { - EntryPointOperation = entryPoint; - this.qirBytecode = qirBytecode; - this.logger = logger; - this.runner = runner; - this.sourceGenerator = sourceGenerator; - } - - /// - /// Creates a QIR-based executable. - /// - /// Directory where the libraries to link to are located. - /// Directory where the headers needed for compilation are located. - /// File to write the executable to. - public async Task BuildAsync(DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, FileInfo executable) - { - var linkLibraries = GetLinkLibraries(); - var sourceDirectory = new DirectoryInfo(SourceDirectoryPath); - await sourceGenerator.GenerateQirSourceFilesAsync(sourceDirectory, EntryPointOperation, qirBytecode); - await executableGenerator.GenerateExecutableAsync(executable, sourceDirectory, libraryDirectory, includeDirectory, linkLibraries); - } - - public async Task RunAsync(FileInfo executable, FileInfo outputFile) - { - var stringArguments = GetCommandLineArguments(EntryPointOperation.Arguments); - await runner.RunExecutableAsync(executable, outputFile, stringArguments); - } - - protected abstract Task GenerateDriverAsync(Stream driver); - - protected abstract string GetCommandLineArguments(IList arguments); - - protected abstract IList GetLinkLibraries(); - } -} diff --git a/src/Qir/Execution/Tools/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/QirFullStateExecutable.cs deleted file mode 100644 index 0a9aab85546..00000000000 --- a/src/Qir/Execution/Tools/QirFullStateExecutable.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.Quantum.QsCompiler; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Microsoft.Quantum.Qir.Tools -{ - /// - /// Class to create and run QIR-based executables that use the full-state simulator. - /// - public class QirFullStateExecutable : QirExecutable - { - /// - public QirFullStateExecutable(EntryPointOperation entryPoint, byte[] qirBytecode) : - base(entryPoint, qirBytecode) - { - } - - protected override async Task GenerateDriverAsync(Stream driver) - { - await Task.Run(() => QirDriverGeneration.GenerateQirDriverCpp(EntryPointOperation, driver)); - } - - protected override string GetCommandLineArguments(IList arguments) - { - return QirDriverGeneration.GenerateCommandLineArguments(arguments); - } - - protected override IList GetLinkLibraries() - { - return new List { - "Microsoft.Quantum.Qir.Runtime", - "Microsoft.Quantum.Qir.QSharp.Foundation", - "Microsoft.Quantum.Qir.QSharp.Core" - }; - } - } -} diff --git a/src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs b/src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs deleted file mode 100644 index e2f4668c49a..00000000000 --- a/src/Qir/Execution/Tools/SourceGeneration/IQirSourceFileGenerator.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; - -namespace Microsoft.Quantum.Qir.Tools.SourceGeneration -{ - public interface IQirSourceFileGenerator - { - /// - /// Generates the C++ driver source file and writes the bytecode to a file. - /// - /// Directory to which driver and bytecode will be written. - /// Entry point information. - /// The QIR bytecode. - /// - Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode); - } -} diff --git a/src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs b/src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs deleted file mode 100644 index 7d8af3c0915..00000000000 --- a/src/Qir/Execution/Tools/SourceGeneration/QirSourceFileGenerator.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; - -namespace Microsoft.Quantum.Qir.Tools.SourceGeneration -{ - public class QirSourceFileGenerator : IQirSourceFileGenerator - { - public delegate Task DriverGenerationMethod(Stream stream); - - private const string BytecodeFileName = "qir.bc"; - private const string DriverFileName = "driver.cpp"; - private readonly ILogger logger; - private readonly DriverGenerationMethod driverGenerationMethod; - - public QirSourceFileGenerator(ILogger logger, DriverGenerationMethod driverGenerationMethod) - { - this.logger = logger; - this.driverGenerationMethod = driverGenerationMethod; - } - - public async Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode) - { - if (sourceDirectory.Exists) - { - sourceDirectory.Delete(true); - } - - sourceDirectory.Create(); - logger.LogInfo($"Created source directory at {sourceDirectory.FullName}."); - - // Create driver. - var driverFile = new FileInfo(Path.Combine(sourceDirectory.FullName, DriverFileName)); - using var driverFileStream = driverFile.OpenWrite(); - await driverGenerationMethod.Invoke(driverFileStream); - logger.LogInfo($"Created driver file at {driverFile.FullName}."); - - // Create bytecode file. - var bytecodeFile = new FileInfo(Path.Combine(sourceDirectory.FullName, BytecodeFileName)); - using var bytecodeFileStream = bytecodeFile.OpenWrite(); - await bytecodeFileStream.WriteAsync(bytecode.Array, bytecode.Offset, bytecode.Count); - logger.LogInfo($"Created bytecode file at {bytecodeFile.FullName}."); - } - } -} From 9255b98a687b64cdbde201e9910de92cd1197635 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Wed, 21 Apr 2021 11:31:59 -0700 Subject: [PATCH 04/11] Update test file --- .../test-cases/standalone-input-test.in | Bin 44066 -> 44066 bytes .../UnitTest1.cs | 14 -------------- 2 files changed, 14 deletions(-) delete mode 100644 src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs diff --git a/src/Qir/Controller/test-cases/standalone-input-test.in b/src/Qir/Controller/test-cases/standalone-input-test.in index 2251eb15f36354835911c0b1eb4f81ff2a71e3a6..82a74f144f9776df10dafdcf777f60566c6767c0 100644 GIT binary patch delta 7903 zcmeHLdvH|M8Q*(%2`jr?*4K4%P5dzLst;|S@cEHxUw3(@`Px@YgZ@ZNXfo_A~|`M#BznZ?i9o$yO{kN81{J16Jkl69L;*dG}c z46Z-o^knSaS1m~O_WXTOXjs+owcWuI3uG~V8z8j%cgKS1cS3f9vs606`P&w_7EZoe z(*G9s{xHN8v|yIUzH^v;9vW^lNhB@SQ8Ru0qVvN{&XSb8FeSb=Sa$WMr{9CJTL+n> zssD%TaGzuF2E4_2;Gs2VAHCYpyURz4Zdc_X(WGRnW&OfIoW*TznzikN*VmS9wGN`g znU?C3`|?g8+2KKK+>&|c<4P{`@@K!u`%TwiOVv%K&#R9O=2VY9bEW^>ptTlt z!#9v#Wy6Ukozz`1{);r#YxM8qufSi}8`Ks%#;@w1u-9pSd8DiNFV1mpC}=528>XoL50yku6tA;7iz2?1 z@rB_2(gy3Q$OLe()TfwS=1G|x2d7JmChdE?TQ_-g;l?G~yO;-jP1_Ml&8k4(5|?=&vB&*G%$#@J|hJBS+BD`qZ;L#EQmerKqYWYnFsnAN?q^z*Y$v9 zk?Y>}4fdM)Huwu03@ay?urO${ATl#|ZNhQ6-j+HJ>TzljH{59tNg_)OaT)Xb0!O7vlfH z@P=Z0zUI`fw3Qz8k!9hn?Qo7RbO7;22;DbsZ{+P`X^%G)_v!K-BS=qW>OKhY=Iq?D zJq`?$&c5>63Oll&c2$vDYW0@A3ZNPv%=CcQ@9>W~A8ImEPKD}y^iDnl>T7(NhwHty z0bOBubT7r%tC!J_fZ~dRvheg+YT>1j0ul8h%g)DrIlUVqP#DSF57o9qw0^!AI8l@D z4<9{ftce>iJiW(Yfv2|7+H`Rl;A*|jrc=vOC#nEX)V>BE>I1)OEOLDa&$Z3a4eOi( znwNB1dmJcmEzPt35uOXsrG?gng9Q20y#;O;=Gmy%%|xhWrM6$}Li{03CG@NpJh^l} zVC!e5L*=&Pn&~a$w}4Ic3!OVcMrqm#7FF-Li3Yqd{p*auZ(*80|BXnwF*Vnja^J7{ zCZ*ikIosevXTc8_6ivR=R%<({rA>ELqwVyDpql*c>uQyHJzSk>9F!;f9X+o|@WFzY zzH+iI|L(ol+GCR2MJfreb(MGs{>fe5NUHaAs4)58p%(9gw9W62TOa#*>pA4U_q4FOR5cfzn(s|- zub!&}ojS?7T{yTn-@h1cw4Kpo^GKZnAy|pcfwp;bPANg6&dQOjo=|2>s^yk?(sc3t z(Be7oYbzBP(qT8;n4N-0>I|r80W0oN8p%EL{Fc!tDZOPTyau$4e#>CNm3yw$$!?X1 zy14e)YqZK+<%Iopjo_0e1T0_+0l|6v@NBm}_H-YaWk7hg(_q1){7moyr!BdQEmfDz z`|@S8j2*sQR#CAOfGH(e3CAUKI4?7Y^Gj~<$&{S2Wdr6A9y5pVfokyS%uEnnp5gCo zuTM?sJ51OaGFkAVK>0i44ThKrJKIebR6Hny<>E~}^&QflnlevmYE+!;DJ@^VI%Ck> z?Dhr;Jtr+H)Jn*b?Og~`^YjF^pM znFpJ*+ZKPkXe@1ADojiLGm=E>QEW@iR{0(+#{y`o2EjwF}Ao8Zevys)~wcx z*?tptSJh>|tLukNSXE~;S&;ugWkFTlG?Ou4RUK)vp#6bYu91^BVt#(%zPg*X`>JG? zeVbAyb`#=xQS%Lo=cCQv@OZvedd#<@i*8zT?+uRj^meQI@uAIu6TzLW{zv3tbL08&GnBx!n>+h2K+`WVT05$7pKuyst>uH`NE}X+7>!bfAV%N>jqxl_$vjEPEJtu6L(&XK@KPW^ z(uN?;;5aQv0YXw#lY+?5BF)G=MW8H!1$fC26mXPh8HJ;9l$1r9#RWp-7)GWQ8&OVH z1*4yLgkS`h1X{*q4C90VNue}H(vl>xguvhoieorf1TcicagpaqOrltlWC(!`FsQ)c zj6e~B#7iRRQG|xzmv%&wMINO^MHeZO3>#n>hM^S;C|r^RhLw}*I8{9x5XAr!AbD2c zXjXD>q{<=04FS@hZXU{Ij|C^6 z8B!8xfENo>V{D7jB1fQe4Vv9Xoi zkP2wP$O(!K#lS=%h{Mvg15Qib#6W3nCHSbc(h(-~WwC~unW}}-dw1_6BW6euEmAYH zlW)HJ-~V;~|D6AxT_?@EPMX8-Sf)=)DJyGgDtN&$bEfa5+b!cV-$w)G8#X(?ztx{( zY3q9|>M7-aP(8rbbw!|YW#iXgC@VIN_}c)%-CJJENxTiR8EmN|nDh31&ieM7OAXS* znH)V8O&iuNH<=AKDWR@h+?_z59l@D`X{LG8J2M~2Ni_sbrhZe}ZEvsrF#3Tx$)FX{ zy=hs;x>AD%85ccoa=tu*GkL7h_Px)qUtt?Rl0{69erI_0>OqsmU=_*tkF3Z3GR|X= zCPnhWwR6J$gb}17T5)PUaqkE&X8tD^2JRopDbc_6$j9_ZPSMyi_i}}M|NS`%LpcYm z^`>c`akGBnP5U!~Ba|vxdd_#ingRZrSWtL*TC=7`FHh_F8Ibb~nC(0R^+mELAQoHn z;?av<|3B{pweDXew4BO_cvBiCIHn!Q@L8>Mn=v-~?yyM~*2;kl*C9j+4#*mqsG57pzNgYnm=7S^??exL1sPz&L7B(La+ zgwMKP6L;NlFjaNkb#<=C@{@&rUVa(~H9si_^S@YrVn!Y%e*}R~*gTs}Szo)C1vRwC zTbdkzxjK`_X6=CuSeiUfXMug@VsM5gm3P5xCgZAf{E``?sSTihfgKdC$p#x2OiB;i zJvA0>QDK)OUL6B=j|&v>nRIb$AV*uV{y=G4T8dJ1C9Qf$jmZ$AMpvi6pc3fI)xcTq z_2%d3<&tN>e3!w3M`tYpJ>~Y)y!&-BpVeU=h?cJecir+8Wo9v$ws79v7gyCe*0m&| z@u=!Osi8Msv5v1Pu;s+l)MagQ!53ErJ6g?|s!&goJA|oOFu6l-zGB^2Q!t>)x8IDp zO0^85;8zQ?Cd1C@hlc+Y_p6 zg(x+?)8O@r9Pfqx1Nu7#ybuWMEO1rUdgq>Btwl1&fOAh6EO@-~uz9c!{Jb{b+3{+* zWxnd>_HxirzdZqVP6N(W*$Hj)DgnGIFClEFK+dgh-+&!+wW)jFzCdGB+)A{k|ADvz z(C%dL#HuBLsa}v6nr%5252SIp9c-$;+tCrwx0QCVV&2X#(Sa9l{fdh5Yu2Xwu8Y1& zt+>j*`Ci5K+c#?$Z8Z-zg2$HT7oRIzWH}zM{qC|_%NrZ~NY-F5*n3V=#Sy#-V=J)zNYnm1vpdn?A7<~ zjgkc9_Rjx#r#MQE$%_>~GERfg5^r(4JER{c3XDA)G`W{rH@`b&ee}o8gP!HTioC+^S#}hAlUvFinhT?NvnnySM)%uS1BTd4A#A@?txxAA#&muu zNgK*L0m|NA3dP%e0>0EPB9Pnfw_`5(DE(Io9b>?roh-qERVIdlj-{!GFn!?ziD0R(zxBQ zoj9;ab3t^~Y1<|Oo|C-GZgC0PvsnsM&hbX)P?A2a_f|Zl?!9{q*ln)LIH}t2TdYHK zmBnB|&ON0*bJZ;dqeF8QZm^*BoA==mPXX%^$( zOTEQ!ft}4MGfUpm=kp|YZx})>7I<~3EfZ3&NU~2Ixi>styLXGa{n~UJ1|K$0qW5XH z!=ZE?`lk=|hqTL_a|Rp=1a%h7e%KE-K5S1t*S}69hXOi02f7{}?VR|g?7t>o;Sr?u zALjhVtiiA|K6P|i|BTrGrrt%@H)ib`@c>k;pAR)go?Jh}lJ9SBPnZ=S{zB>9C)VWH|xFDV$>^j>1R^!z7NUc#7c!MKJQO_JG-g^CW^| zD2fOYhjS7l`^=LVPjNU#;FQRK6#(&Y63vpR#Iuyh;{q;nnCvNv69OV~wCpSh%R*zw zORGm97!H9+f`kbQ$6$eF5tbK4oTV9tqEIqsj^+>=qevEIc?^f;@I*q87=gi1hG#?; ziiEsR2GR*G31!-6B=$kX5U@F+@g3__B!+c+y5rC?G{D%B6#W#Ah1Dvfh)y@gI(<58{Gj$ZuS53~5m{ zUOAX4prrFwuygl35bT-+TDQBv_-EXpX-hiT*O?dDy8R;vOxSJ(pLR}^iJKx%?!X`g ZfcHA5fQj2Dfj?}SAPXc$PCQ%V_z#g@MX~?@ diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs deleted file mode 100644 index e4a276fe6c7..00000000000 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/UnitTest1.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Xunit; - -namespace Tests.Microsoft.Quantum.Qir.Tools -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} From 11570e42e782a7e290b0790f36a9fcde09d0d060 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Wed, 21 Apr 2021 11:50:34 -0700 Subject: [PATCH 05/11] Fix compile issue --- .../Tools/Executable/QirExecutable.cs | 18 ++++++++++++------ .../Tools/Executable/QirFullStateExecutable.cs | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs index ccc88ff2568..10216677af2 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -15,11 +15,12 @@ namespace Microsoft.Quantum.Qir.Tools.Executable /// public abstract class QirExecutable : IQirExecutable { - private const string DriverFileName = "qir.driver"; + private const string DriverFileName = "driver"; private const string BytecodeFileName = "qir.bc"; protected FileInfo ExecutableFile { get; } private readonly byte[] qirBytecode; public virtual string SourceDirectoryPath => "src"; + public abstract string DriverFileExtension { get; } private readonly ILogger logger; private readonly IQuantumExecutableRunner runner; private readonly IQirDriverGenerator driverGenerator; @@ -47,15 +48,20 @@ public async Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libra logger.LogInfo($"Created source directory at {sourceDirectory.FullName}."); // Create driver. - var driverFile = new FileInfo(Path.Combine(sourceDirectory.FullName, DriverFileName)); - using var driverFileStream = driverFile.OpenWrite(); - await driverGenerator.GenerateAsync(entryPoint, driverFileStream); + var driverFileNameWithExtension = Path.ChangeExtension(DriverFileName, DriverFileExtension); + var driverFile = new FileInfo(Path.Combine(sourceDirectory.FullName, driverFileNameWithExtension)); + using (var driverFileStream = driverFile.OpenWrite()) + { + await driverGenerator.GenerateAsync(entryPoint, driverFileStream); + } logger.LogInfo($"Created driver file at {driverFile.FullName}."); // Create bytecode file. var bytecodeFile = new FileInfo(Path.Combine(sourceDirectory.FullName, BytecodeFileName)); - using var bytecodeFileStream = bytecodeFile.OpenWrite(); - await bytecodeFileStream.WriteAsync(qirBytecode); + using (var bytecodeFileStream = bytecodeFile.OpenWrite()) + { + await bytecodeFileStream.WriteAsync(qirBytecode); + } logger.LogInfo($"Created bytecode file at {bytecodeFile.FullName}."); await executableGenerator.GenerateExecutableAsync(ExecutableFile, sourceDirectory, libraryDirectory, includeDirectory, LinkLibraries); diff --git a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs index e70bba806e6..6f935d8c221 100644 --- a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs @@ -33,5 +33,7 @@ public static IQirExecutable CreateInstance(FileInfo executableFile, byte[] qirB "Microsoft.Quantum.Qir.QSharp.Foundation", "Microsoft.Quantum.Qir.QSharp.Core" }; + + public override string DriverFileExtension => "cpp"; } } From db7fcfca33a04d3799ec95142073580e8992510b Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Wed, 21 Apr 2021 11:53:07 -0700 Subject: [PATCH 06/11] Fix unit test --- .../Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs index bddd00965d7..d70470406e4 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs @@ -48,6 +48,7 @@ public QirExecutableTests() linkLibraries = new List { "lib1", "lib2" }; qirExecutable.SetupGet(obj => obj.LinkLibraries).Returns(linkLibraries); qirExecutable.SetupGet(obj => obj.SourceDirectoryPath).Returns(sourceDirectory.FullName); + qirExecutable.SetupGet(obj => obj.DriverFileExtension).Returns(".cpp"); } public void Dispose() @@ -83,7 +84,7 @@ public async Task TestBuild() } // Verify that the driver was written to the correct file. - var driver = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.driver")); + var driver = new FileInfo(Path.Combine(sourceDirectory.FullName, "driver.cpp")); using var driverStreamReader = driver.OpenText(); var actualDriverContents = driverStreamReader.ReadToEnd(); Assert.Equal(driverFileContents, actualDriverContents); From bf5c8946a3eac85748dad39c799473f0a5a50aeb Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Wed, 21 Apr 2021 11:56:11 -0700 Subject: [PATCH 07/11] Clean up --- .../Execution/Tools/Executable/IQirExecutable.cs | 13 +++++++++++++ src/Qir/Execution/Tools/Executable/QirExecutable.cs | 7 +++++-- .../Tools/Executable/QirFullStateExecutable.cs | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Qir/Execution/Tools/Executable/IQirExecutable.cs b/src/Qir/Execution/Tools/Executable/IQirExecutable.cs index 7caaa8328b8..6dc5914b5ea 100644 --- a/src/Qir/Execution/Tools/Executable/IQirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/IQirExecutable.cs @@ -9,8 +9,21 @@ namespace Microsoft.Quantum.Qir.Tools.Executable { public interface IQirExecutable { + /// + /// Builds the executable. + /// + /// Entry point operation. + /// Directory containing libraries to link. + /// Directory containing files to include. + /// Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory); + /// + /// Runs the executable. + /// + /// Entry point operation. + /// Stream to which output will be written. + /// Task RunAsync(EntryPointOperation entryPoint, Stream output); } } diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs index 10216677af2..176e2a96097 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -17,10 +17,13 @@ public abstract class QirExecutable : IQirExecutable { private const string DriverFileName = "driver"; private const string BytecodeFileName = "qir.bc"; - protected FileInfo ExecutableFile { get; } - private readonly byte[] qirBytecode; + public virtual string SourceDirectoryPath => "src"; public abstract string DriverFileExtension { get; } + + protected FileInfo ExecutableFile { get; } + + private readonly byte[] qirBytecode; private readonly ILogger logger; private readonly IQuantumExecutableRunner runner; private readonly IQirDriverGenerator driverGenerator; diff --git a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs index 6f935d8c221..7ad32de8283 100644 --- a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs @@ -25,7 +25,7 @@ public static IQirExecutable CreateInstance(FileInfo executableFile, byte[] qirB var clangClient = new ClangClient(logger); var executableGenerator = new QirExecutableGenerator(clangClient, logger); var runner = new QuantumExecutableRunner(logger); - return new QirFullStateExecutable(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner) as IQirExecutable; + return new QirFullStateExecutable(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner); } public override IList LinkLibraries => new List { From 9789e5541f5c38b9b7f47094e321fe204afaa755 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Thu, 22 Apr 2021 10:08:50 -0700 Subject: [PATCH 08/11] Fix visibility of classes --- src/Qir/Execution/Tools/AssemblyInfo.cs | 23 +++++++++++++++++++ .../Execution/Tools/Executable/ClangClient.cs | 2 +- .../Tools/Executable/IClangClient.cs | 2 +- .../Executable/IQirExecutableGenerator.cs | 2 +- .../Executable/IQuantumExecutableRunner.cs | 2 +- .../Tools/Executable/QirExecutable.cs | 2 +- .../Executable/QirExecutableGenerator.cs | 2 +- .../Executable/QirFullStateExecutable.cs | 15 ++++++------ .../Executable/QuantumExecutableRunner.cs | 2 +- 9 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 src/Qir/Execution/Tools/AssemblyInfo.cs diff --git a/src/Qir/Execution/Tools/AssemblyInfo.cs b/src/Qir/Execution/Tools/AssemblyInfo.cs new file mode 100644 index 00000000000..1a412a83263 --- /dev/null +++ b/src/Qir/Execution/Tools/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties + + +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid("1594aae8-0e24-442b-9201-430ce9ee4d2e")] + +[assembly: InternalsVisibleTo("Tests.Microsoft.Quantum.Qir.Tools")] + +// This is required to mock internals in tests. +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/src/Qir/Execution/Tools/Executable/ClangClient.cs b/src/Qir/Execution/Tools/Executable/ClangClient.cs index 036483c16c8..d4a204642a2 100644 --- a/src/Qir/Execution/Tools/Executable/ClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/ClangClient.cs @@ -7,7 +7,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable { - public class ClangClient : IClangClient + internal class ClangClient : IClangClient { private const string LinkFlag = " -l "; private readonly ILogger logger; diff --git a/src/Qir/Execution/Tools/Executable/IClangClient.cs b/src/Qir/Execution/Tools/Executable/IClangClient.cs index e7eacd96db1..f5a1bb38135 100644 --- a/src/Qir/Execution/Tools/Executable/IClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/IClangClient.cs @@ -8,7 +8,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable /// /// Wraps the 'clang' tool used for compilation. /// - public interface IClangClient + internal interface IClangClient { Task CreateExecutableAsync(string[] inputFiles, string[] libraries, string libraryPath, string includePath, string outputPath); } diff --git a/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs b/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs index 88328b0a85c..3ddb310f98e 100644 --- a/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs +++ b/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs @@ -7,7 +7,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable { - public interface IQirExecutableGenerator + internal interface IQirExecutableGenerator { /// /// Generates a quantum simulation program executable. diff --git a/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs b/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs index 0ac0edf4fa5..292cb394737 100644 --- a/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs +++ b/src/Qir/Execution/Tools/Executable/IQuantumExecutableRunner.cs @@ -6,7 +6,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable { - public interface IQuantumExecutableRunner + internal interface IQuantumExecutableRunner { /// /// Runs a quantum program executable with the given arguments. diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs index 176e2a96097..019d9fed532 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -29,7 +29,7 @@ public abstract class QirExecutable : IQirExecutable private readonly IQirDriverGenerator driverGenerator; private readonly IQirExecutableGenerator executableGenerator; - public QirExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) + internal QirExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) { ExecutableFile = executableFile; this.qirBytecode = qirBytecode; diff --git a/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs b/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs index b99d471e7c1..80eaf8a768a 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs @@ -9,7 +9,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable { - public class QirExecutableGenerator : IQirExecutableGenerator + internal class QirExecutableGenerator : IQirExecutableGenerator { private readonly IClangClient clangClient; private readonly ILogger logger; diff --git a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs index 7ad32de8283..6728a92b2bf 100644 --- a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs @@ -13,19 +13,20 @@ namespace Microsoft.Quantum.Qir.Tools.Executable /// public class QirFullStateExecutable : QirExecutable { - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) + internal QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) : base(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner) { } - public static IQirExecutable CreateInstance(FileInfo executableFile, byte[] qirBytecode, ILogger logger) + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger) + : this(executableFile, + qirBytecode, + logger, + new QirFullStateDriverGenerator(), + new QirExecutableGenerator(new ClangClient(logger), logger), + new QuantumExecutableRunner(logger)) { - var driverGenerator = new QirFullStateDriverGenerator(); - var clangClient = new ClangClient(logger); - var executableGenerator = new QirExecutableGenerator(clangClient, logger); - var runner = new QuantumExecutableRunner(logger); - return new QirFullStateExecutable(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner); } public override IList LinkLibraries => new List { diff --git a/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs b/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs index 9cf123c31ab..987d6212799 100644 --- a/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs +++ b/src/Qir/Execution/Tools/Executable/QuantumExecutableRunner.cs @@ -8,7 +8,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable { - public class QuantumExecutableRunner : IQuantumExecutableRunner + internal class QuantumExecutableRunner : IQuantumExecutableRunner { private readonly ILogger logger; From c99315cccdb68b3eea9f753eb0e6b9cdefe4f822 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Thu, 22 Apr 2021 10:38:42 -0700 Subject: [PATCH 09/11] Fix controller and rename test namespace --- src/Qir/Controller/Controller.cs | 2 +- .../Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs | 2 +- .../QirExecutableGeneratorTests.cs | 2 +- .../Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs | 2 +- src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Qir/Controller/Controller.cs b/src/Qir/Controller/Controller.cs index 013b0f62901..09a8b1ef8c8 100644 --- a/src/Qir/Controller/Controller.cs +++ b/src/Qir/Controller/Controller.cs @@ -37,7 +37,7 @@ public static async Task ExecuteAsync( logger.LogInfo("Creating executable."); var bytecodeArray = input.QirBytecode.Array.Skip(input.QirBytecode.Offset).Take(input.QirBytecode.Count).ToList().ToArray(); var executableFile = new FileInfo(Path.Combine(BinaryDirectoryPath, ExecutableName)); - var executable = QirFullStateExecutable.CreateInstance(executableFile, bytecodeArray, logger); + var executable = new QirFullStateExecutable(executableFile, bytecodeArray, logger); await executable.BuildAsync(input.EntryPoint, libraryDirectory, includeDirectory); // Step 3: Run executable. diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs index ee7adfee70a..1df4bf96552 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/LoggerTests.cs @@ -7,7 +7,7 @@ using Moq; using Xunit; -namespace Tests.QirController +namespace Tests.Microsoft.Quantum.Qir.Tools { public class LoggerTests { diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs index a84a13c03b5..93a6f98a786 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs @@ -11,7 +11,7 @@ using Moq; using Xunit; -namespace Tests.QirController +namespace Tests.Microsoft.Quantum.Qir.Tools { public class QirExecutableGeneratorTests : IDisposable { diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs index d70470406e4..217a12ca09c 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs @@ -12,7 +12,7 @@ using Moq; using Xunit; -namespace Tests.QirController +namespace Tests.Microsoft.Quantum.Qir.Tools { public class QirExecutableTests : IDisposable { diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs index 476848705ea..85c1d3a90e6 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/Util.cs @@ -5,7 +5,7 @@ using System.Reflection; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; -namespace Tests.QirController +namespace Tests.Microsoft.Quantum.Qir.Tools { public static class Util { From 1a8babad796e03bb8d282b3114e2cb6cbd86d919 Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Thu, 22 Apr 2021 15:23:58 -0700 Subject: [PATCH 10/11] Clean up and change constructors --- .../Tests.Microsoft.Quantum.Qir.Tools.csproj | 1 - .../Tools/Executable/QirFullStateExecutable.cs | 17 +++++++++++++---- .../Tools/Microsoft.Quantum.Qir.Tools.csproj | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) 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 e4f0be4e5f2..55ffaa29bd4 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 @@ -2,7 +2,6 @@ netcoreapp3.1 - false diff --git a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs index 6728a92b2bf..72a75f6351c 100644 --- a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs @@ -19,16 +19,25 @@ internal QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILo } - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger) + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger = null) : this(executableFile, qirBytecode, - logger, new QirFullStateDriverGenerator(), - new QirExecutableGenerator(new ClangClient(logger), logger), - new QuantumExecutableRunner(logger)) + logger) { } + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, IQirDriverGenerator driverGenerator, ILogger logger = null) + : this(executableFile, + qirBytecode, + logger ?? new Logger(new Clock()), + driverGenerator, + new QirExecutableGenerator(new ClangClient(logger), logger), + new QuantumExecutableRunner(logger)) + { + } + + public override IList LinkLibraries => new List { "Microsoft.Quantum.Qir.Runtime", "Microsoft.Quantum.Qir.QSharp.Foundation", diff --git a/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj b/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj index 9cad908d26d..06ca9571b79 100644 --- a/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj +++ b/src/Qir/Execution/Tools/Microsoft.Quantum.Qir.Tools.csproj @@ -13,5 +13,5 @@ - + From ed31121a0baa61a65fa37e2280d6f8b68e25bc8b Mon Sep 17 00:00:00 2001 From: Alexander Chocron Date: Fri, 23 Apr 2021 11:09:17 -0700 Subject: [PATCH 11/11] Fix constructors --- .../Tools/Executable/QirExecutable.cs | 10 ++++++++++ .../Executable/QirFullStateExecutable.cs | 19 +------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs index 019d9fed532..265006d2034 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -29,6 +29,16 @@ public abstract class QirExecutable : IQirExecutable private readonly IQirDriverGenerator driverGenerator; private readonly IQirExecutableGenerator executableGenerator; + public QirExecutable(FileInfo executableFile, byte[] qirBytecode, IQirDriverGenerator driverGenerator, ILogger logger = null) + : this(executableFile, + qirBytecode, + logger ?? new Logger(new Clock()), + driverGenerator, + new QirExecutableGenerator(new ClangClient(logger), logger), + new QuantumExecutableRunner(logger)) + { + } + internal QirExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) { ExecutableFile = executableFile; diff --git a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs index 72a75f6351c..03362d370a6 100644 --- a/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirFullStateExecutable.cs @@ -13,31 +13,14 @@ namespace Microsoft.Quantum.Qir.Tools.Executable /// public class QirFullStateExecutable : QirExecutable { - internal QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) - : base(executableFile, qirBytecode, logger, driverGenerator, executableGenerator, runner) - { - - } - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger = null) - : this(executableFile, + : base(executableFile, qirBytecode, new QirFullStateDriverGenerator(), logger) { } - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, IQirDriverGenerator driverGenerator, ILogger logger = null) - : this(executableFile, - qirBytecode, - logger ?? new Logger(new Clock()), - driverGenerator, - new QirExecutableGenerator(new ClangClient(logger), logger), - new QuantumExecutableRunner(logger)) - { - } - - public override IList LinkLibraries => new List { "Microsoft.Quantum.Qir.Runtime", "Microsoft.Quantum.Qir.QSharp.Foundation",