-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Creating CLI extension for Azure Quantum #1879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
9ddc0e4
6663ec4
43c4bd7
739e015
75410f3
000e8c4
a5eb2e1
d389d1e
aa8a4b2
91e8e99
1d3f7f8
cdd3c07
b6c5cf6
e5b5aa4
d092bb3
8446cd8
485a152
ff55bea
41d8837
14909d0
dd5d5d1
912ce6f
0b230ee
6445133
6325cb0
189b388
e334f5c
4b2d993
3cd44d9
097e3d0
ac57090
184f269
6166a86
9d3cd76
2d8813b
172e848
17b5ff2
4b13bb6
f5ee1c0
2113afa
0be66ff
ddc6450
efcd2dd
a8b84d5
5ae53c4
b4879b5
407a80c
2b8da4d
150e7e6
d0aaa9e
815a6f9
ccadf4a
eae418f
0b05b48
f596766
b0bafff
8e83036
e3775db
ffa92d6
42ee982
2e47a48
dca48d8
a34aa0f
70e095c
55cabc5
fdfae06
76c53fd
b6da8f1
91e7224
350d952
931a28c
dd71ef3
090c3fe
cd42ead
46a61c2
847fd48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| .. :changelog: | ||
| Release History | ||
| =============== | ||
|
|
||
| 0.11.2906.2 | ||
ricardo-espinoza marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ++++++ | ||
| * Initial release. Version intended to work with Azure Quantum Private Preview | ||
| and with QDK version 0.11.2906.* | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| Microsoft Azure CLI 'quantum' Extension | ||
anpaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ========================================== | ||
|
|
||
| Azure Quantum is the first open Quantum computing platform. It offers a range of services | ||
| from quantum hardware to full-state simulators and quantum inspired optimizations, | ||
| providing developers and customers access to the most competitive quantum offering | ||
| on the market. | ||
|
|
||
| To learn more about azure quantum visit: | ||
ricardo-espinoza marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| https://azure.microsoft.com/en-us/services/quantum/ | ||
|
|
||
| To learn more about quantum computing and Microsoft's Quantum Development Kit visit: | ||
ricardo-espinoza marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| https://docs.microsoft.com/quantum/ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
| from azure.cli.core import AzCommandsLoader | ||
|
|
||
| import azext_quantum._help # pylint: disable=unused-import | ||
|
|
||
|
|
||
| class QuantumCommandsLoader(AzCommandsLoader): | ||
|
|
||
| def __init__(self, cli_ctx=None): | ||
| super(QuantumCommandsLoader, self).__init__(cli_ctx=cli_ctx) | ||
|
|
||
| def load_command_table(self, args): | ||
| from azext_quantum.commands import load_command_table | ||
| load_command_table(self, args) | ||
| return self.command_table | ||
|
|
||
| def load_arguments(self, command): | ||
| from azext_quantum._params import load_arguments | ||
| load_arguments(self, command) | ||
|
|
||
|
|
||
| COMMAND_LOADER_CLS = QuantumCommandsLoader |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
| # pylint: disable=line-too-long | ||
|
|
||
| import os | ||
|
|
||
|
|
||
| def is_env(name): | ||
| return 'AZURE_QUANTUM_ENV' in os.environ and os.environ['AZURE_QUANTUM_ENV'] == name | ||
|
|
||
|
|
||
| def base_url(): | ||
| if 'AZURE_QUANTUM_BASEURL' in os.environ: | ||
| return os.environ['AZURE_QUANTUM_BASEURL'] | ||
| if is_env('canary'): | ||
| return "https://app-jobs-canarysouthcentralus.azurewebsites.net/" | ||
| return "https://app-jobscheduler-prod.azurewebsites.net/" | ||
anpaz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| def _get_data_credentials(cli_ctx, subscription_id=None): | ||
| from azure.cli.core._profile import Profile | ||
| profile = Profile(cli_ctx=cli_ctx) | ||
| creds, _, _ = profile.get_login_credentials(subscription_id=subscription_id, resource="https://quantum.microsoft.com") | ||
anpaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return creds | ||
|
|
||
|
|
||
| def cf_quantum(cli_ctx, subscription_id=None, resource_group_name=None, workspace_name=None): | ||
| from .vendored_sdks.azure_quantum import QuantumClient | ||
| creds = _get_data_credentials(cli_ctx, subscription_id) | ||
| return QuantumClient(creds, subscription_id, resource_group_name, workspace_name, base_url=base_url()) | ||
|
|
||
|
|
||
| def cf_quantum_mgmt(cli_ctx, *_): | ||
| from azure.cli.core.commands.client_factory import get_mgmt_service_client | ||
| from .vendored_sdks.azure_mgmt_quantum import QuantumManagementClient | ||
| return get_mgmt_service_client(cli_ctx, QuantumManagementClient) | ||
|
|
||
|
|
||
| def cf_workspaces(cli_ctx, *_): | ||
| return cf_quantum_mgmt(cli_ctx).workspaces | ||
|
|
||
|
|
||
| def cf_providers(cli_ctx, subscription_id=None, resource_group_name=None, workspace_name=None): | ||
| return cf_quantum(cli_ctx, subscription_id, resource_group_name, workspace_name).providers | ||
|
|
||
|
|
||
| def cf_jobs(cli_ctx, subscription_id=None, resource_group_name=None, workspace_name=None): | ||
| return cf_quantum(cli_ctx, subscription_id, resource_group_name, workspace_name).jobs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # coding=utf-8 | ||
ricardo-espinoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
| from knack.help_files import helps # pylint: disable=unused-import | ||
|
|
||
| helps['quantum'] = """ | ||
| type: group | ||
| short-summary: Manage Azure Quantum Workspaces and submit jobs to Azure Quantum Providers. | ||
anpaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
|
|
||
| helps['quantum job'] = """ | ||
| type: group | ||
| short-summary: Manage jobs for Azure Quantum. | ||
| """ | ||
|
|
||
| helps['quantum target'] = """ | ||
| type: group | ||
| short-summary: Manage execution targets for Azure Quantum workspaces. | ||
| """ | ||
|
|
||
| helps['quantum workspace'] = """ | ||
| type: group | ||
| short-summary: Manage Azure Quantum workspaces. | ||
| """ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Azure CLI's convention, we create help entry for each command group and command. We put examples in command's help. For example,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Thanks for the suggestion. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
| # pylint: disable=line-too-long | ||
|
|
||
| from knack.arguments import CLIArgumentType | ||
|
|
||
|
|
||
| def load_arguments(self, _): | ||
| workspace_name_type = CLIArgumentType(options_list=['--workspace-name', '-w'], help='Name of the Quantum Workspace. You can configure the default workspace using `az quantum workspace set`.', id_part=None, required=False) | ||
| program_args = CLIArgumentType(nargs='*', help='List of arguments expected by the Q# operation specified as --name=value after `--`.') | ||
anpaz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| target_id = CLIArgumentType(options_list=['--target-id', '-t'], help='Target id.') | ||
| project = CLIArgumentType(help='The location of the Q# project to submit. Defaults to current folder.') | ||
| job_name = CLIArgumentType(help='A friendly name to give to this execution of the program.') | ||
| shots = CLIArgumentType(help='The number of times to execute the Q# program on the given target.') | ||
| no_build = CLIArgumentType(help='If specified, the Q# program is not built before submitting.') | ||
|
|
||
| with self.argument_context('quantum workspace') as c: | ||
| c.argument('workspace_name', workspace_name_type) | ||
|
|
||
| with self.argument_context('quantum target') as c: | ||
| c.argument('workspace_name', workspace_name_type) | ||
| c.argument('target_id', options_list=['--target-id', '-t'], help='Target id.') | ||
|
|
||
| with self.argument_context('quantum job') as c: | ||
| c.argument('workspace_name', workspace_name_type) | ||
| c.argument('job_id', options_list=['--job-id', '-id'], help='Job id.') | ||
| c.argument('target_id', target_id) | ||
| c.argument('project', project) | ||
| c.argument('job_name', job_name) | ||
| c.argument('shots', shots) | ||
| c.argument('no_build', no_build) | ||
|
|
||
| with self.argument_context('quantum job submit') as c: | ||
| c.positional('program_args', program_args) | ||
|
|
||
| with self.argument_context('quantum execute') as c: | ||
| c.argument('workspace_name', workspace_name_type) | ||
| c.argument('target_id', target_id) | ||
| c.argument('project', project) | ||
| c.argument('job_name', job_name) | ||
| c.argument('shots', shots) | ||
| c.argument('no_build', no_build) | ||
| c.positional('program_args', program_args) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
| # pylint: disable=line-too-long | ||
|
|
||
| import os | ||
|
|
||
| from .operations.workspace import WorkspaceInfo | ||
| from .operations.target import TargetInfo | ||
|
|
||
|
|
||
| def validate_workspace_info(cmd, namespace): | ||
| """ | ||
| Makes sure all parameters for a workspace are available. | ||
| """ | ||
| group = getattr(namespace, 'resource_group_name', None) | ||
| name = getattr(namespace, 'workspace_name', None) | ||
| ws = WorkspaceInfo(cmd, group, name) | ||
|
|
||
| if not ws.subscription: | ||
anpaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| raise ValueError("Missing subscription argument") | ||
ricardo-espinoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if not ws.resource_group: | ||
| raise ValueError("Missing resource-group argument") | ||
| if not ws.name: | ||
| raise ValueError("Missing workspace-name argument") | ||
|
|
||
|
|
||
| def validate_target_info(cmd, namespace): | ||
| """ | ||
| Makes sure all parameters for a target are available. | ||
| """ | ||
| target_id = getattr(namespace, 'target_id', None) | ||
| target = TargetInfo(cmd, target_id) | ||
|
|
||
| if not target.target_id: | ||
anpaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| raise ValueError("Missing target-id argument") | ||
|
|
||
|
|
||
| def validate_workspace_and_target_info(cmd, namespace): | ||
| """ | ||
| Makes sure all parameters for both, a workspace and a target are available. | ||
| """ | ||
| validate_workspace_info(cmd, namespace) | ||
| validate_target_info(cmd, namespace) | ||
|
|
||
| # For the time being (Private Preview), we also need the AZURE_QUANTUM_STORAGE env variable populated | ||
| # with the Azure Storage connection string to use to upload the program. | ||
| if 'AZURE_QUANTUM_STORAGE' not in os.environ: | ||
anpaz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| raise ValueError(f"Please set the AZURE_QUANTUM_STORAGE environment variable with an Azure Storage's connection string.") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "azext.isPreview": true, | ||
| "azext.minCliCoreVersion": "2.5.1" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
| # pylint: disable=line-too-long | ||
|
|
||
| from collections import OrderedDict | ||
| from azure.cli.core.commands import CliCommandType | ||
| from ._validators import validate_workspace_info, validate_target_info, validate_workspace_and_target_info | ||
|
|
||
|
|
||
| def transform_targets(providers): | ||
| def one(provider, target): | ||
| return OrderedDict([ | ||
| ('Provider', provider), | ||
| ('Target-id', target['id']), | ||
| ('Current Availability', target['currentAvailability']), | ||
| ('Average Queue Time', target['averageQueueTime']) | ||
| ]) | ||
|
|
||
| return [ | ||
| one(provider['id'], target) | ||
| for provider in providers | ||
| for target in provider['targets'] | ||
| ] | ||
|
|
||
|
|
||
| def transform_job(result): | ||
| result = OrderedDict([ | ||
| ('Id', result['id']), | ||
| ('Status', result['status']), | ||
| ('Target', result['target']), | ||
| ('Submission time', result['creationTime']), | ||
| ('Completion time', result['endExecutionTime']) | ||
| ]) | ||
| return result | ||
|
|
||
|
|
||
| def transform_jobs(results): | ||
| def creation(job): | ||
| return job['creationTime'] | ||
|
|
||
| return [transform_job(job) for job in sorted(results, key=creation, reverse=True)] | ||
|
|
||
|
|
||
| def transform_output(results): | ||
| def one(key, value): | ||
| repeat = round(20 * value) | ||
| barra = "\u2588" * repeat | ||
anpaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return OrderedDict([ | ||
| ('Result', key), | ||
| ('Frequency', f"{value:10.8f}"), | ||
| ('', f"\u2590{barra:<22} |"), | ||
| ]) | ||
|
|
||
| if 'Histogram' in results: | ||
| histogram = results['Histogram'] | ||
| # The Histogram serialization is odd entries are key and even entries values | ||
| # Make sure we have even entries | ||
| if (len(histogram) % 2) == 0: | ||
| table = [] | ||
| items = range(0, len(histogram), 2) | ||
| for i in items: | ||
| key = histogram[i] | ||
| value = histogram[i + 1] | ||
| table.append(one(key, value)) | ||
| return table | ||
|
|
||
| elif 'histogram' in results: | ||
| histogram = results['histogram'] | ||
| return [one(key, histogram[key]) for key in histogram] | ||
|
|
||
| return results | ||
|
|
||
|
|
||
| def load_command_table(self, _): | ||
|
|
||
| workspace_ops = CliCommandType(operations_tmpl='azext_quantum.operations.workspace#{}') | ||
| job_ops = CliCommandType(operations_tmpl='azext_quantum.operations.job#{}') | ||
| target_ops = CliCommandType(operations_tmpl='azext_quantum.operations.target#{}') | ||
|
|
||
| with self.command_group('quantum workspace', workspace_ops) as w: | ||
| w.command('list', 'list') | ||
| w.command('show', 'show', validator=validate_workspace_info) | ||
| w.command('set', 'set', validator=validate_workspace_info) | ||
| w.command('clear', 'clear') | ||
|
|
||
| with self.command_group('quantum target', target_ops) as w: | ||
anpaz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| w.command('list', 'list', validator=validate_workspace_info, table_transformer=transform_targets) | ||
| w.command('show', 'show', validator=validate_target_info) | ||
| w.command('set', 'set', validator=validate_target_info) | ||
| w.command('clear', 'clear') | ||
|
|
||
| with self.command_group('quantum job', job_ops) as j: | ||
| j.command('list', 'list', validator=validate_workspace_info, table_transformer=transform_jobs) | ||
| j.command('show', 'show', validator=validate_workspace_info, table_transformer=transform_job) | ||
| j.command('submit', 'submit', validator=validate_workspace_and_target_info, table_transformer=transform_job) | ||
| j.command('wait', 'wait', validator=validate_workspace_info, table_transformer=transform_job) | ||
| j.command('output', 'output', validator=validate_workspace_info, table_transformer=transform_output) | ||
|
|
||
| with self.command_group('quantum', job_ops, is_preview=True) as q: | ||
| q.command('execute', 'execute', validator=validate_workspace_and_target_info, table_transformer=transform_output) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- |
Uh oh!
There was an error while loading. Please reload this page.