Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/quantum/azext_quantum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=import-outside-toplevel
from azure.cli.core import AzCommandsLoader

import azext_quantum._help # pylint: disable=unused-import


class QuantumCommandsLoader(AzCommandsLoader):

def __init__(self, cli_ctx=None):
Expand Down
10 changes: 9 additions & 1 deletion src/quantum/azext_quantum/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,49 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=import-outside-toplevel,line-too-long
# 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/"


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")
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
1 change: 1 addition & 0 deletions src/quantum/azext_quantum/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

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 `--`.')
Expand Down
4 changes: 2 additions & 2 deletions src/quantum/azext_quantum/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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.
Expand Down Expand Up @@ -46,6 +47,5 @@ def validate_workspace_and_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 not 'AZURE_QUANTUM_STORAGE' in os.environ:
if 'AZURE_QUANTUM_STORAGE' not in os.environ:
raise ValueError(f"Please set the AZURE_QUANTUM_STORAGE environment variable with an Azure Storage's connection string.")

9 changes: 4 additions & 5 deletions src/quantum/azext_quantum/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def one(provider, target):
for target in provider['targets']
]


def transform_job(result):
result = OrderedDict([
('Id', result['id']),
Expand All @@ -35,6 +36,7 @@ def transform_job(result):
])
return result


def transform_jobs(results):
def creation(job):
return job['creationTime']
Expand All @@ -61,7 +63,7 @@ def one(key, value):
items = range(0, len(histogram), 2)
for i in items:
key = histogram[i]
value = histogram[i+1]
value = histogram[i + 1]
table.append(one(key, value))
return table

Expand All @@ -80,25 +82,22 @@ def load_command_table(self, _):

with self.command_group('quantum workspace', workspace_ops) as w:
w.command('list', 'list')
w.command('show', 'show', validator=validate_workspace_info) ## TODO: argument list/help
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:
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)
11 changes: 7 additions & 4 deletions src/quantum/azext_quantum/operations/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=import-outside-toplevel
# pylint: disable=redefined-builtin

import logging

Expand All @@ -15,7 +15,7 @@

logger = logging.getLogger(__name__)

# pylint: disable=redefined-builtin

def list(cmd, resource_group_name=None, workspace_name=None):
"""
Returns the list of jobs in a Quantum Workspace.
Expand Down Expand Up @@ -112,6 +112,7 @@ def _generate_submit_args(program_args, ws, target, token, project, job_name, sh

return args


def submit(cmd, program_args, resource_group_name=None, workspace_name=None, target_id=None, project=None,
job_name=None, shots=None, no_build=False):
"""
Expand Down Expand Up @@ -158,6 +159,7 @@ def _parse_blob_url(url):
"sas_token": sas_token
}


def output(cmd, job_id, resource_group_name=None, workspace_name=None):
""" Returns back the results of a Q# execution """
import tempfile
Expand All @@ -168,9 +170,9 @@ def output(cmd, job_id, resource_group_name=None, workspace_name=None):
path = os.path.join(tempfile.gettempdir(), job_id)

if os.path.exists(path):
logger.debug(f"Using existing blob from {path}")
logger.debug("Using existing blob from %s", path)
else:
logger.debug(f"Downloading job results blob into {path}")
logger.debug("Downloading job results blob into %s", path)

info = WorkspaceInfo(cmd, resource_group_name, workspace_name)
client = cf_jobs(cmd.cli_ctx, info.subscription, info.resource_group, info.name)
Expand Down Expand Up @@ -217,6 +219,7 @@ def has_completed(job):

return job


def execute(cmd, program_args, resource_group_name=None, workspace_name=None, target_id=None, project=None,
job_name=None, shots=None, no_build=False):
"""
Expand Down
13 changes: 8 additions & 5 deletions src/quantum/azext_quantum/operations/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=import-outside-toplevel,line-too-long,redefined-builtin
# pylint: disable=line-too-long,redefined-builtin

from .._client_factory import cf_providers
from .workspace import WorkspaceInfo


class TargetInfo(object):
def __init__(self, cmd, target_id=None):

def select_value(key, value):
if not value is None:
if value is not None:
return value
value = cmd.cli_ctx.config.get('quantum', key, None)
if not value is None:
if value is not None:
return value
value = cmd.cli_ctx.config.get(cmd.cli_ctx.config.defaults_section_name, key, None)
if not value is None:
return value
return value

self.target_id = select_value('target_id', target_id)

Expand All @@ -40,6 +40,7 @@ def show(cmd, target_id=None):
info = TargetInfo(cmd, target_id)
return info


def set(cmd, target_id=None):
"""
Selects the default target to use when submitting jobs to Azure Quantum.
Expand All @@ -49,6 +50,7 @@ def set(cmd, target_id=None):
info.save(cmd)
return info


def list(cmd, resource_group_name=None, workspace_name=None):
"""
Returns the list of providers and their targets in a Quantum Workspace.
Expand All @@ -57,6 +59,7 @@ def list(cmd, resource_group_name=None, workspace_name=None):
client = cf_providers(cmd.cli_ctx, info.subscription, info.resource_group, info.name)
return client.get_status()


def clear(cmd):
"""
Unsets the default target-id.
Expand Down
15 changes: 9 additions & 6 deletions src/quantum/azext_quantum/operations/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=import-outside-toplevel,line-too-long,redefined-builtin
# pylint: disable=line-too-long,redefined-builtin

from knack.util import CLIError

from .._client_factory import cf_workspaces


class WorkspaceInfo(object):
def __init__(self, cmd, resource_group_name=None, workspace_name=None):
from azure.cli.core.commands.client_factory import get_subscription_id
Expand All @@ -18,14 +19,13 @@ def __init__(self, cmd, resource_group_name=None, workspace_name=None):
# then it checks if the key exists in the 'quantum' section in config, and uses that if available.
# finally, it checks in the 'global' section in the config.
def select_value(key, value):
if not value is None:
if value is not None:
return value
value = cmd.cli_ctx.config.get('quantum', key, None)
if not value is None:
if value is not None:
return value
value = cmd.cli_ctx.config.get(cmd.cli_ctx.config.defaults_section_name, key, None)
if not value is None:
return value
return value

self.subscription = get_subscription_id(cmd.cli_ctx)
self.resource_group = select_value('group', resource_group_name)
Expand All @@ -51,6 +51,7 @@ def list(cmd, resource_group_name=None, tag=None, location=None):
from azure.cli.command_modules.resource.custom import list_resources
return list_resources(cmd, resource_group_name=resource_group_name, resource_type="Microsoft.Quantum/Workspaces", tag=tag, location=location)


def show(cmd, resource_group_name=None, workspace_name=None):
"""
Returns the details of the given (or current) Quantum Workspace.
Expand All @@ -62,6 +63,7 @@ def show(cmd, resource_group_name=None, workspace_name=None):
ws = client.get(info.resource_group, info.name)
return ws


def set(cmd, workspace_name, resource_group_name=None):
"""
Sets the default Quantum Workspace.
Expand All @@ -71,7 +73,8 @@ def set(cmd, workspace_name, resource_group_name=None):
ws = client.get(info.resource_group, info.name)
if ws:
info.save(cmd)
return ws
return ws


def clear(cmd):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def test_submit_args(self):
self.assertIn("--job-name", args)
self.assertIn("--shots", args)

def test_parse_blob_url(self):
def test_parse_blob_url(self):
sas = "sv=2018-03-28&sr=c&sig=some-sig&sp=racwl"
url = f"https://getest2.blob.core.windows.net/qio/rawOutputData?{sas}"
args = _parse_blob_url(url)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


class QuantumScenarioTest(ScenarioTest):

def test_targets(self):
# Since azure quantum is still in private preview, we require
# these tests to run in a specific subscription (AzureQuantum-test)
Expand All @@ -28,7 +28,7 @@ def test_targets(self):

# clear current target
self.cmd(f'az quantum target clear')

# list
targets = self.cmd('az quantum target list -o json').get_output_in_json()
assert len(targets) > 0
Expand All @@ -51,4 +51,3 @@ def test_targets(self):

# clear
self.cmd(f'az quantum target clear')

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


class QuantumScenarioTest(ScenarioTest):

def test_workspace(self):
# Since azure quantum is still in private preview, we require
# these tests to run in a specific subscription (AzureQuantum-test)
Expand All @@ -25,10 +25,10 @@ def test_workspace(self):

# clear
self.cmd(f'az quantum workspace clear')

# list
workspaces = self.cmd('az quantum workspace list -o json').get_output_in_json()
assert len(workspaces) > 0
assert len(workspaces) > 0
self.cmd('az quantum workspace list -o json', checks=[
self.check(f"[?name=='{TEST_WORKSPACE}'].resourceGroup | [0]", TEST_RG)
])
Expand All @@ -45,4 +45,3 @@ def test_workspace(self):

# clear
self.cmd(f'az quantum workspace clear')

1 change: 1 addition & 0 deletions src/quantum/azext_quantum/tests/latest/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
TEST_RG = 'aqua-provider-validator'
TEST_WORKSPACE = 'validator-workspace-westus'


def is_private_preview_subscription(scenario):
""" Returns True if running in AzureQuantum-test """

Expand Down