This repository was archived by the owner on Jan 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
Initial IQ# magic command implementation for Azure Quantum #144
Merged
rmshaffer
merged 17 commits into
feature/azure-quantum-preview
from
rmshaffer/azure-magic
May 20, 2020
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
d43f74b
Add IAzureClient definition and initial magic commands
rmshaffer fb1b23d
Add AzureClient assembly to manifest
rmshaffer 328b64d
Add base class with common functionality for AzureClient magic commands
rmshaffer ccd366f
Initial implementation of AzureClient methods
rmshaffer 2ea0041
Move AbstractMagic to Jupyter project
rmshaffer 6032442
Add key=value input parameter parsing to AbstractMagic
rmshaffer 5b3511c
Merge branch 'rmshaffer/abstract-magic' into rmshaffer/azure-connect-…
rmshaffer b5fd232
Initial qsharp.azure Python implementation
rmshaffer 134eb70
Begin to unify magic command parsing
rmshaffer a85c62a
Minor updates to a few magics
rmshaffer 74f0909
Merge branch 'feature/azure-quantum-preview' into rmshaffer/azure-magic
rmshaffer cacb452
Integrate latest Azure.Quantum.Client package
rmshaffer e067373
Merge branch 'feature/azure-quantum-preview' into rmshaffer/azure-magic
rmshaffer 5386fdd
Minor cleanup
rmshaffer e8bcad4
Merge branch 'feature/azure-quantum-preview' into rmshaffer/azure-magic
rmshaffer e7efc5e
Improvements to address PR comments
rmshaffer 6296c79
Minor change to fix syntax highlighting
rmshaffer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,222 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #nullable enable | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Text; | ||
| using Microsoft.Jupyter.Core; | ||
| using System.Linq; | ||
| using System.IO; | ||
| using System.Threading.Tasks; | ||
|
|
||
| using Microsoft.Azure.Quantum.Client; | ||
| using Microsoft.Identity.Client; | ||
| using Microsoft.Identity.Client.Extensions.Msal; | ||
| using System.Linq; | ||
| using System.IO; | ||
| using Microsoft.Quantum.Runtime; | ||
| using Microsoft.Jupyter.Core; | ||
| using Microsoft.Quantum.Simulation.Core; | ||
|
|
||
| namespace Microsoft.Quantum.IQSharp.AzureClient | ||
| { | ||
| /// <inheritdoc/> | ||
| public class AzureClient : IAzureClient | ||
| { | ||
| private string ConnectionString { get; set; } = string.Empty; | ||
| private string ActiveTargetName { get; set; } = string.Empty; | ||
| private AuthenticationResult? AuthenticationResult { get; set; } | ||
| private IQuantumClient? QuantumClient { get; set; } | ||
| private Azure.Quantum.Workspace? ActiveWorkspace { get; set; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> ConnectAsync( | ||
| IChannel channel, | ||
| string subscriptionId, | ||
| string resourceGroupName, | ||
| string workspaceName, | ||
| string storageAccountConnectionString, | ||
| bool forceLoginPrompt = false) | ||
| { | ||
| ConnectionString = storageAccountConnectionString; | ||
|
|
||
| var clientId = "84ba0947-6c53-4dd2-9ca9-b3694761521b"; // Microsoft Quantum Development Kit | ||
| var authority = "https://login.microsoftonline.com/common"; | ||
| var msalApp = PublicClientApplicationBuilder.Create(clientId).WithAuthority(authority).Build(); | ||
|
|
||
| // Register the token cache for serialization | ||
| var cacheFileName = "aad.bin"; | ||
| var cacheDirectoryEnvVarName = "AZURE_QUANTUM_TOKEN_CACHE"; | ||
| var cacheDirectory = System.Environment.GetEnvironmentVariable(cacheDirectoryEnvVarName); | ||
| if (string.IsNullOrEmpty(cacheDirectory)) | ||
| { | ||
| cacheDirectory = Path.Join(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), ".azure-quantum"); | ||
| } | ||
|
|
||
| var storageCreationProperties = new StorageCreationPropertiesBuilder(cacheFileName, cacheDirectory, clientId).Build(); | ||
| var cacheHelper = await MsalCacheHelper.CreateAsync(storageCreationProperties); | ||
| cacheHelper.RegisterCache(msalApp.UserTokenCache); | ||
|
|
||
| var scopes = new List<string>() { "https://quantum.microsoft.com/Jobs.ReadWrite" }; | ||
|
|
||
| bool shouldShowLoginPrompt = forceLoginPrompt; | ||
| if (!shouldShowLoginPrompt) | ||
| { | ||
| try | ||
| { | ||
| var accounts = await msalApp.GetAccountsAsync(); | ||
| AuthenticationResult = await msalApp.AcquireTokenSilent( | ||
| scopes, accounts.FirstOrDefault()).WithAuthority(msalApp.Authority).ExecuteAsync(); | ||
| } | ||
| catch (MsalUiRequiredException) | ||
| { | ||
| shouldShowLoginPrompt = true; | ||
| } | ||
| } | ||
|
|
||
| if (shouldShowLoginPrompt) | ||
| { | ||
| AuthenticationResult = await msalApp.AcquireTokenWithDeviceCode(scopes, | ||
| deviceCodeResult => | ||
| { | ||
| channel.Stdout(deviceCodeResult.Message); | ||
| return Task.FromResult(0); | ||
| }).WithAuthority(msalApp.Authority).ExecuteAsync(); | ||
| } | ||
|
|
||
| if (AuthenticationResult == null) | ||
| { | ||
| return AzureClientError.AuthenticationFailed.ToExecutionResult(); | ||
| } | ||
|
|
||
| var credentials = new Rest.TokenCredentials(AuthenticationResult.AccessToken); | ||
| QuantumClient = new QuantumClient(credentials) | ||
| { | ||
| SubscriptionId = subscriptionId, | ||
| ResourceGroupName = resourceGroupName, | ||
| WorkspaceName = workspaceName | ||
| }; | ||
| ActiveWorkspace = new Azure.Quantum.Workspace( | ||
| QuantumClient.SubscriptionId, QuantumClient.ResourceGroupName, | ||
| QuantumClient.WorkspaceName, AuthenticationResult?.AccessToken); | ||
|
|
||
| try | ||
| { | ||
| var jobsList = await QuantumClient.Jobs.ListAsync(); | ||
| channel.Stdout($"Successfully connected to Azure Quantum workspace {workspaceName}."); | ||
| } | ||
| catch (Exception e) | ||
| { | ||
| channel.Stderr(e.ToString()); | ||
| return AzureClientError.WorkspaceNotFound.ToExecutionResult(); | ||
| } | ||
|
|
||
| return QuantumClient.ToJupyterTable().ToExecutionResult(); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> PrintConnectionStatusAsync(IChannel channel) => | ||
| QuantumClient == null | ||
| ? AzureClientError.NotConnected.ToExecutionResult() | ||
| : QuantumClient.ToJupyterTable().ToExecutionResult(); | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> SubmitJobAsync( | ||
| IChannel channel, | ||
| IOperationResolver operationResolver, | ||
| string operationName) | ||
| { | ||
| if (ActiveWorkspace == null) | ||
| { | ||
| channel.Stderr("Please call %connect before submitting a job."); | ||
| return AzureClientError.NotConnected.ToExecutionResult(); | ||
| } | ||
|
|
||
| if (ActiveTargetName == null) | ||
| { | ||
| channel.Stderr("Please call %target before submitting a job."); | ||
| return AzureClientError.NoTarget.ToExecutionResult(); | ||
| } | ||
|
|
||
| if (string.IsNullOrEmpty(operationName)) | ||
| { | ||
| channel.Stderr("Please pass a valid Q# operation name to %submit."); | ||
| return AzureClientError.NoOperationName.ToExecutionResult(); | ||
| } | ||
|
|
||
| var operationInfo = operationResolver.Resolve(operationName); | ||
| var entryPointInfo = new EntryPointInfo<QVoid, Result>(operationInfo.RoslynType); | ||
| var entryPointInput = QVoid.Instance; | ||
| var machine = Azure.Quantum.QuantumMachineFactory.CreateMachine(ActiveWorkspace, ActiveTargetName, ConnectionString); | ||
| if (machine == null) | ||
| { | ||
| channel.Stderr($"Could not find an execution target for target {ActiveTargetName}."); | ||
| return AzureClientError.NoTarget.ToExecutionResult(); | ||
rmshaffer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| var job = await machine.SubmitAsync(entryPointInfo, entryPointInput); | ||
| return job.ToJupyterTable().ToExecutionResult(); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> SetActiveTargetAsync( | ||
| IChannel channel, | ||
| string targetName) | ||
| { | ||
| // TODO: Validate that this target name is valid in the workspace. | ||
| ActiveTargetName = targetName; | ||
| return $"Active target is now {ActiveTargetName}".ToExecutionResult(); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> PrintTargetListAsync( | ||
| IChannel channel) | ||
| { | ||
| if (QuantumClient == null) | ||
| { | ||
| channel.Stderr("Please call %connect before listing targets."); | ||
| return AzureClientError.NotConnected.ToExecutionResult(); | ||
| } | ||
|
|
||
| var providersStatus = await QuantumClient.Providers.GetStatusAsync(); | ||
| return providersStatus.ToJupyterTable().ToExecutionResult(); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> PrintJobStatusAsync( | ||
| IChannel channel, | ||
| string jobId) | ||
| { | ||
| if (QuantumClient == null) | ||
| { | ||
| channel.Stderr("Please call %connect before getting job status."); | ||
| return AzureClientError.NotConnected.ToExecutionResult(); | ||
| } | ||
|
|
||
| var jobDetails = await QuantumClient.Jobs.GetAsync(jobId); | ||
| if (jobDetails == null) | ||
| { | ||
| channel.Stderr($"Job ID {jobId} not found in current Azure Quantum workspace."); | ||
| return AzureClientError.JobNotFound.ToExecutionResult(); | ||
| } | ||
|
|
||
| return jobDetails.ToJupyterTable().ToExecutionResult(); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<ExecutionResult> PrintJobListAsync( | ||
| IChannel channel) | ||
| { | ||
| if (QuantumClient == null) | ||
| { | ||
| channel.Stderr("Please call %connect before listing jobs."); | ||
| return AzureClientError.NotConnected.ToExecutionResult(); | ||
| } | ||
|
|
||
| var jobsList = await QuantumClient.Jobs.ListAsync(); | ||
| if (jobsList == null || jobsList.Count() == 0) | ||
| { | ||
| channel.Stderr("No jobs found in current Azure Quantum workspace."); | ||
| return AzureClientError.JobNotFound.ToExecutionResult(); | ||
| } | ||
|
|
||
| return jobsList.ToJupyterTable().ToExecutionResult(); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.