azure-quantum 0.29.2__tar.gz → 1.0.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 (126) hide show
  1. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/PKG-INFO +1 -1
  2. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/_version.py +1 -1
  3. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/service.py +7 -0
  4. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/targets/quantinuum.py +1 -1
  5. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/__init__.py +1 -0
  6. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/base_job.py +41 -15
  7. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/job.py +35 -1
  8. azure-quantum-1.0.0/azure/quantum/job/job_failed_with_results_error.py +41 -0
  9. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/backend.py +130 -35
  10. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/ionq.py +65 -5
  11. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/qci.py +35 -2
  12. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/quantinuum.py +25 -4
  13. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/rigetti.py +8 -1
  14. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/job.py +7 -16
  15. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/provider.py +18 -2
  16. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/storage.py +2 -1
  17. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/__init__.py +1 -0
  18. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/ionq.py +37 -12
  19. azure-quantum-1.0.0/azure/quantum/target/microsoft/elements/dft/__init__.py +4 -0
  20. azure-quantum-1.0.0/azure/quantum/target/microsoft/elements/dft/job.py +46 -0
  21. azure-quantum-1.0.0/azure/quantum/target/microsoft/elements/dft/target.py +66 -0
  22. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/microsoft/target.py +36 -9
  23. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/params.py +1 -1
  24. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/pasqal/target.py +16 -2
  25. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/quantinuum.py +34 -9
  26. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/rigetti/target.py +21 -3
  27. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/solvers.py +7 -1
  28. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/target.py +82 -0
  29. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/target_factory.py +0 -2
  30. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/version.py +1 -1
  31. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/workspace.py +11 -8
  32. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure_quantum.egg-info/PKG-INFO +1 -1
  33. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure_quantum.egg-info/SOURCES.txt +4 -28
  34. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure_quantum.egg-info/requires.txt +2 -4
  35. azure-quantum-1.0.0/requirements-qsharp.txt +1 -0
  36. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/requirements.txt +0 -2
  37. azure-quantum-0.29.2/azure/quantum/_client/aio/__init__.py +0 -23
  38. azure-quantum-0.29.2/azure/quantum/_client/aio/_client.py +0 -124
  39. azure-quantum-0.29.2/azure/quantum/_client/aio/_configuration.py +0 -89
  40. azure-quantum-0.29.2/azure/quantum/_client/aio/operations/_operations.py +0 -1291
  41. azure-quantum-0.29.2/azure/quantum/_client/models/_patch.py +0 -20
  42. azure-quantum-0.29.2/azure/quantum/_client/operations/__init__.py +0 -29
  43. azure-quantum-0.29.2/azure/quantum/_client/operations/_patch.py +0 -20
  44. azure-quantum-0.29.2/azure/quantum/aio/__init__.py +0 -14
  45. azure-quantum-0.29.2/azure/quantum/aio/_authentication/__init__.py +0 -9
  46. azure-quantum-0.29.2/azure/quantum/aio/_authentication/_chained.py +0 -94
  47. azure-quantum-0.29.2/azure/quantum/aio/_authentication/_default.py +0 -212
  48. azure-quantum-0.29.2/azure/quantum/aio/_authentication/_token.py +0 -81
  49. azure-quantum-0.29.2/azure/quantum/aio/job/__init__.py +0 -1
  50. azure-quantum-0.29.2/azure/quantum/aio/job/base_job.py +0 -326
  51. azure-quantum-0.29.2/azure/quantum/aio/job/job.py +0 -104
  52. azure-quantum-0.29.2/azure/quantum/aio/optimization/__init__.py +0 -11
  53. azure-quantum-0.29.2/azure/quantum/aio/optimization/online_problem.py +0 -17
  54. azure-quantum-0.29.2/azure/quantum/aio/optimization/problem.py +0 -102
  55. azure-quantum-0.29.2/azure/quantum/aio/optimization/streaming_problem.py +0 -280
  56. azure-quantum-0.29.2/azure/quantum/aio/storage.py +0 -390
  57. azure-quantum-0.29.2/azure/quantum/aio/target/__init__.py +0 -19
  58. azure-quantum-0.29.2/azure/quantum/aio/target/ionq.py +0 -47
  59. azure-quantum-0.29.2/azure/quantum/aio/target/quantinuum.py +0 -47
  60. azure-quantum-0.29.2/azure/quantum/aio/target/solvers.py +0 -96
  61. azure-quantum-0.29.2/azure/quantum/aio/target/target.py +0 -68
  62. azure-quantum-0.29.2/azure/quantum/aio/target/target_factory.py +0 -72
  63. azure-quantum-0.29.2/azure/quantum/aio/target/toshiba.py +0 -6
  64. azure-quantum-0.29.2/azure/quantum/aio/workspace.py +0 -337
  65. azure-quantum-0.29.2/requirements-qsharp.txt +0 -1
  66. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/README.md +0 -0
  67. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/__init__.py +0 -0
  68. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_authentication/__init__.py +0 -0
  69. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_authentication/_chained.py +0 -0
  70. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_authentication/_default.py +0 -0
  71. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_authentication/_token.py +0 -0
  72. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/__init__.py +0 -0
  73. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/_client.py +0 -0
  74. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/_configuration.py +0 -0
  75. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/_patch.py +0 -0
  76. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/_serialization.py +0 -0
  77. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/_vendor.py +0 -0
  78. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/models/__init__.py +0 -0
  79. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/models/_enums.py +0 -0
  80. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/models/_models.py +0 -0
  81. {azure-quantum-0.29.2/azure/quantum/_client/aio → azure-quantum-1.0.0/azure/quantum/_client/models}/_patch.py +0 -0
  82. {azure-quantum-0.29.2/azure/quantum/_client/aio → azure-quantum-1.0.0/azure/quantum/_client}/operations/__init__.py +0 -0
  83. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/_client/operations/_operations.py +0 -0
  84. {azure-quantum-0.29.2/azure/quantum/_client/aio → azure-quantum-1.0.0/azure/quantum/_client}/operations/_patch.py +0 -0
  85. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/argument_types/__init__.py +0 -0
  86. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/argument_types/types.py +0 -0
  87. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/chemistry/__init__.py +0 -0
  88. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/__init__.py +0 -0
  89. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/job.py +0 -0
  90. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/targets/__init__.py +0 -0
  91. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/targets/ionq.py +0 -0
  92. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/cirq/targets/target.py +0 -0
  93. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/filtered_job.py +0 -0
  94. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/session.py +0 -0
  95. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/workspace_item.py +0 -0
  96. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/job/workspace_item_factory.py +0 -0
  97. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/__init__.py +0 -0
  98. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/online_problem.py +0 -0
  99. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/problem.py +0 -0
  100. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/solvers.py +0 -0
  101. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/streaming_problem.py +0 -0
  102. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/term.py +0 -0
  103. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/toshiba/__init__.py +0 -0
  104. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/optimization/toshiba/solvers.py +0 -0
  105. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/__init__.py +0 -0
  106. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/__init__.py +0 -0
  107. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/backends/microsoft.py +0 -0
  108. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/results/__init__.py +0 -0
  109. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/qiskit/results/resource_estimator.py +0 -0
  110. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/microsoft/__init__.py +0 -0
  111. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/microsoft/job.py +0 -0
  112. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/microsoft/result.py +0 -0
  113. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/pasqal/__init__.py +0 -0
  114. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/pasqal/result.py +0 -0
  115. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/rigetti/__init__.py +0 -0
  116. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/rigetti/result.py +0 -0
  117. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/toshiba/__init__.py +0 -0
  118. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure/quantum/target/toshiba/solvers.py +0 -0
  119. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure_quantum.egg-info/dependency_links.txt +0 -0
  120. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/azure_quantum.egg-info/top_level.txt +0 -0
  121. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/requirements-cirq.txt +0 -0
  122. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/requirements-dev.txt +0 -0
  123. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/requirements-qiskit.txt +0 -0
  124. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/requirements-quil.txt +0 -0
  125. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/setup.cfg +0 -0
  126. {azure-quantum-0.29.2 → azure-quantum-1.0.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-quantum
3
- Version: 0.29.2
3
+ Version: 1.0.0
4
4
  Summary: Python client for Azure Quantum
5
5
  Home-page: https://github.com/microsoft/azure-quantum-python
6
6
  Author: Microsoft
@@ -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 = "0.29.2"
9
+ VERSION = "1.0.0"
@@ -43,6 +43,13 @@ class AzureQuantumService:
43
43
  :param default_target: Default target name, defaults to None
44
44
  :type default_target: Optional[str], optional
45
45
  """
46
+ if kwargs is not None and len(kwargs) > 0:
47
+ from warnings import warn
48
+ warn(f"""Consider passing \"workspace\" argument explicitly.
49
+ The ability to initialize AzureQuantumService with arguments {', '.join(f'"{argName}"' for argName in kwargs)} is going to be deprecated in future versions.""",
50
+ DeprecationWarning,
51
+ stacklevel=2)
52
+
46
53
  if workspace is None:
47
54
  workspace = Workspace(**kwargs)
48
55
 
@@ -91,7 +91,7 @@ class QuantinuumTarget(Quantinuum, CirqTarget):
91
91
  serialized_program = self._translate_circuit(program)
92
92
  return super().estimate_cost(
93
93
  circuit=serialized_program,
94
- num_shots=repetitions
94
+ shots=repetitions
95
95
  )
96
96
 
97
97
  def submit(
@@ -4,6 +4,7 @@
4
4
  ##
5
5
 
6
6
  from azure.quantum.job.job import Job
7
+ from azure.quantum.job.job_failed_with_results_error import JobFailedWithResultsError
7
8
  from azure.quantum.job.workspace_item import WorkspaceItem
8
9
  from azure.quantum.job.workspace_item_factory import WorkspaceItemFactory
9
10
  from azure.quantum.job.session import Session, SessionHost
@@ -11,7 +11,7 @@ from urllib.parse import urlparse
11
11
  from typing import Any, Dict, Optional, TYPE_CHECKING
12
12
  from azure.storage.blob import BlobClient
13
13
 
14
- from azure.quantum.storage import upload_blob, download_blob, ContainerClient
14
+ from azure.quantum.storage import upload_blob, download_blob, download_blob_properties, ContainerClient
15
15
  from azure.quantum._client.models import JobDetails
16
16
  from azure.quantum.job.workspace_item import WorkspaceItem
17
17
 
@@ -26,6 +26,7 @@ DEFAULT_TIMEOUT = 300 # Default timeout for waiting for job to complete
26
26
 
27
27
  class ContentType(str, Enum):
28
28
  json = "application/json"
29
+ text_plain = "text/plain"
29
30
 
30
31
  class BaseJob(WorkspaceItem):
31
32
  # Optionally override these to create a Provider-specific Job subclass
@@ -261,6 +262,7 @@ class BaseJob(WorkspaceItem):
261
262
  )
262
263
  return uploaded_blob_uri
263
264
 
265
+
264
266
  def download_data(self, blob_uri: str) -> dict:
265
267
  """Download file from blob uri
266
268
 
@@ -269,23 +271,26 @@ class BaseJob(WorkspaceItem):
269
271
  :return: Payload from blob
270
272
  :rtype: dict
271
273
  """
272
- url = urlparse(blob_uri)
273
- if url.query.find("se=") == -1:
274
- # blob_uri does not contains SAS token,
275
- # get sas url from service
276
- blob_client = BlobClient.from_blob_url(
277
- blob_uri
278
- )
279
- blob_uri = self.workspace._get_linked_storage_sas_uri(
280
- blob_client.container_name, blob_client.blob_name
281
- )
282
- payload = download_blob(blob_uri)
283
- else:
284
- # blob_uri contains SAS token, use it
285
- payload = download_blob(blob_uri)
274
+
275
+ blob_uri_with_sas_token = self._get_blob_uri_with_sas_token(blob_uri)
276
+ payload = download_blob(blob_uri_with_sas_token)
286
277
 
287
278
  return payload
288
279
 
280
+
281
+ def download_blob_properties(self, blob_uri: str):
282
+ """Download Blob properties
283
+
284
+ :param blob_uri: Blob URI
285
+ :type blob_uri: str
286
+ :return: Blob properties
287
+ :rtype: dict
288
+ """
289
+
290
+ blob_uri_with_sas_token = self._get_blob_uri_with_sas_token(blob_uri)
291
+ return download_blob_properties(blob_uri_with_sas_token)
292
+
293
+
289
294
  def upload_attachment(
290
295
  self,
291
296
  name: str,
@@ -345,3 +350,24 @@ class BaseJob(WorkspaceItem):
345
350
  blob_client = container_client.get_blob_client(name)
346
351
  response = blob_client.download_blob().readall()
347
352
  return response
353
+
354
+
355
+ def _get_blob_uri_with_sas_token(self, blob_uri: str) -> str:
356
+ """Get Blob URI with SAS-token if one was not specified in blob_uri parameter
357
+ :param blob_uri: Blob URI
358
+ :type blob_uri: str
359
+ :return: Blob URI with SAS-token
360
+ :rtype: str
361
+ """
362
+ url = urlparse(blob_uri)
363
+ if url.query.find("se=") == -1:
364
+ # blob_uri does not contains SAS token,
365
+ # get sas url from service
366
+ blob_client = BlobClient.from_blob_url(
367
+ blob_uri
368
+ )
369
+ blob_uri = self.workspace._get_linked_storage_sas_uri(
370
+ blob_client.container_name, blob_client.blob_name
371
+ )
372
+
373
+ return blob_uri
@@ -11,6 +11,7 @@ import json
11
11
  from typing import TYPE_CHECKING
12
12
 
13
13
  from azure.quantum._client.models import JobDetails
14
+ from azure.quantum.job.job_failed_with_results_error import JobFailedWithResultsError
14
15
  from azure.quantum.job.base_job import BaseJob, ContentType, DEFAULT_TIMEOUT
15
16
  from azure.quantum.job.filtered_job import FilteredJob
16
17
 
@@ -117,6 +118,12 @@ class Job(BaseJob, FilteredJob):
117
118
  self.wait_until_completed(timeout_secs=timeout_secs)
118
119
 
119
120
  if not self.details.status == "Succeeded":
121
+ if self.details.status == "Failed" and self._allow_failure_results():
122
+ job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
123
+ if job_blob_properties.size > 0:
124
+ job_failure_data = self.download_data(self.details.output_data_uri)
125
+ raise JobFailedWithResultsError("An error occurred during job execution.", job_failure_data)
126
+
120
127
  raise RuntimeError(
121
128
  f'{"Cannot retrieve results as job execution failed"}'
122
129
  + f"(status: {self.details.status}."
@@ -126,7 +133,34 @@ class Job(BaseJob, FilteredJob):
126
133
  payload = self.download_data(self.details.output_data_uri)
127
134
  try:
128
135
  payload = payload.decode("utf8")
129
- return json.loads(payload)
136
+ results = json.loads(payload)
137
+
138
+ if self.details.output_data_format == "microsoft.quantum-results.v1":
139
+ if "Histogram" not in results:
140
+ raise f"\"Histogram\" array was expected to be in the Job results for \"{self.details.output_data_format}\" output format."
141
+
142
+ histogram_values = results["Histogram"]
143
+
144
+ if len(histogram_values) % 2 == 0:
145
+ # Re-mapping {'Histogram': ['[0]', 0.50, '[1]', 0.50] } to {'[0]': 0.50, '[1]': 0.50}
146
+ return {histogram_values[i]: histogram_values[i + 1] for i in range(0, len(histogram_values), 2)}
147
+ else:
148
+ raise f"\"Histogram\" array has invalid format. Even number of items is expected."
149
+
150
+ return results
130
151
  except:
131
152
  # If errors decoding the data, return the raw payload:
132
153
  return payload
154
+
155
+
156
+ @classmethod
157
+ def _allow_failure_results(cls) -> bool:
158
+ """
159
+ Allow to download job results even if the Job status is "Failed".
160
+
161
+ This method can be overridden in derived classes to alter the default
162
+ behaviour.
163
+
164
+ The default is False.
165
+ """
166
+ return False
@@ -0,0 +1,41 @@
1
+ import json
2
+ from typing import Any, Dict, Union
3
+
4
+ class JobFailedWithResultsError(RuntimeError):
5
+ """
6
+ Error produced when Job completes with status "Failed" and the Job
7
+ supports producing failure results.
8
+
9
+ The failure results can be accessed with get_failure_results() method
10
+ """
11
+
12
+ def __init__(self, message: str, failure_results: Any, *args: object) -> None:
13
+ self._set_error_details(message, failure_results)
14
+ super().__init__(message, *args)
15
+
16
+
17
+ def _set_error_details(self, message: str, failure_results: Any) -> None:
18
+ self._message = message
19
+ try:
20
+ decoded_failure_results = failure_results.decode("utf8")
21
+ self._failure_results: Dict[str, Any] = json.loads(decoded_failure_results)
22
+ except:
23
+ self._failure_results = failure_results
24
+
25
+
26
+ def get_message(self) -> str:
27
+ """
28
+ Get error message.
29
+ """
30
+ return self._message
31
+
32
+
33
+ def get_failure_results(self) -> Union[Dict[str, Any], str]:
34
+ """
35
+ Get failure results produced by the job.
36
+ """
37
+ return self._failure_results
38
+
39
+
40
+ def __str__(self) -> str:
41
+ return f"{self._message}\nFailure results: {self._failure_results}"
@@ -10,7 +10,7 @@ import warnings
10
10
 
11
11
  logger = logging.getLogger(__name__)
12
12
 
13
- from typing import Any, Dict, Tuple, Union, List
13
+ from typing import Any, Dict, Tuple, Union, List, Optional
14
14
  from azure.quantum.version import __version__
15
15
  from azure.quantum.qiskit.job import (
16
16
  MICROSOFT_OUTPUT_DATA_FORMAT,
@@ -38,6 +38,11 @@ To install run: pip install azure-quantum[qiskit]"
38
38
 
39
39
 
40
40
  class AzureBackendBase(Backend, SessionHost):
41
+
42
+ # Name of the provider's input parameter which specifies number of shots for a submitted job.
43
+ # If None, backend will not pass this input parameter.
44
+ _SHOTS_PARAM_NAME = None
45
+
41
46
  @abstractmethod
42
47
  def __init__(
43
48
  self,
@@ -46,6 +51,41 @@ class AzureBackendBase(Backend, SessionHost):
46
51
  **fields
47
52
  ):
48
53
  super().__init__(configuration, provider, **fields)
54
+
55
+ @abstractmethod
56
+ def run(
57
+ self,
58
+ run_input: Union[QuantumCircuit, List[QuantumCircuit]] = [],
59
+ shots: int = None,
60
+ **options,
61
+ ) -> AzureQuantumJob:
62
+ """Run on the backend.
63
+
64
+ This method returns a
65
+ :class:`~azure.quantum.qiskit.job.AzureQuantumJob` object
66
+ that runs circuits.
67
+
68
+ Args:
69
+ run_input (QuantumCircuit or List[QuantumCircuit]): An individual or a
70
+ list of :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
71
+ shots (int, optional): Number of shots, defaults to None.
72
+ options: Any kwarg options to pass to the backend for running the
73
+ config. If a key is also present in the options
74
+ attribute/object then the expectation is that the value
75
+ specified will be used instead of what's set in the options
76
+ object.
77
+
78
+ Returns:
79
+ Job: The job object for the run
80
+ """
81
+ pass
82
+
83
+ @classmethod
84
+ def _can_send_shots_input_param(cls) -> bool:
85
+ """
86
+ Tells if provider's backend class is able to specify shots number for its jobs.
87
+ """
88
+ return cls._SHOTS_PARAM_NAME is not None
49
89
 
50
90
  @classmethod
51
91
  @abstractmethod
@@ -80,30 +120,47 @@ class AzureBackendBase(Backend, SessionHost):
80
120
  output_data_format = options.pop("output_data_format", azure_defined_override)
81
121
 
82
122
  return output_data_format
83
-
84
- def _get_input_params(self, options) -> Dict[str, Any]:
123
+
124
+ def _get_input_params(self, options, shots: int = None) -> Dict[str, Any]:
85
125
  # Backend options are mapped to input_params.
86
126
  input_params: Dict[str, Any] = vars(self.options).copy()
87
127
 
88
- # The shots/count number can be specified in different ways for different providers,
89
- # so let's get it first. Values in 'kwargs' take precedence over options, and to keep
90
- # the convention, 'count' takes precedence over 'shots' afterwards.
91
- shots_count = (
92
- options["count"]
93
- if "count" in options
94
- else options["shots"]
95
- if "shots" in options
96
- else input_params["count"]
97
- if "count" in input_params
98
- else input_params["shots"]
99
- if "shots" in input_params
100
- else None
101
- )
128
+ # Determine shots number, if needed.
129
+ if self._can_send_shots_input_param():
130
+ options_shots = options.pop(self.__class__._SHOTS_PARAM_NAME, None)
131
+
132
+ # First we check for the explicitly specified 'shots' parameter, then for a provider-specific
133
+ # field in options, then for a backend's default value.
134
+
135
+ # Warn abount options conflict, default to 'shots'.
136
+ if shots is not None and options_shots is not None:
137
+ warnings.warn(
138
+ f"Parameter 'shots' conflicts with the '{self.__class__._SHOTS_PARAM_NAME}' parameter. "
139
+ "Please, provide only one option for setting shots. Defaulting to 'shots' parameter."
140
+ )
141
+ final_shots = shots
142
+
143
+ elif shots is not None:
144
+ final_shots = shots
145
+ else:
146
+ warnings.warn(
147
+ f"Parameter '{self.__class__._SHOTS_PARAM_NAME}' is subject to change in future versions. "
148
+ "Please, use 'shots' parameter instead."
149
+ )
150
+ final_shots = options_shots
151
+
152
+ # If nothing is found, try to get from default values.
153
+ if final_shots is None:
154
+ final_shots = input_params.get(self.__class__._SHOTS_PARAM_NAME)
155
+
156
+ # Also add all possible shots options into input_params to make sure
157
+ # that all backends covered.
158
+ # TODO: Double check all backends for shots options in order to remove this extra check.
159
+ input_params["shots"] = final_shots
160
+ input_params["count"] = final_shots
102
161
 
103
- # Let's clear the options of both properties regardless of which one was used to prevent
104
- # double specification of the value.
105
- options.pop("shots", None)
106
- options.pop("count", None)
162
+ input_params[self.__class__._SHOTS_PARAM_NAME] = final_shots
163
+
107
164
 
108
165
  if "items" in options:
109
166
  input_params["items"] = options.pop("items")
@@ -114,9 +171,6 @@ class AzureBackendBase(Backend, SessionHost):
114
171
  if opt in input_params:
115
172
  input_params[opt] = options.pop(opt)
116
173
 
117
- input_params["count"] = shots_count
118
- input_params["shots"] = shots_count
119
-
120
174
  return input_params
121
175
 
122
176
  def _run(self, job_name, input_data, input_params, metadata, **options):
@@ -223,18 +277,21 @@ class AzureQirBackend(AzureBackendBase):
223
277
  }
224
278
 
225
279
  def run(
226
- self, run_input: Union[QuantumCircuit, List[QuantumCircuit]] = [], **options
280
+ self,
281
+ run_input: Union[QuantumCircuit, List[QuantumCircuit]] = [],
282
+ shots: int = None,
283
+ **options,
227
284
  ) -> AzureQuantumJob:
228
285
  """Run on the backend.
229
286
 
230
287
  This method returns a
231
288
  :class:`~azure.quantum.qiskit.job.AzureQuantumJob` object
232
- that runs circuits. This is an async call.
289
+ that runs circuits.
233
290
 
234
291
  Args:
235
292
  run_input (QuantumCircuit or List[QuantumCircuit]): An individual or a
236
293
  list of :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
237
-
294
+ shots (int, optional): Number of shots, defaults to None.
238
295
  options: Any kwarg options to pass to the backend for running the
239
296
  config. If a key is also present in the options
240
297
  attribute/object then the expectation is that the value
@@ -261,13 +318,18 @@ class AzureQirBackend(AzureBackendBase):
261
318
  )
262
319
 
263
320
  # config normalization
264
- input_params = self._get_input_params(options)
321
+ input_params = self._get_input_params(options, shots=shots)
322
+
323
+ shots_count = None
265
324
 
266
- shots_count = input_params["count"]
325
+ if self._can_send_shots_input_param():
326
+ shots_count = input_params.get(self.__class__._SHOTS_PARAM_NAME)
267
327
 
268
328
  job_name = ""
269
329
  if len(circuits) > 1:
270
- job_name = f"batch-{len(circuits)}-{shots_count}"
330
+ job_name = f"batch-{len(circuits)}"
331
+ if shots_count is not None:
332
+ job_name = f"{job_name}-{shots_count}"
271
333
  else:
272
334
  job_name = circuits[0].name
273
335
  job_name = options.pop("job_name", job_name)
@@ -386,9 +448,13 @@ class AzureBackend(AzureBackendBase):
386
448
  def _translate_input(self, circuit):
387
449
  pass
388
450
 
389
- def run(self, run_input=None, **kwargs):
390
- """Submits the given circuit to run on an Azure Quantum backend."""
391
- options = kwargs
451
+ def run(
452
+ self,
453
+ run_input: Union[QuantumCircuit, List[QuantumCircuit]] = [],
454
+ shots: int = None,
455
+ **options,
456
+ ):
457
+ """Submits the given circuit to run on an Azure Quantum backend."""
392
458
  circuit = self._normalize_run_input_params(run_input, **options)
393
459
  options.pop("run_input", None)
394
460
  options.pop("circuit", None)
@@ -417,16 +483,45 @@ class AzureBackend(AzureBackendBase):
417
483
  job_name = options.pop("job_name", circuit.name)
418
484
  metadata = options.pop("metadata", self._prepare_job_metadata(circuit))
419
485
 
420
- input_params = self._get_input_params(options)
486
+ input_params = self._get_input_params(options, shots=shots)
421
487
 
422
488
  input_data = self._translate_input(circuit)
423
489
 
424
490
  job = super()._run(job_name, input_data, input_params, metadata, **options)
425
491
 
426
- shots_count = input_params["count"]
492
+ shots_count = None
493
+ if self._can_send_shots_input_param():
494
+ shots_count = input_params.get(self.__class__._SHOTS_PARAM_NAME)
495
+
427
496
  logger.info(
428
497
  f"Submitted job with id '{job.id()}' for circuit '{circuit.name}' with shot count of {shots_count}:"
429
498
  )
430
499
  logger.info(input_data)
431
500
 
432
501
  return job
502
+
503
+ def _get_shots_or_deprecated_count_input_param(
504
+ param_name: str,
505
+ shots: int = None,
506
+ count: int = None,
507
+ ) -> Optional[int]:
508
+ """
509
+ This helper function checks if the deprecated 'count' option is specified.
510
+ In earlier versions it was possible to pass this option to specify shots number for a job,
511
+ but now we only check for it for compatibility reasons.
512
+ """
513
+
514
+ final_shots = None
515
+
516
+ if shots is not None:
517
+ final_shots = shots
518
+
519
+ elif count is not None:
520
+ final_shots = count
521
+ warnings.warn(
522
+ "The 'count' parameter will be deprecated. "
523
+ f"Please, use '{param_name}' parameter instead.",
524
+ category=DeprecationWarning,
525
+ )
526
+
527
+ return final_shots
@@ -2,12 +2,19 @@
2
2
  # Copyright (c) Microsoft Corporation.
3
3
  # Licensed under the MIT License.
4
4
  ##
5
- from typing import TYPE_CHECKING, Dict
5
+ from typing import TYPE_CHECKING, Dict, List, Union
6
6
  from azure.quantum import __version__
7
+ from azure.quantum.qiskit.job import AzureQuantumJob
7
8
  from azure.quantum.target.ionq import IonQ
8
9
  from abc import abstractmethod
9
10
 
10
- from .backend import AzureBackend, AzureQirBackend
11
+ from qiskit import QuantumCircuit
12
+
13
+ from .backend import (
14
+ AzureBackend,
15
+ AzureQirBackend,
16
+ _get_shots_or_deprecated_count_input_param
17
+ )
11
18
 
12
19
  from qiskit.providers.models import BackendConfiguration
13
20
  from qiskit.providers import Options, Provider
@@ -42,10 +49,14 @@ __all__ = [
42
49
  "IonQForteNativeBackend",
43
50
  ]
44
51
 
52
+ _IONQ_SHOTS_INPUT_PARAM_NAME = "shots"
53
+ _DEFAULT_SHOTS_COUNT = 500
45
54
 
46
55
  class IonQQirBackendBase(AzureQirBackend):
47
56
  """Base class for interfacing with an IonQ QIR backend"""
48
57
 
58
+ _SHOTS_PARAM_NAME = _IONQ_SHOTS_INPUT_PARAM_NAME
59
+
49
60
  @abstractmethod
50
61
  def __init__(
51
62
  self, configuration: BackendConfiguration, provider: Provider = None, **fields
@@ -54,7 +65,12 @@ class IonQQirBackendBase(AzureQirBackend):
54
65
 
55
66
  @classmethod
56
67
  def _default_options(cls) -> Options:
57
- return Options(shots=500, targetCapability="BasicExecution")
68
+ return Options(
69
+ **{
70
+ cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
71
+ },
72
+ targetCapability="BasicExecution",
73
+ )
58
74
 
59
75
  def _azure_config(self) -> Dict[str, str]:
60
76
  config = super()._azure_config()
@@ -64,6 +80,25 @@ class IonQQirBackendBase(AzureQirBackend):
64
80
  }
65
81
  )
66
82
  return config
83
+
84
+ def run(
85
+ self,
86
+ run_input: Union[QuantumCircuit, List[QuantumCircuit]] = [],
87
+ shots: int = None,
88
+ **options,
89
+ ) -> AzureQuantumJob:
90
+
91
+ # In earlier versions, backends for all providers accepted the 'count' option,
92
+ # but now we accept it only for a compatibility reasons and do not recommend using it.
93
+ count = options.pop("count", None)
94
+
95
+ final_shots = _get_shots_or_deprecated_count_input_param(
96
+ param_name=self.__class__._SHOTS_PARAM_NAME,
97
+ shots=shots,
98
+ count=count,
99
+ )
100
+
101
+ return super().run(run_input, shots=final_shots, **options)
67
102
 
68
103
 
69
104
  class IonQSimulatorQirBackend(IonQQirBackendBase):
@@ -199,15 +234,40 @@ class IonQBackend(AzureBackend):
199
234
 
200
235
  backend_name = None
201
236
 
237
+ _SHOTS_PARAM_NAME = _IONQ_SHOTS_INPUT_PARAM_NAME
238
+
202
239
  @abstractmethod
203
240
  def __init__(
204
241
  self, configuration: BackendConfiguration, provider: Provider = None, **fields
205
242
  ):
206
243
  super().__init__(configuration, provider, **fields)
207
244
 
245
+ def run(
246
+ self,
247
+ run_input=None,
248
+ shots: int = None,
249
+ **options,
250
+ ) -> AzureQuantumJob:
251
+
252
+ # In earlier versions, backends for all providers accepted the 'count' option,
253
+ # but now we accept it only for a compatibility reasons and do not recommend using it.
254
+ count = options.pop("count", None)
255
+
256
+ final_shots = _get_shots_or_deprecated_count_input_param(
257
+ param_name=self.__class__._SHOTS_PARAM_NAME,
258
+ shots=shots,
259
+ count=count,
260
+ )
261
+
262
+ return super().run(run_input, shots=final_shots, **options)
263
+
208
264
  @classmethod
209
265
  def _default_options(cls):
210
- return Options(shots=500)
266
+ return Options(
267
+ **{
268
+ cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
269
+ },
270
+ )
211
271
 
212
272
  def _azure_config(self) -> Dict[str, str]:
213
273
  return {
@@ -249,7 +309,7 @@ class IonQBackend(AzureBackend):
249
309
  }
250
310
  workspace = self.provider().get_workspace()
251
311
  target = workspace.get_targets(self.name())
252
- return target.estimate_cost(input_data, num_shots=shots)
312
+ return target.estimate_cost(input_data, shots=shots)
253
313
 
254
314
 
255
315
  class IonQSimulatorBackend(IonQBackend):
@@ -5,8 +5,12 @@
5
5
 
6
6
  from typing import TYPE_CHECKING, Dict
7
7
  from azure.quantum.version import __version__
8
+ from azure.quantum.qiskit.job import AzureQuantumJob
8
9
  from abc import abstractmethod
9
- from .backend import AzureQirBackend
10
+ from .backend import (
11
+ AzureQirBackend,
12
+ _get_shots_or_deprecated_count_input_param,
13
+ )
10
14
 
11
15
  from qiskit.providers.models import BackendConfiguration
12
16
  from qiskit.providers import Options, Provider
@@ -43,7 +47,12 @@ logger = logging.getLogger(__name__)
43
47
  __all__ = ["QCISimulatorBackend" "QCIQPUBackend"]
44
48
 
45
49
 
50
+ _DEFAULT_SHOTS_COUNT = 500
51
+
46
52
  class QCIBackend(AzureQirBackend):
53
+
54
+ _SHOTS_PARAM_NAME = "shots"
55
+
47
56
  @abstractmethod
48
57
  def __init__(
49
58
  self, configuration: BackendConfiguration, provider: Provider = None, **fields
@@ -52,7 +61,12 @@ class QCIBackend(AzureQirBackend):
52
61
 
53
62
  @classmethod
54
63
  def _default_options(cls) -> Options:
55
- return Options(shots=500, targetCapability="AdaptiveExecution")
64
+ return Options(
65
+ **{
66
+ cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
67
+ },
68
+ targetCapability="AdaptiveExecution",
69
+ )
56
70
 
57
71
  def _azure_config(self) -> Dict[str, str]:
58
72
  config = super()._azure_config()
@@ -62,6 +76,25 @@ class QCIBackend(AzureQirBackend):
62
76
  }
63
77
  )
64
78
  return config
79
+
80
+ def run(
81
+ self,
82
+ run_input=None,
83
+ shots: int = None,
84
+ **options,
85
+ ) -> AzureQuantumJob:
86
+
87
+ # In earlier versions, backends for all providers accepted the 'count' option,
88
+ # but now we accept it only for a compatibility reasons and do not recommend using it.
89
+ count = options.pop("count", None)
90
+
91
+ final_shots = _get_shots_or_deprecated_count_input_param(
92
+ param_name=self.__class__._SHOTS_PARAM_NAME,
93
+ shots=shots,
94
+ count=count,
95
+ )
96
+
97
+ return super().run(run_input, shots=final_shots, **options)
65
98
 
66
99
 
67
100
  class QCISimulatorBackend(QCIBackend):