Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 3 additions & 0 deletions src/quantum/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
azext_quantum/tests/latest/input_data/bin
azext_quantum/tests/latest/input_data/obj
azext_quantum/tests/latest/recordings/test_results.xml
2 changes: 1 addition & 1 deletion src/quantum/azext_quantum/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

helps['quantum job output'] = """
type: command
short-summary: Get the results of running a Q# job.
short-summary: Get the results of running a job.
examples:
- name: Print the results of a successful Azure Quantum job.
text: |-
Expand Down
165 changes: 92 additions & 73 deletions src/quantum/azext_quantum/operations/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
logger = logging.getLogger(__name__)
knack_logger = knack.log.get_logger(__name__)

_targets_with_allowed_failure_output = {"microsoft.dft"}


def list(cmd, resource_group_name, workspace_name, location):
"""
Expand Down Expand Up @@ -535,83 +537,25 @@ def _validate_item(provided_value, num_items):

def output(cmd, job_id, resource_group_name, workspace_name, location, item=None):
"""
Get the results of running a Q# job.
Get the results of running a job.
"""
import tempfile
from azure.cli.command_modules.storage._client_factory import blob_data_service_factory

path = os.path.join(tempfile.gettempdir(), job_id)
info = WorkspaceInfo(cmd, resource_group_name, workspace_name, location)
client = cf_jobs(cmd.cli_ctx, info.subscription, info.resource_group, info.name, info.location)
job = client.get(job_id)

if os.path.exists(path):
logger.debug("Using existing blob from %s", path)
else:
logger.debug("Downloading job results blob into %s", path)
if job.status != "Succeeded":
if job.status == "Failed" and job.target in _targets_with_allowed_failure_output:
logger.debug("Job submitted against target \"%s\" failed, but the job output can still be returned. Trying to produce the output.", job.target)
job_output = _get_job_output(cmd, job, item)
if job_output is not None:
return job_output

if job.status != "Succeeded":
return job # If "-o table" is specified, this allows transform_output() in commands.py
# to format the output, so the error info is shown. If "-o json" or no "-o"
# parameter is specified, then the full JSON job output is displayed, being
# consistent with other commands.

args = _parse_blob_url(job.output_data_uri)
blob_service = blob_data_service_factory(cmd.cli_ctx, args)
blob_service.get_blob_to_path(args['container'], args['blob'], path)

with open(path) as json_file:
lines = [line.strip() for line in json_file.readlines()]

# Receiving an empty response is valid.
if len(lines) == 0:
return
return job # If "-o table" is specified, this allows transform_output() in commands.py
# to format the output, so the error info is shown. If "-o json" or no "-o"
# parameter is specified, then the full JSON job output is displayed, being
# consistent with other commands.

if job.target.startswith("microsoft.simulator") and job.target != "microsoft.simulator.resources-estimator":
result_start_line = len(lines) - 1
is_result_string = lines[-1].endswith('"')
if is_result_string:
while result_start_line >= 0 and not lines[result_start_line].startswith('"'):
result_start_line -= 1
if result_start_line < 0:
raise AzureResponseError("Job output is malformed, mismatched quote characters.")

# Print the job output and then the result of the operation as a histogram.
# If the result is a string, trim the quotation marks.
print('\n'.join(lines[:result_start_line]))
raw_result = ' '.join(lines[result_start_line:])
result = raw_result[1:-1] if is_result_string else raw_result
print('_' * len(result) + '\n')

json_string = '{ "histogram" : { "' + result + '" : 1 } }'
data = json.loads(json_string)
else:
json_file.seek(0) # Reset the file pointer before loading
data = json.load(json_file)

# Consider item if it's a batch job, otherwise ignore
import builtins # list has been overriden as a function above
if item and isinstance(data, builtins.list):
item = _validate_item(item, len(data))
return data[item]

return data


def _validate_max_poll_wait_secs(max_poll_wait_secs):
valid_max_poll_wait_secs = 0.0
error_message = f"--max-poll-wait-secs parameter is not valid: {max_poll_wait_secs}"
error_recommendation = f"Must be a number greater than or equal to {MINIMUM_MAX_POLL_WAIT_SECS}"

try:
valid_max_poll_wait_secs = float(max_poll_wait_secs)
except ValueError as e:
raise InvalidArgumentValueError(error_message, error_recommendation) from e

if valid_max_poll_wait_secs < MINIMUM_MAX_POLL_WAIT_SECS:
raise InvalidArgumentValueError(error_message, error_recommendation)

return valid_max_poll_wait_secs
return _get_job_output(cmd, job, item)


def wait(cmd, job_id, resource_group_name, workspace_name, location, max_poll_wait_secs=5):
Expand Down Expand Up @@ -668,9 +612,6 @@ def run(cmd, program_args, resource_group_name, workspace_name, location, target
job = wait(cmd, job.id, resource_group_name, workspace_name, location)
logger.debug(job)

if not job.status == "Succeeded":
return job

return output(cmd, job.id, resource_group_name, workspace_name, location)


Expand All @@ -691,3 +632,81 @@ def cancel(cmd, job_id, resource_group_name, workspace_name, location):

# Wait for the job status to complete or be reported as cancelled
return wait(cmd, job_id, info.resource_group, info.name, info.location)


def _get_job_output(cmd, job, item=None):

import tempfile
path = os.path.join(tempfile.gettempdir(), job.id)

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

from azure.cli.command_modules.storage._client_factory import blob_data_service_factory

args = _parse_blob_url(job.output_data_uri)
blob_service = blob_data_service_factory(cmd.cli_ctx, args)

containerName = args['container']
blobName = args['blob']
blobProperties = blob_service.get_blob_properties(containerName, blobName)

if blobProperties.properties.content_length == 0:
return

blob_service.get_blob_to_path(containerName, blobName, path)

with open(path) as json_file:
lines = [line.strip() for line in json_file.readlines()]

# Receiving an empty response is valid.
if len(lines) == 0:
return

if job.target.startswith("microsoft.simulator") and job.target != "microsoft.simulator.resources-estimator":
result_start_line = len(lines) - 1
is_result_string = lines[-1].endswith('"')
if is_result_string:
while result_start_line >= 0 and not lines[result_start_line].startswith('"'):
result_start_line -= 1
if result_start_line < 0:
raise AzureResponseError("Job output is malformed, mismatched quote characters.")

# Print the job output and then the result of the operation as a histogram.
# If the result is a string, trim the quotation marks.
print('\n'.join(lines[:result_start_line]))
raw_result = ' '.join(lines[result_start_line:])
result = raw_result[1:-1] if is_result_string else raw_result
print('_' * len(result) + '\n')

json_string = '{ "histogram" : { "' + result + '" : 1 } }'
data = json.loads(json_string)
else:
json_file.seek(0) # Reset the file pointer before loading
data = json.load(json_file)

# Consider item if it's a batch job, otherwise ignore
import builtins # list has been overriden as a function above
if item and isinstance(data, builtins.list):
item = _validate_item(item, len(data))
return data[item]

return data


def _validate_max_poll_wait_secs(max_poll_wait_secs):
valid_max_poll_wait_secs = 0.0
error_message = f"--max-poll-wait-secs parameter is not valid: {max_poll_wait_secs}"
error_recommendation = f"Must be a number greater than or equal to {MINIMUM_MAX_POLL_WAIT_SECS}"

try:
valid_max_poll_wait_secs = float(max_poll_wait_secs)
except ValueError as e:
raise InvalidArgumentValueError(error_message, error_recommendation) from e

if valid_max_poll_wait_secs < MINIMUM_MAX_POLL_WAIT_SECS:
raise InvalidArgumentValueError(error_message, error_recommendation)

return valid_max_poll_wait_secs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
31

C 0.178137 0.023139 0.412368
H 0.835553 0.524899 -0.301980
H 0.782031 -0.601689 1.073610
C -0.598103 1.050400 1.225652
H -1.210310 0.552618 1.979206
H -1.266376 1.627120 0.573655
C -0.027758 2.625324 3.022268
C 0.497943 4.539918 4.462321
H 1.423171 4.969348 4.853950
H 0.000025 4.019113 5.283332
C -0.401626 5.639009 3.900566
H -1.335017 5.213661 3.526287
H 0.104762 6.162247 3.086168
S -0.791628 6.843304 5.230175
H -1.627570 7.616867 4.497656
S -1.006687 -1.030090 -0.507799
N 0.335023 1.932662 1.905919
H 1.076110 2.322214 1.337153
N 0.865882 3.568581 3.448681
H 1.581480 3.859943 2.795574
O -1.073352 2.375031 3.651291
H -0.087670 -1.812849 -1.096189
O -2.865982 8.576796 3.230102
H -2.607661 9.505735 3.243622
H -3.711020 8.550691 3.694031
O 2.173214 8.263152 4.538554
H 2.667328 8.395473 5.355399
H 1.310047 7.923676 4.827433
O -3.015476 4.585503 6.407597
H -2.385804 5.284068 6.165503
H -3.619558 5.007262 7.028684
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
60

O 97.873900000 103.017000000 100.816000000
H 98.128600000 103.038000000 99.848800000
H 97.173800000 102.317000000 100.960000000
O 100.645000000 100.169000000 95.891500000
H 101.491000000 100.305000000 96.406200000
H 99.888700000 100.618000000 96.367800000
O 99.814000000 100.835000000 101.232000000
H 99.329200000 99.976800000 101.063000000
H 99.151600000 101.561000000 101.414000000
O 98.804000000 98.512200000 97.758100000
H 99.782100000 98.646900000 97.916700000
H 98.421800000 99.326500000 97.321300000
O 100.747000000 100.164000000 103.736000000
H 100.658000000 100.628000000 102.855000000
H 100.105000000 99.398600000 103.776000000
O 98.070300000 98.516900000 100.438000000
H 97.172800000 98.878600000 100.690000000
H 98.194000000 98.592200000 99.448100000
O 98.548000000 101.265000000 97.248600000
H 98.688900000 102.140000000 97.711000000
H 97.919900000 101.391000000 96.480800000
O 103.898000000 98.427900000 99.984500000
H 103.015000000 98.654900000 99.573700000
H 104.128000000 97.477300000 99.776100000
O 99.166600000 96.442100000 101.723000000
H 98.843200000 97.206600000 101.166000000
H 99.643900000 95.783700000 101.141000000
O 102.891000000 100.842000000 97.477600000
H 103.837000000 100.662000000 97.209700000
H 102.868000000 101.166000000 98.423400000
O 96.227200000 100.990000000 101.698000000
H 96.148800000 100.422000000 102.517000000
H 95.313600000 101.237000000 101.375000000
O 98.864800000 98.222500000 103.917000000
H 98.949800000 97.463000000 103.272000000
H 99.054800000 97.896400000 104.843000000
O 104.578000000 100.035000000 101.952000000
H 104.419000000 101.011000000 101.802000000
H 104.206000000 99.514900000 101.184000000
O 102.429000000 104.060000000 101.348000000
H 101.757000000 103.665000000 101.974000000
H 102.209000000 105.021000000 101.185000000
O 98.708200000 103.752000000 98.244300000
H 98.397100000 104.234000000 97.425400000
H 99.598500000 104.111000000 98.524400000
O 95.630300000 99.996600000 98.245400000
H 96.540400000 100.410000000 98.268900000
H 94.982900000 100.638000000 97.834500000
O 102.360000000 101.551000000 99.964500000
H 102.675000000 102.370000000 100.444000000
H 101.556000000 101.180000000 100.430000000
O 101.836000000 97.446700000 102.110000000
H 100.860000000 97.397400000 101.898000000
H 101.991000000 97.133400000 103.047000000
O 101.665000000 98.316100000 98.319400000
H 101.904000000 99.233800000 98.002000000
H 102.224000000 97.640900000 97.837700000
O 99.984700000 103.272000000 102.307000000
H 99.640700000 103.104000000 103.231000000
H 99.216500000 103.453000000 101.693000000
Loading