From 0e12f1d99a29afbbb26a5075d70b2e1aae447a16 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 4 May 2020 19:19:55 -0700 Subject: [PATCH 01/39] Add command-line options for Azure submission --- src/Simulation/EntryPointDriver/Driver.cs | 137 ++++++++++++++---- .../EntryPointDriver/EntryPointDriver.csproj | 1 + 2 files changed, 107 insertions(+), 31 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 118d7031dbf..68af2e4e3f4 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -10,6 +10,7 @@ using System.CommandLine.Parsing; using System.Linq; using System.Threading.Tasks; +using Microsoft.Azure.Quantum; using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.Simulation.Core; using Microsoft.Quantum.Simulation.Simulators; @@ -44,20 +45,26 @@ public async Task Run(string[] args) { Handler = CommandHandler.Create(Simulate) }; - TryCreateOption( - SimulatorOptions, - () => entryPoint.DefaultSimulator, - "The name of the simulator to use.") - .Then(simulator => simulate.AddOption(simulator.WithSuggestions( + AddOptionIfAvailable(simulate, SimulatorOptions, entryPoint.DefaultSimulator, + "The name of the simulator to use.", + new[] + { AssemblyConstants.QuantumSimulator, AssemblyConstants.ToffoliSimulator, AssemblyConstants.ResourcesEstimator, - entryPoint.DefaultSimulator))); + entryPoint.DefaultSimulator + }); var submit = new Command("submit", "Submit the program to Azure Quantum.") { - Handler = CommandHandler.Create(Submit) + Handler = CommandHandler.Create(Submit) }; + // TODO: Define the aliases as constants. + AddOptionIfAvailable(submit, new[] { "--target" }, "The target device ID."); + AddOptionIfAvailable(submit, new[] { "--subscription" }, "The Azure subscription ID."); + AddOptionIfAvailable(submit, new[] { "--resource-group" }, "The Azure resource group name."); + AddOptionIfAvailable(submit, new[] { "--workspace" }, "The Azure workspace name."); + AddOptionIfAvailable(submit, new[] { "--storage" }, "The Azure storage account connection string."); var root = new RootCommand(entryPoint.Summary) { simulate, submit }; foreach (var option in entryPoint.Options) { root.AddGlobalOption(option); } @@ -110,16 +117,15 @@ private async Task Simulate(ParseResult parseResult, string simulator) /// Submits the entry point to Azure Quantum. /// /// The command-line parsing result. - private async Task Submit(ParseResult parseResult) + /// The submission settings. + private static void Submit(ParseResult parseResult, AzureQuantumSettings settings) { - // TODO: Use an actual quantum machine. - var machine = new SimulatorMachine(); - var output = await machine.ExecuteAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); - // TODO: Provide output options and show the most frequent output by default. - foreach (var (result, frequency) in output.Histogram) - { - Console.WriteLine($"{result} (frequency = {frequency})"); - } + // TODO + Console.WriteLine($"Target: {settings.Target}"); + Console.WriteLine($"Subscription: {settings.Subscription}"); + Console.WriteLine($"ResourceGroup: {settings.ResourceGroup}"); + Console.WriteLine($"Workspace: {settings.Workspace}"); + Console.WriteLine($"Storage: {settings.Storage}"); } /// @@ -183,23 +189,55 @@ private T DefaultIfShadowed(string alias, T value, T defaultValue) } /// - /// Tries to create an option by removing aliases that are already in use by the entry point. If the first - /// alias, which is considered the primary alias, is in use, then the option is not created. + /// Evaluates the given function on the collection of available aliases if the primary (first) alias is + /// available. /// - /// The type of the option's argument. - /// The option's aliases. - /// A function that returns the option's default value. - /// The option's description. - /// A validation of the option. - private Validation> TryCreateOption( - IReadOnlyCollection aliases, Func getDefaultValue, string? description = null) => + /// The aliases to check for availability. + /// The function to evaluate on the available aliases. + /// The given function's return type. + /// The return value of the given function or a failure if the first alias is not available. + private Validation TryWithAvailableAliases( + IReadOnlyCollection aliases, Func, T> func) => IsAliasAvailable(aliases.First()) - ? Validation>.Success( - new Option( - aliases.Where(IsAliasAvailable).ToArray(), - getDefaultValue, - description)) - : Validation>.Failure(); + ? Validation.Success(func(aliases.Where(IsAliasAvailable).ToArray())) + : Validation.Failure(); + + /// + /// Adds the required option to the command using only the aliases that are available, and only if the primary + /// (first) alias is available. + /// + /// The command to add the option to. + /// The collection of option aliases. + /// The option description. + /// The type of the option values. + private void AddOptionIfAvailable( + Command command, IReadOnlyCollection aliases, string? description = default) => + TryWithAvailableAliases(aliases, validAliases => + new Option(validAliases.ToArray(), description) { Required = true }) + .Then(command.AddOption); + + /// + /// Adds the option to the command using only the aliases that are available, and only if the primary (first) + /// alias is available. + /// + /// The command to add the option to. + /// The collection of option aliases. + /// The default value of the option. + /// The option description. + /// The suggestions for the option's values. + /// The type of the option values. + private void AddOptionIfAvailable( + Command command, + IReadOnlyCollection aliases, + T defaultValue, + string? description = default, + string[]? suggestions = default) => + TryWithAvailableAliases(aliases, validAliases => + new Option(validAliases.ToArray(), () => defaultValue, description)) + .Then(option => + command.AddOption(suggestions is null || !suggestions.Any() + ? option + : option.WithSuggestions(suggestions))); /// /// Displays an error message for using a non-default custom simulator. @@ -235,6 +273,43 @@ internal static class Driver "-" + CommandLineArguments.SimulatorOption.Item2 }); } + + /// + /// Settings for a submission to Azure Quantum. + /// + internal sealed class AzureQuantumSettings + { + /// + /// The target device ID. + /// + public string? Target { get; set; } + + /// + /// The Azure subscription ID. + /// + public string? Subscription { get; set; } + + /// + /// The Azure resource group name. + /// + public string? ResourceGroup { get; set; } + + /// + /// The Azure workspace name. + /// + public string? Workspace { get; set; } + + /// + /// The Azure storage account connection string. + /// + public string? Storage { get; set; } + + /// + /// Converts these settings into a . + /// + /// The corresponding to these settings. + internal Workspace ToWorkspace() => new Workspace(Subscription, ResourceGroup, Workspace, Storage); + } /// /// A modification of the command-line class. diff --git a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj index 7760104f92a..c52b876e3fd 100644 --- a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj +++ b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj @@ -10,6 +10,7 @@ + From 5ee822dfb218b7a17054be5a16e43c7bdd421ef3 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 5 May 2020 12:11:56 -0700 Subject: [PATCH 02/39] Small code style fixes --- src/Simulation/EntryPointDriver/Driver.cs | 36 +++++++++++++---------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 68af2e4e3f4..7a02bc09f79 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -57,7 +57,7 @@ public async Task Run(string[] args) var submit = new Command("submit", "Submit the program to Azure Quantum.") { - Handler = CommandHandler.Create(Submit) + Handler = CommandHandler.Create(Submit) }; // TODO: Define the aliases as constants. AddOptionIfAvailable(submit, new[] { "--target" }, "The target device ID."); @@ -67,10 +67,16 @@ public async Task Run(string[] args) AddOptionIfAvailable(submit, new[] { "--storage" }, "The Azure storage account connection string."); var root = new RootCommand(entryPoint.Summary) { simulate, submit }; - foreach (var option in entryPoint.Options) { root.AddGlobalOption(option); } + foreach (var option in entryPoint.Options) + { + root.AddGlobalOption(option); + } // Set the simulate command as the default. - foreach (var option in simulate.Options) { root.AddOption(option); } + foreach (var option in simulate.Options) + { + root.AddOption(option); + } root.Handler = simulate.Handler; return await new CommandLineBuilder(root) @@ -99,10 +105,10 @@ private async Task Simulate(ParseResult parseResult, string simulator) { var (isCustom, createSimulator) = simulator == AssemblyConstants.QuantumSimulator - ? (false, () => new QuantumSimulator()) - : simulator == AssemblyConstants.ToffoliSimulator - ? (false, new Func(() => new ToffoliSimulator())) - : (true, entryPoint.CreateDefaultCustomSimulator); + ? (false, () => new QuantumSimulator()) + : simulator == AssemblyConstants.ToffoliSimulator + ? (false, new Func(() => new ToffoliSimulator())) + : (true, entryPoint.CreateDefaultCustomSimulator); if (isCustom && simulator != entryPoint.DefaultSimulator) { DisplayCustomSimulatorError(simulator); @@ -118,7 +124,7 @@ private async Task Simulate(ParseResult parseResult, string simulator) /// /// The command-line parsing result. /// The submission settings. - private static void Submit(ParseResult parseResult, AzureQuantumSettings settings) + private static void Submit(ParseResult parseResult, AzureSettings settings) { // TODO Console.WriteLine($"Target: {settings.Target}"); @@ -213,8 +219,7 @@ private Validation TryWithAvailableAliases( private void AddOptionIfAvailable( Command command, IReadOnlyCollection aliases, string? description = default) => TryWithAvailableAliases(aliases, validAliases => - new Option(validAliases.ToArray(), description) { Required = true }) - .Then(command.AddOption); + new Option(validAliases.ToArray(), description) { Required = true }).Then(command.AddOption); /// /// Adds the option to the command using only the aliases that are available, and only if the primary (first) @@ -233,11 +238,10 @@ private void AddOptionIfAvailable( string? description = default, string[]? suggestions = default) => TryWithAvailableAliases(aliases, validAliases => - new Option(validAliases.ToArray(), () => defaultValue, description)) - .Then(option => - command.AddOption(suggestions is null || !suggestions.Any() - ? option - : option.WithSuggestions(suggestions))); + new Option(validAliases.ToArray(), () => defaultValue, description)).Then(option => + command.AddOption(suggestions is null || !suggestions.Any() + ? option + : option.WithSuggestions(suggestions))); /// /// Displays an error message for using a non-default custom simulator. @@ -277,7 +281,7 @@ internal static class Driver /// /// Settings for a submission to Azure Quantum. /// - internal sealed class AzureQuantumSettings + internal sealed class AzureSettings { /// /// The target device ID. From f4335c4dd9ecdecd6ee038e280cd395b8c823c32 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 6 May 2020 18:23:29 -0700 Subject: [PATCH 03/39] Use real provider for Azure submission --- src/Simulation/EntryPointDriver/Azure.cs | 83 ++++++++++++ src/Simulation/EntryPointDriver/Driver.cs | 123 ++---------------- .../EntryPointDriver/EntryPointDriver.csproj | 1 - src/Simulation/EntryPointDriver/Simulation.cs | 98 ++++++++++++++ 4 files changed, 189 insertions(+), 116 deletions(-) create mode 100644 src/Simulation/EntryPointDriver/Azure.cs create mode 100644 src/Simulation/EntryPointDriver/Simulation.cs diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs new file mode 100644 index 00000000000..3dad5e8caf2 --- /dev/null +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -0,0 +1,83 @@ +using System; +using System.CommandLine.Parsing; +using System.Threading.Tasks; +using Microsoft.Quantum.Runtime; + +namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver +{ + /// + /// Provides entry point submission to Azure Quantum. + /// + internal static class Azure + { + /// + /// Submits the entry point to Azure Quantum. + /// + /// The entry point. + /// The command-line parsing result. + /// The submission settings. + /// The entry point's argument type. + /// The entry point's return type. + internal static async Task Submit( + IEntryPoint entryPoint, ParseResult parseResult, AzureSettings settings) + { + // TODO: DefaultIfShadowed for settings + var machineType = Type.GetType( + "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", + throwOnError: true); + var machine = (IQuantumMachine)Activator.CreateInstance( + machineType, settings.Target, settings.Storage, settings.ToWorkspace()); + var output = await machine.ExecuteAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); + // TODO: Provide output options and show the most frequent output by default. + foreach (var (result, frequency) in output.Histogram) + { + Console.WriteLine($"{result} (frequency = {frequency})"); + } + } + } + + /// + /// Settings for a submission to Azure Quantum. + /// + internal sealed class AzureSettings + { + /// + /// The target device ID. + /// + public string? Target { get; set; } + + /// + /// The Azure subscription ID. + /// + public string? Subscription { get; set; } + + /// + /// The Azure resource group name. + /// + public string? ResourceGroup { get; set; } + + /// + /// The Azure workspace name. + /// + public string? Workspace { get; set; } + + /// + /// The Azure storage account connection string. + /// + public string? Storage { get; set; } + + /// + /// Converts these settings into a Microsoft.Azure.Quantum.Workspace object. + /// + /// The workspace object corresponding to these settings. + internal object ToWorkspace() + { + var workspaceType = Type.GetType( + "Microsoft.Azure.Quantum.Workspace, Microsoft.Azure.Quantum.Client", throwOnError: true); + var tokenCredentialType = Type.GetType("Azure.Core.TokenCredential, Azure.Core", throwOnError: true); + var constructor = workspaceType.GetConstructor(new[] + { typeof(string), typeof(string), typeof(string), tokenCredentialType, typeof(Uri) }); + return constructor.Invoke(new object?[] { Subscription, ResourceGroup, Workspace, null, null }); + } + } +} diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 7a02bc09f79..8f4552f250f 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -10,10 +10,8 @@ using System.CommandLine.Parsing; using System.Linq; using System.Threading.Tasks; -using Microsoft.Azure.Quantum; using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.Simulation.Core; -using Microsoft.Quantum.Simulation.Simulators; using static Microsoft.Quantum.CsharpGeneration.EntryPointDriver.Driver; namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver @@ -21,6 +19,9 @@ namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver /// /// The entry point driver is the entry point for the C# application that executes the Q# entry point. /// + /// The entry point's callable type. + /// The entry point's argument type. + /// The entry point's return type. public sealed class Driver where TCallable : AbstractCallable, ICallable { /// @@ -95,28 +96,7 @@ public async Task Run(string[] args) private async Task Simulate(ParseResult parseResult, string simulator) { simulator = DefaultIfShadowed(SimulatorOptions.First(), simulator, entryPoint.DefaultSimulator); - if (simulator == AssemblyConstants.ResourcesEstimator) - { - var resourcesEstimator = new ResourcesEstimator(); - await resourcesEstimator.Run(entryPoint.CreateArgument(parseResult)); - Console.WriteLine(resourcesEstimator.ToTSV()); - } - else - { - var (isCustom, createSimulator) = - simulator == AssemblyConstants.QuantumSimulator - ? (false, () => new QuantumSimulator()) - : simulator == AssemblyConstants.ToffoliSimulator - ? (false, new Func(() => new ToffoliSimulator())) - : (true, entryPoint.CreateDefaultCustomSimulator); - if (isCustom && simulator != entryPoint.DefaultSimulator) - { - DisplayCustomSimulatorError(simulator); - return 1; - } - await RunSimulator(parseResult, createSimulator); - } - return 0; + return await Simulation.Simulate(entryPoint, parseResult, simulator); } /// @@ -124,41 +104,10 @@ private async Task Simulate(ParseResult parseResult, string simulator) /// /// The command-line parsing result. /// The submission settings. - private static void Submit(ParseResult parseResult, AzureSettings settings) - { - // TODO - Console.WriteLine($"Target: {settings.Target}"); - Console.WriteLine($"Subscription: {settings.Subscription}"); - Console.WriteLine($"ResourceGroup: {settings.ResourceGroup}"); - Console.WriteLine($"Workspace: {settings.Workspace}"); - Console.WriteLine($"Storage: {settings.Storage}"); - } - - /// - /// Runs the entry point on a simulator and displays its return value. - /// - /// The command-line parsing result. - /// A function that creates an instance of the simulator to use. - private async Task RunSimulator(ParseResult parseResult, Func createSimulator) - { - var simulator = createSimulator(); - try - { - var value = await simulator.Run(entryPoint.CreateArgument(parseResult)); - if (!(value is QVoid)) - { - Console.WriteLine(value); - } - } - finally - { - if (simulator is IDisposable disposable) - { - disposable.Dispose(); - } - } - } - + private async Task Submit(ParseResult parseResult, AzureSettings settings) => + // TODO: DefaultIfShadowed + await Azure.Submit(entryPoint, parseResult, settings); + /// /// Returns true if the alias is not already used by an entry point option. /// @@ -242,25 +191,6 @@ private void AddOptionIfAvailable( command.AddOption(suggestions is null || !suggestions.Any() ? option : option.WithSuggestions(suggestions))); - - /// - /// Displays an error message for using a non-default custom simulator. - /// - /// The name of the custom simulator. - private static void DisplayCustomSimulatorError(string name) - { - var originalForeground = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine($"The simulator '{name}' could not be found."); - Console.ForegroundColor = originalForeground; - Console.Error.WriteLine(); - Console.Error.WriteLine( - $"If '{name}' is a custom simulator, it must be set in the DefaultSimulator project property:"); - Console.Error.WriteLine(); - Console.Error.WriteLine(""); - Console.Error.WriteLine($" {name}"); - Console.Error.WriteLine(""); - } } /// @@ -278,43 +208,6 @@ internal static class Driver }); } - /// - /// Settings for a submission to Azure Quantum. - /// - internal sealed class AzureSettings - { - /// - /// The target device ID. - /// - public string? Target { get; set; } - - /// - /// The Azure subscription ID. - /// - public string? Subscription { get; set; } - - /// - /// The Azure resource group name. - /// - public string? ResourceGroup { get; set; } - - /// - /// The Azure workspace name. - /// - public string? Workspace { get; set; } - - /// - /// The Azure storage account connection string. - /// - public string? Storage { get; set; } - - /// - /// Converts these settings into a . - /// - /// The corresponding to these settings. - internal Workspace ToWorkspace() => new Workspace(Subscription, ResourceGroup, Workspace, Storage); - } - /// /// A modification of the command-line class. /// diff --git a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj index c52b876e3fd..7760104f92a 100644 --- a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj +++ b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj @@ -10,7 +10,6 @@ - diff --git a/src/Simulation/EntryPointDriver/Simulation.cs b/src/Simulation/EntryPointDriver/Simulation.cs new file mode 100644 index 00000000000..9769b6b5ee4 --- /dev/null +++ b/src/Simulation/EntryPointDriver/Simulation.cs @@ -0,0 +1,98 @@ +using System; +using System.CommandLine.Parsing; +using System.Threading.Tasks; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver +{ + /// + /// Provides entry point simulation. + /// + /// The entry point's callable type. + /// The entry point's argument type. + /// The entry point's return type. + internal static class Simulation where TCallable : AbstractCallable, ICallable + { + /// + /// Simulates the entry point. + /// + /// The entry point. + /// The command-line parsing result. + /// The simulator to use. + /// The exit code. + internal static async Task Simulate( + IEntryPoint entryPoint, ParseResult parseResult, string simulator) + { + if (simulator == AssemblyConstants.ResourcesEstimator) + { + var resourcesEstimator = new ResourcesEstimator(); + await resourcesEstimator.Run(entryPoint.CreateArgument(parseResult)); + Console.WriteLine(resourcesEstimator.ToTSV()); + } + else + { + var (isCustom, createSimulator) = + simulator == AssemblyConstants.QuantumSimulator + ? (false, () => new QuantumSimulator()) + : simulator == AssemblyConstants.ToffoliSimulator + ? (false, new Func(() => new ToffoliSimulator())) + : (true, entryPoint.CreateDefaultCustomSimulator); + if (isCustom && simulator != entryPoint.DefaultSimulator) + { + DisplayCustomSimulatorError(simulator); + return 1; + } + await RunSimulator(entryPoint, parseResult, createSimulator); + } + return 0; + } + + /// + /// Runs the entry point on a simulator and displays its return value. + /// + /// The entry point. + /// The command-line parsing result. + /// A function that creates an instance of the simulator to use. + private static async Task RunSimulator( + IEntryPoint entryPoint, ParseResult parseResult, Func createSimulator) + { + var simulator = createSimulator(); + try + { + var value = await simulator.Run(entryPoint.CreateArgument(parseResult)); + if (!(value is QVoid)) + { + Console.WriteLine(value); + } + } + finally + { + if (simulator is IDisposable disposable) + { + disposable.Dispose(); + } + } + } + + /// + /// Displays an error message for using a non-default custom simulator. + /// + /// The name of the custom simulator. + private static void DisplayCustomSimulatorError(string name) + { + var originalForeground = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine($"The simulator '{name}' could not be found."); + Console.ForegroundColor = originalForeground; + Console.Error.WriteLine(); + Console.Error.WriteLine( + $"If '{name}' is a custom simulator, it must be set in the DefaultSimulator project property:"); + Console.Error.WriteLine(); + Console.Error.WriteLine(""); + Console.Error.WriteLine($" {name}"); + Console.Error.WriteLine(""); + } + } +} From 00aa3a08f22b13a147180437ce6c8b1aa7d86319 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 6 May 2020 18:25:00 -0700 Subject: [PATCH 04/39] Remove TODO from the wrong place --- src/Simulation/EntryPointDriver/Azure.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 3dad5e8caf2..b3b80b20377 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -21,7 +21,6 @@ internal static class Azure internal static async Task Submit( IEntryPoint entryPoint, ParseResult parseResult, AzureSettings settings) { - // TODO: DefaultIfShadowed for settings var machineType = Type.GetType( "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", throwOnError: true); From 102c98c3fb7879abe021e0ef4f32b4dd98c9895b Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 09:01:44 -0700 Subject: [PATCH 05/39] Use SimulatorMachine based on target name --- src/Simulation/EntryPointDriver/Azure.cs | 51 ++++++++++++++++--- src/Simulation/EntryPointDriver/Driver.cs | 2 +- .../EntryPointDriver/SimulatorMachine.cs | 8 +++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index b3b80b20377..64823397a57 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -18,20 +18,59 @@ internal static class Azure /// The submission settings. /// The entry point's argument type. /// The entry point's return type. - internal static async Task Submit( + internal static async Task Submit( IEntryPoint entryPoint, ParseResult parseResult, AzureSettings settings) { - var machineType = Type.GetType( - "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", - throwOnError: true); - var machine = (IQuantumMachine)Activator.CreateInstance( - machineType, settings.Target, settings.Storage, settings.ToWorkspace()); + var machine = CreateMachine(settings); + if (machine is null) + { + DisplayUnknownTargetError(settings.Target); + return 1; + } var output = await machine.ExecuteAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); // TODO: Provide output options and show the most frequent output by default. foreach (var (result, frequency) in output.Histogram) { Console.WriteLine($"{result} (frequency = {frequency})"); } + return 0; + } + + /// + /// Creates a quantum machine based on the Azure Quantum submission settings. + /// + /// The Azure Quantum submission settings. + /// A quantum machine. + private static IQuantumMachine? CreateMachine(AzureSettings settings) + { + if (settings.Target == "quantum.simulator") + { + return new SimulatorMachine(); + } + else if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) + { + var ionQType = Type.GetType( + "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", + throwOnError: true); + return (IQuantumMachine)Activator.CreateInstance( + ionQType, settings.Target, settings.Storage, settings.ToWorkspace()); + } + else + { + return null; + } + } + + /// + /// Displays an error message for attempting to use an unknown target machine. + /// + /// The target machine. + private static void DisplayUnknownTargetError(string? target) + { + var originalForeground = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine($"The target '{target}' was not recognized."); + Console.ForegroundColor = originalForeground; } } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 8f4552f250f..52c0ee41d8e 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -104,7 +104,7 @@ private async Task Simulate(ParseResult parseResult, string simulator) /// /// The command-line parsing result. /// The submission settings. - private async Task Submit(ParseResult parseResult, AzureSettings settings) => + private async Task Submit(ParseResult parseResult, AzureSettings settings) => // TODO: DefaultIfShadowed await Azure.Submit(entryPoint, parseResult, settings); diff --git a/src/Simulation/EntryPointDriver/SimulatorMachine.cs b/src/Simulation/EntryPointDriver/SimulatorMachine.cs index 8c7b5909bfe..4e0bf99422a 100644 --- a/src/Simulation/EntryPointDriver/SimulatorMachine.cs +++ b/src/Simulation/EntryPointDriver/SimulatorMachine.cs @@ -35,6 +35,10 @@ public Task SubmitAsync(EntryPointInfo throw new NotImplementedException(); } + /// + /// The output of executing the . + /// + /// The output result type. internal class SimulatorMachineOutput : IQuantumMachineOutput { public IReadOnlyDictionary Histogram { get; } @@ -43,6 +47,10 @@ internal class SimulatorMachineOutput : IQuantumMachineOutput public int Shots => 1; + /// + /// Creates a new output from the result. + /// + /// The result. internal SimulatorMachineOutput(T result) => Histogram = ImmutableDictionary.Empty.Add(result, 1); } } From 89a6932b35754bef91b4c442ac9c37e87d2db254 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 10:30:45 -0700 Subject: [PATCH 06/39] Add option for number of shots --- src/Simulation/EntryPointDriver/Azure.cs | 8 ++- src/Simulation/EntryPointDriver/Driver.cs | 3 + .../EntryPointDriver/EntryPointDriver.csproj | 1 + .../EntryPointDriver/SimulatorMachine.cs | 60 ++++++++++++++----- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 64823397a57..3680f7202d9 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -45,10 +45,11 @@ internal static async Task Submit( { if (settings.Target == "quantum.simulator") { - return new SimulatorMachine(); + return new SimulatorMachine(settings.Shots); } else if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) { + // TODO: Number of shots? var ionQType = Type.GetType( "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", throwOnError: true); @@ -103,6 +104,11 @@ internal sealed class AzureSettings /// The Azure storage account connection string. /// public string? Storage { get; set; } + + /// + /// The number of times the program is executed on the target machine. + /// + public int Shots { get; set; } /// /// Converts these settings into a Microsoft.Azure.Quantum.Workspace object. diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 52c0ee41d8e..2fa6eb2d335 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -66,6 +66,9 @@ public async Task Run(string[] args) AddOptionIfAvailable(submit, new[] { "--resource-group" }, "The Azure resource group name."); AddOptionIfAvailable(submit, new[] { "--workspace" }, "The Azure workspace name."); AddOptionIfAvailable(submit, new[] { "--storage" }, "The Azure storage account connection string."); + // TODO: Validate that shots is non-negative. + AddOptionIfAvailable(submit, new [] { "--shots" }, 500, + "The number of times the program is executed on the target machine."); var root = new RootCommand(entryPoint.Summary) { simulate, submit }; foreach (var option in entryPoint.Options) diff --git a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj index 7760104f92a..8538186421b 100644 --- a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj +++ b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Simulation/EntryPointDriver/SimulatorMachine.cs b/src/Simulation/EntryPointDriver/SimulatorMachine.cs index 4e0bf99422a..fec0f3aef74 100644 --- a/src/Simulation/EntryPointDriver/SimulatorMachine.cs +++ b/src/Simulation/EntryPointDriver/SimulatorMachine.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading.Tasks; using Microsoft.Quantum.Runtime; using Microsoft.Quantum.Simulation.Core; @@ -9,7 +10,7 @@ namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver { /// - /// A quantum machine that runs an operation using a local quantum simulator. + /// A quantum machine that runs the entry point using a local quantum simulator. /// /// /// TODO: This class is only for testing the interface. It should be removed once an @@ -21,18 +22,43 @@ internal class SimulatorMachine : IQuantumMachine public string Target => "QuantumSimulator"; + /// + /// The number of times to simulate the entry point. + /// + private readonly int shots; + + /// + /// Creates a simulator machine. + /// + /// The number of times to simulate the entry point. + internal SimulatorMachine(int shots) => this.shots = shots; + public async Task> ExecuteAsync( - EntryPointInfo info, TIn input) - { - var result = await (Task)typeof(QuantumSimulator) - .GetMethod(nameof(QuantumSimulator.Run)) - .MakeGenericMethod(info.Operation, info.InType, info.OutType) - .Invoke(new QuantumSimulator(), new object?[] { input }); - return new SimulatorMachineOutput(result); - } + EntryPointInfo info, TIn input) => + new SimulatorMachineOutput(await RunShots(info, input).ToListAsync()); public Task SubmitAsync(EntryPointInfo info, TIn input) => throw new NotImplementedException(); + + /// + /// Runs all of the simulator shots and yields the results. + /// + /// The entry point information. + /// The input to the entry point. + /// The entry point input type. + /// The entry point output type. + /// The results of simulating the entry point. + private async IAsyncEnumerable RunShots(EntryPointInfo info, TIn input) + { + var run = typeof(QuantumSimulator) + .GetMethod(nameof(QuantumSimulator.Run)) + .MakeGenericMethod(info.Operation, info.InType, info.OutType); + using var simulator = new QuantumSimulator(); + foreach (var _ in Enumerable.Range(0, shots)) + { + yield return await (Task)run.Invoke(simulator, new object?[] { input }); + } + } } /// @@ -42,15 +68,21 @@ public Task SubmitAsync(EntryPointInfo internal class SimulatorMachineOutput : IQuantumMachineOutput { public IReadOnlyDictionary Histogram { get; } - + public IQuantumMachineJob Job => throw new NotImplementedException(); - public int Shots => 1; + public int Shots { get; } /// - /// Creates a new output from the result. + /// Creates a new output from the results. /// - /// The result. - internal SimulatorMachineOutput(T result) => Histogram = ImmutableDictionary.Empty.Add(result, 1); + /// The results. + internal SimulatorMachineOutput(IReadOnlyCollection results) + { + Histogram = results + .GroupBy(result => result) + .ToImmutableDictionary(group => group.Key, group => (double)group.Count() / results.Count); + Shots = results.Count; + } } } From 6a780f2a4bad0bc7e48ab1413823cd3b7ccd79c3 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 11:13:18 -0700 Subject: [PATCH 07/39] Validate that shots is positive --- src/Simulation/EntryPointDriver/Driver.cs | 20 ++++++++++++++----- .../EntryPointDriver/SimulatorMachine.cs | 4 ---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 2fa6eb2d335..3bed338f0d6 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -48,7 +48,7 @@ public async Task Run(string[] args) }; AddOptionIfAvailable(simulate, SimulatorOptions, entryPoint.DefaultSimulator, "The name of the simulator to use.", - new[] + suggestions: new[] { AssemblyConstants.QuantumSimulator, AssemblyConstants.ToffoliSimulator, @@ -66,9 +66,11 @@ public async Task Run(string[] args) AddOptionIfAvailable(submit, new[] { "--resource-group" }, "The Azure resource group name."); AddOptionIfAvailable(submit, new[] { "--workspace" }, "The Azure workspace name."); AddOptionIfAvailable(submit, new[] { "--storage" }, "The Azure storage account connection string."); - // TODO: Validate that shots is non-negative. - AddOptionIfAvailable(submit, new [] { "--shots" }, 500, - "The number of times the program is executed on the target machine."); + AddOptionIfAvailable(submit, new[] { "--shots" }, 500, + "The number of times the program is executed on the target machine.", + result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 + ? $"The number of shots is {value}, but it must be a positive number." + : default); var root = new RootCommand(entryPoint.Summary) { simulate, submit }; foreach (var option in entryPoint.Options) @@ -181,6 +183,7 @@ private void AddOptionIfAvailable( /// The collection of option aliases. /// The default value of the option. /// The option description. + /// The option validator. /// The suggestions for the option's values. /// The type of the option values. private void AddOptionIfAvailable( @@ -188,12 +191,19 @@ private void AddOptionIfAvailable( IReadOnlyCollection aliases, T defaultValue, string? description = default, + ValidateSymbol? validator = default, string[]? suggestions = default) => TryWithAvailableAliases(aliases, validAliases => new Option(validAliases.ToArray(), () => defaultValue, description)).Then(option => + { + if (!(validator is null)) + { + option.AddValidator(validator); + } command.AddOption(suggestions is null || !suggestions.Any() ? option - : option.WithSuggestions(suggestions))); + : option.WithSuggestions(suggestions)); + }); } /// diff --git a/src/Simulation/EntryPointDriver/SimulatorMachine.cs b/src/Simulation/EntryPointDriver/SimulatorMachine.cs index fec0f3aef74..2215e48a1bb 100644 --- a/src/Simulation/EntryPointDriver/SimulatorMachine.cs +++ b/src/Simulation/EntryPointDriver/SimulatorMachine.cs @@ -12,10 +12,6 @@ namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver /// /// A quantum machine that runs the entry point using a local quantum simulator. /// - /// - /// TODO: This class is only for testing the interface. It should be removed once an - /// actual quantum machine implementation can be used. - /// internal class SimulatorMachine : IQuantumMachine { public string ProviderId => nameof(SimulatorMachine); From 290469cfa41e3640e4bce071e5e499f3e08aea44 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 11:30:25 -0700 Subject: [PATCH 08/39] Use DefaultIfShadowed for Azure settings --- src/Simulation/EntryPointDriver/Driver.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 3bed338f0d6..12c74eb2343 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -110,8 +110,15 @@ private async Task Simulate(ParseResult parseResult, string simulator) /// The command-line parsing result. /// The submission settings. private async Task Submit(ParseResult parseResult, AzureSettings settings) => - // TODO: DefaultIfShadowed - await Azure.Submit(entryPoint, parseResult, settings); + await Azure.Submit(entryPoint, parseResult, new AzureSettings + { + Target = DefaultIfShadowed("--target", settings.Target, default), + Subscription = DefaultIfShadowed("--subscription", settings.Subscription, default), + ResourceGroup = DefaultIfShadowed("--resource-group", settings.ResourceGroup, default), + Workspace = DefaultIfShadowed("--workspace", settings.Workspace, default), + Storage = DefaultIfShadowed("--storage", settings.Storage, default), + Shots = DefaultIfShadowed("--shots", settings.Shots, 500) + }); /// /// Returns true if the alias is not already used by an entry point option. From 7702e3a96f3551c86cd85b2119ae9f806f5bd661 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 13:19:05 -0700 Subject: [PATCH 09/39] Simplify adding/defaulting options --- src/Simulation/EntryPointDriver/Driver.cs | 184 +++++++++++----------- 1 file changed, 96 insertions(+), 88 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 12c74eb2343..7489e6a08ee 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Builder; using System.CommandLine.Help; @@ -46,28 +45,19 @@ public async Task Run(string[] args) { Handler = CommandHandler.Create(Simulate) }; - AddOptionIfAvailable(simulate, SimulatorOptions, entryPoint.DefaultSimulator, - "The name of the simulator to use.", - suggestions: new[] - { - AssemblyConstants.QuantumSimulator, - AssemblyConstants.ToffoliSimulator, - AssemblyConstants.ResourcesEstimator, - entryPoint.DefaultSimulator - }); + AddOptionIfAvailable(simulate, SimulatorOption(entryPoint.DefaultSimulator)); var submit = new Command("submit", "Submit the program to Azure Quantum.") { Handler = CommandHandler.Create(Submit) }; - // TODO: Define the aliases as constants. - AddOptionIfAvailable(submit, new[] { "--target" }, "The target device ID."); - AddOptionIfAvailable(submit, new[] { "--subscription" }, "The Azure subscription ID."); - AddOptionIfAvailable(submit, new[] { "--resource-group" }, "The Azure resource group name."); - AddOptionIfAvailable(submit, new[] { "--workspace" }, "The Azure workspace name."); - AddOptionIfAvailable(submit, new[] { "--storage" }, "The Azure storage account connection string."); - AddOptionIfAvailable(submit, new[] { "--shots" }, 500, - "The number of times the program is executed on the target machine.", + // TODO: Prevent the submit command from being used if required options are shadowed. + AddOptionIfAvailable(submit, TargetOption); + AddOptionIfAvailable(submit, SubscriptionOption); + AddOptionIfAvailable(submit, ResourceGroupOption); + AddOptionIfAvailable(submit, WorkspaceOption); + AddOptionIfAvailable(submit, StorageOption); + AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 ? $"The number of shots is {value}, but it must be a positive number." : default); @@ -100,7 +90,7 @@ public async Task Run(string[] args) /// The exit code. private async Task Simulate(ParseResult parseResult, string simulator) { - simulator = DefaultIfShadowed(SimulatorOptions.First(), simulator, entryPoint.DefaultSimulator); + simulator = DefaultIfShadowed(SimulatorOption(entryPoint.DefaultSimulator), simulator); return await Simulation.Simulate(entryPoint, parseResult, simulator); } @@ -112,12 +102,12 @@ private async Task Simulate(ParseResult parseResult, string simulator) private async Task Submit(ParseResult parseResult, AzureSettings settings) => await Azure.Submit(entryPoint, parseResult, new AzureSettings { - Target = DefaultIfShadowed("--target", settings.Target, default), - Subscription = DefaultIfShadowed("--subscription", settings.Subscription, default), - ResourceGroup = DefaultIfShadowed("--resource-group", settings.ResourceGroup, default), - Workspace = DefaultIfShadowed("--workspace", settings.Workspace, default), - Storage = DefaultIfShadowed("--storage", settings.Storage, default), - Shots = DefaultIfShadowed("--shots", settings.Shots, 500) + Target = settings.Target, + Subscription = settings.Subscription, + ResourceGroup = settings.ResourceGroup, + Workspace = settings.Workspace, + Storage = settings.Storage, + Shots = DefaultIfShadowed(ShotsOption, settings.Shots) }); /// @@ -126,20 +116,19 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) /// The alias to check. /// True if the alias is available for use by the driver. private bool IsAliasAvailable(string alias) => - !entryPoint.Options.SelectMany(option => option.RawAliases).Contains(alias); + !entryPoint.Options.SelectMany(option => option.Aliases).Contains(alias); /// - /// Returns the default value and displays a warning if the alias is shadowed by an entry point option, and - /// returns the original value otherwise. + /// Returns the default value and displays a warning if the primary (first) alias is shadowed by an entry point + /// option, and returns the original value otherwise. /// /// The type of the option values. - /// The primary option alias corresponding to the value. + /// The option. /// The value of the option given on the command line. - /// The default value for the option. /// The default value or the original value. - private T DefaultIfShadowed(string alias, T value, T defaultValue) + private T DefaultIfShadowed(Option option, T value) { - if (IsAliasAvailable(alias)) + if (IsAliasAvailable(option.Aliases.First())) { return value; } @@ -148,84 +137,103 @@ private T DefaultIfShadowed(string alias, T value, T defaultValue) var originalForeground = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Yellow; Console.Error.WriteLine( - $"Warning: Option {alias} is overridden by an entry point parameter name. " + - $"Using default value {defaultValue}."); + $"Warning: Option {option.Aliases.First()} is overridden by an entry point parameter name. " + + $"Using default value {option.Argument.GetDefaultValue()}."); Console.ForegroundColor = originalForeground; - return defaultValue; + return (T)option.Argument.GetDefaultValue(); } } - /// - /// Evaluates the given function on the collection of available aliases if the primary (first) alias is - /// available. - /// - /// The aliases to check for availability. - /// The function to evaluate on the available aliases. - /// The given function's return type. - /// The return value of the given function or a failure if the first alias is not available. - private Validation TryWithAvailableAliases( - IReadOnlyCollection aliases, Func, T> func) => - IsAliasAvailable(aliases.First()) - ? Validation.Success(func(aliases.Where(IsAliasAvailable).ToArray())) - : Validation.Failure(); - - /// - /// Adds the required option to the command using only the aliases that are available, and only if the primary - /// (first) alias is available. - /// - /// The command to add the option to. - /// The collection of option aliases. - /// The option description. - /// The type of the option values. - private void AddOptionIfAvailable( - Command command, IReadOnlyCollection aliases, string? description = default) => - TryWithAvailableAliases(aliases, validAliases => - new Option(validAliases.ToArray(), description) { Required = true }).Then(command.AddOption); - /// /// Adds the option to the command using only the aliases that are available, and only if the primary (first) /// alias is available. /// /// The command to add the option to. - /// The collection of option aliases. - /// The default value of the option. - /// The option description. - /// The option validator. - /// The suggestions for the option's values. - /// The type of the option values. + /// The option to add. + /// A validator for the option. + /// The type of the option's argument. private void AddOptionIfAvailable( - Command command, - IReadOnlyCollection aliases, - T defaultValue, - string? description = default, - ValidateSymbol? validator = default, - string[]? suggestions = default) => - TryWithAvailableAliases(aliases, validAliases => - new Option(validAliases.ToArray(), () => defaultValue, description)).Then(option => + Command command, Option option, ValidateSymbol? validator = default) + { + if (IsAliasAvailable(option.Aliases.First())) { + var validAliases = option.Aliases.Where(IsAliasAvailable).ToArray(); + var validOption = option.Argument.HasDefaultValue + ? new Option(validAliases, () => (T)option.Argument.GetDefaultValue(), option.Description) + : new Option(validAliases, option.Description); if (!(validator is null)) { - option.AddValidator(validator); + validOption.AddValidator(validator); } - command.AddOption(suggestions is null || !suggestions.Any() - ? option - : option.WithSuggestions(suggestions)); - }); + command.AddOption(validOption.WithSuggestions(option.GetSuggestions().ToArray())); + } + } } /// - /// Static members for that do not depend on its type parameters. + /// Static members for . /// internal static class Driver { /// - /// The option aliases for the simulator option. + /// Returns the simulator option that uses the default simulator as its default value. /// - internal static readonly IReadOnlyCollection SimulatorOptions = Array.AsReadOnly(new[] - { - "--" + CommandLineArguments.SimulatorOption.Item1, - "-" + CommandLineArguments.SimulatorOption.Item2 - }); + /// The default simulator. + /// The simulator option. + internal static Option SimulatorOption(string defaultSimulator) => new Option( + new[] + { + "--" + CommandLineArguments.SimulatorOption.Item1, + "-" + CommandLineArguments.SimulatorOption.Item2 + }, + () => defaultSimulator, + "The name of the simulator to use.") + { + Required = true + } + .WithSuggestions( + AssemblyConstants.QuantumSimulator, + AssemblyConstants.ToffoliSimulator, + AssemblyConstants.ResourcesEstimator, + defaultSimulator); + + // TODO: Define the aliases as constants. + + /// + /// The target option. + /// + internal static Option TargetOption => new Option( + new[] { "--target" }, "The target device ID.") { Required = true }; + + /// + /// The subscription option. + /// + internal static Option SubscriptionOption => new Option( + new[] { "--subscription" }, "The Azure subscription ID.") { Required = true }; + + /// + /// The resource group option. + /// + internal static Option ResourceGroupOption => new Option( + new[] { "--resource-group" }, "The Azure resource group name.") { Required = true }; + + /// + /// The workspace option. + /// + internal static Option WorkspaceOption => new Option( + new[] { "--workspace" }, "The Azure workspace name.") { Required = true }; + + /// + /// The storage option. + /// + internal static Option StorageOption => new Option( + new[] { "--storage" }, "The Azure storage account connection string.") { Required = true }; + + /// + /// The shots option. + /// + internal static Option ShotsOption => new Option( + new[] { "--shots" }, () => 500, "The number of times the program is executed on the target machine."); } /// From 3a75b78e6a6a958a75eeb53f563db31e236359e7 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 13:32:24 -0700 Subject: [PATCH 10/39] Make SimulatorOption non-static --- src/Simulation/EntryPointDriver/Driver.cs | 52 +++++++++++------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 7489e6a08ee..7e14e3d979a 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -28,6 +28,26 @@ public sealed class Driver where TCallable : AbstractCalla /// private readonly IEntryPoint entryPoint; + /// + /// The simulator option. + /// + private Option SimulatorOption => new Option( + new[] + { + "--" + CommandLineArguments.SimulatorOption.Item1, + "-" + CommandLineArguments.SimulatorOption.Item2 + }, + () => entryPoint.DefaultSimulator, + "The name of the simulator to use.") + { + Required = true + } + .WithSuggestions( + AssemblyConstants.QuantumSimulator, + AssemblyConstants.ToffoliSimulator, + AssemblyConstants.ResourcesEstimator, + entryPoint.DefaultSimulator); + /// /// Creates a new driver for the entry point. /// @@ -45,7 +65,7 @@ public async Task Run(string[] args) { Handler = CommandHandler.Create(Simulate) }; - AddOptionIfAvailable(simulate, SimulatorOption(entryPoint.DefaultSimulator)); + AddOptionIfAvailable(simulate, SimulatorOption); var submit = new Command("submit", "Submit the program to Azure Quantum.") { @@ -88,11 +108,9 @@ public async Task Run(string[] args) /// The command-line parsing result. /// The simulator to use. /// The exit code. - private async Task Simulate(ParseResult parseResult, string simulator) - { - simulator = DefaultIfShadowed(SimulatorOption(entryPoint.DefaultSimulator), simulator); - return await Simulation.Simulate(entryPoint, parseResult, simulator); - } + private async Task Simulate(ParseResult parseResult, string simulator) => + await Simulation.Simulate( + entryPoint, parseResult, DefaultIfShadowed(SimulatorOption, simulator)); /// /// Submits the entry point to Azure Quantum. @@ -175,28 +193,6 @@ private void AddOptionIfAvailable( /// internal static class Driver { - /// - /// Returns the simulator option that uses the default simulator as its default value. - /// - /// The default simulator. - /// The simulator option. - internal static Option SimulatorOption(string defaultSimulator) => new Option( - new[] - { - "--" + CommandLineArguments.SimulatorOption.Item1, - "-" + CommandLineArguments.SimulatorOption.Item2 - }, - () => defaultSimulator, - "The name of the simulator to use.") - { - Required = true - } - .WithSuggestions( - AssemblyConstants.QuantumSimulator, - AssemblyConstants.ToffoliSimulator, - AssemblyConstants.ResourcesEstimator, - defaultSimulator); - // TODO: Define the aliases as constants. /// From 77e177a5dabe7597da54b882270e2c76f667884f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 13:37:38 -0700 Subject: [PATCH 11/39] Add AddOptionsIfAvailable method --- src/Simulation/EntryPointDriver/Driver.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 7e14e3d979a..d7afb3ebd1c 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -72,11 +72,8 @@ public async Task Run(string[] args) Handler = CommandHandler.Create(Submit) }; // TODO: Prevent the submit command from being used if required options are shadowed. - AddOptionIfAvailable(submit, TargetOption); - AddOptionIfAvailable(submit, SubscriptionOption); - AddOptionIfAvailable(submit, ResourceGroupOption); - AddOptionIfAvailable(submit, WorkspaceOption); - AddOptionIfAvailable(submit, StorageOption); + AddOptionsIfAvailable(submit, + TargetOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption, StorageOption); AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 ? $"The number of shots is {value}, but it must be a positive number." @@ -186,6 +183,21 @@ private void AddOptionIfAvailable( command.AddOption(validOption.WithSuggestions(option.GetSuggestions().ToArray())); } } + + /// + /// Adds the option to the command using only the aliases that are available, and only if the primary (first) + /// alias is available. + /// + /// The command to add the option to. + /// The options to add. + /// The type of the option's argument. + private void AddOptionsIfAvailable(Command command, params Option[] options) + { + foreach (var option in options) + { + AddOptionIfAvailable(command, option); + } + } } /// From da6efc38739db199c36b072b281e34035cbef59c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 13:45:50 -0700 Subject: [PATCH 12/39] Fix option aliases missing dashes --- src/Simulation/EntryPointDriver/Driver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index d7afb3ebd1c..58b6c04b736 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -172,7 +172,7 @@ private void AddOptionIfAvailable( { if (IsAliasAvailable(option.Aliases.First())) { - var validAliases = option.Aliases.Where(IsAliasAvailable).ToArray(); + var validAliases = option.RawAliases.Where(IsAliasAvailable).ToArray(); var validOption = option.Argument.HasDefaultValue ? new Option(validAliases, () => (T)option.Argument.GetDefaultValue(), option.Description) : new Option(validAliases, option.Description); From da767f87b75b1740f3f4cf37f389d54b2e5f5547 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 13:54:20 -0700 Subject: [PATCH 13/39] Disable command if required options are shadowed --- src/Simulation/EntryPointDriver/Driver.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 58b6c04b736..38c934cddd7 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -71,7 +71,6 @@ public async Task Run(string[] args) { Handler = CommandHandler.Create(Submit) }; - // TODO: Prevent the submit command from being used if required options are shadowed. AddOptionsIfAvailable(submit, TargetOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption, StorageOption); AddOptionIfAvailable(submit, ShotsOption, @@ -161,7 +160,7 @@ private T DefaultIfShadowed(Option option, T value) /// /// Adds the option to the command using only the aliases that are available, and only if the primary (first) - /// alias is available. + /// alias is available. If a required option is not available, the command is disabled. /// /// The command to add the option to. /// The option to add. @@ -182,6 +181,11 @@ private void AddOptionIfAvailable( } command.AddOption(validOption.WithSuggestions(option.GetSuggestions().ToArray())); } + else if (option.Required) + { + command.AddValidator(commandResult => + $"The required option {option.RawAliases.First()} conflicts with an entry point parameter name."); + } } /// From c363cc38856754bb5b382c6698865ad7a5dfd3e9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 13:58:15 -0700 Subject: [PATCH 14/39] Add copyright headers --- src/Simulation/EntryPointDriver/Azure.cs | 5 ++++- src/Simulation/EntryPointDriver/IEntryPoint.cs | 5 ++++- src/Simulation/EntryPointDriver/Options.cs | 5 ++++- src/Simulation/EntryPointDriver/Simulation.cs | 5 ++++- src/Simulation/EntryPointDriver/SimulatorMachine.cs | 5 ++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 3680f7202d9..1cbfba0f128 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.CommandLine.Parsing; using System.Threading.Tasks; using Microsoft.Quantum.Runtime; diff --git a/src/Simulation/EntryPointDriver/IEntryPoint.cs b/src/Simulation/EntryPointDriver/IEntryPoint.cs index 8ab574bd0dc..65250d086d3 100644 --- a/src/Simulation/EntryPointDriver/IEntryPoint.cs +++ b/src/Simulation/EntryPointDriver/IEntryPoint.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Parsing; diff --git a/src/Simulation/EntryPointDriver/Options.cs b/src/Simulation/EntryPointDriver/Options.cs index 75a6149388e..fb64d2dce5c 100644 --- a/src/Simulation/EntryPointDriver/Options.cs +++ b/src/Simulation/EntryPointDriver/Options.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; using System.CommandLine; using System.Linq; diff --git a/src/Simulation/EntryPointDriver/Simulation.cs b/src/Simulation/EntryPointDriver/Simulation.cs index 9769b6b5ee4..f9d533ae0e7 100644 --- a/src/Simulation/EntryPointDriver/Simulation.cs +++ b/src/Simulation/EntryPointDriver/Simulation.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.CommandLine.Parsing; using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.ReservedKeywords; diff --git a/src/Simulation/EntryPointDriver/SimulatorMachine.cs b/src/Simulation/EntryPointDriver/SimulatorMachine.cs index 2215e48a1bb..0ea762de6d3 100644 --- a/src/Simulation/EntryPointDriver/SimulatorMachine.cs +++ b/src/Simulation/EntryPointDriver/SimulatorMachine.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; From 4bda02bfee102f4cf89c9169959f550f25462a8d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 14:40:07 -0700 Subject: [PATCH 15/39] Add histogram option --- src/Simulation/EntryPointDriver/Azure.cs | 30 ++++++++++++++++++++--- src/Simulation/EntryPointDriver/Driver.cs | 22 +++++++++++------ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 1cbfba0f128..d1464589d75 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -2,7 +2,9 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.CommandLine.Parsing; +using System.Linq; using System.Threading.Tasks; using Microsoft.Quantum.Runtime; @@ -30,11 +32,18 @@ internal static async Task Submit( DisplayUnknownTargetError(settings.Target); return 1; } + var output = await machine.ExecuteAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); - // TODO: Provide output options and show the most frequent output by default. - foreach (var (result, frequency) in output.Histogram) + if (settings.Histogram) { - Console.WriteLine($"{result} (frequency = {frequency})"); + foreach (var (result, frequency) in output.Histogram) + { + Console.WriteLine($"{result} (frequency = {frequency})"); + } + } + else + { + Console.WriteLine(MostFrequentOutput(output.Histogram)); } return 0; } @@ -65,6 +74,16 @@ internal static async Task Submit( } } + /// + /// Returns the most frequent output in the histogram. + /// + /// The histogram. + /// The output type. + /// The most frequent output in the histogram. + private static T MostFrequentOutput(IReadOnlyDictionary histogram) => histogram + .Aggregate((a, b) => a.Value > b.Value ? a : b) + .Key; + /// /// Displays an error message for attempting to use an unknown target machine. /// @@ -112,6 +131,11 @@ internal sealed class AzureSettings /// The number of times the program is executed on the target machine. /// public int Shots { get; set; } + + /// + /// Show a histogram of all outputs instead of the most frequent output. + /// + public bool Histogram { get; set; } /// /// Converts these settings into a Microsoft.Azure.Quantum.Workspace object. diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 38c934cddd7..9f59b8215a0 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -73,6 +73,7 @@ public async Task Run(string[] args) }; AddOptionsIfAvailable(submit, TargetOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption, StorageOption); + AddOptionIfAvailable(submit, HistogramOption); AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 ? $"The number of shots is {value}, but it must be a positive number." @@ -121,7 +122,8 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) ResourceGroup = settings.ResourceGroup, Workspace = settings.Workspace, Storage = settings.Storage, - Shots = DefaultIfShadowed(ShotsOption, settings.Shots) + Shots = DefaultIfShadowed(ShotsOption, settings.Shots), + Histogram = DefaultIfShadowed(HistogramOption, settings.Histogram) }); /// @@ -215,37 +217,43 @@ internal static class Driver /// The target option. /// internal static Option TargetOption => new Option( - new[] { "--target" }, "The target device ID.") { Required = true }; + "--target", "The target device ID.") { Required = true }; /// /// The subscription option. /// internal static Option SubscriptionOption => new Option( - new[] { "--subscription" }, "The Azure subscription ID.") { Required = true }; + "--subscription", "The Azure subscription ID.") { Required = true }; /// /// The resource group option. /// internal static Option ResourceGroupOption => new Option( - new[] { "--resource-group" }, "The Azure resource group name.") { Required = true }; + "--resource-group", "The Azure resource group name.") { Required = true }; /// /// The workspace option. /// internal static Option WorkspaceOption => new Option( - new[] { "--workspace" }, "The Azure workspace name.") { Required = true }; + "--workspace", "The Azure workspace name.") { Required = true }; /// /// The storage option. /// internal static Option StorageOption => new Option( - new[] { "--storage" }, "The Azure storage account connection string.") { Required = true }; + "--storage", "The Azure storage account connection string.") { Required = true }; /// /// The shots option. /// internal static Option ShotsOption => new Option( - new[] { "--shots" }, () => 500, "The number of times the program is executed on the target machine."); + "--shots", () => 500, "The number of times the program is executed on the target machine."); + + /// + /// The histogram option. + /// + internal static Option HistogramOption => new Option( + "--histogram", () => false, "Show a histogram of all outputs instead of the most frequent output."); } /// From 28ba9778077677974200417ca515b3081ca4afcf Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 18:19:43 -0700 Subject: [PATCH 16/39] Make histogram pretty --- src/Simulation/EntryPointDriver/Azure.cs | 59 ++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index d1464589d75..09d7f38d9c0 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.Collections.Generic; using System.CommandLine.Parsing; using System.Linq; @@ -10,6 +9,8 @@ namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver { + using System; + /// /// Provides entry point submission to Azure Quantum. /// @@ -36,10 +37,7 @@ internal static async Task Submit( var output = await machine.ExecuteAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); if (settings.Histogram) { - foreach (var (result, frequency) in output.Histogram) - { - Console.WriteLine($"{result} (frequency = {frequency})"); - } + DisplayHistogram(output.Histogram); } else { @@ -84,6 +82,57 @@ private static T MostFrequentOutput(IReadOnlyDictionary histogram) .Aggregate((a, b) => a.Value > b.Value ? a : b) .Key; + /// + /// Displays the histogram. + /// + /// The histogram. + /// The type of the results in the histogram. + private static void DisplayHistogram(IReadOnlyDictionary histogram) + { + const int barWidth = 20; + var maxFrequency = histogram.Values.Max(); + + string Bar(double frequency) => + new string('█', Convert.ToInt32(Math.Round(frequency / maxFrequency * barWidth))); + + var results = CreateTableColumn("Result", histogram.Keys); + var bars = CreateTableColumn("Frequency", histogram.Values.Select(Bar)); + var numbers = CreateTableColumn("", histogram.Values); + DisplayTable(results, bars, numbers); + } + + /// + /// Creates a table column. + /// + /// The name of the column. + /// The items in the column. + /// The type of the items. + /// A table column. + private static IReadOnlyList CreateTableColumn(string name, IEnumerable items) + { + var divider = new string('-', name.Length); + var column = new[] { name, divider } + .Concat(items.Select(item => item?.ToString() ?? "")) + .ToList(); + var width = column.Max(item => item.Length); + return column.Select(row => row.PadRight(width)).ToList(); + } + + /// + /// Displays the table. + /// + /// The columns in the table. + private static void DisplayTable(params IReadOnlyList[] columns) + { + var height = columns.Max(column => column.Count); + foreach (var row in Enumerable.Range(0, height)) + { + Console.WriteLine(string.Join( + " ", + columns.Select(column => column.ElementAtOrDefault(row) ?? ""))); + } + } + /// /// Displays an error message for attempting to use an unknown target machine. /// From 64188d8dec389cc73a6576934046258b0fc88aba Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 18:36:18 -0700 Subject: [PATCH 17/39] Fix shadowing --- src/Simulation/EntryPointDriver/Driver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 9f59b8215a0..add609adf56 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -132,7 +132,7 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) /// The alias to check. /// True if the alias is available for use by the driver. private bool IsAliasAvailable(string alias) => - !entryPoint.Options.SelectMany(option => option.Aliases).Contains(alias); + !entryPoint.Options.SelectMany(option => option.RawAliases).Contains(alias); /// /// Returns the default value and displays a warning if the primary (first) alias is shadowed by an entry point @@ -144,7 +144,7 @@ private bool IsAliasAvailable(string alias) => /// The default value or the original value. private T DefaultIfShadowed(Option option, T value) { - if (IsAliasAvailable(option.Aliases.First())) + if (IsAliasAvailable(option.RawAliases.First())) { return value; } @@ -171,7 +171,7 @@ private T DefaultIfShadowed(Option option, T value) private void AddOptionIfAvailable( Command command, Option option, ValidateSymbol? validator = default) { - if (IsAliasAvailable(option.Aliases.First())) + if (IsAliasAvailable(option.RawAliases.First())) { var validAliases = option.RawAliases.Where(IsAliasAvailable).ToArray(); var validOption = option.Argument.HasDefaultValue From 5c411f7171bd6887e236778ebe461c62b14e5fe6 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 18:48:15 -0700 Subject: [PATCH 18/39] Update histogram divider dash --- src/Simulation/EntryPointDriver/Azure.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 09d7f38d9c0..c2f245f55b1 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -110,7 +110,7 @@ string Bar(double frequency) => /// A table column. private static IReadOnlyList CreateTableColumn(string name, IEnumerable items) { - var divider = new string('-', name.Length); + var divider = new string('─', name.Length); var column = new[] { name, divider } .Concat(items.Select(item => item?.ToString() ?? "")) .ToList(); From f5ae08e11bccb3723dbe583eb78806a01368370b Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 19:10:21 -0700 Subject: [PATCH 19/39] Preserve required state of options --- src/Simulation/EntryPointDriver/Driver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index add609adf56..b039eb755d0 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -177,6 +177,7 @@ private void AddOptionIfAvailable( var validOption = option.Argument.HasDefaultValue ? new Option(validAliases, () => (T)option.Argument.GetDefaultValue(), option.Description) : new Option(validAliases, option.Description); + validOption.Required = option.Required; if (!(validator is null)) { validOption.AddValidator(validator); From ed35ff4fb346e31c38141e30150b4cd746c3b03c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 May 2020 19:19:59 -0700 Subject: [PATCH 20/39] Simulator option is not required --- src/Simulation/EntryPointDriver/Driver.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index b039eb755d0..6a3f556af1d 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -39,9 +39,6 @@ public sealed class Driver where TCallable : AbstractCalla }, () => entryPoint.DefaultSimulator, "The name of the simulator to use.") - { - Required = true - } .WithSuggestions( AssemblyConstants.QuantumSimulator, AssemblyConstants.ToffoliSimulator, From c4d0bb27ab7f6eaf8bfaefd5b0878077d72d019c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 8 May 2020 15:53:18 -0700 Subject: [PATCH 21/39] Use SubmitAsync instead of ExecuteAsync --- src/Simulation/EntryPointDriver/Azure.cs | 91 +++---------------- src/Simulation/EntryPointDriver/Driver.cs | 12 +-- .../EntryPointDriver/EntryPointDriver.csproj | 1 - .../EntryPointDriver/NothingMachine.cs | 51 +++++++++++ .../EntryPointDriver/SimulatorMachine.cs | 87 ------------------ 5 files changed, 72 insertions(+), 170 deletions(-) create mode 100644 src/Simulation/EntryPointDriver/NothingMachine.cs delete mode 100644 src/Simulation/EntryPointDriver/SimulatorMachine.cs diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index c2f245f55b1..c99415eebaa 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -1,16 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Collections.Generic; +using System; using System.CommandLine.Parsing; -using System.Linq; using System.Threading.Tasks; using Microsoft.Quantum.Runtime; namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver { - using System; - /// /// Provides entry point submission to Azure Quantum. /// @@ -34,14 +31,17 @@ internal static async Task Submit( return 1; } - var output = await machine.ExecuteAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); - if (settings.Histogram) + var job = await machine.SubmitAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); + if (settings.IdOnly) { - DisplayHistogram(output.Histogram); + Console.WriteLine(job.Id); } else { - Console.WriteLine(MostFrequentOutput(output.Histogram)); + Console.WriteLine("Job submitted. To track your job status and see the results use:"); + Console.WriteLine(); + // TODO: Show the friendly URL. + Console.WriteLine(job.Uri); } return 0; } @@ -53,11 +53,7 @@ internal static async Task Submit( /// A quantum machine. private static IQuantumMachine? CreateMachine(AzureSettings settings) { - if (settings.Target == "quantum.simulator") - { - return new SimulatorMachine(settings.Shots); - } - else if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) + if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) { // TODO: Number of shots? var ionQType = Type.GetType( @@ -66,70 +62,13 @@ internal static async Task Submit( return (IQuantumMachine)Activator.CreateInstance( ionQType, settings.Target, settings.Storage, settings.ToWorkspace()); } - else + else if (settings.Target == "nothing") { - return null; + return new NothingMachine(); } - } - - /// - /// Returns the most frequent output in the histogram. - /// - /// The histogram. - /// The output type. - /// The most frequent output in the histogram. - private static T MostFrequentOutput(IReadOnlyDictionary histogram) => histogram - .Aggregate((a, b) => a.Value > b.Value ? a : b) - .Key; - - /// - /// Displays the histogram. - /// - /// The histogram. - /// The type of the results in the histogram. - private static void DisplayHistogram(IReadOnlyDictionary histogram) - { - const int barWidth = 20; - var maxFrequency = histogram.Values.Max(); - - string Bar(double frequency) => - new string('█', Convert.ToInt32(Math.Round(frequency / maxFrequency * barWidth))); - - var results = CreateTableColumn("Result", histogram.Keys); - var bars = CreateTableColumn("Frequency", histogram.Values.Select(Bar)); - var numbers = CreateTableColumn("", histogram.Values); - DisplayTable(results, bars, numbers); - } - - /// - /// Creates a table column. - /// - /// The name of the column. - /// The items in the column. - /// The type of the items. - /// A table column. - private static IReadOnlyList CreateTableColumn(string name, IEnumerable items) - { - var divider = new string('─', name.Length); - var column = new[] { name, divider } - .Concat(items.Select(item => item?.ToString() ?? "")) - .ToList(); - var width = column.Max(item => item.Length); - return column.Select(row => row.PadRight(width)).ToList(); - } - - /// - /// Displays the table. - /// - /// The columns in the table. - private static void DisplayTable(params IReadOnlyList[] columns) - { - var height = columns.Max(column => column.Count); - foreach (var row in Enumerable.Range(0, height)) + else { - Console.WriteLine(string.Join( - " ", - columns.Select(column => column.ElementAtOrDefault(row) ?? ""))); + return null; } } @@ -182,9 +121,9 @@ internal sealed class AzureSettings public int Shots { get; set; } /// - /// Show a histogram of all outputs instead of the most frequent output. + /// Show only the job ID after the job is submitted. /// - public bool Histogram { get; set; } + public bool IdOnly { get; set; } /// /// Converts these settings into a Microsoft.Azure.Quantum.Workspace object. diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 6a3f556af1d..462a1ba2b55 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -70,7 +70,7 @@ public async Task Run(string[] args) }; AddOptionsIfAvailable(submit, TargetOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption, StorageOption); - AddOptionIfAvailable(submit, HistogramOption); + AddOptionIfAvailable(submit, IdOnlyOption); AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 ? $"The number of shots is {value}, but it must be a positive number." @@ -120,7 +120,7 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) Workspace = settings.Workspace, Storage = settings.Storage, Shots = DefaultIfShadowed(ShotsOption, settings.Shots), - Histogram = DefaultIfShadowed(HistogramOption, settings.Histogram) + IdOnly = DefaultIfShadowed(IdOnlyOption, settings.IdOnly) }); /// @@ -246,12 +246,12 @@ internal static class Driver /// internal static Option ShotsOption => new Option( "--shots", () => 500, "The number of times the program is executed on the target machine."); - + /// - /// The histogram option. + /// The ID-only option. /// - internal static Option HistogramOption => new Option( - "--histogram", () => false, "Show a histogram of all outputs instead of the most frequent output."); + internal static Option IdOnlyOption => new Option( + "--id-only", () => false, "Show only the job ID after the job is submitted."); } /// diff --git a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj index 8538186421b..7760104f92a 100644 --- a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj +++ b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Simulation/EntryPointDriver/NothingMachine.cs b/src/Simulation/EntryPointDriver/NothingMachine.cs new file mode 100644 index 00000000000..38a7006be6a --- /dev/null +++ b/src/Simulation/EntryPointDriver/NothingMachine.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Quantum.Runtime; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver +{ + /// + /// A quantum machine that does nothing. + /// + internal class NothingMachine : IQuantumMachine + { + public string ProviderId => nameof(NothingMachine); + + public string Target => "Nothing"; + + public Task> ExecuteAsync(EntryPointInfo info, TIn input) => + throw new NotImplementedException(); + + public Task SubmitAsync(EntryPointInfo info, TIn input) => + Task.FromResult(new DefaultJob()); + + /// + /// A quantum machine job with default properties. + /// + private class DefaultJob : IQuantumMachineJob + { + public string Id { get; } = "00000000-0000-0000-0000-0000000000000"; + + public string Status { get; } = "NotImplemented"; + + public bool InProgress { get; } = false; + + public bool Succeeded { get; } = false; + + public bool Failed { get; } = true; + + public Uri Uri => new Uri("https://example.com/" + Id); + + public Task CancelAsync(CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + public Task RefreshAsync(CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + } + } +} diff --git a/src/Simulation/EntryPointDriver/SimulatorMachine.cs b/src/Simulation/EntryPointDriver/SimulatorMachine.cs deleted file mode 100644 index 0ea762de6d3..00000000000 --- a/src/Simulation/EntryPointDriver/SimulatorMachine.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Quantum.Runtime; -using Microsoft.Quantum.Simulation.Core; -using Microsoft.Quantum.Simulation.Simulators; - -namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver -{ - /// - /// A quantum machine that runs the entry point using a local quantum simulator. - /// - internal class SimulatorMachine : IQuantumMachine - { - public string ProviderId => nameof(SimulatorMachine); - - public string Target => "QuantumSimulator"; - - /// - /// The number of times to simulate the entry point. - /// - private readonly int shots; - - /// - /// Creates a simulator machine. - /// - /// The number of times to simulate the entry point. - internal SimulatorMachine(int shots) => this.shots = shots; - - public async Task> ExecuteAsync( - EntryPointInfo info, TIn input) => - new SimulatorMachineOutput(await RunShots(info, input).ToListAsync()); - - public Task SubmitAsync(EntryPointInfo info, TIn input) => - throw new NotImplementedException(); - - /// - /// Runs all of the simulator shots and yields the results. - /// - /// The entry point information. - /// The input to the entry point. - /// The entry point input type. - /// The entry point output type. - /// The results of simulating the entry point. - private async IAsyncEnumerable RunShots(EntryPointInfo info, TIn input) - { - var run = typeof(QuantumSimulator) - .GetMethod(nameof(QuantumSimulator.Run)) - .MakeGenericMethod(info.Operation, info.InType, info.OutType); - using var simulator = new QuantumSimulator(); - foreach (var _ in Enumerable.Range(0, shots)) - { - yield return await (Task)run.Invoke(simulator, new object?[] { input }); - } - } - } - - /// - /// The output of executing the . - /// - /// The output result type. - internal class SimulatorMachineOutput : IQuantumMachineOutput - { - public IReadOnlyDictionary Histogram { get; } - - public IQuantumMachineJob Job => throw new NotImplementedException(); - - public int Shots { get; } - - /// - /// Creates a new output from the results. - /// - /// The results. - internal SimulatorMachineOutput(IReadOnlyCollection results) - { - Histogram = results - .GroupBy(result => result) - .ToImmutableDictionary(group => group.Key, group => (double)group.Count() / results.Count); - Shots = results.Count; - } - } -} From 4aa57a4b011b0687db47899ce9d9cd02dd79f251 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 8 May 2020 16:15:27 -0700 Subject: [PATCH 22/39] Add access token option --- src/Simulation/EntryPointDriver/Azure.cs | 23 +++++++++++++++++++---- src/Simulation/EntryPointDriver/Driver.cs | 22 +++++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index c99415eebaa..a68d0aedb62 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -115,6 +115,11 @@ internal sealed class AzureSettings /// public string? Storage { get; set; } + /// + /// The Azure account access token. + /// + public string? Access { get; set; } + /// /// The number of times the program is executed on the target machine. /// @@ -133,10 +138,20 @@ internal object ToWorkspace() { var workspaceType = Type.GetType( "Microsoft.Azure.Quantum.Workspace, Microsoft.Azure.Quantum.Client", throwOnError: true); - var tokenCredentialType = Type.GetType("Azure.Core.TokenCredential, Azure.Core", throwOnError: true); - var constructor = workspaceType.GetConstructor(new[] - { typeof(string), typeof(string), typeof(string), tokenCredentialType, typeof(Uri) }); - return constructor.Invoke(new object?[] { Subscription, ResourceGroup, Workspace, null, null }); + if (Access is null) + { + // We can't use Activator.CreateInstance because the constructor is ambiguous when the last two + // arguments are null. + var tokenCredentialType = Type.GetType("Azure.Core.TokenCredential, Azure.Core", throwOnError: true); + var constructor = workspaceType.GetConstructor(new[] + { typeof(string), typeof(string), typeof(string), tokenCredentialType, typeof(Uri) }); + return constructor.Invoke(new object?[] { Subscription, ResourceGroup, Workspace, null, null }); + } + else + { + return Activator.CreateInstance( + workspaceType, Subscription, ResourceGroup, Workspace, Access, null); + } } } } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 462a1ba2b55..279a1a366a4 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -62,16 +62,17 @@ public async Task Run(string[] args) { Handler = CommandHandler.Create(Simulate) }; - AddOptionIfAvailable(simulate, SimulatorOption); + AddOptionIfAvailable(simulate, SimulatorOption); var submit = new Command("submit", "Submit the program to Azure Quantum.") { Handler = CommandHandler.Create(Submit) }; - AddOptionsIfAvailable(submit, + AddOptionsIfAvailable(submit, TargetOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption, StorageOption); - AddOptionIfAvailable(submit, IdOnlyOption); - AddOptionIfAvailable(submit, ShotsOption, + AddOptionIfAvailable(submit, AccessOption); + AddOptionIfAvailable(submit, IdOnlyOption); + AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 ? $"The number of shots is {value}, but it must be a positive number." : default); @@ -119,6 +120,7 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) ResourceGroup = settings.ResourceGroup, Workspace = settings.Workspace, Storage = settings.Storage, + Access = DefaultIfShadowed(AccessOption, settings.Access), Shots = DefaultIfShadowed(ShotsOption, settings.Shots), IdOnly = DefaultIfShadowed(IdOnlyOption, settings.IdOnly) }); @@ -166,7 +168,7 @@ private T DefaultIfShadowed(Option option, T value) /// A validator for the option. /// The type of the option's argument. private void AddOptionIfAvailable( - Command command, Option option, ValidateSymbol? validator = default) + Command command, Option option, ValidateSymbol? validator = default) { if (IsAliasAvailable(option.RawAliases.First())) { @@ -195,11 +197,11 @@ private void AddOptionIfAvailable( /// The command to add the option to. /// The options to add. /// The type of the option's argument. - private void AddOptionsIfAvailable(Command command, params Option[] options) + private void AddOptionsIfAvailable(Command command, params Option[] options) { foreach (var option in options) { - AddOptionIfAvailable(command, option); + AddOptionIfAvailable(command, option); } } } @@ -241,6 +243,12 @@ internal static class Driver internal static Option StorageOption => new Option( "--storage", "The Azure storage account connection string.") { Required = true }; + /// + /// The access option. + /// + internal static Option AccessOption => new Option( + "--access", () => default, "The Azure account access token."); + /// /// The shots option. /// From 7e7007cec009a921b87301e66ac3ac353292bc5d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 8 May 2020 16:17:50 -0700 Subject: [PATCH 23/39] Rename ToWorkspace to CreateWorkspace --- src/Simulation/EntryPointDriver/Azure.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index a68d0aedb62..95e7807d432 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -60,7 +60,7 @@ internal static async Task Submit( "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", throwOnError: true); return (IQuantumMachine)Activator.CreateInstance( - ionQType, settings.Target, settings.Storage, settings.ToWorkspace()); + ionQType, settings.Target, settings.Storage, settings.CreateWorkspace()); } else if (settings.Target == "nothing") { @@ -131,10 +131,10 @@ internal sealed class AzureSettings public bool IdOnly { get; set; } /// - /// Converts these settings into a Microsoft.Azure.Quantum.Workspace object. + /// Creates a workspace object based on the settings. /// - /// The workspace object corresponding to these settings. - internal object ToWorkspace() + /// The workspace object based on the settings. + internal object CreateWorkspace() { var workspaceType = Type.GetType( "Microsoft.Azure.Quantum.Workspace, Microsoft.Azure.Quantum.Client", throwOnError: true); From 15f5f67249268e219d5700e69a48363c70aa367c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 8 May 2020 16:24:34 -0700 Subject: [PATCH 24/39] Remove "Azure" from option descriptions --- src/Simulation/EntryPointDriver/Azure.cs | 10 +++++----- src/Simulation/EntryPointDriver/Driver.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 95e7807d432..9f595535490 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -96,27 +96,27 @@ internal sealed class AzureSettings public string? Target { get; set; } /// - /// The Azure subscription ID. + /// The subscription ID. /// public string? Subscription { get; set; } /// - /// The Azure resource group name. + /// The resource group name. /// public string? ResourceGroup { get; set; } /// - /// The Azure workspace name. + /// The workspace name. /// public string? Workspace { get; set; } /// - /// The Azure storage account connection string. + /// The storage account connection string. /// public string? Storage { get; set; } /// - /// The Azure account access token. + /// The account access token. /// public string? Access { get; set; } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 279a1a366a4..af60b874ae3 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -223,31 +223,31 @@ internal static class Driver /// The subscription option. /// internal static Option SubscriptionOption => new Option( - "--subscription", "The Azure subscription ID.") { Required = true }; + "--subscription", "The subscription ID.") { Required = true }; /// /// The resource group option. /// internal static Option ResourceGroupOption => new Option( - "--resource-group", "The Azure resource group name.") { Required = true }; + "--resource-group", "The resource group name.") { Required = true }; /// /// The workspace option. /// internal static Option WorkspaceOption => new Option( - "--workspace", "The Azure workspace name.") { Required = true }; + "--workspace", "The workspace name.") { Required = true }; /// /// The storage option. /// internal static Option StorageOption => new Option( - "--storage", "The Azure storage account connection string.") { Required = true }; + "--storage", "The storage account connection string.") { Required = true }; /// /// The access option. /// internal static Option AccessOption => new Option( - "--access", () => default, "The Azure account access token."); + "--access", () => default, "The account access token."); /// /// The shots option. From a67da46e5022eebad04401097c5df1a08e25376d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 8 May 2020 17:08:06 -0700 Subject: [PATCH 25/39] Print job ID instead of URI for now --- src/Simulation/EntryPointDriver/Azure.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 9f595535490..0ece4e11afe 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -41,7 +41,7 @@ internal static async Task Submit( Console.WriteLine("Job submitted. To track your job status and see the results use:"); Console.WriteLine(); // TODO: Show the friendly URL. - Console.WriteLine(job.Uri); + Console.WriteLine(job.Id); } return 0; } From 6568a9e0e050fa9c15c472e62430f372572a3b9f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 11:11:53 -0700 Subject: [PATCH 26/39] Add --base-uri option --- src/Simulation/EntryPointDriver/Azure.cs | 18 ++++++++++------- src/Simulation/EntryPointDriver/Driver.cs | 24 +++++++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 0ece4e11afe..166f637d494 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -95,6 +95,11 @@ internal sealed class AzureSettings /// public string? Target { get; set; } + /// + /// The storage account connection string. + /// + public string? Storage { get; set; } + /// /// The subscription ID. /// @@ -111,14 +116,14 @@ internal sealed class AzureSettings public string? Workspace { get; set; } /// - /// The storage account connection string. + /// The account access token. /// - public string? Storage { get; set; } + public string? Access { get; set; } /// - /// The account access token. + /// The workspace base URI. /// - public string? Access { get; set; } + public Uri? BaseUri { get; set; } /// /// The number of times the program is executed on the target machine. @@ -145,12 +150,11 @@ internal object CreateWorkspace() var tokenCredentialType = Type.GetType("Azure.Core.TokenCredential, Azure.Core", throwOnError: true); var constructor = workspaceType.GetConstructor(new[] { typeof(string), typeof(string), typeof(string), tokenCredentialType, typeof(Uri) }); - return constructor.Invoke(new object?[] { Subscription, ResourceGroup, Workspace, null, null }); + return constructor.Invoke(new object?[] { Subscription, ResourceGroup, Workspace, null, BaseUri }); } else { - return Activator.CreateInstance( - workspaceType, Subscription, ResourceGroup, Workspace, Access, null); + return Activator.CreateInstance(workspaceType, Subscription, ResourceGroup, Workspace, Access, BaseUri); } } } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index af60b874ae3..e53cd6cb08b 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -69,8 +69,9 @@ public async Task Run(string[] args) Handler = CommandHandler.Create(Submit) }; AddOptionsIfAvailable(submit, - TargetOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption, StorageOption); + TargetOption, StorageOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption); AddOptionIfAvailable(submit, AccessOption); + AddOptionIfAvailable(submit, BaseUriOption); AddOptionIfAvailable(submit, IdOnlyOption); AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 @@ -116,11 +117,12 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) await Azure.Submit(entryPoint, parseResult, new AzureSettings { Target = settings.Target, + Storage = settings.Storage, Subscription = settings.Subscription, ResourceGroup = settings.ResourceGroup, Workspace = settings.Workspace, - Storage = settings.Storage, Access = DefaultIfShadowed(AccessOption, settings.Access), + BaseUri = DefaultIfShadowed(BaseUriOption, settings.BaseUri), Shots = DefaultIfShadowed(ShotsOption, settings.Shots), IdOnly = DefaultIfShadowed(IdOnlyOption, settings.IdOnly) }); @@ -219,6 +221,12 @@ internal static class Driver internal static Option TargetOption => new Option( "--target", "The target device ID.") { Required = true }; + /// + /// The storage option. + /// + internal static Option StorageOption => new Option( + "--storage", "The storage account connection string.") { Required = true }; + /// /// The subscription option. /// @@ -237,18 +245,18 @@ internal static class Driver internal static Option WorkspaceOption => new Option( "--workspace", "The workspace name.") { Required = true }; - /// - /// The storage option. - /// - internal static Option StorageOption => new Option( - "--storage", "The storage account connection string.") { Required = true }; - /// /// The access option. /// internal static Option AccessOption => new Option( "--access", () => default, "The account access token."); + /// + /// The base URI option. + /// + internal static Option BaseUriOption => new Option( + "--base-uri", () => default, "The workspace base URI."); + /// /// The shots option. /// From f72feae258785dad99d8d5333d5a3782cd5574d5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 11:18:17 -0700 Subject: [PATCH 27/39] Rename --access to --aad-token --- src/Simulation/EntryPointDriver/Azure.cs | 9 +++++---- src/Simulation/EntryPointDriver/Driver.cs | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 166f637d494..0be4791f9ad 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -116,9 +116,9 @@ internal sealed class AzureSettings public string? Workspace { get; set; } /// - /// The account access token. + /// The Azure Active Directory authentication token. /// - public string? Access { get; set; } + public string? AadToken { get; set; } /// /// The workspace base URI. @@ -143,7 +143,7 @@ internal object CreateWorkspace() { var workspaceType = Type.GetType( "Microsoft.Azure.Quantum.Workspace, Microsoft.Azure.Quantum.Client", throwOnError: true); - if (Access is null) + if (AadToken is null) { // We can't use Activator.CreateInstance because the constructor is ambiguous when the last two // arguments are null. @@ -154,7 +154,8 @@ internal object CreateWorkspace() } else { - return Activator.CreateInstance(workspaceType, Subscription, ResourceGroup, Workspace, Access, BaseUri); + return Activator.CreateInstance( + workspaceType, Subscription, ResourceGroup, Workspace, AadToken, BaseUri); } } } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index e53cd6cb08b..397092efd08 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -70,7 +70,7 @@ public async Task Run(string[] args) }; AddOptionsIfAvailable(submit, TargetOption, StorageOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption); - AddOptionIfAvailable(submit, AccessOption); + AddOptionIfAvailable(submit, AadTokenOption); AddOptionIfAvailable(submit, BaseUriOption); AddOptionIfAvailable(submit, IdOnlyOption); AddOptionIfAvailable(submit, ShotsOption, @@ -121,7 +121,7 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) Subscription = settings.Subscription, ResourceGroup = settings.ResourceGroup, Workspace = settings.Workspace, - Access = DefaultIfShadowed(AccessOption, settings.Access), + AadToken = DefaultIfShadowed(AadTokenOption, settings.AadToken), BaseUri = DefaultIfShadowed(BaseUriOption, settings.BaseUri), Shots = DefaultIfShadowed(ShotsOption, settings.Shots), IdOnly = DefaultIfShadowed(IdOnlyOption, settings.IdOnly) @@ -246,10 +246,10 @@ internal static class Driver "--workspace", "The workspace name.") { Required = true }; /// - /// The access option. + /// The AAD token option. /// - internal static Option AccessOption => new Option( - "--access", () => default, "The account access token."); + internal static Option AadTokenOption => new Option( + "--aad-token", () => default, "The Azure Active Directory authentication token."); /// /// The base URI option. From 3b105b0dc0b40b99cd39cac9a3ffefe6ab15177e Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 11:48:03 -0700 Subject: [PATCH 28/39] Rename --id-only to --output --- src/Simulation/EntryPointDriver/Azure.cs | 45 ++++++++++++++++------- src/Simulation/EntryPointDriver/Driver.cs | 11 +++--- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 0be4791f9ad..5c86ee80619 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -30,18 +30,21 @@ internal static async Task Submit( DisplayUnknownTargetError(settings.Target); return 1; } - + var job = await machine.SubmitAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); - if (settings.IdOnly) - { - Console.WriteLine(job.Id); - } - else + switch (settings.Output) { - Console.WriteLine("Job submitted. To track your job status and see the results use:"); - Console.WriteLine(); - // TODO: Show the friendly URL. - Console.WriteLine(job.Id); + case OutputFormat.FriendlyUri: + Console.WriteLine("Job submitted. To track your job status and see the results use:"); + Console.WriteLine(); + // TODO: Show the friendly URI. + Console.WriteLine(job.Id); + break; + case OutputFormat.Id: + Console.WriteLine(job.Id); + break; + default: + throw new ArgumentOutOfRangeException($"Invalid output format '{settings.Output}'."); } return 0; } @@ -84,6 +87,22 @@ private static void DisplayUnknownTargetError(string? target) Console.ForegroundColor = originalForeground; } } + + /// + /// The information to show in the output after the job is submitted. + /// + internal enum OutputFormat + { + /// + /// Show a friendly message with a URI that can be used to see the job results. + /// + FriendlyUri, + + /// + /// Show only the job ID. + /// + Id + } /// /// Settings for a submission to Azure Quantum. @@ -129,11 +148,11 @@ internal sealed class AzureSettings /// The number of times the program is executed on the target machine. /// public int Shots { get; set; } - + /// - /// Show only the job ID after the job is submitted. + /// The information to show in the output after the job is submitted. /// - public bool IdOnly { get; set; } + public OutputFormat Output { get; set; } /// /// Creates a workspace object based on the settings. diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 397092efd08..44d8ef5a679 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -72,7 +72,7 @@ public async Task Run(string[] args) TargetOption, StorageOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption); AddOptionIfAvailable(submit, AadTokenOption); AddOptionIfAvailable(submit, BaseUriOption); - AddOptionIfAvailable(submit, IdOnlyOption); + AddOptionIfAvailable(submit, OutputOption); AddOptionIfAvailable(submit, ShotsOption, result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 ? $"The number of shots is {value}, but it must be a positive number." @@ -124,7 +124,7 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) AadToken = DefaultIfShadowed(AadTokenOption, settings.AadToken), BaseUri = DefaultIfShadowed(BaseUriOption, settings.BaseUri), Shots = DefaultIfShadowed(ShotsOption, settings.Shots), - IdOnly = DefaultIfShadowed(IdOnlyOption, settings.IdOnly) + Output = DefaultIfShadowed(OutputOption, settings.Output) }); /// @@ -264,10 +264,11 @@ internal static class Driver "--shots", () => 500, "The number of times the program is executed on the target machine."); /// - /// The ID-only option. + /// The output option. /// - internal static Option IdOnlyOption => new Option( - "--id-only", () => false, "Show only the job ID after the job is submitted."); + internal static Option OutputOption => new Option( + "--output", () => OutputFormat.FriendlyUri, + "The information to show in the output after the job is submitted."); } /// From ec1b9355d96bb15b419c661a9e7525538a1d4224 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 14:43:18 -0700 Subject: [PATCH 29/39] Add OptionInfo class --- src/Simulation/EntryPointDriver/Driver.cs | 141 ++++++++---------- src/Simulation/EntryPointDriver/OptionInfo.cs | 110 ++++++++++++++ 2 files changed, 174 insertions(+), 77 deletions(-) create mode 100644 src/Simulation/EntryPointDriver/OptionInfo.cs diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 44d8ef5a679..88bdd15a4c5 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -31,25 +31,31 @@ public sealed class Driver where TCallable : AbstractCalla /// /// The simulator option. /// - private Option SimulatorOption => new Option( + private OptionInfo SimulatorOption { get; } + + /// + /// Creates a new driver for the entry point. + /// + /// The entry point. + public Driver(IEntryPoint entryPoint) + { + this.entryPoint = entryPoint; + SimulatorOption = new OptionInfo( new[] { "--" + CommandLineArguments.SimulatorOption.Item1, "-" + CommandLineArguments.SimulatorOption.Item2 }, - () => entryPoint.DefaultSimulator, - "The name of the simulator to use.") - .WithSuggestions( - AssemblyConstants.QuantumSimulator, - AssemblyConstants.ToffoliSimulator, - AssemblyConstants.ResourcesEstimator, - entryPoint.DefaultSimulator); - - /// - /// Creates a new driver for the entry point. - /// - /// The entry point. - public Driver(IEntryPoint entryPoint) => this.entryPoint = entryPoint; + entryPoint.DefaultSimulator, + "The name of the simulator to use.", + suggestions: new[] + { + AssemblyConstants.QuantumSimulator, + AssemblyConstants.ToffoliSimulator, + AssemblyConstants.ResourcesEstimator, + entryPoint.DefaultSimulator + }); + } /// /// Runs the entry point using the command-line arguments. @@ -62,21 +68,21 @@ public async Task Run(string[] args) { Handler = CommandHandler.Create(Simulate) }; - AddOptionIfAvailable(simulate, SimulatorOption); + AddOptionIfAvailable(simulate, SimulatorOption); var submit = new Command("submit", "Submit the program to Azure Quantum.") { Handler = CommandHandler.Create(Submit) }; - AddOptionsIfAvailable(submit, - TargetOption, StorageOption, SubscriptionOption, ResourceGroupOption, WorkspaceOption); - AddOptionIfAvailable(submit, AadTokenOption); - AddOptionIfAvailable(submit, BaseUriOption); - AddOptionIfAvailable(submit, OutputOption); - AddOptionIfAvailable(submit, ShotsOption, - result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 - ? $"The number of shots is {value}, but it must be a positive number." - : default); + AddOptionIfAvailable(submit, TargetOption); + AddOptionIfAvailable(submit, StorageOption); + AddOptionIfAvailable(submit, SubscriptionOption); + AddOptionIfAvailable(submit, ResourceGroupOption); + AddOptionIfAvailable(submit, WorkspaceOption); + AddOptionIfAvailable(submit, AadTokenOption); + AddOptionIfAvailable(submit, BaseUriOption); + AddOptionIfAvailable(submit, OutputOption); + AddOptionIfAvailable(submit, ShotsOption); var root = new RootCommand(entryPoint.Summary) { simulate, submit }; foreach (var option in entryPoint.Options) @@ -143,9 +149,9 @@ private bool IsAliasAvailable(string alias) => /// The option. /// The value of the option given on the command line. /// The default value or the original value. - private T DefaultIfShadowed(Option option, T value) + private T DefaultIfShadowed(OptionInfo option, T value) { - if (IsAliasAvailable(option.RawAliases.First())) + if (IsAliasAvailable(option.Aliases.First())) { return value; } @@ -155,9 +161,9 @@ private T DefaultIfShadowed(Option option, T value) Console.ForegroundColor = ConsoleColor.Yellow; Console.Error.WriteLine( $"Warning: Option {option.Aliases.First()} is overridden by an entry point parameter name. " + - $"Using default value {option.Argument.GetDefaultValue()}."); + $"Using default value {option.DefaultValue}."); Console.ForegroundColor = originalForeground; - return (T)option.Argument.GetDefaultValue(); + return option.DefaultValue; } } @@ -167,43 +173,17 @@ private T DefaultIfShadowed(Option option, T value) /// /// The command to add the option to. /// The option to add. - /// A validator for the option. /// The type of the option's argument. - private void AddOptionIfAvailable( - Command command, Option option, ValidateSymbol? validator = default) + private void AddOptionIfAvailable(Command command, OptionInfo option) { - if (IsAliasAvailable(option.RawAliases.First())) + if (IsAliasAvailable(option.Aliases.First())) { - var validAliases = option.RawAliases.Where(IsAliasAvailable).ToArray(); - var validOption = option.Argument.HasDefaultValue - ? new Option(validAliases, () => (T)option.Argument.GetDefaultValue(), option.Description) - : new Option(validAliases, option.Description); - validOption.Required = option.Required; - if (!(validator is null)) - { - validOption.AddValidator(validator); - } - command.AddOption(validOption.WithSuggestions(option.GetSuggestions().ToArray())); + command.AddOption(option.Create(option.Aliases.Where(IsAliasAvailable))); } else if (option.Required) { command.AddValidator(commandResult => - $"The required option {option.RawAliases.First()} conflicts with an entry point parameter name."); - } - } - - /// - /// Adds the option to the command using only the aliases that are available, and only if the primary (first) - /// alias is available. - /// - /// The command to add the option to. - /// The options to add. - /// The type of the option's argument. - private void AddOptionsIfAvailable(Command command, params Option[] options) - { - foreach (var option in options) - { - AddOptionIfAvailable(command, option); + $"The required option {option.Aliases.First()} conflicts with an entry point parameter name."); } } } @@ -218,56 +198,63 @@ internal static class Driver /// /// The target option. /// - internal static Option TargetOption => new Option( - "--target", "The target device ID.") { Required = true }; + internal static readonly OptionInfo TargetOption = new OptionInfo( + new[] { "--target" }, "The target device ID."); /// /// The storage option. /// - internal static Option StorageOption => new Option( - "--storage", "The storage account connection string.") { Required = true }; + internal static readonly OptionInfo StorageOption = new OptionInfo( + new[] { "--storage" }, "The storage account connection string."); /// /// The subscription option. /// - internal static Option SubscriptionOption => new Option( - "--subscription", "The subscription ID.") { Required = true }; + internal static readonly OptionInfo SubscriptionOption = new OptionInfo( + new[] { "--subscription" }, "The subscription ID."); /// /// The resource group option. /// - internal static Option ResourceGroupOption => new Option( - "--resource-group", "The resource group name.") { Required = true }; + internal static readonly OptionInfo ResourceGroupOption = new OptionInfo( + new[] { "--resource-group" }, "The resource group name."); /// /// The workspace option. /// - internal static Option WorkspaceOption => new Option( - "--workspace", "The workspace name.") { Required = true }; + internal static readonly OptionInfo WorkspaceOption = new OptionInfo( + new[] { "--workspace" }, "The workspace name."); /// /// The AAD token option. /// - internal static Option AadTokenOption => new Option( - "--aad-token", () => default, "The Azure Active Directory authentication token."); + internal static readonly OptionInfo AadTokenOption = new OptionInfo( + new[] { "--aad-token" }, default, "The Azure Active Directory authentication token."); /// /// The base URI option. /// - internal static Option BaseUriOption => new Option( - "--base-uri", () => default, "The workspace base URI."); - + internal static readonly OptionInfo BaseUriOption = new OptionInfo( + new[] { "--base-uri" }, default, "The workspace base URI."); + /// /// The shots option. /// - internal static Option ShotsOption => new Option( - "--shots", () => 500, "The number of times the program is executed on the target machine."); + internal static readonly OptionInfo ShotsOption = new OptionInfo( + new[] { "--shots" }, + 500, + "The number of times the program is executed on the target machine.", + validator: result => + int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 + ? $"The number of shots is {value}, but it must be a positive number." + : default); /// /// The output option. /// - internal static Option OutputOption => new Option( - "--output", () => OutputFormat.FriendlyUri, + internal static readonly OptionInfo OutputOption = new OptionInfo( + new[] { "--output" }, + OutputFormat.FriendlyUri, "The information to show in the output after the job is submitted."); } diff --git a/src/Simulation/EntryPointDriver/OptionInfo.cs b/src/Simulation/EntryPointDriver/OptionInfo.cs new file mode 100644 index 00000000000..91bb0e5ef9c --- /dev/null +++ b/src/Simulation/EntryPointDriver/OptionInfo.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Parsing; +using System.Linq; + +namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver +{ + /// + /// Information about a command-line option. + /// + /// The type of the option's argument. + internal sealed class OptionInfo + { + /// + /// The option aliases. + /// + internal IReadOnlyCollection Aliases { get; } + + /// + /// The option's default value if the option has one. + /// + /// Thrown if the option does not have a default value. + internal T DefaultValue => defaultValue(); + + /// + /// True if the option is required. + /// + internal bool Required { get; } + + /// + /// A function that returns the default value or throws a . + /// + private readonly Func defaultValue; + + /// + /// The option description. + /// + private readonly string description; + + /// + /// The option suggestions. + /// + private readonly IEnumerable? suggestions; + + /// + /// The option validator. + /// + private readonly ValidateSymbol? validator; + + /// + /// Creates an for a non-required option. + /// + /// The option aliases. + /// The option's default value. + /// The option description. + /// The option suggestions. + /// The option validator. + internal OptionInfo( + IReadOnlyCollection aliases, + T defaultValue, + string description, + IEnumerable? suggestions = default, + ValidateSymbol? validator = default) + { + Aliases = aliases; + Required = false; + this.defaultValue = () => defaultValue; + this.description = description; + this.suggestions = suggestions; + this.validator = validator; + } + + /// + /// Creates an for a required option. + /// + /// The option aliases. + /// The option description. + /// The option suggestions. + /// The option validator. + internal OptionInfo( + IReadOnlyCollection aliases, + string description, + IEnumerable? suggestions = default, + ValidateSymbol? validator = default) + { + Aliases = aliases; + Required = true; + defaultValue = () => throw new NotSupportedException("The option does not have a default value."); + this.description = description; + this.suggestions = suggestions; + this.validator = validator; + } + + /// + /// Creates an option based on the information in the . + /// + /// The option aliases. + /// The option. + internal Option Create(IEnumerable aliases) + { + var option = new Option(aliases.ToArray(), defaultValue, description) { Required = Required }; + if (!(validator is null)) + { + option.AddValidator(validator); + } + return suggestions is null ? option : option.WithSuggestions(suggestions.ToArray()); + } + } +} From 9bf31b6a755f0e3c4b72adefefdfd0c4471e977a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 14:49:56 -0700 Subject: [PATCH 30/39] Update --shots validator message --- src/Simulation/EntryPointDriver/Driver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 88bdd15a4c5..44deadc00cd 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -246,7 +246,7 @@ internal static class Driver "The number of times the program is executed on the target machine.", validator: result => int.TryParse(result.Tokens.SingleOrDefault()?.Value, out var value) && value <= 0 - ? $"The number of shots is {value}, but it must be a positive number." + ? "The number of shots must be a positive number." : default); /// From 44383c33c040b878cac80867d022f9a7fb58aad7 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 14:53:29 -0700 Subject: [PATCH 31/39] Update --base-uri description --- src/Simulation/EntryPointDriver/Azure.cs | 2 +- src/Simulation/EntryPointDriver/Driver.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 5c86ee80619..a00ea0b51d5 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -140,7 +140,7 @@ internal sealed class AzureSettings public string? AadToken { get; set; } /// - /// The workspace base URI. + /// The base URI of the Azure Quantum endpoint. /// public Uri? BaseUri { get; set; } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 44deadc00cd..30be066d3d9 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -235,7 +235,7 @@ internal static class Driver /// The base URI option. /// internal static readonly OptionInfo BaseUriOption = new OptionInfo( - new[] { "--base-uri" }, default, "The workspace base URI."); + new[] { "--base-uri" }, default, "The base URI of the Azure Quantum endpoint."); /// /// The shots option. From 4c2f68f22b5f3e24ac52f00ce8ffde83f1082ff9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 15:08:39 -0700 Subject: [PATCH 32/39] Add TODO for "submit" tests --- src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs b/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs index b9f1afdec73..fa76a174f80 100644 --- a/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs +++ b/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs @@ -481,6 +481,9 @@ let ``Supports default custom simulator`` () = given ["--simulator"; typeof.FullName; "--use-h"; "false"] |> fails +// TODO: Add tests for the "submit" command. + + // Help [] From 6cf2873a8af2c747a3cac83835799bf8783d5f34 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 15:12:51 -0700 Subject: [PATCH 33/39] Replace NotImplementedExceptions with NotSupportedExceptions --- src/Simulation/EntryPointDriver/NothingMachine.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Simulation/EntryPointDriver/NothingMachine.cs b/src/Simulation/EntryPointDriver/NothingMachine.cs index 38a7006be6a..193150b7a64 100644 --- a/src/Simulation/EntryPointDriver/NothingMachine.cs +++ b/src/Simulation/EntryPointDriver/NothingMachine.cs @@ -19,7 +19,7 @@ internal class NothingMachine : IQuantumMachine public string Target => "Nothing"; public Task> ExecuteAsync(EntryPointInfo info, TIn input) => - throw new NotImplementedException(); + throw new NotSupportedException(); public Task SubmitAsync(EntryPointInfo info, TIn input) => Task.FromResult(new DefaultJob()); @@ -41,11 +41,10 @@ private class DefaultJob : IQuantumMachineJob public Uri Uri => new Uri("https://example.com/" + Id); - public Task CancelAsync(CancellationToken cancellationToken = default) => - throw new NotImplementedException(); + public Task CancelAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException(); - public Task RefreshAsync(CancellationToken cancellationToken = default) => - throw new NotImplementedException(); + public Task RefreshAsync(CancellationToken cancellationToken = default) => + throw new NotSupportedException(); } } } From fbcdf601e8629e26fd66185e007bf60c91ee3054 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 11 May 2020 15:18:08 -0700 Subject: [PATCH 34/39] More specific TODO for number of shots --- src/Simulation/EntryPointDriver/Azure.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index a00ea0b51d5..40b80a0a907 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -58,7 +58,7 @@ internal static async Task Submit( { if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) { - // TODO: Number of shots? + // TODO: Give the number of shots to the quantum machine. var ionQType = Type.GetType( "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", throwOnError: true); From eca8091933d1d0bfdc5df7362e9ae5df9a73f426 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 12 May 2020 15:48:27 -0700 Subject: [PATCH 35/39] Don't include default value for required options --- src/Simulation/EntryPointDriver/OptionInfo.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Simulation/EntryPointDriver/OptionInfo.cs b/src/Simulation/EntryPointDriver/OptionInfo.cs index 91bb0e5ef9c..7f96ec423de 100644 --- a/src/Simulation/EntryPointDriver/OptionInfo.cs +++ b/src/Simulation/EntryPointDriver/OptionInfo.cs @@ -99,7 +99,9 @@ internal OptionInfo( /// The option. internal Option Create(IEnumerable aliases) { - var option = new Option(aliases.ToArray(), defaultValue, description) { Required = Required }; + var option = Required + ? new Option(aliases.ToArray(), description) { Required = true } + : new Option(aliases.ToArray(), defaultValue, description); if (!(validator is null)) { option.AddValidator(validator); From be0e5d73a738bf84314acf5543ef5add551ac505 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 13 May 2020 10:02:29 -0700 Subject: [PATCH 36/39] Hide "submit" command --- src/Simulation/EntryPointDriver/Driver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 30be066d3d9..13ea4c1ee61 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -72,6 +72,7 @@ public async Task Run(string[] args) var submit = new Command("submit", "Submit the program to Azure Quantum.") { + IsHidden = true, Handler = CommandHandler.Create(Submit) }; AddOptionIfAvailable(submit, TargetOption); From 3c66e568c9ba0a3dfc87c3f06a29589d299eb91a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 13 May 2020 10:04:10 -0700 Subject: [PATCH 37/39] Update help test --- src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs b/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs index fa76a174f80..e138a12d6a1 100644 --- a/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs +++ b/src/Simulation/CsharpGeneration.Tests/EntryPointTests.fs @@ -504,8 +504,7 @@ Options: -?, -h, --help Show help and usage information Commands: - simulate (default) Run the program using a local simulator. - submit Submit the program to Azure Quantum." + simulate (default) Run the program using a local simulator." let given = test 31 given ["--help"] |> yields message From 256ebbd8cffc7d3a9e9dde2f19e573a565f9459d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 14 May 2020 12:14:58 -0700 Subject: [PATCH 38/39] Update TODO comments --- src/Simulation/EntryPointDriver/Azure.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 40b80a0a907..70b691f4ca4 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -31,13 +31,14 @@ internal static async Task Submit( return 1; } + // TODO: Specify the number of shots. The IQuantumMachine interface should be updated. var job = await machine.SubmitAsync(entryPoint.Info, entryPoint.CreateArgument(parseResult)); switch (settings.Output) { case OutputFormat.FriendlyUri: Console.WriteLine("Job submitted. To track your job status and see the results use:"); Console.WriteLine(); - // TODO: Show the friendly URI. + // TODO: Show the friendly URI. The friendly URI is not yet available from the job. Console.WriteLine(job.Id); break; case OutputFormat.Id: @@ -58,7 +59,6 @@ internal static async Task Submit( { if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) { - // TODO: Give the number of shots to the quantum machine. var ionQType = Type.GetType( "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", throwOnError: true); From 3845b95a54f6cd0bfeabafd16a7865ec72c1f3df Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 15 May 2020 12:59:37 -0700 Subject: [PATCH 39/39] Add reference to Microsoft.Azure.Quantum.Client --- src/Simulation/EntryPointDriver/Azure.cs | 51 ++++--------------- .../EntryPointDriver/EntryPointDriver.csproj | 1 + 2 files changed, 12 insertions(+), 40 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 70b691f4ca4..b9edb7c5477 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -4,6 +4,7 @@ using System; using System.CommandLine.Parsing; using System.Threading.Tasks; +using Microsoft.Azure.Quantum; using Microsoft.Quantum.Runtime; namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver @@ -55,25 +56,10 @@ internal static async Task Submit( /// /// The Azure Quantum submission settings. /// A quantum machine. - private static IQuantumMachine? CreateMachine(AzureSettings settings) - { - if (!(settings.Target is null) && settings.Target.StartsWith("ionq.")) - { - var ionQType = Type.GetType( - "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", - throwOnError: true); - return (IQuantumMachine)Activator.CreateInstance( - ionQType, settings.Target, settings.Storage, settings.CreateWorkspace()); - } - else if (settings.Target == "nothing") - { - return new NothingMachine(); - } - else - { - return null; - } - } + private static IQuantumMachine? CreateMachine(AzureSettings settings) => + settings.Target == "nothing" + ? new NothingMachine() + : QuantumMachineFactory.CreateMachine(settings.CreateWorkspace(), settings.Target, settings.Storage); /// /// Displays an error message for attempting to use an unknown target machine. @@ -155,27 +141,12 @@ internal sealed class AzureSettings public OutputFormat Output { get; set; } /// - /// Creates a workspace object based on the settings. + /// Creates a based on the settings. /// - /// The workspace object based on the settings. - internal object CreateWorkspace() - { - var workspaceType = Type.GetType( - "Microsoft.Azure.Quantum.Workspace, Microsoft.Azure.Quantum.Client", throwOnError: true); - if (AadToken is null) - { - // We can't use Activator.CreateInstance because the constructor is ambiguous when the last two - // arguments are null. - var tokenCredentialType = Type.GetType("Azure.Core.TokenCredential, Azure.Core", throwOnError: true); - var constructor = workspaceType.GetConstructor(new[] - { typeof(string), typeof(string), typeof(string), tokenCredentialType, typeof(Uri) }); - return constructor.Invoke(new object?[] { Subscription, ResourceGroup, Workspace, null, BaseUri }); - } - else - { - return Activator.CreateInstance( - workspaceType, Subscription, ResourceGroup, Workspace, AadToken, BaseUri); - } - } + /// The based on the settings. + internal Workspace CreateWorkspace() => + AadToken is null + ? new Workspace(Subscription, ResourceGroup, Workspace, baseUri: BaseUri) + : new Workspace(Subscription, ResourceGroup, Workspace, AadToken, BaseUri); } } diff --git a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj index 7760104f92a..6125aa21b4d 100644 --- a/src/Simulation/EntryPointDriver/EntryPointDriver.csproj +++ b/src/Simulation/EntryPointDriver/EntryPointDriver.csproj @@ -15,6 +15,7 @@ +