azure-quantum 3.4.1.dev1__tar.gz → 3.5.0.dev0__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 (76) hide show
  1. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/PKG-INFO +5 -13
  2. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/_version.py +1 -1
  3. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/models/_enums.py +10 -0
  4. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/models/_models.py +4 -4
  5. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/targets/ionq.py +5 -0
  6. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/job.py +5 -4
  7. azure_quantum-3.5.0.dev0/azure/quantum/qiskit/backends/_qiskit_ionq.py +377 -0
  8. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/backends/backend.py +370 -138
  9. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/backends/ionq.py +27 -34
  10. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/backends/qci.py +12 -14
  11. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/backends/quantinuum.py +46 -44
  12. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/backends/rigetti.py +10 -14
  13. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/job.py +21 -14
  14. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/provider.py +12 -6
  15. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/pasqal/result.py +1 -1
  16. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/rigetti/result.py +1 -1
  17. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/target.py +4 -56
  18. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/version.py +1 -1
  19. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure_quantum.egg-info/PKG-INFO +5 -13
  20. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure_quantum.egg-info/SOURCES.txt +1 -0
  21. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure_quantum.egg-info/requires.txt +4 -12
  22. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/requirements-dev.txt +1 -0
  23. azure_quantum-3.5.0.dev0/requirements-qiskit.txt +1 -0
  24. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/requirements.txt +0 -2
  25. azure_quantum-3.4.1.dev1/requirements-qiskit.txt +0 -5
  26. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/README.md +0 -0
  27. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/__init__.py +0 -0
  28. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/__init__.py +0 -0
  29. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/_client.py +0 -0
  30. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/_configuration.py +0 -0
  31. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/_model_base.py +0 -0
  32. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/_patch.py +0 -0
  33. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/_serialization.py +0 -0
  34. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/models/__init__.py +0 -0
  35. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/models/_patch.py +0 -0
  36. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/operations/__init__.py +0 -0
  37. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/operations/_operations.py +0 -0
  38. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_client/operations/_patch.py +0 -0
  39. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_constants.py +0 -0
  40. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/_workspace_connection_params.py +0 -0
  41. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/argument_types/__init__.py +0 -0
  42. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/argument_types/types.py +0 -0
  43. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/__init__.py +0 -0
  44. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/job.py +0 -0
  45. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/service.py +0 -0
  46. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/targets/__init__.py +0 -0
  47. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/targets/quantinuum.py +0 -0
  48. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/cirq/targets/target.py +0 -0
  49. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/__init__.py +0 -0
  50. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/base_job.py +0 -0
  51. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/filtered_job.py +0 -0
  52. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/job_failed_with_results_error.py +0 -0
  53. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/session.py +0 -0
  54. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/workspace_item.py +0 -0
  55. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/job/workspace_item_factory.py +0 -0
  56. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/__init__.py +0 -0
  57. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/qiskit/backends/__init__.py +0 -0
  58. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/storage.py +0 -0
  59. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/__init__.py +0 -0
  60. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/ionq.py +0 -0
  61. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/params.py +0 -0
  62. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/pasqal/__init__.py +0 -0
  63. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/pasqal/target.py +0 -0
  64. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/quantinuum.py +0 -0
  65. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/rigetti/__init__.py +0 -0
  66. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/rigetti/target.py +0 -0
  67. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/target/target_factory.py +0 -0
  68. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure/quantum/workspace.py +0 -0
  69. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure_quantum.egg-info/dependency_links.txt +0 -0
  70. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/azure_quantum.egg-info/top_level.txt +0 -0
  71. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/requirements-cirq.txt +0 -0
  72. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/requirements-pulser.txt +0 -0
  73. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/requirements-qsharp.txt +0 -0
  74. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/requirements-quil.txt +0 -0
  75. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/setup.cfg +0 -0
  76. {azure_quantum-3.4.1.dev1 → azure_quantum-3.5.0.dev0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-quantum
3
- Version: 3.4.1.dev1
3
+ Version: 3.5.0.dev0
4
4
  Summary: Python client for Azure Quantum
5
5
  Home-page: https://github.com/microsoft/azure-quantum-python
6
6
  Author: Microsoft
@@ -16,8 +16,6 @@ Requires-Dist: azure-storage-blob==12.20
16
16
  Requires-Dist: msrest<1.0,>=0.7.1
17
17
  Requires-Dist: numpy>=1.21.0
18
18
  Requires-Dist: deprecated<2.0,>=1.2.12
19
- Requires-Dist: Markdown>=3.4.1
20
- Requires-Dist: python-markdown-math>=0.8
21
19
  Provides-Extra: cirq
22
20
  Requires-Dist: cirq-core<=1.4.1,>=1.3.0; extra == "cirq"
23
21
  Requires-Dist: cirq-ionq<=1.4.1,>=1.3.0; extra == "cirq"
@@ -27,14 +25,11 @@ Requires-Dist: pytest-xdist<4.0,>=3.8.0; extra == "dev"
27
25
  Requires-Dist: vcrpy>=4.3.1; extra == "dev"
28
26
  Requires-Dist: azure-devtools<2.0,>=1.2.0; extra == "dev"
29
27
  Requires-Dist: graphviz>=0.20.1; extra == "dev"
28
+ Requires-Dist: tox>=4.32.0; extra == "dev"
30
29
  Provides-Extra: pulser
31
30
  Requires-Dist: pulser<0.19,>=0.18; extra == "pulser"
32
31
  Provides-Extra: qiskit
33
- Requires-Dist: qiskit-ionq<0.6,>=0.5; extra == "qiskit"
34
- Requires-Dist: qsharp[qiskit]<2.0,>=1.9.0; extra == "qiskit"
35
- Requires-Dist: pyqir<0.11,>=0.10.6; extra == "qiskit"
36
- Requires-Dist: Markdown<4.0,>=3.4.1; extra == "qiskit"
37
- Requires-Dist: python-markdown-math<1.0,>=0.8.0; extra == "qiskit"
32
+ Requires-Dist: qsharp[qiskit]<2.0,>=1.22.0; extra == "qiskit"
38
33
  Provides-Extra: qsharp
39
34
  Requires-Dist: qsharp<2.0,>=1.0.33; extra == "qsharp"
40
35
  Provides-Extra: quil
@@ -47,12 +42,9 @@ Requires-Dist: pytest-xdist<4.0,>=3.8.0; extra == "all"
47
42
  Requires-Dist: vcrpy>=4.3.1; extra == "all"
48
43
  Requires-Dist: azure-devtools<2.0,>=1.2.0; extra == "all"
49
44
  Requires-Dist: graphviz>=0.20.1; extra == "all"
45
+ Requires-Dist: tox>=4.32.0; extra == "all"
50
46
  Requires-Dist: pulser<0.19,>=0.18; extra == "all"
51
- Requires-Dist: qiskit-ionq<0.6,>=0.5; extra == "all"
52
- Requires-Dist: qsharp[qiskit]<2.0,>=1.9.0; extra == "all"
53
- Requires-Dist: pyqir<0.11,>=0.10.6; extra == "all"
54
- Requires-Dist: Markdown<4.0,>=3.4.1; extra == "all"
55
- Requires-Dist: python-markdown-math<1.0,>=0.8.0; extra == "all"
47
+ Requires-Dist: qsharp[qiskit]<2.0,>=1.22.0; extra == "all"
56
48
  Requires-Dist: qsharp<2.0,>=1.0.33; extra == "all"
57
49
  Requires-Dist: pyquil==4.13.1; extra == "all"
58
50
 
@@ -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 = "3.4.1.dev1"
9
+ VERSION = "3.5.0.dev0"
@@ -33,8 +33,18 @@ class JobStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta):
33
33
 
34
34
  WAITING = "Waiting"
35
35
  """The job is waiting in the queue to be executed."""
36
+ QUEUED = "Queued"
37
+ """The job is queued for execution."""
36
38
  EXECUTING = "Executing"
37
39
  """The job is being executed."""
40
+ FINISHING = "Finishing"
41
+ """The job is in the process of finishing."""
42
+ CANCELLATION_REQUESTED = "CancellationRequested"
43
+ """Cancellation for the job has been requested."""
44
+ CANCELLING = "Cancelling"
45
+ """The job is in the process of being cancelled."""
46
+ COMPLETED = "Completed"
47
+ """The job has completed execution."""
38
48
  SUCCEEDED = "Succeeded"
39
49
  """The job completed with success."""
40
50
  FAILED = "Failed"
@@ -252,8 +252,8 @@ class JobDetails(ItemDetails, discriminator="Job"):
252
252
  :vartype input_data_uri: str
253
253
  :ivar input_data_format: The format of the input data.
254
254
  :vartype input_data_format: str
255
- :ivar status: The status of the job. Known values are: "Waiting", "Executing", "Succeeded",
256
- "Failed", and "Cancelled".
255
+ :ivar status: The status of the job. Known values are: "Waiting", "Queued", "Executing", "Finishing", "Completed",
256
+ "Succeeded", "Failed", "Cancelling", "CancellationRequested" and "Cancelled".
257
257
  :vartype status: str or ~azure.quantum.models.JobStatus
258
258
  :ivar metadata: The job metadata. Metadata provides client the ability to store client-specific
259
259
  information.
@@ -289,8 +289,8 @@ class JobDetails(ItemDetails, discriminator="Job"):
289
289
  input_data_format: Optional[str] = rest_field(name="inputDataFormat", visibility=["read", "create"])
290
290
  """The format of the input data."""
291
291
  status: Optional[Union[str, "_models.JobStatus"]] = rest_field(visibility=["read"])
292
- """The status of the job. Known values are: \"Waiting\", \"Executing\", \"Succeeded\", \"Failed\",
293
- and \"Cancelled\"."""
292
+ """The status of the job. Known values are: \"Waiting\", \"Queued\", \"Executing\", \"Finishing\", \"Completed\",
293
+ \"Succeeded\", \"Failed\", \"Cancelling\", \"CancellationRequested\" and \"Cancelled\"."""
294
294
  metadata: Optional[Any] = rest_field(visibility=["read", "create", "update"])
295
295
  """The job metadata. Metadata provides client the ability to store client-specific information."""
296
296
  cancellation_time: Optional[datetime.datetime] = rest_field(
@@ -30,10 +30,15 @@ class _IonQClient:
30
30
  def _to_ionq_status(status: str):
31
31
  from azure.quantum._client.models._enums import JobStatus
32
32
  _STATUS_DICT = {
33
+ JobStatus.COMPLETED: 'completed',
33
34
  JobStatus.SUCCEEDED: 'completed',
34
35
  JobStatus.CANCELLED: 'canceled',
35
36
  JobStatus.FAILED: 'failed',
36
37
  JobStatus.EXECUTING: 'running',
38
+ JobStatus.FINISHING: 'running',
39
+ JobStatus.CANCELLATION_REQUESTED: 'running',
40
+ JobStatus.CANCELLING: 'running',
41
+ JobStatus.QUEUED: 'ready',
37
42
  JobStatus.WAITING: 'ready'
38
43
  }
39
44
  return _STATUS_DICT.get(status, 'submitted')
@@ -59,7 +59,8 @@ class Job(BaseJob, FilteredJob):
59
59
  def has_completed(self) -> bool:
60
60
  """Check if the job has completed."""
61
61
  return (
62
- self.details.status == "Succeeded"
62
+ self.details.status == "Completed"
63
+ or self.details.status == "Succeeded"
63
64
  or self.details.status == "Failed"
64
65
  or self.details.status == "Cancelled"
65
66
  )
@@ -124,7 +125,7 @@ class Job(BaseJob, FilteredJob):
124
125
  if not self.has_completed():
125
126
  self.wait_until_completed(timeout_secs=timeout_secs)
126
127
 
127
- if not self.details.status == "Succeeded":
128
+ if not self.details.status == "Succeeded" or self.details.status == "Completed":
128
129
  if self.details.status == "Failed" and self._allow_failure_results():
129
130
  job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
130
131
  if job_blob_properties.size > 0:
@@ -204,7 +205,7 @@ class Job(BaseJob, FilteredJob):
204
205
  if not self.has_completed():
205
206
  self.wait_until_completed(timeout_secs=timeout_secs)
206
207
 
207
- if not self.details.status == "Succeeded":
208
+ if not self.details.status == "Succeeded" or self.details.status == "Completed":
208
209
  if self.details.status == "Failed" and self._allow_failure_results():
209
210
  job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
210
211
  if job_blob_properties.size > 0:
@@ -287,7 +288,7 @@ class Job(BaseJob, FilteredJob):
287
288
  if not self.has_completed():
288
289
  self.wait_until_completed(timeout_secs=timeout_secs)
289
290
 
290
- if not self.details.status == "Succeeded":
291
+ if not self.details.status == "Succeeded" or self.details.status == "Completed":
291
292
  if self.details.status == "Failed" and self._allow_failure_results():
292
293
  job_blob_properties = self.download_blob_properties(self.details.output_data_uri)
293
294
  if job_blob_properties.size > 0:
@@ -0,0 +1,377 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2017, 2018.
4
+ #
5
+ # This code is licensed under the Apache License, Version 2.0. You may
6
+ # obtain a copy of this license in the LICENSE.txt file in the root directory
7
+ # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
+ #
9
+ # Any modifications or derivative works of this code must retain this
10
+ # copyright notice, and modified files need to carry a notice indicating
11
+ # that they have been altered from the originals.
12
+
13
+ # Copyright 2020 IonQ, Inc. (www.ionq.com)
14
+ #
15
+ # Licensed under the Apache License, Version 2.0 (the "License");
16
+ # you may not use this file except in compliance with the License.
17
+ # You may obtain a copy of the License at
18
+ #
19
+ # http://www.apache.org/licenses/LICENSE-2.0
20
+ #
21
+ # Unless required by applicable law or agreed to in writing, software
22
+ # distributed under the License is distributed on an "AS IS" BASIS,
23
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
+ # See the License for the specific language governing permissions and
25
+ # limitations under the License.
26
+
27
+ # This code is copied from:
28
+ # - <https://github.com/qiskit-community/qiskit-ionq/blob/cea8f9874b992f82a35648582c06958869370c69/qiskit_ionq/helpers.py>
29
+ # - <https://github.com/qiskit-community/qiskit-ionq/blob/cea8f9874b992f82a35648582c06958869370c69/qiskit_ionq/exceptions.py>
30
+ # to enable Qiskit 1 & 2 compatibility as the original package only
31
+ # supports Qiskit 1 OR 2 depending on the package version.
32
+ # Modified by Microsoft Corporation for Azure Quantum integration and BackendV2 support (2025-11-10).
33
+
34
+ # the qiskit gates that the IonQ backend can serialize to our IR
35
+ # not the actual hardware basis gates for the system — we do our own transpilation pass.
36
+ # also not an exact/complete list of the gates IonQ's backend takes
37
+ # by name — please refer to IonQ docs for that.
38
+ #
39
+ # Some of these gates may be deprecated or removed in qiskit 1.0
40
+ ionq_basis_gates = [
41
+ "ccx",
42
+ "ch",
43
+ "cnot",
44
+ "cp",
45
+ "crx",
46
+ "cry",
47
+ "crz",
48
+ "csx",
49
+ "cx",
50
+ "cy",
51
+ "cz",
52
+ "h",
53
+ "i",
54
+ "id",
55
+ "mcp",
56
+ "mcphase",
57
+ "mct",
58
+ "mcx",
59
+ "measure",
60
+ "p",
61
+ "rx",
62
+ "rxx",
63
+ "ry",
64
+ "ryy",
65
+ "rz",
66
+ "rzz",
67
+ "s",
68
+ "sdg",
69
+ "swap",
70
+ "sx",
71
+ "sxdg",
72
+ "t",
73
+ "tdg",
74
+ "toffoli",
75
+ "x",
76
+ "y",
77
+ "z",
78
+ "PauliEvolution",
79
+ ]
80
+
81
+ # https://ionq.com/docs/getting-started-with-native-gates
82
+ ionq_native_basis_gates = [
83
+ "gpi",
84
+ "gpi2",
85
+ "ms", # Pairwise MS gate
86
+ "zz", # ZZ gate
87
+ ]
88
+
89
+ # Each language corresponds to a different set of basis gates.
90
+ GATESET_MAP = {
91
+ "qis": ionq_basis_gates,
92
+ "native": ionq_native_basis_gates,
93
+ }
94
+
95
+ ionq_api_aliases = { # todo fix alias bug
96
+ "cp": "cz",
97
+ "csx": "cv",
98
+ "mcphase": "cz",
99
+ "ccx": "cx", # just one C for all mcx
100
+ "mcx": "cx", # just one C for all mcx
101
+ "tdg": "ti",
102
+ "p": "z",
103
+ "PauliEvolution": "pauliexp",
104
+ "rxx": "xx",
105
+ "ryy": "yy",
106
+ "rzz": "zz",
107
+ "sdg": "si",
108
+ "sx": "v",
109
+ "sxdg": "vi",
110
+ }
111
+
112
+ from qiskit.circuit import (
113
+ controlledgate as q_cgates,
114
+ QuantumCircuit,
115
+ )
116
+
117
+ from typing import Literal, Any
118
+
119
+ from qiskit.exceptions import QiskitError
120
+
121
+ class IonQError(QiskitError):
122
+ """Base class for errors raised by an IonQProvider."""
123
+
124
+ def __str__(self) -> str:
125
+ return f"{self.__class__.__name__}({self.message!r})"
126
+
127
+ def __repr__(self) -> str:
128
+ return repr(str(self))
129
+
130
+ class JobError(QiskitError):
131
+ """Base class for errors raised by Jobs."""
132
+
133
+ pass
134
+
135
+ class IonQGateError(IonQError, JobError):
136
+ """Errors generated from invalid gate defs
137
+
138
+ Attributes:
139
+ gate_name: The name of the gate which caused this error.
140
+ """
141
+
142
+ def __init__(self, gate_name: str, gateset: Literal["qis", "native"]):
143
+ self.gate_name = gate_name
144
+ self.gateset = gateset
145
+ super().__init__(
146
+ (
147
+ f"gate '{gate_name}' is not supported on the '{gateset}' IonQ backends. "
148
+ "Please use the qiskit.transpile method, manually rewrite to remove the gate, "
149
+ "or change the gateset selection as appropriate."
150
+ )
151
+ )
152
+
153
+ def __repr__(self):
154
+ return f"{self.__class__.__name__}(gate_name={self.gate_name!r}, gateset={self.gateset!r})"
155
+
156
+ class IonQMidCircuitMeasurementError(IonQError, JobError):
157
+ """Errors generated from attempting mid-circuit measurement, which is not supported.
158
+ Measurement must come after all instructions.
159
+
160
+ Attributes:
161
+ qubit_index: The qubit index to be measured mid-circuit
162
+ """
163
+
164
+ def __init__(self, qubit_index: int, gate_name: str):
165
+ self.qubit_index = qubit_index
166
+ self.gate_name = gate_name
167
+ super().__init__(
168
+ f"Attempting to put '{gate_name}' after a measurement on qubit {qubit_index}. "
169
+ "Mid-circuit measurement is not supported."
170
+ )
171
+
172
+ def __str__(self):
173
+ kwargs = f"qubit_index={self.qubit_index!r}, gate_name={self.gate_name!r}"
174
+ return f"{self.__class__.__name__}({kwargs})"
175
+
176
+ class IonQPauliExponentialError(IonQError):
177
+ """Errors generated from improper usage of Pauli exponentials."""
178
+
179
+ def paulis_commute(pauli_terms: list[str]) -> bool:
180
+ """Check if a list of Pauli terms commute.
181
+
182
+ Args:
183
+ pauli_terms (list): A list of Pauli terms.
184
+
185
+ Returns:
186
+ bool: Whether the Pauli terms commute.
187
+ """
188
+ for i, term in enumerate(pauli_terms):
189
+ for other_term in pauli_terms[i:]:
190
+ assert len(term) == len(other_term)
191
+ anticommutation_parity = 0
192
+ for index, char in enumerate(term):
193
+ other_char = other_term[index]
194
+ if "I" not in (char, other_char):
195
+ if char != other_char:
196
+ anticommutation_parity += 1
197
+ if anticommutation_parity % 2 == 1:
198
+ return False
199
+ return True
200
+
201
+ def qiskit_circ_to_ionq_circ(
202
+ input_circuit: QuantumCircuit,
203
+ gateset: Literal["qis", "native"] = "qis",
204
+ ionq_compiler_synthesis: bool = False,
205
+ ):
206
+ """Build a circuit in IonQ's instruction format from qiskit instructions.
207
+
208
+ .. ATTENTION:: This function ignores the following compiler directives:
209
+ * ``barrier``
210
+
211
+ Parameters:
212
+ input_circuit (:class:`qiskit.circuit.QuantumCircuit`): A Qiskit quantum circuit.
213
+ gateset (string): Set of gates to target. It can be QIS (required transpilation pass in
214
+ IonQ backend, which is sent standard gates) or native (only IonQ native gates are
215
+ allowed, in the future we may provide transpilation to these gates in Qiskit).
216
+ ionq_compiler_synthesis (bool): Whether to opt-in to IonQ compiler's intelligent
217
+ trotterization.
218
+
219
+ Raises:
220
+ IonQGateError: If an unsupported instruction is supplied.
221
+ IonQMidCircuitMeasurementError: If a mid-circuit measurement is detected.
222
+ IonQPauliExponentialError: If non-commuting PauliExponentials are found without
223
+ the appropriate flag.
224
+
225
+ Returns:
226
+ list[dict]: A list of instructions in a converted dict format.
227
+ int: The number of measurements.
228
+ dict: The measurement map from qubit number to classical bit number.
229
+ """
230
+ compiler_directives = ["barrier"]
231
+ output_circuit = []
232
+ num_meas = 0
233
+ meas_map = [None] * len(input_circuit.clbits)
234
+ for inst in input_circuit.data:
235
+ instruction, qargs, cargs = inst.operation, inst.qubits, inst.clbits
236
+
237
+ # Don't process compiler directives.
238
+ instruction_name = instruction.name
239
+ if instruction_name in compiler_directives:
240
+ continue
241
+
242
+ # Don't process measurement instructions.
243
+ if instruction_name == "measure":
244
+ meas_map[input_circuit.clbits.index(cargs[0])] = input_circuit.qubits.index(
245
+ qargs[0]
246
+ )
247
+ num_meas += 1
248
+ continue
249
+
250
+ # serialized identity gate is a no-op
251
+ if instruction_name == "id":
252
+ continue
253
+
254
+ # Raise out for instructions we don't support.
255
+ if instruction_name not in GATESET_MAP[gateset]:
256
+ raise IonQGateError(instruction_name, gateset)
257
+
258
+ # Process the instruction and convert.
259
+ rotation: dict[str, Any] = {}
260
+ if len(instruction.params) > 0:
261
+ if gateset == "qis" or (
262
+ len(instruction.params) == 1 and instruction_name != "zz"
263
+ ):
264
+ # The float is here to cast Qiskit ParameterExpressions to numbers
265
+ rotation = {
266
+ ("rotation" if gateset == "qis" else "phase"): float(
267
+ instruction.params[0]
268
+ )
269
+ }
270
+ if instruction_name == "PauliEvolution":
271
+ # rename rotation to time
272
+ rotation["time"] = rotation.pop("rotation")
273
+ elif instruction_name in {"zz"}:
274
+ rotation = {"angle": instruction.params[0]}
275
+ else:
276
+ rotation = {
277
+ "phases": [float(t) for t in instruction.params[:2]],
278
+ "angle": instruction.params[2],
279
+ }
280
+
281
+ # Default conversion is simple, just gate & target(s).
282
+ targets = [input_circuit.qubits.index(qargs[0])]
283
+ if instruction_name in {"ms", "zz"}:
284
+ targets.append(input_circuit.qubits.index(qargs[1]))
285
+
286
+ converted = (
287
+ {"gate": instruction_name, "targets": targets}
288
+ if instruction_name not in {"gpi", "gpi2"}
289
+ else {
290
+ "gate": instruction_name,
291
+ "target": targets[0],
292
+ }
293
+ )
294
+
295
+ # re-alias certain names
296
+ if instruction_name in ionq_api_aliases:
297
+ instruction_name = ionq_api_aliases[instruction_name]
298
+ converted["gate"] = instruction_name
299
+
300
+ # Make sure uncontrolled multi-targets use all qargs.
301
+ if instruction.num_qubits > 1 and not hasattr(instruction, "num_ctrl_qubits"):
302
+ converted["targets"] = [
303
+ input_circuit.qubits.index(qargs[i])
304
+ for i in range(instruction.num_qubits)
305
+ ]
306
+
307
+ # If this is a controlled gate, make sure to set control qubits.
308
+ if isinstance(instruction, q_cgates.ControlledGate):
309
+ gate = instruction_name[1:] # trim the leading c
310
+ controls = [input_circuit.qubits.index(qargs[0])]
311
+ targets = [input_circuit.qubits.index(qargs[1])]
312
+ # If this is a multi-control, use more than one qubit.
313
+ if instruction.num_ctrl_qubits > 1:
314
+ controls = [
315
+ input_circuit.qubits.index(qargs[i])
316
+ for i in range(instruction.num_ctrl_qubits)
317
+ ]
318
+ targets = [
319
+ input_circuit.qubits.index(qargs[instruction.num_ctrl_qubits])
320
+ ]
321
+ if gate == "swap":
322
+ # If this is a cswap, we have two targets:
323
+ targets = [
324
+ input_circuit.qubits.index(qargs[-2]),
325
+ input_circuit.qubits.index(qargs[-1]),
326
+ ]
327
+
328
+ # Update converted gate values.
329
+ converted.update(
330
+ {
331
+ "gate": gate,
332
+ "controls": controls,
333
+ "targets": targets,
334
+ }
335
+ )
336
+
337
+ if instruction_name == "pauliexp":
338
+ imag_coeff = any(coeff.imag for coeff in instruction.operator.coeffs)
339
+ assert not imag_coeff, (
340
+ "PauliEvolution gate must have real coefficients, "
341
+ f"but got {imag_coeff}"
342
+ )
343
+ terms = [term[0] for term in instruction.operator.to_list()]
344
+ if not ionq_compiler_synthesis and not paulis_commute(terms):
345
+ raise IonQPauliExponentialError(
346
+ f"You have included a PauliEvolutionGate with non-commuting terms: {terms}."
347
+ "To decompose it with IonQ hardware-aware synthesis, resubmit with the "
348
+ "IONQ_COMPILER_SYNTHESIS flag."
349
+ )
350
+ targets = [
351
+ input_circuit.qubits.index(qargs[i])
352
+ for i in range(instruction.num_qubits)
353
+ ]
354
+ coefficients = [coeff.real for coeff in instruction.operator.coeffs]
355
+ gate = {
356
+ "gate": instruction_name,
357
+ "targets": targets,
358
+ "terms": terms,
359
+ "coefficients": coefficients,
360
+ }
361
+ converted.update(gate)
362
+
363
+ # if there's a valid instruction after a measurement,
364
+ if num_meas > 0:
365
+ # see if any of the involved qubits have been measured,
366
+ # and raise if so — no mid-circuit measurement!
367
+ controls_and_targets = converted.get("targets", []) + converted.get(
368
+ "controls", []
369
+ )
370
+ if any(i in meas_map for i in controls_and_targets):
371
+ raise IonQMidCircuitMeasurementError(
372
+ input_circuit.qubits.index(qargs[0]), instruction_name
373
+ )
374
+
375
+ output_circuit.append({**converted, **rotation})
376
+
377
+ return output_circuit, num_meas, meas_map