From 3a24769f0cb5910151ebfe0c46630cf0d766e007 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 30 Jun 2020 18:50:09 -0700 Subject: [PATCH 1/3] Use a default target when submitting to Azure Quantum --- src/Simulation/CsharpGeneration/EntryPoint.fs | 21 ++- .../EntryPointDriver.Tests/Tests.fs | 123 +++++++++++++++--- src/Simulation/EntryPointDriver/Azure.cs | 18 ++- src/Simulation/EntryPointDriver/Driver.cs | 22 ++-- .../EntryPointDriver/DriverSettings.cs | 2 +- .../EntryPointDriver/IEntryPoint.cs | 5 + 6 files changed, 149 insertions(+), 42 deletions(-) diff --git a/src/Simulation/CsharpGeneration/EntryPoint.fs b/src/Simulation/CsharpGeneration/EntryPoint.fs index 8906a7403c7..f359f26f1f3 100644 --- a/src/Simulation/CsharpGeneration/EntryPoint.fs +++ b/src/Simulation/CsharpGeneration/EntryPoint.fs @@ -133,21 +133,28 @@ let private mainMethod context entryPoint = let private entryPointClass context entryPoint = let callableName, argTypeName, returnTypeName = callableTypeNames context entryPoint let property name typeName value = ``property-arrow_get`` typeName name [``public``] get (``=>`` value) - let summaryProperty = property "Summary" "string" (literal ((PrintSummary entryPoint.Documentation false).Trim ())) + let summaryProperty = + (PrintSummary entryPoint.Documentation false).Trim () + |> literal + |> property "Summary" "string" let parameters = parameters context entryPoint.Documentation entryPoint.ArgumentTuple let defaultSimulator = context.assemblyConstants.TryGetValue AssemblyConstants.DefaultSimulator - |> snd - |> (fun value -> if String.IsNullOrWhiteSpace value then AssemblyConstants.QuantumSimulator else value) - let defaultSimulatorNameProperty = property "DefaultSimulatorName" "string" (literal defaultSimulator) + |> fun (_, value) -> if String.IsNullOrWhiteSpace value then AssemblyConstants.QuantumSimulator else value + let defaultSimulatorNameProperty = literal defaultSimulator |> property "DefaultSimulatorName" "string" + let defaultExecutionTargetProperty = + context.assemblyConstants.TryGetValue AssemblyConstants.ExecutionTarget + |> (fun (_, value) -> if value = null then "" else value) + |> literal + |> property "DefaultExecutionTarget" "string" let infoProperty = - property "Info" - (sprintf "EntryPointInfo<%s, %s>" argTypeName returnTypeName) - (ident callableName <|.|> ident "Info") + property "Info" (sprintf "EntryPointInfo<%s, %s>" argTypeName returnTypeName) + (ident callableName <|.|> ident "Info") let members : MemberDeclarationSyntax list = [ summaryProperty parameterOptionsProperty parameters defaultSimulatorNameProperty + defaultExecutionTargetProperty infoProperty customSimulatorFactory defaultSimulator createArgument context entryPoint diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index 9d3c9c5d0aa..ac14673997e 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -4,6 +4,7 @@ module Microsoft.Quantum.EntryPointDriver.Tests open System +open System.Collections.Generic open System.Collections.Immutable open System.Globalization open System.IO @@ -61,12 +62,8 @@ let private compileQsharp source = /// Generates C# source code from the compiled Q# syntax tree. The given default simulator is set as an assembly /// constant. -let private generateCsharp defaultSimulator (compilation : QsCompilation) = - let assemblyConstants = - match defaultSimulator with - | Some simulator -> ImmutableDictionary.Empty.Add (AssemblyConstants.DefaultSimulator, simulator) - | None -> ImmutableDictionary.Empty - let context = CodegenContext.Create (compilation, assemblyConstants) +let private generateCsharp constants (compilation : QsCompilation) = + let context = CodegenContext.Create (compilation, constants) let entryPoint = context.allCallables.[Seq.exactlyOne compilation.EntryPoints] [ SimulationCode.generate (NonNullable<_>.New testFile) context @@ -113,11 +110,11 @@ let private compileCsharp (sources : string seq) = Assembly.Load (stream.ToArray ()) /// The assembly for the given test case and default simulator. -let private testAssembly testNum defaultSimulator = +let private testAssembly testNum constants = testNum |> testCase |> compileQsharp - |> generateCsharp defaultSimulator + |> generateCsharp constants |> compileCsharp /// Runs the entry point in the assembly with the given command-line arguments, and returns the output, errors, and exit @@ -164,17 +161,26 @@ let private failsWith expected (assembly, args) = Assert.True (0 <> exitCode, sprintf "Expected non-zero exit code, but got 0 with:\n\n%s\n\n%s" error out) Assert.StartsWith (normalize expected, normalize (error + out)) -/// A tuple of the test assembly and arguments using the standard default simulator. The tuple can be passed to yields -/// or fails. -let private test testNum = - let assembly = testAssembly testNum None +/// A tuple of the test assembly and arguments using the given assembly constants. The tuple can be passed to yields or +/// fails. +let private testWithConstants constants testNum = + let assembly = testAssembly testNum constants fun args -> assembly, Array.ofList args +/// A tuple of the test assembly and arguments with no assembly constants. The tuple can be passed to yields or fails. +let private test = testWithConstants ImmutableDictionary.Empty + /// A tuple of the test assembly and arguments using the given default simulator. The tuple can be passed to yields or /// fails. -let private testWith testNum defaultSimulator = - let assembly = testAssembly testNum (Some defaultSimulator) - fun args -> assembly, Array.ofList args +let private testWithSim defaultSimulator = + ImmutableDictionary.CreateRange [KeyValuePair (AssemblyConstants.DefaultSimulator, defaultSimulator)] + |> testWithConstants + +/// A tuple of the test assembly and arguments using the given default target. The tuple can be passed to yields or +/// fails. +let private testWithTarget defaultTarget = + ImmutableDictionary.CreateRange [KeyValuePair (AssemblyConstants.ExecutionTarget, defaultTarget)] + |> testWithConstants /// Standard command-line arguments for the "submit" command without specifying a target. let private submitWithoutTarget = @@ -509,7 +515,7 @@ let ``Rejects unknown simulator`` () = [] let ``Supports default standard simulator`` () = - let given = testWith 32 AssemblyConstants.ResourcesEstimator + let given = testWithSim AssemblyConstants.ResourcesEstimator 32 given ["--use-h"; "false"] |> yields resourceSummary given ["--simulator"; AssemblyConstants.QuantumSimulator; "--use-h"; "false"] |> yields "Hello, World!" @@ -517,7 +523,7 @@ let ``Supports default standard simulator`` () = let ``Supports default custom simulator`` () = // This is not really a "custom" simulator, but the driver does not recognize the fully-qualified name of the // standard simulators, so it is treated as one. - let given = testWith 32 typeof.FullName + let given = testWithSim typeof.FullName 32 given ["--use-h"; "false"] |> yields "Hello, World!" given ["--use-h"; "true"] |> fails given ["--simulator"; typeof.FullName; "--use-h"; "false"] |> yields "Hello, World!" @@ -547,10 +553,30 @@ let ``Submit uses default values`` () = let given = test 1 given (submitWithNothingTarget @ ["--verbose"]) |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead. + Subscription: mySubscription + Resource Group: myResourceGroup + Workspace: myWorkspace Target: test.nothing + Storage: + AAD Token: + Base URI: + Job Name: + Shots: 500 + Output: FriendlyUri + Dry Run: False + Verbose: True + + 00000000-0000-0000-0000-0000000000000" + +[] +let ``Submit uses default values with default target`` () = + let given = testWithTarget "test.nothing" 1 + given (submitWithoutTarget @ ["--verbose"]) + |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead. Subscription: mySubscription Resource Group: myResourceGroup Workspace: myWorkspace + Target: test.nothing Storage: AAD Token: Base URI: @@ -579,10 +605,42 @@ let ``Submit allows overriding default values`` () = "750" ]) |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead. + Subscription: mySubscription + Resource Group: myResourceGroup + Workspace: myWorkspace Target: test.nothing + Storage: myStorage + AAD Token: myToken + Base URI: myBaseUri + Job Name: myJobName + Shots: 750 + Output: FriendlyUri + Dry Run: False + Verbose: True + + 00000000-0000-0000-0000-0000000000000" + +[] +let ``Submit allows overriding default values with default target`` () = + let given = testWithTarget "foo.target" 1 + given (submitWithNothingTarget @ [ + "--verbose" + "--storage" + "myStorage" + "--aad-token" + "myToken" + "--base-uri" + "myBaseUri" + "--job-name" + "myJobName" + "--shots" + "750" + ]) + |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead. Subscription: mySubscription Resource Group: myResourceGroup Workspace: myWorkspace + Target: test.nothing Storage: myStorage AAD Token: myToken Base URI: myBaseUri @@ -685,10 +743,10 @@ let ``Shows help text for submit command`` () = %s submit [options] Options: - --target (REQUIRED) The target device ID. --subscription (REQUIRED) The subscription ID. --resource-group (REQUIRED) The resource group name. --workspace (REQUIRED) The workspace name. + --target (REQUIRED) The target device ID. --storage The storage account connection string. --aad-token The Azure Active Directory authentication token. --base-uri The base URI of the Azure Quantum endpoint. @@ -701,6 +759,33 @@ let ``Shows help text for submit command`` () = --pauli (REQUIRED) The name of a Pauli matrix. --my-cool-bool (REQUIRED) A neat bit. -?, -h, --help Show help and usage information" - let given = test 33 given ["submit"; "--help"] |> yields message + +[] +let ``Shows help text for submit command with default target`` () = + let name = Path.GetFileNameWithoutExtension (Assembly.GetEntryAssembly().Location) + let message = + name + |> sprintf "Usage: + %s submit [options] + + Options: + --subscription (REQUIRED) The subscription ID. + --resource-group (REQUIRED) The resource group name. + --workspace (REQUIRED) The workspace name. + --target The target device ID. + --storage The storage account connection string. + --aad-token The Azure Active Directory authentication token. + --base-uri The base URI of the Azure Quantum endpoint. + --job-name The name of the submitted job. + --shots The number of times the program is executed on the target machine. + --output The information to show in the output after the job is submitted. + --dry-run Validate the program and options, but do not submit to Azure Quantum. + --verbose Show additional information about the submission. + -n (REQUIRED) A number. + --pauli (REQUIRED) The name of a Pauli matrix. + --my-cool-bool (REQUIRED) A neat bit. + -?, -h, --help Show help and usage information" + let given = testWithTarget "foo.target" 33 + given ["submit"; "--help"] |> yields message diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 0f3b3a91e77..5a902c69d08 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -150,12 +150,16 @@ private static void DisplayError(string summary, string message) /// Creates a quantum machine based on the Azure Quantum submission settings. /// /// The Azure Quantum submission settings. + /// Thrown if .Target is null. /// A quantum machine. private static IQuantumMachine? CreateMachine(AzureSettings settings) => settings.Target switch { NothingMachine.TargetId => new NothingMachine(), ErrorMachine.TargetId => new ErrorMachine(), - _ => QuantumMachineFactory.CreateMachine(settings.CreateWorkspace(), settings.Target, settings.Storage) + _ => QuantumMachineFactory.CreateMachine( + settings.CreateWorkspace(), + settings.Target ?? throw new ArgumentNullException(nameof(settings), "Target is null."), + settings.Storage) }; /// @@ -190,11 +194,6 @@ internal enum OutputFormat /// internal sealed class AzureSettings { - /// - /// The target device ID. - /// - public string? Target { get; set; } - /// /// The subscription ID. /// @@ -210,6 +209,11 @@ internal sealed class AzureSettings /// public string? Workspace { get; set; } + /// + /// The target device ID. + /// + public string? Target { get; set; } + /// /// The storage account connection string. /// @@ -261,10 +265,10 @@ AadToken is null public override string ToString() => string.Join(System.Environment.NewLine, - $"Target: {Target}", $"Subscription: {Subscription}", $"Resource Group: {ResourceGroup}", $"Workspace: {Workspace}", + $"Target: {Target}", $"Storage: {Storage}", $"AAD Token: {AadToken}", $"Base URI: {BaseUri}", diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 13a836e85f2..3ecbb770a3f 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -40,6 +40,11 @@ public sealed class Driver where TCallable : AbstractCalla /// private OptionInfo SimulatorOption { get; } + /// + /// The target option. + /// + private OptionInfo TargetOption { get; } + /// /// Creates a new driver for the entry point. /// @@ -49,6 +54,7 @@ public Driver(DriverSettings settings, IEntryPoint entryPoint) { this.settings = settings; this.entryPoint = entryPoint; + SimulatorOption = new OptionInfo( settings.SimulatorOptionAliases, entryPoint.DefaultSimulatorName, @@ -60,6 +66,12 @@ public Driver(DriverSettings settings, IEntryPoint entryPoint) settings.ResourcesEstimatorName, entryPoint.DefaultSimulatorName }); + + var targetAliases = ImmutableList.Create("--target"); + const string targetDescription = "The target device ID."; + TargetOption = string.IsNullOrWhiteSpace(entryPoint.DefaultExecutionTarget) + ? new OptionInfo(targetAliases, targetDescription) + : new OptionInfo(targetAliases, entryPoint.DefaultExecutionTarget, targetDescription); } /// @@ -80,10 +92,10 @@ public async Task Run(string[] args) IsHidden = true, Handler = CommandHandler.Create(Submit) }; - AddOptionIfAvailable(submit, TargetOption); AddOptionIfAvailable(submit, SubscriptionOption); AddOptionIfAvailable(submit, ResourceGroupOption); AddOptionIfAvailable(submit, WorkspaceOption); + AddOptionIfAvailable(submit, TargetOption); AddOptionIfAvailable(submit, StorageOption); AddOptionIfAvailable(submit, AadTokenOption); AddOptionIfAvailable(submit, BaseUriOption); @@ -132,10 +144,10 @@ await Simulation.Simulate( private async Task Submit(ParseResult parseResult, AzureSettings azureSettings) => await Azure.Submit(entryPoint, parseResult, new AzureSettings { - Target = azureSettings.Target, Subscription = azureSettings.Subscription, ResourceGroup = azureSettings.ResourceGroup, Workspace = azureSettings.Workspace, + Target = DefaultIfShadowed(TargetOption, azureSettings.Target), Storage = DefaultIfShadowed(StorageOption, azureSettings.Storage), AadToken = DefaultIfShadowed(AadTokenOption, azureSettings.AadToken), BaseUri = DefaultIfShadowed(BaseUriOption, azureSettings.BaseUri), @@ -205,12 +217,6 @@ internal static class Driver { // TODO: Define the aliases as constants. - /// - /// The target option. - /// - internal static readonly OptionInfo TargetOption = new OptionInfo( - ImmutableList.Create("--target"), "The target device ID."); - /// /// The subscription option. /// diff --git a/src/Simulation/EntryPointDriver/DriverSettings.cs b/src/Simulation/EntryPointDriver/DriverSettings.cs index 4b513cf50e4..17ec8e551a3 100644 --- a/src/Simulation/EntryPointDriver/DriverSettings.cs +++ b/src/Simulation/EntryPointDriver/DriverSettings.cs @@ -3,7 +3,7 @@ namespace Microsoft.Quantum.EntryPointDriver { /// - /// Settings for the entry point driver. + /// General settings for the entry point driver that do not depend on the entry point or compilation target. /// public sealed class DriverSettings { diff --git a/src/Simulation/EntryPointDriver/IEntryPoint.cs b/src/Simulation/EntryPointDriver/IEntryPoint.cs index b06f3e7c016..1018fc10f89 100644 --- a/src/Simulation/EntryPointDriver/IEntryPoint.cs +++ b/src/Simulation/EntryPointDriver/IEntryPoint.cs @@ -35,6 +35,11 @@ public interface IEntryPoint /// string DefaultSimulatorName { get; } + /// + /// The default execution target when to use when submitting the entry point to Azure Quantum. + /// + string DefaultExecutionTarget { get; } + /// /// Additional information about the entry point. /// From e47a8fb0d912c9c073e318febfbce280806c6235 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 30 Jun 2020 19:14:04 -0700 Subject: [PATCH 2/3] Update doc comments --- src/Simulation/EntryPointDriver.Tests/Tests.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index ac14673997e..0059809c97b 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -60,8 +60,7 @@ let private compileQsharp source = Assert.Empty errors compilation.BuiltCompilation -/// Generates C# source code from the compiled Q# syntax tree. The given default simulator is set as an assembly -/// constant. +/// Generates C# source code from the compiled Q# syntax tree using the given assembly constants. let private generateCsharp constants (compilation : QsCompilation) = let context = CodegenContext.Create (compilation, constants) let entryPoint = context.allCallables.[Seq.exactlyOne compilation.EntryPoints] @@ -109,7 +108,7 @@ let private compileCsharp (sources : string seq) = Assert.Equal (0L, stream.Seek (0L, SeekOrigin.Begin)) Assembly.Load (stream.ToArray ()) -/// The assembly for the given test case and default simulator. +/// The assembly for the given test case assembly constants. let private testAssembly testNum constants = testNum |> testCase From eb48e4eb33f06d12ddeefa32962efef52543f991 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 1 Jul 2020 10:19:31 -0700 Subject: [PATCH 3/3] Move settings.Target null check to switch case --- src/Simulation/EntryPointDriver/Azure.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 5a902c69d08..2f53a878836 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -154,12 +154,10 @@ private static void DisplayError(string summary, string message) /// A quantum machine. private static IQuantumMachine? CreateMachine(AzureSettings settings) => settings.Target switch { + null => throw new ArgumentNullException(nameof(settings), "Target is null."), NothingMachine.TargetId => new NothingMachine(), ErrorMachine.TargetId => new ErrorMachine(), - _ => QuantumMachineFactory.CreateMachine( - settings.CreateWorkspace(), - settings.Target ?? throw new ArgumentNullException(nameof(settings), "Target is null."), - settings.Storage) + _ => QuantumMachineFactory.CreateMachine(settings.CreateWorkspace(), settings.Target, settings.Storage) }; ///