Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Initial Support for microsoft.simulator Targets on Jupyter Notebooks #601

Merged
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c9e0db1
wip
Jul 15, 2021
36a3eb4
... and here is where I'll leave it
Jul 15, 2021
74b30c6
more wip
Jul 16, 2021
51f3cd1
removing handle
Jul 16, 2021
9e26d74
could not find llvmlib
Jul 16, 2021
2e30505
the basics work
Jul 16, 2021
947a4ca
updating compiler package to prepare QIR generation
Jul 16, 2021
47f183d
some minor things during debugging
Jul 17, 2021
de9a651
fixing the missing reference implementations
Jul 17, 2021
bfd3e9f
working proof of concept (could be done nicer)
Jul 17, 2021
0f3ec8e
Merge remote-tracking branch 'origin/main' into cgranade/experimental…
cgranade Jan 7, 2022
6eb4d40
Update to latest package versions.
cgranade Jan 8, 2022
7eb1d0d
Add new %qir magic.
cgranade Jan 8, 2022
3430a90
Add Python support for new %qir command.
cgranade Jan 14, 2022
2d0a8bb
Added a SubmitAsync for the submitter object.
ScottCarda-MS Feb 24, 2022
f2505d9
Change from QSharpSubmitter handle to QirSubmitter handle.
ScottCarda-MS Mar 1, 2022
c32ba8a
merged in from main
ScottCarda-MS Mar 1, 2022
ae6b3dd
updated version number
ScottCarda-MS Mar 1, 2022
449971b
updated comment
ScottCarda-MS Mar 1, 2022
805cd74
updated QDK version
ScottCarda-MS Mar 4, 2022
1a49b0a
WIP for tests.
ScottCarda-MS Mar 4, 2022
8def9e1
update QDK version in AzureClient and fix unit test.
ScottCarda-MS Mar 4, 2022
fda57b2
more unit tests
ScottCarda-MS Mar 4, 2022
4b1fd03
updated unit test
ScottCarda-MS Mar 4, 2022
4fde2e5
cleaned out comment
ScottCarda-MS Mar 4, 2022
f9f47d5
Removed unnecessary usings and updated copy write messages.
ScottCarda-MS Mar 5, 2022
e3f7601
Added some documentation.
ScottCarda-MS Mar 7, 2022
4d164a5
updated todo
ScottCarda-MS Mar 7, 2022
aec5f36
Link to issue.
ScottCarda-MS Mar 8, 2022
5c84a09
some PR suggestions
ScottCarda-MS Mar 8, 2022
78ecc32
nullable QIR Stream in Entry Point
ScottCarda-MS Mar 8, 2022
889ee1b
more PR suggestions
ScottCarda-MS Mar 8, 2022
9e0f3f4
Added error handling to the microsoft.simulator case.
ScottCarda-MS Mar 8, 2022
16a89cc
Deduplicate error handling in AzureClient.cs.
Mar 9, 2022
85f0378
fixed tests
ScottCarda-MS Mar 9, 2022
de038fa
updated as_qir in the loader.py
ScottCarda-MS Mar 9, 2022
36f2ebf
changed forceQIR to generateQIR
ScottCarda-MS Mar 11, 2022
e6db0d4
Added doc comments to the mock QIR submitter.
ScottCarda-MS Mar 11, 2022
03cbc4c
comment
ScottCarda-MS Mar 11, 2022
17aa9ba
Don't check ActiveTarget in AzureClient.cs
ScottCarda-MS Mar 11, 2022
352dfdc
Added GitHub issue links to ToDos.
ScottCarda-MS Mar 11, 2022
e4d51de
removed external reference to Log
ScottCarda-MS Mar 11, 2022
e1080cf
Added EntryPointNamespaceName to CompilerService.
ScottCarda-MS Mar 12, 2022
ec54759
reworked tests
ScottCarda-MS Mar 17, 2022
75c72e3
python changes
ScottCarda-MS Mar 17, 2022
b4d075c
EntryPointNamespaceName
ScottCarda-MS Mar 18, 2022
c9a5c41
PackageName
ScottCarda-MS Mar 18, 2022
2b5b805
WIP
ScottCarda-MS Mar 18, 2022
86b8e92
Added ToDo for logic for PrepareQirGeneration to be moved to the Subm…
ScottCarda-MS Mar 18, 2022
25d1fb6
Undo PackageName
ScottCarda-MS Mar 18, 2022
36f3ef0
Added comments to Where clause.
ScottCarda-MS Mar 21, 2022
22b5d59
WIP IsQuantumExecutionTarget
ScottCarda-MS Mar 21, 2022
d37146d
Added the MicrosoftSimulator const string back in.
ScottCarda-MS Mar 21, 2022
0e0c655
merged in from main
ScottCarda-MS Mar 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 60 additions & 32 deletions src/AzureClient/AzureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
Expand All @@ -26,6 +27,8 @@ namespace Microsoft.Quantum.IQSharp.AzureClient
/// <inheritdoc/>
public class AzureClient : IAzureClient
{
private const string MicrosoftSimulator = "microsoft.simulator";
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved

/// <inheritdoc />
public Microsoft.Azure.Quantum.IWorkspace? ActiveWorkspace { get; private set; }
private TokenCredential? Credential { get; set; }
Expand All @@ -39,7 +42,10 @@ public class AzureClient : IAzureClient
private AzureExecutionTarget? ActiveTarget { get; set; }
private string MostRecentJobId { get; set; } = string.Empty;
private IEnumerable<ProviderStatusInfo>? AvailableProviders { get; set; }
private IEnumerable<TargetStatusInfo>? AvailableTargets => AvailableProviders?.SelectMany(provider => provider.Targets);
private IEnumerable<TargetStatusInfo>? AvailableTargets =>
AvailableProviders?
.SelectMany(provider => provider.Targets)
.Where(t => t.TargetId != null && (t.TargetId.StartsWith(MicrosoftSimulator) || AzureExecutionTarget.GetProvider(t.TargetId) != AzureProvider.Microsoft));
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved
private IEnumerable<TargetStatusInfo>? ValidExecutionTargets => AvailableTargets?.Where(AzureExecutionTarget.IsValid);
private string ValidExecutionTargetsDisplayText =>
(ValidExecutionTargets == null || ValidExecutionTargets.Count() == 0)
Expand Down Expand Up @@ -315,16 +321,6 @@ private async Task<ExecutionResult> SubmitOrExecuteJobAsync(
return connectionResult;
}

var machine = AzureFactory.CreateMachine(this.ActiveWorkspace, this.ActiveTarget.TargetId, this.StorageConnectionString);
if (machine == null)
{
// We should never get here, since ActiveTarget should have already been validated at the time it was set.
channel?.Stderr($"Unexpected error while preparing job for execution on target {ActiveTarget.TargetId}.");
return AzureClientError.InvalidTarget.ToExecutionResult();
}

channel?.Stdout($"Submitting {submissionContext.OperationName} to target {ActiveTarget.TargetId}...");

IEntryPoint? entryPoint;
try
{
Expand All @@ -346,33 +342,62 @@ private async Task<ExecutionResult> SubmitOrExecuteJobAsync(
return AzureClientError.InvalidEntryPoint.ToExecutionResult();
}

try
channel?.Stdout($"Submitting {submissionContext.OperationName} to target {ActiveTarget.TargetId}...");

if (this.ActiveTarget.TargetId.StartsWith(MicrosoftSimulator))
{
Logger.LogDebug("About to submit entry point for {OperationName}.", submissionContext.OperationName);
var job = await entryPoint.SubmitAsync(machine, submissionContext);
channel?.Stdout($"Job successfully submitted for {submissionContext.Shots} shots.");
var submitter = SubmitterFactory.QirSubmitter(this.ActiveTarget.TargetId, this.ActiveWorkspace, this.StorageConnectionString);
if (submitter == null)
{
// We should never get here, since ActiveTarget should have already been validated at the time it was set.
channel?.Stderr($"Unexpected error while preparing job for execution on target {ActiveTarget.TargetId}.");
return AzureClientError.InvalidTarget.ToExecutionResult();
}

var job = await entryPoint.SubmitAsync(submitter, submissionContext);
channel?.Stdout($"Job successfully submitted.");
channel?.Stdout($" Job name: {submissionContext.FriendlyName}");
channel?.Stdout($" Job ID: {job.Id}");
MostRecentJobId = job.Id;
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved
}
catch (TaskCanceledException tce)
else
{
throw tce;
}
catch (ArgumentException e)
{
var msg = $"Failed to parse all expected parameters for Q# operation {submissionContext.OperationName}.";
Logger.LogError(e, msg);
var machine = AzureFactory.CreateMachine(this.ActiveWorkspace, this.ActiveTarget.TargetId, this.StorageConnectionString);
if (machine == null)
{
// We should never get here, since ActiveTarget should have already been validated at the time it was set.
channel?.Stderr($"Unexpected error while preparing job for execution on target {ActiveTarget.TargetId}.");
return AzureClientError.InvalidTarget.ToExecutionResult();
}

channel?.Stderr(msg);
channel?.Stderr(e.Message);
return AzureClientError.JobSubmissionFailed.ToExecutionResult();
}
catch (Exception e)
{
channel?.Stderr($"Failed to submit Q# operation {submissionContext.OperationName} for execution.");
channel?.Stderr(e.InnerException?.Message ?? e.Message);
return AzureClientError.JobSubmissionFailed.ToExecutionResult();
try
{
Logger.LogDebug("About to submit entry point for {OperationName}.", submissionContext.OperationName);
var job = await entryPoint.SubmitAsync(machine, submissionContext);
channel?.Stdout($"Job successfully submitted for {submissionContext.Shots} shots.");
channel?.Stdout($" Job name: {submissionContext.FriendlyName}");
channel?.Stdout($" Job ID: {job.Id}");
MostRecentJobId = job.Id;
}
catch (TaskCanceledException tce)
{
throw tce;
}
catch (ArgumentException e)
{
var msg = $"Failed to parse all expected parameters for Q# operation {submissionContext.OperationName}.";
Logger.LogError(e, msg);

channel?.Stderr(msg);
channel?.Stderr(e.Message);
return AzureClientError.JobSubmissionFailed.ToExecutionResult();
}
catch (Exception e)
{
channel?.Stderr($"Failed to submit Q# operation {submissionContext.OperationName} for execution.");
channel?.Stderr(e.InnerException?.Message ?? e.Message);
return AzureClientError.JobSubmissionFailed.ToExecutionResult();
}
}

// If the command was not %azure.execute, simply return the job status.
Expand Down Expand Up @@ -543,7 +568,10 @@ public async Task<ExecutionResult> GetJobResultAsync(IChannel? channel, string j
// cancellation token support.
var request = WebRequest.Create(job.OutputDataUri);
using var responseStream = (await request.GetResponseAsync()).GetResponseStream();
return responseStream.ToHistogram(Logger).ToExecutionResult();
return responseStream.ToHistogram(
cgranade marked this conversation as resolved.
Show resolved Hide resolved
Logger,
isSimulatorOutput: this.ActiveTarget?.TargetId?.StartsWith(MicrosoftSimulator) ?? false)
.ToExecutionResult();
}
catch (Exception e)
{
Expand Down
2 changes: 1 addition & 1 deletion src/AzureClient/AzureClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<ItemGroup>
<PackageReference Include="Azure.Quantum.Jobs" Version="1.0.0-beta.3" />
<PackageReference Include="Microsoft.Azure.Quantum.Client" Version="0.22.186614-beta" />
<PackageReference Include="Microsoft.Azure.Quantum.Client" Version="0.23.195983" />
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.23" />
<PackageReference Include="Microsoft.Rest.ClientRuntime.Azure" Version="3.3.19" />
<PackageReference Include="System.Reactive" Version="4.3.2" />
Expand Down
10 changes: 7 additions & 3 deletions src/AzureClient/AzureExecutionTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.Quantum.IQSharp.AzureClient
{
internal enum AzureProvider { IonQ, Honeywell, QCI, Mock }
internal enum AzureProvider { IonQ, Honeywell, QCI, Microsoft, Mock }

internal class AzureExecutionTarget
{
Expand All @@ -21,13 +21,17 @@ protected AzureExecutionTarget(string? targetId)

public string? TargetId { get; }

public virtual string PackageName => $"Microsoft.Quantum.Providers.{GetProvider(TargetId)}";
public virtual string PackageName =>
GetProvider(TargetId) == AzureProvider.Microsoft
? "Microsoft.Quantum.Providers.Core"
: $"Microsoft.Quantum.Providers.{GetProvider(TargetId)}";

public RuntimeCapability RuntimeCapability => GetProvider(TargetId) switch
{
AzureProvider.IonQ => RuntimeCapability.BasicQuantumFunctionality,
AzureProvider.Honeywell => RuntimeCapability.BasicMeasurementFeedback,
AzureProvider.QCI => RuntimeCapability.BasicMeasurementFeedback,
AzureProvider.Microsoft => RuntimeCapability.FullComputation,
_ => RuntimeCapability.FullComputation
};

Expand Down Expand Up @@ -64,7 +68,7 @@ protected AzureExecutionTarget(string? targetId)
/// Valid target IDs are structured as "provider.target".
/// For example, "ionq.simulator" or "honeywell.qpu".
/// </remarks>
protected static AzureProvider? GetProvider(string? targetId)
protected internal static AzureProvider? GetProvider(string? targetId)
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved
{
if (targetId == null)
{
Expand Down
103 changes: 99 additions & 4 deletions src/AzureClient/EntryPoint/EntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Quantum.IQSharp.Jupyter;
using Microsoft.Quantum.Runtime;
using Microsoft.Quantum.Runtime.Submitters;
using Microsoft.Quantum.Simulation.Core;

namespace Microsoft.Quantum.IQSharp.AzureClient
Expand All @@ -23,6 +25,7 @@ internal class EntryPoint : IEntryPoint
private Type OutputType { get; }
private OperationInfo OperationInfo { get; }
private ILogger? Logger { get; }
public Stream QirStream { get; }
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Creates an object used to submit jobs to Azure Quantum.
Expand All @@ -34,18 +37,21 @@ internal class EntryPoint : IEntryPoint
/// <param name="outputType">Specifies the output parameter type for the
/// <see cref="EntryPointInfo{I,O}"/> object provided as the <c>entryPointInfo</c> argument.</param>
/// <param name="operationInfo">Information about the Q# operation to be used as the entry point.</param>
/// <param name="qirStream">
/// Stream from which QIR bitcode for the entry point can be read.
/// </param>
/// <param name="logger">Logger used to report internal diagnostics.</param>
public EntryPoint(object entryPointInfo, Type inputType, Type outputType, OperationInfo operationInfo, ILogger? logger)
public EntryPoint(object entryPointInfo, Type inputType, Type outputType, OperationInfo operationInfo, Stream qirStream, ILogger? logger)
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved
{
EntryPointInfo = entryPointInfo;
InputType = inputType;
OutputType = outputType;
OperationInfo = operationInfo;
Logger = logger;
QirStream = qirStream;
}

/// <inheritdoc/>
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmissionContext submissionContext, CancellationToken cancellationToken = default)
private object GetEntryPointInputObject(AzureSubmissionContext submissionContext)
{
var parameterTypes = new List<Type>();
var parameterValues = new List<object>();
Expand Down Expand Up @@ -73,12 +79,78 @@ public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmis
}
}

var entryPointInput = parameterValues.Count switch
return parameterValues.Count switch
{
0 => QVoid.Instance,
1 => parameterValues.Single(),
_ => InputType.GetConstructor(parameterTypes.ToArray()).Invoke(parameterValues.ToArray())
};
}

private ArgumentValue GetArgumentValue(string parameterValue, System.Reflection.ParameterInfo parameter)
{
var parameterType = parameter.ParameterType;

if (parameterType == typeof(bool))
{
return new ArgumentValue.Bool(Newtonsoft.Json.JsonConvert.DeserializeObject<bool>(parameterValue));
}
else if (parameterType == typeof(double))
{
return new ArgumentValue.Double(Newtonsoft.Json.JsonConvert.DeserializeObject<double>(parameterValue));
}
else if (parameterType == typeof(long))
{
return new ArgumentValue.Int(Newtonsoft.Json.JsonConvert.DeserializeObject<long>(parameterValue));
}
else if (parameterType == typeof(string))
{
return new ArgumentValue.String(parameterValue);
}
else if (parameterType == typeof(Pauli))
{
return new ArgumentValue.Pauli(Newtonsoft.Json.JsonConvert.DeserializeObject<Pauli>(parameterValue));
}
else if (parameterType == typeof(Result))
{
return new ArgumentValue.Result(Newtonsoft.Json.JsonConvert.DeserializeObject<Result>(parameterValue));
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
throw new ArgumentException($"The given type of {parameterType.Name} is not supported."); ;
}
}

private IReadOnlyList<Argument> GetEntryPointInputArguments(AzureSubmissionContext submissionContext)
{
var argumentList = new List<Argument>();
foreach (var parameter in OperationInfo.RoslynParameters)
{
if (!submissionContext.InputParameters.ContainsKey(parameter.Name))
{
throw new ArgumentException($"Required parameter {parameter.Name} was not specified.");
}

string rawParameterValue = submissionContext.InputParameters[parameter.Name];
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved

try
{
var argument = new Argument(parameter.Name, GetArgumentValue(rawParameterValue, parameter));
argumentList.Add(argument);
}
catch (Exception e)
{
throw new ArgumentException($"The value {rawParameterValue} provided for parameter {parameter.Name} could not be converted to the expected type: {e.Message}");
}
}

return argumentList;
}

/// <inheritdoc/>
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmissionContext submissionContext, CancellationToken cancellationToken = default)
{
var entryPointInput = GetEntryPointInputObject(submissionContext);

try
{
Expand All @@ -104,5 +176,28 @@ public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmis
var submitParameters = new object[] { EntryPointInfo, entryPointInput, submissionContext };
return (Task<IQuantumMachineJob>)submitMethod.Invoke(machine, submitParameters);
}

public Task<IQuantumMachineJob> SubmitAsync(IQirSubmitter submitter, AzureSubmissionContext submissionContext)
{
var entryPointInput = GetEntryPointInputArguments(submissionContext);

var options = SubmissionOptions.Default;
options = options.With(submissionContext.FriendlyName, submissionContext.Shots, submissionContext.InputParams);

// Find and invoke the method on IQirSubmitter that is declared as:
// Task<IQuantumMachineJob> SubmitAsync(Stream qir, string entryPoint, IReadOnlyList<Argument> arguments, SubmissionOptions submissionOptions)
var submitMethod = typeof(IQirSubmitter)
.GetMethods()
.Single(method =>
method.Name == "SubmitAsync"
&& method.GetParameters().Length == 4
&& method.GetParameters()[0].ParameterType == typeof(Stream)
&& method.GetParameters()[1].ParameterType == typeof(string)
&& method.GetParameters()[2].ParameterType == typeof(IReadOnlyList<Argument>)
&& method.GetParameters()[3].ParameterType == typeof(SubmissionOptions)
);
var submitParameters = new object[] { QirStream, $"ENTRYPOINT__{submissionContext.OperationName}", entryPointInput, options };
return (Task<IQuantumMachineJob>)submitMethod.Invoke(submitter, submitParameters);
}
}
}
7 changes: 4 additions & 3 deletions src/AzureClient/EntryPoint/EntryPointGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public EntryPointGenerator(
}

public IEntryPoint Generate(string operationName, string? executionTarget,
RuntimeCapability? runtimeCapability = null)
RuntimeCapability? runtimeCapability = null, bool forceQir = false)
ScottCarda-MS marked this conversation as resolved.
Show resolved Hide resolved
{
Logger?.LogDebug($"Generating entry point: operationName={operationName}, executionTarget={executionTarget}");

Expand Down Expand Up @@ -152,7 +152,8 @@ public IEntryPoint Generate(string operationName, string? executionTarget,
}

EntryPointAssemblyInfo = Compiler.BuildEntryPoint(
operationInfo, compilerMetadata, logger, Path.Combine(Workspace.CacheFolder, "__entrypoint__.dll"), executionTarget, runtimeCapability);
operationInfo, compilerMetadata, logger, Path.Combine(Workspace.CacheFolder, "__entrypoint__.dll"), executionTarget, runtimeCapability,
forceQir: forceQir);
if (EntryPointAssemblyInfo == null || logger.HasErrors)
{
Logger?.LogError($"Error compiling entry point for operation {operationName}.");
Expand Down Expand Up @@ -206,7 +207,7 @@ public IEntryPoint Generate(string operationName, string? executionTarget,
.Invoke(new object[] { entryPointOperationInfo.RoslynType });

return new EntryPoint(
entryPointInfo, entryPointInputType, entryPointOutputType, entryPointOperationInfo,
entryPointInfo, entryPointInputType, entryPointOutputType, entryPointOperationInfo, EntryPointAssemblyInfo.QirBitcode,
logger: ServiceProvider.GetService<ILogger<EntryPoint>>()
);
}
Expand Down
6 changes: 6 additions & 0 deletions src/AzureClient/EntryPoint/IEntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Quantum.Runtime;
using Microsoft.Quantum.Runtime.Submitters;

namespace Microsoft.Quantum.IQSharp.AzureClient
{
Expand All @@ -26,5 +28,9 @@ public interface IEntryPoint
/// <param name="cancellationToken">Cancellation token used to interrupt this submission.</param>
/// <returns>The details of the submitted job.</returns>
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmissionContext submissionContext, CancellationToken cancellationToken = default);

public Task<IQuantumMachineJob> SubmitAsync(IQirSubmitter submitter, AzureSubmissionContext submissionContext);

public Stream QirStream { get; }
}
}
Loading