diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index b9edb7c5477..e5d2604c21e 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Quantum; using Microsoft.Quantum.Runtime; +using static Microsoft.Quantum.CsharpGeneration.EntryPointDriver.Driver; namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver { @@ -28,27 +29,54 @@ internal static async Task Submit( var machine = CreateMachine(settings); if (machine is null) { - DisplayUnknownTargetError(settings.Target); + DisplayWithColor(ConsoleColor.Red, Console.Error, + $"The target '{settings.Target}' was not recognized."); 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) + var input = entryPoint.CreateArgument(parseResult); + if (settings.DryRun) { - case OutputFormat.FriendlyUri: - Console.WriteLine("Job submitted. To track your job status and see the results use:"); + var (isValid, message) = machine.Validate(entryPoint.Info, input); + Console.WriteLine(isValid ? "✔️ The program is valid!" : "❌ The program is invalid."); + if (!string.IsNullOrWhiteSpace(message)) + { Console.WriteLine(); - // TODO: Show the friendly URI. The friendly URI is not yet available from the job. + Console.WriteLine(message); + } + return isValid ? 0 : 1; + } + else + { + var job = await machine.SubmitAsync( + entryPoint.Info, input, new SubmissionContext { Shots = settings.Shots }); + DisplayJob(job, settings.Output); + return 0; + } + } + + /// + /// Displays the job using the output format. + /// + /// The job. + /// The output format. + /// Thrown if the output format is invalid. + private static void DisplayJob(IQuantumMachineJob job, OutputFormat format) + { + switch (format) + { + case OutputFormat.FriendlyUri: + // TODO: + DisplayWithColor(ConsoleColor.Yellow, Console.Error, + "The friendly URI for viewing job results is not available yet. Showing the job ID instead."); Console.WriteLine(job.Id); break; case OutputFormat.Id: Console.WriteLine(job.Id); break; default: - throw new ArgumentOutOfRangeException($"Invalid output format '{settings.Output}'."); + throw new ArgumentOutOfRangeException($"Invalid output format '{format}'."); } - return 0; } /// @@ -62,15 +90,13 @@ internal static async Task Submit( : QuantumMachineFactory.CreateMachine(settings.CreateWorkspace(), settings.Target, settings.Storage); /// - /// Displays an error message for attempting to use an unknown target machine. + /// The quantum machine submission context. /// - /// The target machine. - private static void DisplayUnknownTargetError(string? target) + private sealed class SubmissionContext : IQuantumMachineSubmissionContext { - var originalForeground = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine($"The target '{target}' was not recognized."); - Console.ForegroundColor = originalForeground; + public string? FriendlyName { get; set; } + + public int Shots { get; set; } } } @@ -140,6 +166,11 @@ internal sealed class AzureSettings /// public OutputFormat Output { get; set; } + /// + /// Validate the program and options, but do not submit to Azure Quantum. + /// + public bool DryRun { get; set; } + /// /// Creates a based on the settings. /// diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 13ea4c1ee61..38b68ebe94e 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -7,7 +7,9 @@ using System.CommandLine.Help; using System.CommandLine.Invocation; using System.CommandLine.Parsing; +using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.Simulation.Core; @@ -84,6 +86,7 @@ public async Task Run(string[] args) AddOptionIfAvailable(submit, BaseUriOption); AddOptionIfAvailable(submit, OutputOption); AddOptionIfAvailable(submit, ShotsOption); + AddOptionIfAvailable(submit, DryRunOption); var root = new RootCommand(entryPoint.Summary) { simulate, submit }; foreach (var option in entryPoint.Options) @@ -98,6 +101,7 @@ public async Task Run(string[] args) } root.Handler = simulate.Handler; + Console.OutputEncoding = Encoding.UTF8; return await new CommandLineBuilder(root) .UseDefaults() .UseHelpBuilder(context => new QsHelpBuilder(context.Console)) @@ -131,9 +135,10 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings) AadToken = DefaultIfShadowed(AadTokenOption, settings.AadToken), BaseUri = DefaultIfShadowed(BaseUriOption, settings.BaseUri), Shots = DefaultIfShadowed(ShotsOption, settings.Shots), - Output = DefaultIfShadowed(OutputOption, settings.Output) + Output = DefaultIfShadowed(OutputOption, settings.Output), + DryRun = DefaultIfShadowed(DryRunOption, settings.DryRun) }); - + /// /// Returns true if the alias is not already used by an entry point option. /// @@ -158,12 +163,9 @@ private T DefaultIfShadowed(OptionInfo option, T value) } else { - var originalForeground = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Yellow; - Console.Error.WriteLine( + DisplayWithColor(ConsoleColor.Yellow, Console.Error, $"Warning: Option {option.Aliases.First()} is overridden by an entry point parameter name. " + $"Using default value {option.DefaultValue}."); - Console.ForegroundColor = originalForeground; return option.DefaultValue; } } @@ -257,6 +259,28 @@ internal static class Driver new[] { "--output" }, OutputFormat.FriendlyUri, "The information to show in the output after the job is submitted."); + + /// + /// The dry run option. + /// + internal static readonly OptionInfo DryRunOption = new OptionInfo( + new[] { "--dry-run" }, + false, + "Validate the program and options, but do not submit to Azure Quantum."); + + /// + /// Displays a message to the console using the given color and text writer. + /// + /// The text color. + /// The text writer for the console output stream. + /// The message to display. + internal static void DisplayWithColor(ConsoleColor color, TextWriter writer, string message) + { + var originalForeground = Console.ForegroundColor; + Console.ForegroundColor = color; + writer.WriteLine(message); + Console.ForegroundColor = originalForeground; + } } /// diff --git a/src/Simulation/EntryPointDriver/NothingMachine.cs b/src/Simulation/EntryPointDriver/NothingMachine.cs index d6513eaf77a..f4bf6942e99 100644 --- a/src/Simulation/EntryPointDriver/NothingMachine.cs +++ b/src/Simulation/EntryPointDriver/NothingMachine.cs @@ -14,9 +14,9 @@ namespace Microsoft.Quantum.CsharpGeneration.EntryPointDriver /// internal class NothingMachine : IQuantumMachine { - public string ProviderId => nameof(NothingMachine); + public string ProviderId { get; } = nameof(NothingMachine); - public string Target => "Nothing"; + public string Target { get; } = "Nothing"; public Task> ExecuteAsync( EntryPointInfo info, TInput input) => @@ -71,18 +71,17 @@ public Task SubmitAsync( EntryPointInfo info, TInput input, IQuantumMachineSubmissionContext submissionContext) => - throw new NotSupportedException(); + SubmitAsync(info, input); public Task SubmitAsync( EntryPointInfo info, TInput input, IQuantumMachineSubmissionContext submissionContext, IQuantumMachine.ConfigureJob configureJobCallback) => - throw new NotSupportedException(); + SubmitAsync(info, input); public (bool IsValid, string Message) Validate( - EntryPointInfo info, TInput input) => - throw new NotSupportedException(); + EntryPointInfo info, TInput input) => (true, string.Empty); /// /// A quantum machine job with default properties. @@ -99,7 +98,7 @@ private class DefaultJob : IQuantumMachineJob public bool Failed { get; } = true; - public Uri Uri => new Uri("https://example.com/" + Id); + public Uri Uri => new Uri($"https://www.example.com/{Id}"); public Task CancelAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException(); diff --git a/src/Simulation/EntryPointDriver/Simulation.cs b/src/Simulation/EntryPointDriver/Simulation.cs index f9d533ae0e7..a1949a07d8a 100644 --- a/src/Simulation/EntryPointDriver/Simulation.cs +++ b/src/Simulation/EntryPointDriver/Simulation.cs @@ -7,6 +7,7 @@ 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 { @@ -85,10 +86,7 @@ private static async Task RunSimulator( /// 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; + DisplayWithColor(ConsoleColor.Red, Console.Error, $"The simulator '{name}' could not be found."); Console.Error.WriteLine(); Console.Error.WriteLine( $"If '{name}' is a custom simulator, it must be set in the DefaultSimulator project property:");