azure-quantum 2.0.2.dev0__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/PKG-INFO +2 -1
  2. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/_version.py +1 -1
  3. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_constants.py +2 -0
  4. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/targets/ionq.py +9 -1
  5. azure-quantum-2.1.0/azure/quantum/job/job.py +367 -0
  6. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/target.py +1 -1
  7. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/version.py +1 -1
  8. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure_quantum.egg-info/PKG-INFO +2 -1
  9. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure_quantum.egg-info/SOURCES.txt +1 -0
  10. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure_quantum.egg-info/requires.txt +10 -6
  11. azure-quantum-2.1.0/requirements-cirq.txt +2 -0
  12. azure-quantum-2.1.0/requirements-pulser.txt +1 -0
  13. azure-quantum-2.1.0/requirements.txt +10 -0
  14. azure-quantum-2.0.2.dev0/azure/quantum/job/job.py +0 -171
  15. azure-quantum-2.0.2.dev0/requirements-cirq.txt +0 -2
  16. azure-quantum-2.0.2.dev0/requirements.txt +0 -8
  17. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/README.md +0 -0
  18. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/__init__.py +0 -0
  19. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_authentication/__init__.py +0 -0
  20. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_authentication/_chained.py +0 -0
  21. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_authentication/_default.py +0 -0
  22. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_authentication/_token.py +0 -0
  23. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/__init__.py +0 -0
  24. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/_client.py +0 -0
  25. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/_configuration.py +0 -0
  26. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/_patch.py +0 -0
  27. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/_serialization.py +0 -0
  28. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/_vendor.py +0 -0
  29. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/models/__init__.py +0 -0
  30. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/models/_enums.py +0 -0
  31. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/models/_models.py +0 -0
  32. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/models/_patch.py +0 -0
  33. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/operations/__init__.py +0 -0
  34. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/operations/_operations.py +0 -0
  35. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_client/operations/_patch.py +0 -0
  36. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/_workspace_connection_params.py +0 -0
  37. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/argument_types/__init__.py +0 -0
  38. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/argument_types/types.py +0 -0
  39. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/chemistry/__init__.py +0 -0
  40. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/__init__.py +0 -0
  41. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/job.py +0 -0
  42. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/service.py +0 -0
  43. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/targets/__init__.py +0 -0
  44. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/targets/quantinuum.py +0 -0
  45. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/cirq/targets/target.py +0 -0
  46. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/__init__.py +0 -0
  47. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/base_job.py +0 -0
  48. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/filtered_job.py +0 -0
  49. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/job_failed_with_results_error.py +0 -0
  50. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/session.py +0 -0
  51. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/workspace_item.py +0 -0
  52. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/job/workspace_item_factory.py +0 -0
  53. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/__init__.py +0 -0
  54. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/__init__.py +0 -0
  55. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/backend.py +0 -0
  56. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/ionq.py +0 -0
  57. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/microsoft.py +0 -0
  58. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/qci.py +0 -0
  59. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/quantinuum.py +0 -0
  60. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/backends/rigetti.py +0 -0
  61. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/job.py +0 -0
  62. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/provider.py +0 -0
  63. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/results/__init__.py +0 -0
  64. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/qiskit/results/resource_estimator.py +0 -0
  65. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/storage.py +0 -0
  66. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/__init__.py +0 -0
  67. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/ionq.py +0 -0
  68. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/__init__.py +0 -0
  69. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/elements/__init__.py +0 -0
  70. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/elements/dft/__init__.py +0 -0
  71. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/elements/dft/job.py +0 -0
  72. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/elements/dft/target.py +0 -0
  73. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/job.py +0 -0
  74. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/result.py +0 -0
  75. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/microsoft/target.py +0 -0
  76. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/params.py +0 -0
  77. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/pasqal/__init__.py +0 -0
  78. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/pasqal/result.py +0 -0
  79. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/pasqal/target.py +0 -0
  80. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/quantinuum.py +0 -0
  81. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/rigetti/__init__.py +0 -0
  82. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/rigetti/result.py +0 -0
  83. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/rigetti/target.py +0 -0
  84. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/target/target_factory.py +0 -0
  85. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure/quantum/workspace.py +0 -0
  86. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure_quantum.egg-info/dependency_links.txt +0 -0
  87. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/azure_quantum.egg-info/top_level.txt +0 -0
  88. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/requirements-dev.txt +0 -0
  89. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/requirements-qiskit.txt +0 -0
  90. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/requirements-qsharp.txt +0 -0
  91. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/requirements-quil.txt +0 -0
  92. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/setup.cfg +0 -0
  93. {azure-quantum-2.0.2.dev0 → azure-quantum-2.1.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-quantum
3
- Version: 2.0.2.dev0
3
+ Version: 2.1.0
4
4
  Summary: Python client for Azure Quantum
5
5
  Home-page: https://github.com/microsoft/azure-quantum-python
6
6
  Author: Microsoft
@@ -12,6 +12,7 @@ Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  Provides-Extra: cirq
14
14
  Provides-Extra: dev
15
+ Provides-Extra: pulser
15
16
  Provides-Extra: qiskit
16
17
  Provides-Extra: qsharp
17
18
  Provides-Extra: quil
@@ -6,4 +6,4 @@
6
6
  # Changes may cause incorrect behavior and will be lost if the code is regenerated.
7
7
  # --------------------------------------------------------------------------
8
8
 
9
- VERSION = "2.0.2.dev0"
9
+ VERSION = "2.1.0"
@@ -21,6 +21,7 @@ class EnvironmentVariables:
21
21
  AZURE_CLIENT_ID = SdkEnvironmentVariables.AZURE_CLIENT_ID
22
22
  AZURE_CLIENT_SECRET = SdkEnvironmentVariables.AZURE_CLIENT_SECRET
23
23
  AZURE_CLIENT_CERTIFICATE_PATH = SdkEnvironmentVariables.AZURE_CLIENT_CERTIFICATE_PATH
24
+ AZURE_CLIENT_SEND_CERTIFICATE_CHAIN = SdkEnvironmentVariables.AZURE_CLIENT_SEND_CERTIFICATE_CHAIN
24
25
  AZURE_TENANT_ID = SdkEnvironmentVariables.AZURE_TENANT_ID
25
26
  QUANTUM_TOKEN_FILE = "AZURE_QUANTUM_TOKEN_FILE"
26
27
  CONNECTION_STRING = "AZURE_QUANTUM_CONNECTION_STRING"
@@ -37,6 +38,7 @@ class EnvironmentVariables:
37
38
  AZURE_CLIENT_ID,
38
39
  AZURE_CLIENT_SECRET,
39
40
  AZURE_CLIENT_CERTIFICATE_PATH,
41
+ AZURE_CLIENT_SEND_CERTIFICATE_CHAIN,
40
42
  AZURE_TENANT_ID,
41
43
  QUANTUM_TOKEN_FILE,
42
44
  CONNECTION_STRING,
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) Microsoft Corporation.
3
3
  # Licensed under the MIT License.
4
4
  ##
5
- from typing import TYPE_CHECKING, Any, Dict, Union
5
+ from typing import TYPE_CHECKING, Any, Dict, Union, Optional
6
6
 
7
7
  try:
8
8
  import cirq
@@ -71,6 +71,14 @@ class _IonQClient:
71
71
  def delete_job(self, job_id: str):
72
72
  azure_job = self._workspace.get_job(job_id)
73
73
  self._workspace.cancel_job(azure_job)
74
+
75
+ def get_results(
76
+ self, job_id: str, sharpen: Optional[bool] = None, extra_query_params: Optional[dict] = None
77
+ ):
78
+ azure_job = self._workspace.get_job(job_id)
79
+ job_result = azure_job.get_results()
80
+ return job_result["histogram"]
81
+
74
82
 
75
83
 
76
84
  class IonQTarget(IonQ, CirqTarget):
@@ -0,0 +1,367 @@
1
+ ##
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License.
4
+ ##
5
+
6
+
7
+ import logging
8
+ import time
9
+ import json
10
+
11
+ from typing import TYPE_CHECKING
12
+
13
+ from azure.quantum._client.models import JobDetails
14
+ from azure.quantum.job.job_failed_with_results_error import JobFailedWithResultsError
15
+ from azure.quantum.job.base_job import BaseJob, ContentType, DEFAULT_TIMEOUT
16
+ from azure.quantum.job.filtered_job import FilteredJob
17
+
18
+ __all__ = ["Job", "JobDetails"]
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ if TYPE_CHECKING:
23
+ from azure.quantum.workspace import Workspace
24
+
25
+
26
+ _log = logging.getLogger(__name__)
27
+
28
+
29
+ class Job(BaseJob, FilteredJob):
30
+ """Azure Quantum Job that is submitted to a given Workspace.
31
+
32
+ :param workspace: Workspace instance to submit job to
33
+ :type workspace: Workspace
34
+ :param job_details: Job details model,
35
+ contains Job ID, name and other details
36
+ :type job_details: JobDetails
37
+ """
38
+
39
+ _default_poll_wait = 0.2
40
+
41
+ def __init__(self, workspace: "Workspace", job_details: JobDetails, **kwargs):
42
+ self.results = None
43
+ super().__init__(
44
+ workspace=workspace,
45
+ details=job_details,
46
+ **kwargs
47
+ )
48
+
49
+ def submit(self):
50
+ """Submit a job to Azure Quantum."""
51
+ _log.debug(f"Submitting job with ID {self.id}")
52
+ job = self.workspace.submit_job(self)
53
+ self.details = job.details
54
+
55
+ def refresh(self):
56
+ """Refreshes the Job's details by querying the workspace."""
57
+ self.details = self.workspace.get_job(self.id).details
58
+
59
+ def has_completed(self) -> bool:
60
+ """Check if the job has completed."""
61
+ return (
62
+ self.details.status == "Succeeded"
63
+ or self.details.status == "Failed"
64
+ or self.details.status == "Cancelled"
65
+ )
66
+
67
+ def wait_until_completed(
68
+ self,
69
+ max_poll_wait_secs=30,
70
+ timeout_secs=None,
71
+ print_progress=True
72
+ ) -> None:
73
+ """Keeps refreshing the Job's details
74
+ until it reaches a finished status.
75
+
76
+ :param max_poll_wait_secs: Maximum poll wait time, defaults to 30
77
+ :type max_poll_wait_secs: int
78
+ :param timeout_secs: Timeout in seconds, defaults to None
79
+ :type timeout_secs: int
80
+ :param print_progress: Print "." to stdout to display progress
81
+ :type print_progress: bool
82
+ :raises: :class:`TimeoutError` If the total poll time exceeds timeout, raise.
83
+ """
84
+ self.refresh()
85
+ poll_wait = Job._default_poll_wait
86
+ start_time = time.time()
87
+ while not self.has_completed():
88
+ if timeout_secs is not None and (time.time() - start_time) >= timeout_secs:
89
+ raise TimeoutError(f"The wait time has exceeded {timeout_secs} seconds.")
90
+
91
+ logger.debug(
92
+ f"Waiting for job {self.id},"
93
+ + f"it is in status '{self.details.status}'"
94
+ )
95
+ if print_progress:
96
+ print(".", end="", flush=True)
97
+ time.sleep(poll_wait)
98
+ self.refresh()
99
+ poll_wait = (
100
+ max_poll_wait_secs
101
+ if poll_wait >= max_poll_wait_secs
102
+ else poll_wait * 1.5
103
+ )
104
+
105
+ def get_results(self, timeout_secs: float = DEFAULT_TIMEOUT):
106
+ """Get job results by downloading the results blob from the
107
+ storage container linked via the workspace.
108
+
109
+ Raises :class:`RuntimeError` if job execution fails.
110
+
111
+ Raises :class:`ValueError` if job output is malformed or output format is not compatible.
112
+
113
+ Raises :class:`azure.quantum.job.JobFailedWithResultsError` if job execution fails,
114
+ but failure results could still be retrieved (e.g. for jobs submitted against "microsoft.dft" target).
115
+
116
+ :param timeout_secs: Timeout in seconds, defaults to 300
117
+ :type timeout_secs: float
118
+ :return: Results dictionary with histogram shots, or raw results if not a json object.
119
+ :rtype: typing.Any
120
+ """
121
+ if self.results is not None:
122
+ return self.results
123
+
124
+ if not self.has_completed():
125
+ self.wait_until_completed(timeout_secs=timeout_secs)
126
+
127
+ if not self.details.status == "Succeeded":
128
+ if self.details.status == "Failed" and self._allow_failure_results():
129
+ job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
130
+ if job_blob_properties.size > 0:
131
+ job_failure_data = self.download_data(self.details.output_data_uri)
132
+ raise JobFailedWithResultsError("An error occurred during job execution.", job_failure_data)
133
+
134
+ raise RuntimeError(
135
+ f'{"Cannot retrieve results as job execution failed"}'
136
+ + f"(status: {self.details.status}."
137
+ + f"error: {self.details.error_data})"
138
+ )
139
+
140
+ payload = self.download_data(self.details.output_data_uri)
141
+ try:
142
+ payload = payload.decode("utf8")
143
+ results = json.loads(payload)
144
+
145
+ if self.details.output_data_format == "microsoft.quantum-results.v1":
146
+ if "Histogram" not in results:
147
+ raise ValueError(f"\"Histogram\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
148
+
149
+ histogram_values = results["Histogram"]
150
+
151
+ if len(histogram_values) % 2 == 0:
152
+ # Re-mapping {'Histogram': ['[0]', 0.50, '[1]', 0.50] } to {'[0]': 0.50, '[1]': 0.50}
153
+ return {histogram_values[i]: histogram_values[i + 1] for i in range(0, len(histogram_values), 2)}
154
+ else:
155
+ raise ValueError(f"\"Histogram\" array has invalid format. Even number of items is expected.")
156
+ elif self.details.output_data_format == "microsoft.quantum-results.v2":
157
+ if "DataFormat" not in results or results["DataFormat"] != "microsoft.quantum-results.v2":
158
+ raise ValueError(f"\"DataFormat\" was expected to be \"microsoft.quantum-results.v2\" in the Job results for \"{self.details.output_data_format}\" output format.")
159
+
160
+ if "Results" not in results:
161
+ raise ValueError(f"\"Results\" field was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
162
+
163
+ if len(results["Results"]) < 1:
164
+ raise ValueError("\"Results\" array was expected to contain at least one item")
165
+
166
+ results = results["Results"][0]
167
+
168
+ if "Histogram" not in results:
169
+ raise ValueError(f"\"Histogram\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
170
+
171
+ if "Shots" not in results:
172
+ raise ValueError(f"\"Shots\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
173
+
174
+ histogram_values = results["Histogram"]
175
+
176
+ total_count = len(results["Shots"])
177
+
178
+ # Re-mapping object {'Histogram': [{"Outcome": [0], "Display": '[0]', "Count": 500}, {"Outcome": [1], "Display": '[1]', "Count": 500}]} to {'[0]': 0.50, '[1]': 0.50}
179
+ return {outcome["Display"]: outcome["Count"] / total_count for outcome in histogram_values}
180
+
181
+ return results
182
+ except:
183
+ # If errors decoding the data, return the raw payload:
184
+ return payload
185
+
186
+ def get_results_histogram(self, timeout_secs: float = DEFAULT_TIMEOUT):
187
+ """Get job results histogram by downloading the results blob from the storage container linked via the workspace.
188
+
189
+ Raises :class:`RuntimeError` if job execution fails.
190
+
191
+ Raises :class:`ValueError` if job output is malformed or output format is not compatible.
192
+
193
+ Raises :class:`azure.quantum.job.JobFailedWithResultsError` if job execution fails,
194
+ but failure results could still be retrieved (e.g. for jobs submitted against "microsoft.dft" target).
195
+
196
+ :param timeout_secs: Timeout in seconds, defaults to 300
197
+ :type timeout_secs: float
198
+ :return: Results dictionary with histogram shots, or raw results if not a json object.
199
+ :rtype: typing.Any
200
+ """
201
+ if self.results is not None:
202
+ return self.results
203
+
204
+ if not self.has_completed():
205
+ self.wait_until_completed(timeout_secs=timeout_secs)
206
+
207
+ if not self.details.status == "Succeeded":
208
+ if self.details.status == "Failed" and self._allow_failure_results():
209
+ job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
210
+ if job_blob_properties.size > 0:
211
+ job_failure_data = self.download_data(self.details.output_data_uri)
212
+ raise JobFailedWithResultsError("An error occurred during job execution.", job_failure_data)
213
+
214
+ raise RuntimeError(
215
+ f'{"Cannot retrieve results as job execution failed"}'
216
+ + f"(status: {self.details.status}."
217
+ + f"error: {self.details.error_data})"
218
+ )
219
+
220
+ payload = self.download_data(self.details.output_data_uri)
221
+ try:
222
+ payload = payload.decode("utf8")
223
+ results = json.loads(payload)
224
+
225
+ if self.details.output_data_format == "microsoft.quantum-results.v2":
226
+ if "DataFormat" not in results or results["DataFormat"] != "microsoft.quantum-results.v2":
227
+ raise ValueError(f"\"DataFormat\" was expected to be \"microsoft.quantum-results.v2\" in the Job results for \"{self.details.output_data_format}\" output format.")
228
+ if "Results" not in results:
229
+ raise ValueError(f"\"Results\" field was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
230
+
231
+ if len(results["Results"]) < 1:
232
+ raise ValueError("\"Results\" array was expected to contain at least one item")
233
+
234
+ results = results["Results"]
235
+
236
+ if len(results) == 1:
237
+ results = results[0]
238
+ if "Histogram" not in results:
239
+ raise ValueError(f"\"Histogram\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
240
+
241
+ histogram_values = results["Histogram"]
242
+ outcome_keys = self._process_outcome(histogram_values)
243
+
244
+ # Re-mapping object {'Histogram': [{"Outcome": [0], "Display": '[0]', "Count": 500}, {"Outcome": [1], "Display": '[1]', "Count": 500}]} to {'[0]': {"Outcome": [0], "Count": 500}, '[1]': {"Outcome": [1], "Count": 500}}
245
+ return {hist_val["Display"]: {"outcome": outcome, "count": hist_val["Count"]} for outcome, hist_val in zip(outcome_keys, histogram_values)}
246
+
247
+ else:
248
+ # This is handling the BatchResults edge case
249
+ resultsArray = []
250
+ for i, result in enumerate(results):
251
+ if "Histogram" not in result:
252
+ raise ValueError(f"\"Histogram\" array was expected to be in the Job results for result {i} for \"{self.details.output_data_format}\" output format.")
253
+
254
+ histogram_values = result["Histogram"]
255
+ outcome_keys = self._process_outcome(histogram_values)
256
+
257
+ # Re-mapping object {'Histogram': [{"Outcome": [0], "Display": '[0]', "Count": 500}, {"Outcome": [1], "Display": '[1]', "Count": 500}]} to {'[0]': {"Outcome": [0], "Count": 500}, '[1]': {"Outcome": [1], "Count": 500}}
258
+ resultsArray.append({hist_val["Display"]: {"outcome": outcome, "count": hist_val["Count"]} for outcome, hist_val in zip(outcome_keys, histogram_values)})
259
+
260
+ return resultsArray
261
+
262
+ else:
263
+ raise ValueError(f"Getting a results histogram with counts instead of probabilities is not a supported feature for jobs using the \"{self.details.output_data_format}\" output format.")
264
+
265
+ except Exception as e:
266
+ raise e
267
+
268
+ def get_results_shots(self, timeout_secs: float = DEFAULT_TIMEOUT):
269
+ """Get job results per shot data by downloading the results blob from the
270
+ storage container linked via the workspace.
271
+
272
+ Raises :class:`RuntimeError` if job execution fails.
273
+
274
+ Raises :class:`ValueError` if job output is malformed or output format is not compatible.
275
+
276
+ Raises :class:`azure.quantum.job.JobFailedWithResultsError` if job execution fails,
277
+ but failure results could still be retrieved (e.g. for jobs submitted against "microsoft.dft" target).
278
+
279
+ :param timeout_secs: Timeout in seconds, defaults to 300
280
+ :type timeout_secs: float
281
+ :return: Results dictionary with histogram shots, or raw results if not a json object.
282
+ :rtype: typing.Any
283
+ """
284
+ if self.results is not None:
285
+ return self.results
286
+
287
+ if not self.has_completed():
288
+ self.wait_until_completed(timeout_secs=timeout_secs)
289
+
290
+ if not self.details.status == "Succeeded":
291
+ if self.details.status == "Failed" and self._allow_failure_results():
292
+ job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
293
+ if job_blob_properties.size > 0:
294
+ job_failure_data = self.download_data(self.details.output_data_uri)
295
+ raise JobFailedWithResultsError("An error occurred during job execution.", job_failure_data)
296
+
297
+ raise RuntimeError(
298
+ f'{"Cannot retrieve results as job execution failed"}'
299
+ + f"(status: {self.details.status}."
300
+ + f"error: {self.details.error_data})"
301
+ )
302
+
303
+ payload = self.download_data(self.details.output_data_uri)
304
+ try:
305
+ payload = payload.decode("utf8")
306
+ results = json.loads(payload)
307
+
308
+ if self.details.output_data_format == "microsoft.quantum-results.v2":
309
+ if "DataFormat" not in results or results["DataFormat"] != "microsoft.quantum-results.v2":
310
+ raise ValueError(f"\"DataFormat\" was expected to be \"microsoft.quantum-results.v2\" in the Job results for \"{self.details.output_data_format}\" output format.")
311
+ if "Results" not in results:
312
+ raise ValueError(f"\"Results\" field was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
313
+
314
+ results = results["Results"]
315
+
316
+ if len(results) < 1:
317
+ raise ValueError("\"Results\" array was expected to contain at least one item")
318
+
319
+ if len(results) == 1:
320
+ result = results[0]
321
+ if "Shots" not in result:
322
+ raise ValueError(f"\"Shots\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format.")
323
+
324
+ return [self._convert_tuples(shot) for shot in result["Shots"]]
325
+ else:
326
+ # This is handling the BatchResults edge case
327
+ shotsArray = []
328
+ for i, result in enumerate(results):
329
+ if "Shots" not in result:
330
+ raise ValueError(f"\"Shots\" array was expected to be in the Job results for result {i} of \"{self.details.output_data_format}\" output format.")
331
+ shotsArray.append([self._convert_tuples(shot) for shot in result["Shots"]])
332
+
333
+ return shotsArray
334
+ else:
335
+ raise ValueError(f"Individual shot results are not supported for jobs using the \"{self.details.output_data_format}\" output format.")
336
+ except Exception as e:
337
+ raise e
338
+
339
+ def _process_outcome(self, histogram_results):
340
+ return [self._convert_tuples(v['Outcome']) for v in histogram_results]
341
+
342
+ def _convert_tuples(self, data):
343
+ if isinstance(data, dict):
344
+ # Check if the dictionary represents a tuple
345
+ if all(isinstance(k, str) and k.startswith("Item") for k in data.keys()):
346
+ # Convert the dictionary to a tuple
347
+ return tuple(self._convert_tuples(data[f"Item{i+1}"]) for i in range(len(data)))
348
+ else:
349
+ raise "Malformed tuple output"
350
+ elif isinstance(data, list):
351
+ # Recursively process list elements
352
+ return [self._convert_tuples(item) for item in data]
353
+ else:
354
+ # Return the data as is (int, string, etc.)
355
+ return data
356
+
357
+ @classmethod
358
+ def _allow_failure_results(cls) -> bool:
359
+ """
360
+ Allow to download job results even if the Job status is "Failed".
361
+
362
+ This method can be overridden in derived classes to alter the default
363
+ behaviour.
364
+
365
+ The default is False.
366
+ """
367
+ return False
@@ -189,7 +189,7 @@ target '{self.name}' of provider '{self.provider_id}' not found."
189
189
 
190
190
  def _qir_output_data_format(self) -> str:
191
191
  """"Fallback output data format in case of QIR job submission."""
192
- return "microsoft.quantum-results.v1"
192
+ return "microsoft.quantum-results.v2"
193
193
 
194
194
  def submit(
195
195
  self,
@@ -5,4 +5,4 @@
5
5
  # Copyright (c) Microsoft Corporation. All rights reserved.
6
6
  # Licensed under the MIT License.
7
7
  ##
8
- __version__ = "2.0.2.dev0"
8
+ __version__ = "2.1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-quantum
3
- Version: 2.0.2.dev0
3
+ Version: 2.1.0
4
4
  Summary: Python client for Azure Quantum
5
5
  Home-page: https://github.com/microsoft/azure-quantum-python
6
6
  Author: Microsoft
@@ -12,6 +12,7 @@ Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  Provides-Extra: cirq
14
14
  Provides-Extra: dev
15
+ Provides-Extra: pulser
15
16
  Provides-Extra: qiskit
16
17
  Provides-Extra: qsharp
17
18
  Provides-Extra: quil
@@ -1,6 +1,7 @@
1
1
  README.md
2
2
  requirements-cirq.txt
3
3
  requirements-dev.txt
4
+ requirements-pulser.txt
4
5
  requirements-qiskit.txt
5
6
  requirements-qsharp.txt
6
7
  requirements-quil.txt
@@ -1,6 +1,6 @@
1
1
  azure-core<2.0,>=1.30
2
- azure-identity<2.0,>=1.15
3
- azure-storage-blob<13.0,>=12.19
2
+ azure-identity<2.0,>=1.17
3
+ azure-storage-blob==12.20
4
4
  msrest<1.0,>=0.7.1
5
5
  numpy<2.0,>=1.21.0
6
6
  deprecated<2.0,>=1.2.12
@@ -8,11 +8,12 @@ Markdown>=3.4.1
8
8
  python-markdown-math>=0.8
9
9
 
10
10
  [all]
11
- cirq-core<=1.2.0,>=1.1.0
12
- cirq-ionq<=1.2.0,>=1.1.0
11
+ cirq-core<=1.4.0,>=1.3.0
12
+ cirq-ionq<=1.4.0,>=1.3.0
13
13
  vcrpy>=4.3.1
14
14
  azure-devtools<2.0,>=1.2.0
15
15
  graphviz>=0.20.1
16
+ pulser<0.19,>=0.18
16
17
  qiskit-ionq<0.6,>=0.5
17
18
  qiskit-qir<0.6,>=0.5
18
19
  qiskit<2.0,>=1.0
@@ -22,14 +23,17 @@ qsharp<2.0,>=1.0.33
22
23
  pyquil>=3.3.2
23
24
 
24
25
  [cirq]
25
- cirq-core<=1.2.0,>=1.1.0
26
- cirq-ionq<=1.2.0,>=1.1.0
26
+ cirq-core<=1.4.0,>=1.3.0
27
+ cirq-ionq<=1.4.0,>=1.3.0
27
28
 
28
29
  [dev]
29
30
  vcrpy>=4.3.1
30
31
  azure-devtools<2.0,>=1.2.0
31
32
  graphviz>=0.20.1
32
33
 
34
+ [pulser]
35
+ pulser<0.19,>=0.18
36
+
33
37
  [qiskit]
34
38
  qiskit-ionq<0.6,>=0.5
35
39
  qiskit-qir<0.6,>=0.5
@@ -0,0 +1,2 @@
1
+ cirq-core>=1.3.0,<=1.4.0
2
+ cirq-ionq>=1.3.0,<=1.4.0
@@ -0,0 +1 @@
1
+ pulser>=0.18,<0.19
@@ -0,0 +1,10 @@
1
+ azure-core>=1.30,<2.0
2
+ azure-identity>=1.17,<2.0
3
+ # TODO: recent versions break recordings.
4
+ # More than one match for "https://mystorage.blob.core.windows.net/.../rawOutputData"
5
+ azure-storage-blob==12.20
6
+ msrest>=0.7.1,<1.0
7
+ numpy>=1.21.0,<2.0
8
+ deprecated>=1.2.12,<2.0
9
+ Markdown>=3.4.1
10
+ python-markdown-math>=0.8
@@ -1,171 +0,0 @@
1
- ##
2
- # Copyright (c) Microsoft Corporation. All rights reserved.
3
- # Licensed under the MIT License.
4
- ##
5
-
6
-
7
- import logging
8
- import time
9
- import json
10
-
11
- from typing import TYPE_CHECKING
12
-
13
- from azure.quantum._client.models import JobDetails
14
- from azure.quantum.job.job_failed_with_results_error import JobFailedWithResultsError
15
- from azure.quantum.job.base_job import BaseJob, ContentType, DEFAULT_TIMEOUT
16
- from azure.quantum.job.filtered_job import FilteredJob
17
-
18
- __all__ = ["Job", "JobDetails"]
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
- if TYPE_CHECKING:
23
- from azure.quantum.workspace import Workspace
24
-
25
-
26
- _log = logging.getLogger(__name__)
27
-
28
-
29
- class Job(BaseJob, FilteredJob):
30
- """Azure Quantum Job that is submitted to a given Workspace.
31
-
32
- :param workspace: Workspace instance to submit job to
33
- :type workspace: Workspace
34
- :param job_details: Job details model,
35
- contains Job ID, name and other details
36
- :type job_details: JobDetails
37
- """
38
-
39
- _default_poll_wait = 0.2
40
-
41
- def __init__(self, workspace: "Workspace", job_details: JobDetails, **kwargs):
42
- self.results = None
43
- super().__init__(
44
- workspace=workspace,
45
- details=job_details,
46
- **kwargs
47
- )
48
-
49
- def submit(self):
50
- """Submit a job to Azure Quantum."""
51
- _log.debug(f"Submitting job with ID {self.id}")
52
- job = self.workspace.submit_job(self)
53
- self.details = job.details
54
-
55
- def refresh(self):
56
- """Refreshes the Job's details by querying the workspace."""
57
- self.details = self.workspace.get_job(self.id).details
58
-
59
- def has_completed(self) -> bool:
60
- """Check if the job has completed."""
61
- return (
62
- self.details.status == "Succeeded"
63
- or self.details.status == "Failed"
64
- or self.details.status == "Cancelled"
65
- )
66
-
67
- def wait_until_completed(
68
- self,
69
- max_poll_wait_secs=30,
70
- timeout_secs=None,
71
- print_progress=True
72
- ) -> None:
73
- """Keeps refreshing the Job's details
74
- until it reaches a finished status.
75
-
76
- :param max_poll_wait_secs: Maximum poll wait time, defaults to 30
77
- :type max_poll_wait_secs: int
78
- :param timeout_secs: Timeout in seconds, defaults to None
79
- :type timeout_secs: int
80
- :param print_progress: Print "." to stdout to display progress
81
- :type print_progress: bool
82
- :raises: :class:`TimeoutError` If the total poll time exceeds timeout, raise.
83
- """
84
- self.refresh()
85
- poll_wait = Job._default_poll_wait
86
- start_time = time.time()
87
- while not self.has_completed():
88
- if timeout_secs is not None and (time.time() - start_time) >= timeout_secs:
89
- raise TimeoutError(f"The wait time has exceeded {timeout_secs} seconds.")
90
-
91
- logger.debug(
92
- f"Waiting for job {self.id},"
93
- + f"it is in status '{self.details.status}'"
94
- )
95
- if print_progress:
96
- print(".", end="", flush=True)
97
- time.sleep(poll_wait)
98
- self.refresh()
99
- poll_wait = (
100
- max_poll_wait_secs
101
- if poll_wait >= max_poll_wait_secs
102
- else poll_wait * 1.5
103
- )
104
-
105
- def get_results(self, timeout_secs: float = DEFAULT_TIMEOUT):
106
- """Get job results by downloading the results blob from the
107
- storage container linked via the workspace.
108
-
109
- Raises :class:`RuntimeError` if job execution fails.
110
-
111
- Raises :class:`azure.quantum.job.JobFailedWithResultsError` if job execution fails,
112
- but failure results could still be retrieved (e.g. for jobs submitted against "microsoft.dft" target).
113
-
114
- :param timeout_secs: Timeout in seconds, defaults to 300
115
- :type timeout_secs: float
116
- :return: Results dictionary with histogram shots, or raw results if not a json object.
117
- :rtype: typing.Any
118
- """
119
- if self.results is not None:
120
- return self.results
121
-
122
- if not self.has_completed():
123
- self.wait_until_completed(timeout_secs=timeout_secs)
124
-
125
- if not self.details.status == "Succeeded":
126
- if self.details.status == "Failed" and self._allow_failure_results():
127
- job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
128
- if job_blob_properties.size > 0:
129
- job_failure_data = self.download_data(self.details.output_data_uri)
130
- raise JobFailedWithResultsError("An error occurred during job execution.", job_failure_data)
131
-
132
- raise RuntimeError(
133
- f'{"Cannot retrieve results as job execution failed"}'
134
- + f"(status: {self.details.status}."
135
- + f"error: {self.details.error_data})"
136
- )
137
-
138
- payload = self.download_data(self.details.output_data_uri)
139
- try:
140
- payload = payload.decode("utf8")
141
- results = json.loads(payload)
142
-
143
- if self.details.output_data_format == "microsoft.quantum-results.v1":
144
- if "Histogram" not in results:
145
- raise f"\"Histogram\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format."
146
-
147
- histogram_values = results["Histogram"]
148
-
149
- if len(histogram_values) % 2 == 0:
150
- # Re-mapping {'Histogram': ['[0]', 0.50, '[1]', 0.50] } to {'[0]': 0.50, '[1]': 0.50}
151
- return {histogram_values[i]: histogram_values[i + 1] for i in range(0, len(histogram_values), 2)}
152
- else:
153
- raise f"\"Histogram\" array has invalid format. Even number of items is expected."
154
-
155
- return results
156
- except:
157
- # If errors decoding the data, return the raw payload:
158
- return payload
159
-
160
-
161
- @classmethod
162
- def _allow_failure_results(cls) -> bool:
163
- """
164
- Allow to download job results even if the Job status is "Failed".
165
-
166
- This method can be overridden in derived classes to alter the default
167
- behaviour.
168
-
169
- The default is False.
170
- """
171
- return False
@@ -1,2 +0,0 @@
1
- cirq-core>=1.1.0,<=1.2.0
2
- cirq-ionq>=1.1.0,<=1.2.0
@@ -1,8 +0,0 @@
1
- azure-core>=1.30,<2.0
2
- azure-identity>=1.15,<2.0
3
- azure-storage-blob>=12.19,<13.0
4
- msrest>=0.7.1,<1.0
5
- numpy>=1.21.0,<2.0
6
- deprecated>=1.2.12,<2.0
7
- Markdown>=3.4.1
8
- python-markdown-math>=0.8