azure-quantum 2.1.1__py3-none-any.whl → 2.2.0__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.
- azure/quantum/_client/_version.py +1 -1
- azure/quantum/qiskit/backends/backend.py +149 -37
- azure/quantum/qiskit/backends/ionq.py +11 -38
- azure/quantum/qiskit/backends/microsoft.py +62 -27
- azure/quantum/qiskit/backends/qci.py +10 -27
- azure/quantum/qiskit/backends/quantinuum.py +12 -7
- azure/quantum/qiskit/backends/rigetti.py +4 -23
- azure/quantum/qiskit/job.py +37 -10
- azure/quantum/qiskit/provider.py +4 -4
- azure/quantum/target/ionq.py +32 -22
- azure/quantum/target/microsoft/target.py +15 -7
- azure/quantum/target/pasqal/target.py +7 -1
- azure/quantum/target/quantinuum.py +37 -27
- azure/quantum/target/rigetti/target.py +13 -6
- azure/quantum/target/target.py +77 -4
- azure/quantum/version.py +1 -1
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/METADATA +5 -5
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/RECORD +20 -20
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/WHEEL +0 -0
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -21,14 +21,15 @@ from abc import abstractmethod
|
|
|
21
21
|
from azure.quantum.job.session import SessionHost
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
|
-
from qiskit import QuantumCircuit
|
|
24
|
+
from qiskit import QuantumCircuit
|
|
25
25
|
from qiskit.providers import BackendV1 as Backend
|
|
26
26
|
from qiskit.providers import Options
|
|
27
27
|
from qiskit.providers import Provider
|
|
28
28
|
from qiskit.providers.models import BackendConfiguration
|
|
29
29
|
from qiskit.qobj import QasmQobj, PulseQobj
|
|
30
|
-
|
|
31
|
-
from
|
|
30
|
+
import pyqir as pyqir
|
|
31
|
+
from qsharp.interop.qiskit import QSharpBackend
|
|
32
|
+
from qsharp import TargetProfile
|
|
32
33
|
|
|
33
34
|
except ImportError:
|
|
34
35
|
raise ImportError(
|
|
@@ -36,6 +37,39 @@ except ImportError:
|
|
|
36
37
|
To install run: pip install azure-quantum[qiskit]"
|
|
37
38
|
)
|
|
38
39
|
|
|
40
|
+
# barrier is handled by an extra flag which will transpile
|
|
41
|
+
# them away if the backend doesn't support them. This has
|
|
42
|
+
# to be done as a special pass as the transpiler will not
|
|
43
|
+
# remove barriers by default.
|
|
44
|
+
QIR_BASIS_GATES = [
|
|
45
|
+
"measure",
|
|
46
|
+
"reset",
|
|
47
|
+
"ccx",
|
|
48
|
+
"cx",
|
|
49
|
+
"cy",
|
|
50
|
+
"cz",
|
|
51
|
+
"rx",
|
|
52
|
+
"rxx",
|
|
53
|
+
"crx",
|
|
54
|
+
"ry",
|
|
55
|
+
"ryy",
|
|
56
|
+
"cry",
|
|
57
|
+
"rz",
|
|
58
|
+
"rzz",
|
|
59
|
+
"crz",
|
|
60
|
+
"h",
|
|
61
|
+
"s",
|
|
62
|
+
"sdg",
|
|
63
|
+
"swap",
|
|
64
|
+
"t",
|
|
65
|
+
"tdg",
|
|
66
|
+
"x",
|
|
67
|
+
"y",
|
|
68
|
+
"z",
|
|
69
|
+
"id",
|
|
70
|
+
"ch",
|
|
71
|
+
]
|
|
72
|
+
|
|
39
73
|
|
|
40
74
|
class AzureBackendBase(Backend, SessionHost):
|
|
41
75
|
|
|
@@ -280,7 +314,11 @@ class AzureQirBackend(AzureBackendBase):
|
|
|
280
314
|
"content_type": "qir.v1",
|
|
281
315
|
"input_data_format": "qir.v1",
|
|
282
316
|
"output_data_format": "microsoft.quantum-results.v2",
|
|
317
|
+
"is_default": True,
|
|
283
318
|
}
|
|
319
|
+
|
|
320
|
+
def _basis_gates(self) -> List[str]:
|
|
321
|
+
return QIR_BASIS_GATES
|
|
284
322
|
|
|
285
323
|
def run(
|
|
286
324
|
self,
|
|
@@ -368,66 +406,140 @@ class AzureQirBackend(AzureBackendBase):
|
|
|
368
406
|
return {}
|
|
369
407
|
|
|
370
408
|
def _generate_qir(
|
|
371
|
-
self, circuits,
|
|
372
|
-
) ->
|
|
409
|
+
self, circuits: List[QuantumCircuit], target_profile: TargetProfile, **kwargs
|
|
410
|
+
) -> pyqir.Module:
|
|
411
|
+
|
|
412
|
+
if len(circuits) == 0:
|
|
413
|
+
raise ValueError("No QuantumCircuits provided")
|
|
373
414
|
|
|
374
415
|
config = self.configuration()
|
|
375
416
|
# Barriers aren't removed by transpilation and must be explicitly removed in the Qiskit to QIR translation.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
417
|
+
supports_barrier = "barrier" in config.basis_gates
|
|
418
|
+
skip_transpilation = kwargs.pop("skip_transpilation", False)
|
|
419
|
+
|
|
420
|
+
backend = QSharpBackend(
|
|
421
|
+
qiskit_pass_options={"supports_barrier": supports_barrier},
|
|
422
|
+
target_profile=target_profile,
|
|
423
|
+
skip_transpilation=skip_transpilation,
|
|
424
|
+
**kwargs,
|
|
382
425
|
)
|
|
383
426
|
|
|
384
|
-
|
|
385
|
-
|
|
427
|
+
name = "batch"
|
|
428
|
+
if len(circuits) == 1:
|
|
429
|
+
name = circuits[0].name
|
|
430
|
+
|
|
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)
|
|
386
459
|
return str(module)
|
|
387
460
|
|
|
388
461
|
def _translate_input(
|
|
389
|
-
self, circuits: List[QuantumCircuit], input_params: Dict[str, Any]
|
|
462
|
+
self, circuits: Union[QuantumCircuit, List[QuantumCircuit]], input_params: Dict[str, Any]
|
|
390
463
|
) -> bytes:
|
|
391
464
|
"""Translates the input values to the QIR expected by the Backend."""
|
|
392
465
|
logger.info(f"Using QIR as the job's payload format.")
|
|
393
|
-
|
|
466
|
+
if not (isinstance(circuits, list)):
|
|
467
|
+
circuits = [circuits]
|
|
394
468
|
|
|
395
|
-
|
|
396
|
-
# We will record the output by default, but allow the backend to override this, and allow the user to override the backend.
|
|
397
|
-
to_qir_kwargs = input_params.pop(
|
|
398
|
-
"to_qir_kwargs", config.azure.get("to_qir_kwargs", {"record_output": True})
|
|
399
|
-
)
|
|
400
|
-
targetCapability = input_params.pop(
|
|
401
|
-
"targetCapability",
|
|
402
|
-
self.options.get("targetCapability", "AdaptiveExecution"),
|
|
403
|
-
)
|
|
469
|
+
target_profile = self._get_target_profile(input_params)
|
|
404
470
|
|
|
405
471
|
if logger.isEnabledFor(logging.DEBUG):
|
|
406
|
-
qir = self._get_qir_str(circuits,
|
|
472
|
+
qir = self._get_qir_str(circuits, target_profile, skip_transpilation=True)
|
|
407
473
|
logger.debug(f"QIR:\n{qir}")
|
|
408
474
|
|
|
409
475
|
# We'll transpile automatically to the supported gates in QIR unless explicitly skipped.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
476
|
+
skip_transpilation = input_params.pop("skipTranspile", False)
|
|
477
|
+
|
|
478
|
+
module = self._generate_qir(
|
|
479
|
+
circuits, target_profile, skip_transpilation=skip_transpilation
|
|
480
|
+
)
|
|
481
|
+
|
|
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
|
+
)
|
|
488
|
+
|
|
489
|
+
if not skip_transpilation:
|
|
415
490
|
# We'll only log the QIR again if we performed a transpilation.
|
|
416
491
|
if logger.isEnabledFor(logging.DEBUG):
|
|
417
|
-
qir =
|
|
492
|
+
qir = str(module)
|
|
418
493
|
logger.debug(f"QIR (Post-transpilation):\n{qir}")
|
|
419
494
|
|
|
420
|
-
|
|
421
|
-
circuits, targetCapability, **to_qir_kwargs
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
if not "items" in input_params:
|
|
495
|
+
if "items" not in input_params:
|
|
425
496
|
arguments = input_params.pop("arguments", [])
|
|
426
497
|
input_params["items"] = [
|
|
427
498
|
{"entryPoint": name, "arguments": arguments} for name in entry_points
|
|
428
499
|
]
|
|
429
500
|
|
|
430
|
-
return module.
|
|
501
|
+
return str(module).encode("utf-8")
|
|
502
|
+
|
|
503
|
+
def _estimate_cost_qir(
|
|
504
|
+
self, circuits: Union[QuantumCircuit, List[QuantumCircuit]], shots, options={}
|
|
505
|
+
):
|
|
506
|
+
"""Estimate the cost for the given circuit."""
|
|
507
|
+
input_params = self._get_input_params(options, shots=shots)
|
|
508
|
+
|
|
509
|
+
if not (isinstance(circuits, list)):
|
|
510
|
+
circuits = [circuits]
|
|
511
|
+
|
|
512
|
+
skip_transpilation = input_params.pop("skipTranspile", False)
|
|
513
|
+
target_profile = self._get_target_profile(input_params)
|
|
514
|
+
module = self._generate_qir(
|
|
515
|
+
circuits, target_profile, skip_transpilation=skip_transpilation
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
workspace = self.provider().get_workspace()
|
|
519
|
+
target = workspace.get_targets(self.name())
|
|
520
|
+
return target.estimate_cost(module, shots=shots)
|
|
521
|
+
|
|
522
|
+
def _get_target_profile(self, input_params) -> TargetProfile:
|
|
523
|
+
# Default to Adaptive_RI if not specified on the backend
|
|
524
|
+
# this is really just a safeguard in case the backend doesn't have a default
|
|
525
|
+
default_profile = self.options.get("target_profile", TargetProfile.Adaptive_RI)
|
|
526
|
+
|
|
527
|
+
# If the user is using the old targetCapability parameter, we'll warn them
|
|
528
|
+
# and use that value for now. This will be removed in the future.
|
|
529
|
+
if "targetCapability" in input_params:
|
|
530
|
+
warnings.warn(
|
|
531
|
+
"The 'targetCapability' parameter is deprecated and will be ignored in the future. "
|
|
532
|
+
"Please, use 'target_profile' parameter instead.",
|
|
533
|
+
category=DeprecationWarning,
|
|
534
|
+
)
|
|
535
|
+
cap = input_params.pop("targetCapability")
|
|
536
|
+
if cap == "AdaptiveExecution":
|
|
537
|
+
default_profile = TargetProfile.Adaptive_RI
|
|
538
|
+
else:
|
|
539
|
+
default_profile = TargetProfile.Base
|
|
540
|
+
# If the user specifies a target profile, use that.
|
|
541
|
+
# Otherwise, use the profile we got from the backend/targetCapability.
|
|
542
|
+
return input_params.pop("target_profile", default_profile)
|
|
431
543
|
|
|
432
544
|
|
|
433
545
|
class AzureBackend(AzureBackendBase):
|
|
@@ -7,7 +7,7 @@ from azure.quantum import __version__
|
|
|
7
7
|
from azure.quantum.qiskit.job import AzureQuantumJob
|
|
8
8
|
from azure.quantum.target.ionq import IonQ
|
|
9
9
|
from abc import abstractmethod
|
|
10
|
-
|
|
10
|
+
from qsharp import TargetProfile
|
|
11
11
|
from qiskit import QuantumCircuit
|
|
12
12
|
|
|
13
13
|
from .backend import (
|
|
@@ -20,7 +20,6 @@ from qiskit.providers.models import BackendConfiguration
|
|
|
20
20
|
from qiskit.providers import Options, Provider
|
|
21
21
|
|
|
22
22
|
from qiskit_ionq.helpers import (
|
|
23
|
-
ionq_basis_gates,
|
|
24
23
|
GATESET_MAP,
|
|
25
24
|
qiskit_circ_to_ionq_circ,
|
|
26
25
|
)
|
|
@@ -66,7 +65,7 @@ class IonQQirBackendBase(AzureQirBackend):
|
|
|
66
65
|
**{
|
|
67
66
|
cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
|
|
68
67
|
},
|
|
69
|
-
|
|
68
|
+
target_profile=TargetProfile.Base,
|
|
70
69
|
)
|
|
71
70
|
|
|
72
71
|
def _azure_config(self) -> Dict[str, str]:
|
|
@@ -77,6 +76,10 @@ class IonQQirBackendBase(AzureQirBackend):
|
|
|
77
76
|
}
|
|
78
77
|
)
|
|
79
78
|
return config
|
|
79
|
+
|
|
80
|
+
def estimate_cost(self, circuits, shots, options={}):
|
|
81
|
+
"""Estimate the cost for the given circuit."""
|
|
82
|
+
return self._estimate_cost_qir(circuits, shots, options)
|
|
80
83
|
|
|
81
84
|
def run(
|
|
82
85
|
self,
|
|
@@ -103,7 +106,6 @@ class IonQSimulatorQirBackend(IonQQirBackendBase):
|
|
|
103
106
|
|
|
104
107
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
105
108
|
"""Base class for interfacing with an IonQ QIR Simulator backend"""
|
|
106
|
-
|
|
107
109
|
default_config = BackendConfiguration.from_dict(
|
|
108
110
|
{
|
|
109
111
|
"backend_name": name,
|
|
@@ -112,7 +114,7 @@ class IonQSimulatorQirBackend(IonQQirBackendBase):
|
|
|
112
114
|
"local": False,
|
|
113
115
|
"coupling_map": None,
|
|
114
116
|
"description": "IonQ simulator on Azure Quantum",
|
|
115
|
-
"basis_gates":
|
|
117
|
+
"basis_gates": self._basis_gates(),
|
|
116
118
|
"memory": False,
|
|
117
119
|
"n_qubits": 29,
|
|
118
120
|
"conditional": False,
|
|
@@ -135,7 +137,6 @@ class IonQAriaQirBackend(IonQQirBackendBase):
|
|
|
135
137
|
|
|
136
138
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
137
139
|
"""Base class for interfacing with an IonQ Aria QPU backend"""
|
|
138
|
-
|
|
139
140
|
default_config = BackendConfiguration.from_dict(
|
|
140
141
|
{
|
|
141
142
|
"backend_name": name,
|
|
@@ -144,9 +145,9 @@ class IonQAriaQirBackend(IonQQirBackendBase):
|
|
|
144
145
|
"local": False,
|
|
145
146
|
"coupling_map": None,
|
|
146
147
|
"description": "IonQ Aria QPU on Azure Quantum",
|
|
147
|
-
"basis_gates":
|
|
148
|
+
"basis_gates": self._basis_gates(),
|
|
148
149
|
"memory": False,
|
|
149
|
-
"n_qubits":
|
|
150
|
+
"n_qubits": 25,
|
|
150
151
|
"conditional": False,
|
|
151
152
|
"max_shots": 10000,
|
|
152
153
|
"max_experiments": 1,
|
|
@@ -167,7 +168,6 @@ class IonQForteQirBackend(IonQQirBackendBase):
|
|
|
167
168
|
|
|
168
169
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
169
170
|
"""Base class for interfacing with an IonQ Forte QPU backend"""
|
|
170
|
-
|
|
171
171
|
default_config = BackendConfiguration.from_dict(
|
|
172
172
|
{
|
|
173
173
|
"backend_name": name,
|
|
@@ -176,7 +176,7 @@ class IonQForteQirBackend(IonQQirBackendBase):
|
|
|
176
176
|
"local": False,
|
|
177
177
|
"coupling_map": None,
|
|
178
178
|
"description": "IonQ Forte QPU on Azure Quantum",
|
|
179
|
-
"basis_gates":
|
|
179
|
+
"basis_gates": self._basis_gates(),
|
|
180
180
|
"memory": False,
|
|
181
181
|
"n_qubits": 35,
|
|
182
182
|
"conditional": False,
|
|
@@ -241,7 +241,7 @@ class IonQBackend(AzureBackend):
|
|
|
241
241
|
"provider_id": "ionq",
|
|
242
242
|
"input_data_format": "ionq.circuit.v1",
|
|
243
243
|
"output_data_format": "ionq.quantum-results.v1",
|
|
244
|
-
"is_default":
|
|
244
|
+
"is_default": False,
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
def _prepare_job_metadata(self, circuit, **kwargs):
|
|
@@ -316,15 +316,6 @@ class IonQSimulatorNativeBackend(IonQSimulatorBackend):
|
|
|
316
316
|
kwargs["gateset"] = "native"
|
|
317
317
|
super().__init__(name, provider, **kwargs)
|
|
318
318
|
|
|
319
|
-
def _azure_config(self) -> Dict[str, str]:
|
|
320
|
-
config = super()._azure_config()
|
|
321
|
-
config.update(
|
|
322
|
-
{
|
|
323
|
-
"is_default": False,
|
|
324
|
-
}
|
|
325
|
-
)
|
|
326
|
-
return config
|
|
327
|
-
|
|
328
319
|
|
|
329
320
|
class IonQAriaBackend(IonQBackend):
|
|
330
321
|
backend_names = ("ionq.qpu.aria-1", "ionq.qpu.aria-2")
|
|
@@ -398,27 +389,9 @@ class IonQAriaNativeBackend(IonQAriaBackend):
|
|
|
398
389
|
kwargs["gateset"] = "native"
|
|
399
390
|
super().__init__(name, provider, **kwargs)
|
|
400
391
|
|
|
401
|
-
def _azure_config(self) -> Dict[str, str]:
|
|
402
|
-
config = super()._azure_config()
|
|
403
|
-
config.update(
|
|
404
|
-
{
|
|
405
|
-
"is_default": False,
|
|
406
|
-
}
|
|
407
|
-
)
|
|
408
|
-
return config
|
|
409
|
-
|
|
410
392
|
|
|
411
393
|
class IonQForteNativeBackend(IonQForteBackend):
|
|
412
394
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
413
395
|
if "gateset" not in kwargs:
|
|
414
396
|
kwargs["gateset"] = "native"
|
|
415
397
|
super().__init__(name, provider, **kwargs)
|
|
416
|
-
|
|
417
|
-
def _azure_config(self) -> Dict[str, str]:
|
|
418
|
-
config = super()._azure_config()
|
|
419
|
-
config.update(
|
|
420
|
-
{
|
|
421
|
-
"is_default": False,
|
|
422
|
-
}
|
|
423
|
-
)
|
|
424
|
-
return config
|
|
@@ -3,36 +3,17 @@
|
|
|
3
3
|
# Licensed under the MIT License.
|
|
4
4
|
##
|
|
5
5
|
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Dict, List
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Union
|
|
7
7
|
from azure.quantum.version import __version__
|
|
8
8
|
from qiskit import QuantumCircuit
|
|
9
9
|
from abc import abstractmethod
|
|
10
|
-
from .backend import AzureQirBackend
|
|
10
|
+
from .backend import AzureQirBackend, QIR_BASIS_GATES
|
|
11
11
|
|
|
12
12
|
from qiskit.providers.models import BackendConfiguration
|
|
13
13
|
from qiskit.providers import Options, Provider
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"m",
|
|
18
|
-
"ccx",
|
|
19
|
-
"cx",
|
|
20
|
-
"cz",
|
|
21
|
-
"h",
|
|
22
|
-
"reset",
|
|
23
|
-
"rx",
|
|
24
|
-
"ry",
|
|
25
|
-
"rz",
|
|
26
|
-
"s",
|
|
27
|
-
"sdg",
|
|
28
|
-
"swap",
|
|
29
|
-
"t",
|
|
30
|
-
"tdg",
|
|
31
|
-
"x",
|
|
32
|
-
"y",
|
|
33
|
-
"z",
|
|
34
|
-
"id",
|
|
35
|
-
]
|
|
14
|
+
from qsharp import TargetProfile
|
|
15
|
+
from qsharp.interop.qiskit import ResourceEstimatorBackend
|
|
16
|
+
import pyqir as pyqir
|
|
36
17
|
|
|
37
18
|
if TYPE_CHECKING:
|
|
38
19
|
from azure.quantum.qiskit import AzureQuantumProvider
|
|
@@ -55,7 +36,7 @@ class MicrosoftBackend(AzureQirBackend):
|
|
|
55
36
|
|
|
56
37
|
@classmethod
|
|
57
38
|
def _default_options(cls):
|
|
58
|
-
return Options(
|
|
39
|
+
return Options(target_profile=TargetProfile.Adaptive_RI)
|
|
59
40
|
|
|
60
41
|
def _azure_config(self) -> Dict[str, str]:
|
|
61
42
|
config = super()._azure_config()
|
|
@@ -63,11 +44,65 @@ class MicrosoftBackend(AzureQirBackend):
|
|
|
63
44
|
{
|
|
64
45
|
"provider_id": "microsoft-qc",
|
|
65
46
|
"output_data_format": "microsoft.resource-estimates.v1",
|
|
66
|
-
"to_qir_kwargs": {"record_output": False},
|
|
67
47
|
}
|
|
68
48
|
)
|
|
69
49
|
return config
|
|
70
50
|
|
|
51
|
+
def _translate_input(
|
|
52
|
+
self,
|
|
53
|
+
circuits: Union[QuantumCircuit, List[QuantumCircuit]],
|
|
54
|
+
input_params: Dict[str, Any],
|
|
55
|
+
) -> bytes:
|
|
56
|
+
"""Translates the input values to the QIR expected by the Backend."""
|
|
57
|
+
# All the logic is in the base class, but we need to override
|
|
58
|
+
# this method to ensure that the bitcode QIR format is used for RE.
|
|
59
|
+
|
|
60
|
+
# normal translation is to QIR text format in utf-8 encoded bytes
|
|
61
|
+
ir_byte_str = super()._translate_input(circuits, input_params)
|
|
62
|
+
# decode the utf-8 encoded bytes to a string
|
|
63
|
+
ir_str = ir_byte_str.decode('utf-8')
|
|
64
|
+
# convert the QIR text format to QIR bitcode format
|
|
65
|
+
module = pyqir.Module.from_ir(pyqir.Context(), ir_str)
|
|
66
|
+
return module.bitcode
|
|
67
|
+
|
|
68
|
+
def _generate_qir(
|
|
69
|
+
self, circuits: List[QuantumCircuit], target_profile: TargetProfile, **kwargs
|
|
70
|
+
) -> pyqir.Module:
|
|
71
|
+
if len(circuits) == 0:
|
|
72
|
+
raise ValueError("No QuantumCircuits provided")
|
|
73
|
+
|
|
74
|
+
name = "circuits"
|
|
75
|
+
if isinstance(circuits, QuantumCircuit):
|
|
76
|
+
name = circuits.name
|
|
77
|
+
circuits = [circuits]
|
|
78
|
+
elif isinstance(circuits, list):
|
|
79
|
+
for value in circuits:
|
|
80
|
+
if not isinstance(value, QuantumCircuit):
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"Input must be Union[QuantumCircuit, List[QuantumCircuit]]"
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"Input must be Union[QuantumCircuit, List[QuantumCircuit]]"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
skip_transpilation = kwargs.pop("skip_transpilation", False)
|
|
90
|
+
backend = ResourceEstimatorBackend(
|
|
91
|
+
skip_transpilation=skip_transpilation, **kwargs
|
|
92
|
+
)
|
|
93
|
+
context = pyqir.Context()
|
|
94
|
+
llvm_module = pyqir.qir_module(context, name)
|
|
95
|
+
for circuit in circuits:
|
|
96
|
+
qir_str = backend.qir(circuit, target_profile=target_profile)
|
|
97
|
+
module = pyqir.Module.from_ir(context, qir_str)
|
|
98
|
+
llvm_module.link(module)
|
|
99
|
+
|
|
100
|
+
err = llvm_module.verify()
|
|
101
|
+
if err is not None:
|
|
102
|
+
raise Exception(err)
|
|
103
|
+
|
|
104
|
+
return llvm_module
|
|
105
|
+
|
|
71
106
|
|
|
72
107
|
class MicrosoftResourceEstimationBackend(MicrosoftBackend):
|
|
73
108
|
"""Backend class for interfacing with the resource estimator target"""
|
|
@@ -77,7 +112,7 @@ class MicrosoftResourceEstimationBackend(MicrosoftBackend):
|
|
|
77
112
|
@classmethod
|
|
78
113
|
def _default_options(cls):
|
|
79
114
|
return Options(
|
|
80
|
-
|
|
115
|
+
target_profile=TargetProfile.Adaptive_RI,
|
|
81
116
|
errorBudget=1e-3,
|
|
82
117
|
qubitParams={"name": "qubit_gate_ns_e3"},
|
|
83
118
|
qecScheme={"name": "surface_code"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Licensed under the MIT License.
|
|
4
4
|
##
|
|
5
5
|
|
|
6
|
-
from typing import TYPE_CHECKING, Dict
|
|
6
|
+
from typing import TYPE_CHECKING, Dict, List
|
|
7
7
|
from azure.quantum.version import __version__
|
|
8
8
|
from azure.quantum.qiskit.job import AzureQuantumJob
|
|
9
9
|
from abc import abstractmethod
|
|
@@ -14,28 +14,8 @@ from .backend import (
|
|
|
14
14
|
|
|
15
15
|
from qiskit.providers.models import BackendConfiguration
|
|
16
16
|
from qiskit.providers import Options, Provider
|
|
17
|
+
from qsharp import TargetProfile
|
|
17
18
|
|
|
18
|
-
QIR_BASIS_GATES = [
|
|
19
|
-
"measure",
|
|
20
|
-
"m",
|
|
21
|
-
"barrier",
|
|
22
|
-
"cx",
|
|
23
|
-
"cz",
|
|
24
|
-
"h",
|
|
25
|
-
"reset",
|
|
26
|
-
"rx",
|
|
27
|
-
"ry",
|
|
28
|
-
"rz",
|
|
29
|
-
"s",
|
|
30
|
-
"sdg",
|
|
31
|
-
"swap",
|
|
32
|
-
"t",
|
|
33
|
-
"tdg",
|
|
34
|
-
"x",
|
|
35
|
-
"y",
|
|
36
|
-
"z",
|
|
37
|
-
"id",
|
|
38
|
-
]
|
|
39
19
|
|
|
40
20
|
if TYPE_CHECKING:
|
|
41
21
|
from azure.quantum.qiskit import AzureQuantumProvider
|
|
@@ -65,7 +45,7 @@ class QCIBackend(AzureQirBackend):
|
|
|
65
45
|
**{
|
|
66
46
|
cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
|
|
67
47
|
},
|
|
68
|
-
|
|
48
|
+
target_profile=TargetProfile.Adaptive_RI,
|
|
69
49
|
)
|
|
70
50
|
|
|
71
51
|
def _azure_config(self) -> Dict[str, str]:
|
|
@@ -76,9 +56,12 @@ class QCIBackend(AzureQirBackend):
|
|
|
76
56
|
}
|
|
77
57
|
)
|
|
78
58
|
return config
|
|
79
|
-
|
|
59
|
+
|
|
60
|
+
def _basis_gates(self) -> List[str]:
|
|
61
|
+
return super()._basis_gates() + ["barrier"]
|
|
62
|
+
|
|
80
63
|
def run(
|
|
81
|
-
self,
|
|
64
|
+
self,
|
|
82
65
|
run_input=None,
|
|
83
66
|
shots: int = None,
|
|
84
67
|
**options,
|
|
@@ -110,7 +93,7 @@ class QCISimulatorBackend(QCIBackend):
|
|
|
110
93
|
"local": False,
|
|
111
94
|
"coupling_map": None,
|
|
112
95
|
"description": "QCI simulator on Azure Quantum",
|
|
113
|
-
"basis_gates":
|
|
96
|
+
"basis_gates": self._basis_gates(),
|
|
114
97
|
"memory": False,
|
|
115
98
|
"n_qubits": 29,
|
|
116
99
|
"conditional": True,
|
|
@@ -142,7 +125,7 @@ class QCIQPUBackend(QCIBackend):
|
|
|
142
125
|
"local": False,
|
|
143
126
|
"coupling_map": None,
|
|
144
127
|
"description": "QCI QPU on Azure Quantum",
|
|
145
|
-
"basis_gates":
|
|
128
|
+
"basis_gates": self._basis_gates(),
|
|
146
129
|
"memory": False,
|
|
147
130
|
"n_qubits": 11,
|
|
148
131
|
"conditional": True,
|
|
@@ -15,7 +15,7 @@ from qiskit.providers.models import BackendConfiguration
|
|
|
15
15
|
from qiskit.providers import Options
|
|
16
16
|
from qiskit.providers import Provider
|
|
17
17
|
from qiskit.qasm2 import dumps
|
|
18
|
-
|
|
18
|
+
from qsharp import TargetProfile
|
|
19
19
|
import logging
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
@@ -50,6 +50,7 @@ QUANTINUUM_BASIS_GATES = [
|
|
|
50
50
|
"reset",
|
|
51
51
|
]
|
|
52
52
|
|
|
53
|
+
|
|
53
54
|
QUANTINUUM_PROVIDER_ID = "quantinuum"
|
|
54
55
|
QUANTINUUM_PROVIDER_NAME = "Quantinuum"
|
|
55
56
|
|
|
@@ -59,7 +60,7 @@ def _get_n_qubits(name):
|
|
|
59
60
|
if ".h1-" in name or "hqs-lt" in name:
|
|
60
61
|
return 20
|
|
61
62
|
if ".h2-" in name:
|
|
62
|
-
return
|
|
63
|
+
return 56
|
|
63
64
|
warnings.warn(
|
|
64
65
|
UserWarning(f"Number of qubits not known for target {name}. Defaulting to 20."))
|
|
65
66
|
return 20
|
|
@@ -83,7 +84,7 @@ class QuantinuumQirBackendBase(AzureQirBackend):
|
|
|
83
84
|
**{
|
|
84
85
|
cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT
|
|
85
86
|
},
|
|
86
|
-
|
|
87
|
+
target_profile=TargetProfile.Adaptive_RI,
|
|
87
88
|
)
|
|
88
89
|
|
|
89
90
|
def _azure_config(self) -> Dict[str, str]:
|
|
@@ -97,6 +98,10 @@ class QuantinuumQirBackendBase(AzureQirBackend):
|
|
|
97
98
|
|
|
98
99
|
def _get_n_qubits(self, name):
|
|
99
100
|
return _get_n_qubits(name)
|
|
101
|
+
|
|
102
|
+
def estimate_cost(self, circuits, shots, options={}):
|
|
103
|
+
"""Estimate the cost for the given circuit."""
|
|
104
|
+
return self._estimate_cost_qir(circuits, shots, options)
|
|
100
105
|
|
|
101
106
|
|
|
102
107
|
class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
|
|
@@ -118,7 +123,7 @@ class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
|
|
|
118
123
|
"local": False,
|
|
119
124
|
"coupling_map": None,
|
|
120
125
|
"description": f"Quantinuum Syntax Checker on Azure Quantum",
|
|
121
|
-
"basis_gates":
|
|
126
|
+
"basis_gates": self._basis_gates(),
|
|
122
127
|
"memory": True,
|
|
123
128
|
"n_qubits": self._get_n_qubits(name),
|
|
124
129
|
"conditional": False,
|
|
@@ -155,7 +160,7 @@ class QuantinuumEmulatorQirBackend(QuantinuumQirBackendBase):
|
|
|
155
160
|
"local": False,
|
|
156
161
|
"coupling_map": None,
|
|
157
162
|
"description": f"Quantinuum emulator on Azure Quantum",
|
|
158
|
-
"basis_gates":
|
|
163
|
+
"basis_gates": self._basis_gates(),
|
|
159
164
|
"memory": True,
|
|
160
165
|
"n_qubits": self._get_n_qubits(name),
|
|
161
166
|
"conditional": False,
|
|
@@ -192,7 +197,7 @@ class QuantinuumQPUQirBackend(QuantinuumQirBackendBase):
|
|
|
192
197
|
"local": False,
|
|
193
198
|
"coupling_map": None,
|
|
194
199
|
"description": f"Quantinuum QPU on Azure Quantum",
|
|
195
|
-
"basis_gates":
|
|
200
|
+
"basis_gates": self._basis_gates(),
|
|
196
201
|
"memory": True,
|
|
197
202
|
"n_qubits": self._get_n_qubits(name),
|
|
198
203
|
"conditional": False,
|
|
@@ -236,7 +241,7 @@ class QuantinuumBackend(AzureBackend):
|
|
|
236
241
|
"provider_id": self._provider_id,
|
|
237
242
|
"input_data_format": "honeywell.openqasm.v1",
|
|
238
243
|
"output_data_format": "honeywell.quantum-results.v1",
|
|
239
|
-
"is_default":
|
|
244
|
+
"is_default": False,
|
|
240
245
|
}
|
|
241
246
|
|
|
242
247
|
def _translate_input(self, circuit):
|