azure-quantum 3.4.1.dev1__py3-none-any.whl → 3.5.0.dev0__py3-none-any.whl

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.
@@ -2,6 +2,7 @@
2
2
  # Copyright (c) Microsoft Corporation.
3
3
  # Licensed under the MIT License.
4
4
  ##
5
+ import copy
5
6
  import os
6
7
  import json
7
8
 
@@ -10,32 +11,51 @@ import warnings
10
11
 
11
12
  logger = logging.getLogger(__name__)
12
13
 
13
- from typing import Any, Dict, Tuple, Union, List, Optional
14
+ from dataclasses import dataclass, field
15
+ from functools import lru_cache
16
+ from typing import Any, Dict, Union, List, Optional, TYPE_CHECKING, Tuple, Mapping
14
17
  from azure.quantum.version import __version__
15
18
  from azure.quantum.qiskit.job import (
16
19
  MICROSOFT_OUTPUT_DATA_FORMAT,
17
- MICROSOFT_OUTPUT_DATA_FORMAT_V2,
18
20
  AzureQuantumJob,
19
21
  )
20
22
  from abc import abstractmethod
21
23
  from azure.quantum.job.session import SessionHost
22
24
 
25
+ BackendConfigurationType = None
26
+ QOBJ_TYPES: Tuple[type, ...] = tuple()
27
+
28
+ if TYPE_CHECKING:
29
+ from azure.quantum import Workspace
30
+ from azure.quantum.qiskit import AzureQuantumProvider
31
+
23
32
  try:
24
33
  from qiskit import QuantumCircuit
25
- from qiskit.providers import BackendV1 as Backend
34
+ from qiskit.providers import BackendV2 as Backend
26
35
  from qiskit.providers import Options
27
- from qiskit.providers import Provider
28
- from qiskit.providers.models import BackendConfiguration
29
- from qiskit.qobj import QasmQobj, PulseQobj
30
- import pyqir as pyqir
36
+ from qiskit.transpiler import Target
37
+ from qiskit.circuit import Instruction, Parameter
38
+ from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
31
39
  from qsharp.interop.qiskit import QSharpBackend
32
40
  from qsharp import TargetProfile
33
-
34
- except ImportError:
41
+ except ImportError as exc:
35
42
  raise ImportError(
36
43
  "Missing optional 'qiskit' dependencies. \
37
44
  To install run: pip install azure-quantum[qiskit]"
38
- )
45
+ ) from exc
46
+
47
+ try: # Qiskit 1.x legacy support
48
+ from qiskit.providers.models import BackendConfiguration # type: ignore
49
+ BackendConfigurationType = BackendConfiguration
50
+
51
+ from qiskit.qobj import QasmQobj, PulseQobj # type: ignore
52
+ except ImportError: # Qiskit 2.0 removes qobj module
53
+ QasmQobj = None # type: ignore
54
+ PulseQobj = None # type: ignore
55
+
56
+ QOBJ_TYPES = tuple(
57
+ obj_type for obj_type in (QasmQobj, PulseQobj) if obj_type is not None
58
+ )
39
59
 
40
60
  # barrier is handled by an extra flag which will transpile
41
61
  # them away if the backend doesn't support them. This has
@@ -59,6 +79,7 @@ QIR_BASIS_GATES = [
59
79
  "crz",
60
80
  "h",
61
81
  "s",
82
+ "sx",
62
83
  "sdg",
63
84
  "swap",
64
85
  "t",
@@ -71,6 +92,200 @@ QIR_BASIS_GATES = [
71
92
  ]
72
93
 
73
94
 
95
+ @lru_cache(maxsize=None)
96
+ def _standard_gate_map() -> Dict[str, Instruction]:
97
+ mapping = get_standard_gate_name_mapping()
98
+ # Include both canonical and lowercase keys for easier lookup
99
+ lowered = {name.lower(): gate for name, gate in mapping.items()}
100
+ combined = {**mapping, **lowered}
101
+ return combined
102
+
103
+
104
+ def _custom_instruction_builders() -> Dict[str, Instruction]:
105
+ """Provide Instruction stubs for backend-specific gates.
106
+
107
+ Azure Quantum targets expose native operations (for example, IonQ's
108
+ GPI-family gates or Quantinuum's multi-controlled primitives) that are not
109
+ part of Qiskit's standard gate catalogue. When we build a Target instance we
110
+ still need Instruction objects for these names so transpilation and circuit
111
+ validation can succeed. This helper returns lightweight Instruction
112
+ definitions that mirror each provider's gate signatures, ensuring
113
+ ``Target.add_instruction`` has the metadata it requires even though the
114
+ operations themselves are executed remotely.
115
+ """
116
+ param = Parameter
117
+ return {
118
+ "gpi": Instruction("gpi", 1, 0, params=[param("phi")]),
119
+ "gpi2": Instruction("gpi2", 1, 0, params=[param("phi")]),
120
+ "ms": Instruction(
121
+ "ms",
122
+ 2,
123
+ 0,
124
+ params=[param("phi0"), param("phi1"), param("angle")],
125
+ ),
126
+ "zz": Instruction("zz", 2, 0, params=[param("angle")]),
127
+ "v": Instruction("v", 1, 0, params=[]),
128
+ "vdg": Instruction("vdg", 1, 0, params=[]),
129
+ "vi": Instruction("vi", 1, 0, params=[]),
130
+ "si": Instruction("si", 1, 0, params=[]),
131
+ "ti": Instruction("ti", 1, 0, params=[]),
132
+ "mcp": Instruction("mcp", 3, 0, params=[param("angle")]),
133
+ "mcphase": Instruction("mcphase", 3, 0, params=[param("angle")]),
134
+ "mct": Instruction("mct", 3, 0, params=[]),
135
+ "mcx": Instruction("mcx", 3, 0, params=[]),
136
+ "mcx_gray": Instruction("mcx_gray", 3, 0, params=[]),
137
+ "pauliexp": Instruction("pauliexp", 1, 0, params=[param("time")]),
138
+ "paulievolution": Instruction("PauliEvolution", 1, 0, params=[param("time")]),
139
+ }
140
+
141
+
142
+ def _resolve_instruction(gate_name: str) -> Optional[Instruction]:
143
+ mapping = _standard_gate_map()
144
+ instruction = mapping.get(gate_name)
145
+ if instruction is not None:
146
+ return instruction.copy()
147
+
148
+ lower_name = gate_name.lower()
149
+ instruction = mapping.get(lower_name)
150
+ if instruction is not None:
151
+ return instruction.copy()
152
+
153
+ custom_map = _custom_instruction_builders()
154
+ if gate_name in custom_map:
155
+ return custom_map[gate_name]
156
+ if lower_name in custom_map:
157
+ return custom_map[lower_name]
158
+
159
+ # Default to a single-qubit placeholder instruction.
160
+ return Instruction(gate_name, 1, 0, params=[])
161
+
162
+
163
+ @dataclass
164
+ class AzureBackendConfig:
165
+ """Lightweight configuration container for Azure Quantum backends."""
166
+
167
+ backend_name: Optional[str] = None
168
+ backend_version: Optional[str] = None
169
+ description: Optional[str] = None
170
+ n_qubits: Optional[int] = None
171
+ dt: Optional[float] = None
172
+ basis_gates: Tuple[str, ...] = field(default_factory=tuple)
173
+ azure: Dict[str, Any] = field(default_factory=dict)
174
+ metadata: Dict[str, Any] = field(default_factory=dict)
175
+
176
+ def __post_init__(self) -> None:
177
+ self.azure = copy.deepcopy(self.azure or {})
178
+ self.metadata = dict(self.metadata or {})
179
+ if self.basis_gates is None:
180
+ self.basis_gates = tuple()
181
+ else:
182
+ self.basis_gates = tuple(self.basis_gates)
183
+
184
+ @property
185
+ def name(self) -> Optional[str]:
186
+ return self.backend_name
187
+
188
+ @property
189
+ def num_qubits(self) -> Optional[int]:
190
+ """Backward-compatible alias for Qiskit's ``BackendConfiguration.num_qubits``."""
191
+ return self.n_qubits
192
+
193
+ @num_qubits.setter
194
+ def num_qubits(self, value: Optional[int]) -> None:
195
+ self.n_qubits = value
196
+
197
+ def get(self, key: str, default: Any = None) -> Any:
198
+ if key == "basis_gates":
199
+ return list(self.basis_gates)
200
+ if key == "azure":
201
+ return copy.deepcopy(self.azure)
202
+ if hasattr(self, key):
203
+ return getattr(self, key)
204
+ return self.metadata.get(key, default)
205
+
206
+ def __getattr__(self, name: str) -> Any:
207
+ if name == "max_experiments":
208
+ return 1
209
+ try:
210
+ return self.__dict__[name]
211
+ except KeyError as exc:
212
+ if name in self.metadata:
213
+ return self.metadata[name]
214
+ raise AttributeError(
215
+ f"'{type(self).__name__}' object has no attribute '{name}'"
216
+ ) from exc
217
+
218
+ def to_dict(self) -> Dict[str, Any]:
219
+ config_dict: Dict[str, Any] = {
220
+ "backend_name": self.backend_name,
221
+ "backend_version": self.backend_version,
222
+ "description": self.description,
223
+ "max_experiments": 1,
224
+ "n_qubits": self.n_qubits,
225
+ "dt": self.dt,
226
+ "basis_gates": list(self.basis_gates),
227
+ }
228
+
229
+ config_dict.update(self.metadata)
230
+
231
+ if self.azure:
232
+ config_dict["azure"] = copy.deepcopy(self.azure)
233
+
234
+ return config_dict
235
+
236
+ @classmethod
237
+ def from_dict(cls, data: Mapping[str, Any]) -> "AzureBackendConfig":
238
+ raw = dict(data)
239
+ azure_config = copy.deepcopy(raw.get("azure", {}))
240
+ basis_gates = raw.get("basis_gates") or []
241
+
242
+ known_keys = {
243
+ "backend_name",
244
+ "backend_version",
245
+ "description",
246
+ "n_qubits",
247
+ "dt",
248
+ "basis_gates",
249
+ "azure",
250
+ }
251
+
252
+ metadata = {k: v for k, v in raw.items() if k not in known_keys}
253
+
254
+ return cls(
255
+ backend_name=raw.get("backend_name"),
256
+ backend_version=raw.get("backend_version"),
257
+ description=raw.get("description"),
258
+ n_qubits=raw.get("n_qubits"),
259
+ dt=raw.get("dt"),
260
+ basis_gates=tuple(basis_gates),
261
+ azure=azure_config,
262
+ metadata=metadata,
263
+ )
264
+
265
+ @classmethod
266
+ def from_backend_configuration(
267
+ cls, configuration: Any
268
+ ) -> "AzureBackendConfig":
269
+ return cls.from_dict(configuration.to_dict())
270
+
271
+
272
+ def _ensure_backend_config(
273
+ configuration: Any
274
+ ) -> AzureBackendConfig:
275
+ if isinstance(configuration, AzureBackendConfig):
276
+ return configuration
277
+
278
+ if BackendConfigurationType is not None and isinstance(
279
+ configuration, BackendConfigurationType
280
+ ):
281
+ return AzureBackendConfig.from_backend_configuration(configuration)
282
+
283
+ if isinstance(configuration, Mapping):
284
+ return AzureBackendConfig.from_dict(configuration)
285
+
286
+ raise TypeError("Unsupported configuration type for Azure backends")
287
+
288
+
74
289
  class AzureBackendBase(Backend, SessionHost):
75
290
 
76
291
  # Name of the provider's input parameter which specifies number of shots for a submitted job.
@@ -80,11 +295,50 @@ class AzureBackendBase(Backend, SessionHost):
80
295
  @abstractmethod
81
296
  def __init__(
82
297
  self,
83
- configuration: BackendConfiguration,
84
- provider: Provider = None,
298
+ configuration: Any,
299
+ provider: "AzureQuantumProvider" = None,
85
300
  **fields
86
301
  ):
87
- super().__init__(configuration, provider, **fields)
302
+ if configuration is None:
303
+ raise ValueError("Backend configuration is required for Azure backends")
304
+
305
+ if BackendConfigurationType is not None and isinstance(
306
+ configuration, BackendConfigurationType
307
+ ):
308
+ warnings.warn(
309
+ "The BackendConfiguration parameter is deprecated and will be removed from the SDK.",
310
+ DeprecationWarning,
311
+ stacklevel=2,
312
+ )
313
+
314
+ config = _ensure_backend_config(configuration)
315
+
316
+ self._config = config
317
+
318
+ super().__init__(
319
+ provider=provider,
320
+ name=config.backend_name,
321
+ description=config.description,
322
+ backend_version=config.backend_version,
323
+ **fields,
324
+ )
325
+
326
+ self._target = self._build_target(config)
327
+
328
+ def _build_target(self, configuration: AzureBackendConfig) -> Target:
329
+ num_qubits = configuration.n_qubits
330
+ target = Target(
331
+ description=configuration.description,
332
+ num_qubits=num_qubits,
333
+ dt=configuration.dt,
334
+ )
335
+
336
+ basis_gates: List[str] = list(configuration.basis_gates or [])
337
+ for gate_name in dict.fromkeys(basis_gates + ["measure", "reset"]):
338
+ instruction = _resolve_instruction(gate_name)
339
+ target.add_instruction(instruction)
340
+
341
+ return target
88
342
 
89
343
  @abstractmethod
90
344
  def run(
@@ -101,7 +355,7 @@ class AzureBackendBase(Backend, SessionHost):
101
355
 
102
356
  Args:
103
357
  run_input (QuantumCircuit or List[QuantumCircuit]): An individual or a
104
- list of :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
358
+ list of one :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
105
359
  shots (int, optional): Number of shots, defaults to None.
106
360
  options: Any kwarg options to pass to the backend for running the
107
361
  config. If a key is also present in the options
@@ -130,25 +384,32 @@ class AzureBackendBase(Backend, SessionHost):
130
384
  def _azure_config(self) -> Dict[str, str]:
131
385
  pass
132
386
 
387
+ def configuration(self) -> AzureBackendConfig:
388
+ warnings.warn(
389
+ "AzureBackendBase.configuration() is deprecated and will be removed from the SDK.",
390
+ DeprecationWarning,
391
+ stacklevel=2,
392
+ )
393
+ return self._config
394
+
395
+ @property
396
+ def target(self) -> Target:
397
+ return self._target
398
+
399
+ @property
400
+ def max_circuits(self) -> Optional[int]:
401
+ return 1
133
402
  def retrieve_job(self, job_id) -> AzureQuantumJob:
134
403
  """Returns the Job instance associated with the given id."""
135
- return self._provider.get_job(job_id)
404
+ return self.provider.get_job(job_id)
136
405
 
137
406
  def _get_output_data_format(self, options: Dict[str, Any] = {}) -> str:
138
- config: BackendConfiguration = self.configuration()
139
- # output data format default depends on the number of experiments. QIR backends
140
- # that don't define a default in their azure config will use this value
141
- # Once more than one experiment is supported, we should always use the v2 format
142
- default_output_data_format = (
143
- MICROSOFT_OUTPUT_DATA_FORMAT
144
- if config.max_experiments == 1
145
- else MICROSOFT_OUTPUT_DATA_FORMAT_V2
146
- )
407
+ config: AzureBackendConfig = self._config
147
408
 
148
- azure_config: Dict[str, Any] = config.azure
409
+ azure_config: Dict[str, Any] = config.azure or {}
149
410
  # if the backend defines an output format, use that over the default
150
411
  azure_defined_override = azure_config.get(
151
- "output_data_format", default_output_data_format
412
+ "output_data_format", MICROSOFT_OUTPUT_DATA_FORMAT
152
413
  )
153
414
  # if the user specifies an output format, use that over the default azure config
154
415
  output_data_format = options.pop("output_data_format", azure_defined_override)
@@ -171,7 +432,8 @@ class AzureBackendBase(Backend, SessionHost):
171
432
  if shots is not None and options_shots is not None:
172
433
  warnings.warn(
173
434
  f"Parameter 'shots' conflicts with the '{self.__class__._SHOTS_PARAM_NAME}' parameter. "
174
- "Please, provide only one option for setting shots. Defaulting to 'shots' parameter."
435
+ "Please, provide only one option for setting shots. Defaulting to 'shots' parameter.",
436
+ stacklevel=3,
175
437
  )
176
438
  final_shots = shots
177
439
 
@@ -180,7 +442,8 @@ class AzureBackendBase(Backend, SessionHost):
180
442
  elif options_shots is not None:
181
443
  warnings.warn(
182
444
  f"Parameter '{self.__class__._SHOTS_PARAM_NAME}' is subject to change in future versions. "
183
- "Please, use 'shots' parameter instead."
445
+ "Please, use 'shots' parameter instead.",
446
+ stacklevel=3,
184
447
  )
185
448
  final_shots = options_shots
186
449
 
@@ -213,15 +476,16 @@ class AzureBackendBase(Backend, SessionHost):
213
476
  return input_params
214
477
 
215
478
  def _run(self, job_name, input_data, input_params, metadata, **options):
216
- logger.info(f"Submitting new job for backend {self.name()}")
479
+ logger.info(f"Submitting new job for backend {self.name}")
217
480
 
218
481
  # The default of these job parameters come from the AzureBackend configuration:
219
- config = self.configuration()
220
- blob_name = options.pop("blob_name", config.azure["blob_name"])
221
- content_type = options.pop("content_type", config.azure["content_type"])
222
- provider_id = options.pop("provider_id", config.azure["provider_id"])
482
+ config = self._config
483
+ azure_config = config.azure or {}
484
+ blob_name = options.pop("blob_name", azure_config.get("blob_name"))
485
+ content_type = options.pop("content_type", azure_config.get("content_type"))
486
+ provider_id = options.pop("provider_id", azure_config.get("provider_id"))
223
487
  input_data_format = options.pop(
224
- "input_data_format", config.azure["input_data_format"]
488
+ "input_data_format", azure_config.get("input_data_format")
225
489
  )
226
490
  output_data_format = self._get_output_data_format(options)
227
491
 
@@ -238,13 +502,22 @@ class AzureBackendBase(Backend, SessionHost):
238
502
  message += "To find a QIR capable backend, use the following code:"
239
503
  message += os.linesep
240
504
  message += (
241
- f'\tprovider.get_backend("{self.name()}", input_data_format: "qir.v1").'
505
+ f'\tprovider.get_backend("{self.name}", input_data_format: "qir.v1").'
242
506
  )
243
507
  raise ValueError(message)
244
508
 
509
+
510
+ # Update metadata with all remaining options values, then clear options
511
+ # JobDetails model will error if unknown keys are passed down which
512
+ # can happen with estiamtor and backend wrappers
513
+ if metadata is None:
514
+ metadata = {}
515
+ metadata.update(options)
516
+ options.clear()
517
+
245
518
  job = AzureQuantumJob(
246
519
  backend=self,
247
- target=self.name(),
520
+ target=self.name,
248
521
  name=job_name,
249
522
  input_data=input_data,
250
523
  blob_name=blob_name,
@@ -292,10 +565,10 @@ class AzureBackendBase(Backend, SessionHost):
292
565
  raise ValueError("No input provided.")
293
566
 
294
567
  def _get_azure_workspace(self) -> "Workspace":
295
- return self.provider().get_workspace()
568
+ return self.provider.get_workspace()
296
569
 
297
570
  def _get_azure_target_id(self) -> str:
298
- return self.name()
571
+ return self.name
299
572
 
300
573
  def _get_azure_provider_id(self) -> str:
301
574
  return self._azure_config()["provider_id"]
@@ -304,7 +577,10 @@ class AzureBackendBase(Backend, SessionHost):
304
577
  class AzureQirBackend(AzureBackendBase):
305
578
  @abstractmethod
306
579
  def __init__(
307
- self, configuration: BackendConfiguration, provider: Provider = None, **fields
580
+ self,
581
+ configuration: AzureBackendConfig,
582
+ provider: "AzureQuantumProvider" = None,
583
+ **fields,
308
584
  ):
309
585
  super().__init__(configuration, provider, **fields)
310
586
 
@@ -334,7 +610,7 @@ class AzureQirBackend(AzureBackendBase):
334
610
 
335
611
  Args:
336
612
  run_input (QuantumCircuit or List[QuantumCircuit]): An individual or a
337
- list of :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
613
+ list of one :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
338
614
  shots (int, optional): Number of shots, defaults to None.
339
615
  options: Any kwarg options to pass to the backend for running the
340
616
  config. If a key is also present in the options
@@ -349,17 +625,16 @@ class AzureQirBackend(AzureBackendBase):
349
625
  options.pop("run_input", None)
350
626
  options.pop("circuit", None)
351
627
 
352
- circuits = list([])
353
- if isinstance(run_input, QuantumCircuit):
354
- circuits = [run_input]
355
- else:
356
- circuits = run_input
357
-
358
- max_circuits_per_job = self.configuration().max_experiments
359
- if len(circuits) > max_circuits_per_job:
360
- raise NotImplementedError(
361
- f"This backend only supports running a maximum of {max_circuits_per_job} circuits per job."
362
- )
628
+ circuit = run_input
629
+ if isinstance(run_input, list):
630
+ # just in case they passed a list, we only support single-experiment jobs
631
+ if len(run_input) != 1:
632
+ raise NotImplementedError(
633
+ f"This backend only supports running a single circuit per job."
634
+ )
635
+ circuit = run_input[0]
636
+ if not isinstance(circuit, QuantumCircuit):
637
+ raise ValueError("Invalid input: expected a QuantumCircuit.")
363
638
 
364
639
  # config normalization
365
640
  input_params = self._get_input_params(options, shots=shots)
@@ -369,53 +644,38 @@ class AzureQirBackend(AzureBackendBase):
369
644
  if self._can_send_shots_input_param():
370
645
  shots_count = input_params.get(self.__class__._SHOTS_PARAM_NAME)
371
646
 
372
- job_name = ""
373
- if len(circuits) > 1:
374
- job_name = f"batch-{len(circuits)}"
375
- if shots_count is not None:
376
- job_name = f"{job_name}-{shots_count}"
377
- else:
378
- job_name = circuits[0].name
379
- job_name = options.pop("job_name", job_name)
647
+ job_name = options.pop("job_name", circuit.name)
380
648
 
381
- metadata = options.pop("metadata", self._prepare_job_metadata(circuits))
649
+ metadata = options.pop("metadata", self._prepare_job_metadata(circuit))
382
650
 
383
- input_data = self._translate_input(circuits, input_params)
651
+ input_data = self._translate_input(circuit, input_params)
384
652
 
385
653
  job = super()._run(job_name, input_data, input_params, metadata, **options)
386
654
  logger.info(
387
- f"Submitted job with id '{job.id()}' with shot count of {shots_count}:"
655
+ "Submitted job with id '%s' with shot count of %s:",
656
+ job.id(),
657
+ shots_count,
388
658
  )
389
659
 
390
660
  return job
391
661
 
392
- def _prepare_job_metadata(self, circuits: List[QuantumCircuit]) -> Dict[str, str]:
662
+ def _prepare_job_metadata(self, circuit: QuantumCircuit) -> Dict[str, str]:
393
663
  """Returns the metadata relative to the given circuits that will be attached to the Job"""
394
- if len(circuits) == 1:
395
- circuit: QuantumCircuit = circuits[0]
396
- return {
397
- "qiskit": str(True),
398
- "name": circuit.name,
399
- "num_qubits": circuit.num_qubits,
400
- "metadata": json.dumps(circuit.metadata),
401
- }
402
- # for batch jobs, we don't want to store the metadata of each circuit
403
- # we fill out the result header in output processing.
404
- # These headers don't matter for execution are are only used for
405
- # result processing.
406
- return {}
407
-
408
- def _generate_qir(
409
- self, circuits: List[QuantumCircuit], target_profile: TargetProfile, **kwargs
410
- ) -> pyqir.Module:
411
-
412
- if len(circuits) == 0:
413
- raise ValueError("No QuantumCircuits provided")
414
-
415
- config = self.configuration()
664
+ return {
665
+ "qiskit": str(True),
666
+ "name": circuit.name,
667
+ "num_qubits": circuit.num_qubits,
668
+ "metadata": json.dumps(circuit.metadata),
669
+ }
670
+
671
+
672
+ def _get_qir_str(
673
+ self, circuit: QuantumCircuit, target_profile: TargetProfile, **kwargs
674
+ ) -> str:
675
+ config = self._config
416
676
  # Barriers aren't removed by transpilation and must be explicitly removed in the Qiskit to QIR translation.
417
677
  supports_barrier = "barrier" in config.basis_gates
418
- skip_transpilation = kwargs.pop("skip_transpilation", False)
678
+ skip_transpilation = kwargs.pop("skip_transpilation", True)
419
679
 
420
680
  backend = QSharpBackend(
421
681
  qiskit_pass_options={"supports_barrier": supports_barrier},
@@ -424,73 +684,42 @@ class AzureQirBackend(AzureBackendBase):
424
684
  **kwargs,
425
685
  )
426
686
 
427
- name = "batch"
428
- if len(circuits) == 1:
429
- name = circuits[0].name
687
+ qir_str = backend.qir(circuit)
688
+
689
+ return qir_str
430
690
 
431
- if isinstance(circuits, list):
432
- for value in circuits:
433
- if not isinstance(value, QuantumCircuit):
434
- raise ValueError("Input must be List[QuantumCircuit]")
435
- else:
436
- raise ValueError("Input must be List[QuantumCircuit]")
437
-
438
- context = pyqir.Context()
439
- llvm_module = pyqir.qir_module(context, name)
440
- for circuit in circuits:
441
- qir_str = backend.qir(circuit)
442
- module = pyqir.Module.from_ir(context, qir_str)
443
- entry_point = next(filter(pyqir.is_entry_point, module.functions))
444
- entry_point.name = circuit.name
445
- llvm_module.link(module)
446
- err = llvm_module.verify()
447
- if err is not None:
448
- raise Exception(err)
449
-
450
- return llvm_module
451
-
452
- def _get_qir_str(
453
- self,
454
- circuits: List[QuantumCircuit],
455
- target_profile: TargetProfile,
456
- **to_qir_kwargs,
457
- ) -> str:
458
- module = self._generate_qir(circuits, target_profile, **to_qir_kwargs)
459
- return str(module)
460
691
 
461
692
  def _translate_input(
462
- self, circuits: Union[QuantumCircuit, List[QuantumCircuit]], input_params: Dict[str, Any]
693
+ self, circuit: QuantumCircuit, input_params: Dict[str, Any]
463
694
  ) -> bytes:
464
695
  """Translates the input values to the QIR expected by the Backend."""
465
696
  logger.info(f"Using QIR as the job's payload format.")
466
- if not (isinstance(circuits, list)):
467
- circuits = [circuits]
468
697
 
469
698
  target_profile = self._get_target_profile(input_params)
470
699
 
471
700
  if logger.isEnabledFor(logging.DEBUG):
472
- qir = self._get_qir_str(circuits, target_profile, skip_transpilation=True)
701
+ qir = self._get_qir_str(circuit, target_profile, skip_transpilation=True)
473
702
  logger.debug(f"QIR:\n{qir}")
474
703
 
475
- # We'll transpile automatically to the supported gates in QIR unless explicitly skipped.
476
- skip_transpilation = input_params.pop("skipTranspile", False)
477
-
478
- module = self._generate_qir(
479
- circuits, target_profile, skip_transpilation=skip_transpilation
704
+ skip_transpilation = input_params.pop("skipTranspile", True)
705
+ if not skip_transpilation:
706
+ warnings.warn(
707
+ "Transpilation is deprecated and will be removed in future versions. "
708
+ "Please, transpile circuits manually before passing them to the backend.",
709
+ category=DeprecationWarning,
710
+ stacklevel=3,
711
+ )
712
+
713
+ qir_str = self._get_qir_str(
714
+ circuit, target_profile, skip_transpilation=skip_transpilation
480
715
  )
481
716
 
482
- def get_func_name(func: pyqir.Function) -> str:
483
- return func.name
484
-
485
- entry_points = list(
486
- map(get_func_name, filter(pyqir.is_entry_point, module.functions))
487
- )
717
+ entry_points = ["ENTRYPOINT__main"]
488
718
 
489
719
  if not skip_transpilation:
490
720
  # We'll only log the QIR again if we performed a transpilation.
491
721
  if logger.isEnabledFor(logging.DEBUG):
492
- qir = str(module)
493
- logger.debug(f"QIR (Post-transpilation):\n{qir}")
722
+ logger.debug(f"QIR (Post-transpilation):\n{qir_str}")
494
723
 
495
724
  if "items" not in input_params:
496
725
  arguments = input_params.pop("arguments", [])
@@ -498,7 +727,7 @@ class AzureQirBackend(AzureBackendBase):
498
727
  {"entryPoint": name, "arguments": arguments} for name in entry_points
499
728
  ]
500
729
 
501
- return str(module).encode("utf-8")
730
+ return qir_str.encode("utf-8")
502
731
 
503
732
  def _get_target_profile(self, input_params) -> TargetProfile:
504
733
  # Default to Adaptive_RI if not specified on the backend
@@ -528,7 +757,10 @@ class AzureBackend(AzureBackendBase):
528
757
 
529
758
  @abstractmethod
530
759
  def __init__(
531
- self, configuration: BackendConfiguration, provider: Provider = None, **fields
760
+ self,
761
+ configuration: AzureBackendConfig,
762
+ provider: "AzureQuantumProvider" = None,
763
+ **fields,
532
764
  ):
533
765
  super().__init__(configuration, provider, **fields)
534
766
 
@@ -567,7 +799,7 @@ class AzureBackend(AzureBackendBase):
567
799
 
568
800
  # If the circuit was created using qiskit.assemble,
569
801
  # disassemble into QASM here
570
- if isinstance(circuit, QasmQobj) or isinstance(circuit, PulseQobj):
802
+ if QOBJ_TYPES and isinstance(circuit, QOBJ_TYPES):
571
803
  from qiskit.assembler import disassemble
572
804
 
573
805
  circuits, run, _ = disassemble(circuit)