microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/replace-qsharp-with-qdk-python-tests

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/pip/qsharp/interop/qiskit/backends/qsharp_backend.py

233lines · modecode

1# Copyright (c) Microsoft Corporation.
2# Licensed under the MIT License.
3
4from collections import Counter
5import logging
6from typing import Any, Dict, List, Optional, Tuple, Union
7from uuid import uuid4
8
9from qiskit import QuantumCircuit
10from qiskit.providers import Options
11from qiskit.transpiler.target import Target
12from .... import TargetProfile
13from .. import OutputSemantics
14from ..execution import DetaultExecutor
15from ..jobs import QsSimJob
16from .backend_base import BackendBase
17from .compilation import Compilation
18from .errors import Errors
19
20logger = logging.getLogger(__name__)
21
22
23class QSharpBackend(BackendBase):
24 """
25 A virtual backend for running Qiskit circuits using the Q# simulator.
26 """
27
28 # This init is included for the docstring
29 # pylint: disable=useless-parent-delegation
30 def __init__(
31 self,
32 target: Optional[Target] = None,
33 qiskit_pass_options: Optional[Dict[str, Any]] = None,
34 transpile_options: Optional[Dict[str, Any]] = None,
35 qasm_export_options: Optional[Dict[str, Any]] = None,
36 skip_transpilation: bool = False,
37 **options,
38 ):
39 """
40 :param target: The target to use for the backend.
41 :param qiskit_pass_options: Options for the Qiskit passes.
42 :type qiskit_pass_options: Dict
43 :param transpile_options: Options for the transpiler.
44 :type transpile_options: Dict
45 :param qasm_export_options: Options for the QASM3 exporter.
46 :type qasm_export_options: Dict
47 :param skip_transpilation: Skip Qiskit transpilation.
48 :type skip_transpilation: bool
49 :param **options: Default option overrides. These can also be overridden per-call via
50 :meth:`run`. Common options:
51
52 - ``name`` (str): The name of the circuit used as the entry point. Defaults to the circuit name.
53 - ``target_profile`` (TargetProfile): The target profile to use for the compilation.
54 - ``output_semantics`` (OutputSemantics): The output semantics for the compilation.
55 Defaults to ``OutputSemantics.Qiskit``.
56 - ``shots`` (int): The number of shots to run the program for. Defaults to ``1024``.
57 - ``seed`` (int): The seed to use for the random number generator. Defaults to ``None``.
58 - ``search_path`` (str): The path to search for imports. Defaults to ``'.'``.
59 - ``output_fn`` (Callable): A callback function to receive the output of the circuit.
60 Defaults to ``None``.
61 - ``executor``: The executor to be used to submit the job. Defaults to ``SynchronousExecutor``.
62 """
63
64 super().__init__(
65 target,
66 qiskit_pass_options,
67 transpile_options,
68 qasm_export_options,
69 skip_transpilation,
70 **options,
71 )
72
73 @classmethod
74 def _default_options(cls):
75 return Options(
76 name="program",
77 params=None,
78 search_path=".",
79 shots=1024,
80 seed=None,
81 output_fn=None,
82 target_profile=TargetProfile.Unrestricted,
83 output_semantics=OutputSemantics.Qiskit,
84 executor=DetaultExecutor(),
85 )
86
87 def run(
88 self,
89 run_input: Union[QuantumCircuit, List[QuantumCircuit]],
90 **options,
91 ) -> QsSimJob:
92 """
93 Runs the given QuantumCircuit using the Q# simulator.
94
95 :param run_input: The QuantumCircuit to be executed.
96 :param **options: Per-call option overrides. Common options:
97
98 - ``name`` (str): The name of the circuit used as the entry point. Defaults to the circuit name.
99 - ``target_profile`` (TargetProfile): The target profile to use for the compilation.
100 - ``output_semantics`` (OutputSemantics): The output semantics for the compilation.
101 Defaults to ``OutputSemantics.Qiskit``.
102 - ``shots`` (int): The number of shots to run the program for. Defaults to ``1024``.
103 - ``seed`` (int): The seed to use for the random number generator. Defaults to ``None``.
104 - ``search_path`` (str): The path to search for imports. Defaults to ``'.'``.
105 - ``output_fn`` (Callable): A callback function to receive the output of the circuit.
106 Defaults to ``None``.
107 - ``executor``: The executor to be used to submit the job. Defaults to ``SynchronousExecutor``.
108 :return: The simulation job.
109 :rtype: QsSimJob
110 :raises QSharpError: If there is an error evaluating the source code.
111 :raises QasmError: If there is an error generating, parsing, or compiling QASM.
112 :raises ValueError: If run_input is not a QuantumCircuit or List[QuantumCircuit].
113 """
114
115 run_input = self._validate_quantum_circuits(run_input)
116 return self._run(run_input, **options)
117
118 def _execute(self, programs: List[Compilation], **input_params) -> Dict[str, Any]:
119 exec_results: List[Tuple[Compilation, Dict[str, Any]]] = [
120 (
121 program,
122 _run_qasm(program.qasm, vars(self.options).copy(), **input_params),
123 )
124 for program in programs
125 ]
126 job_results = []
127
128 shots = input_params.get("shots")
129 if shots is None:
130 raise ValueError(str(Errors.MISSING_NUMBER_OF_SHOTS))
131
132 for program, exec_result in exec_results:
133 results = [self._shot_to_bitstring(result) for result in exec_result]
134
135 counts = Counter(results)
136 counts_dict = dict(counts)
137 probabilities = {
138 bitstring: (count / shots) for bitstring, count in counts_dict.items()
139 }
140
141 job_result = {
142 "data": {"counts": counts_dict, "probabilities": probabilities},
143 "success": True,
144 "header": {
145 "metadata": {"qasm": program.qasm},
146 "name": program.circuit.name,
147 "compilation_time_taken": program.time_taken,
148 },
149 "shots": shots,
150 }
151 job_results.append(job_result)
152
153 # All of these fields are required by the Result object
154 result_dict = {
155 "results": job_results,
156 "qobj_id": str(uuid4()),
157 "success": True,
158 }
159
160 return result_dict
161
162
163def _run_qasm(
164 qasm: str,
165 default_options: Options,
166 **options,
167) -> Any:
168 """
169 Runs the supplied OpenQASM 3 program.
170 Gates defined by stdgates.inc will be overridden with definitions
171 from the Q# compiler.
172
173 Any gates, such as matrix unitaries, that are not able to be
174 transpiled will result in an error.
175
176 :param source: The input OpenQASM 3 string to be processed.
177 :param default_options: Default backend option values.
178 :param **options: Common options:
179
180 - ``target_profile`` (TargetProfile): The target profile to use for the compilation.
181 - ``output_semantics`` (OutputSemantics): The output semantics for the compilation.
182 - ``name`` (str): The name of the circuit. Defaults to ``'program'``.
183 - ``search_path`` (str): The optional search path for resolving qasm imports.
184 - ``shots`` (int): The number of shots to run the program for.
185 - ``seed`` (int): The seed to use for the random number generator.
186 - ``output_fn`` (Callable): A callback for each output. Defaults to ``None``.
187 :return: A list of results or runtime errors.
188 :raises QSharpError: If there is an error evaluating the source code.
189 :raises QasmError: If there is an error generating, parsing, or compiling QASM.
190 """
191
192 from ...._native import run_qasm_program, Output # type: ignore
193 from ...._fs import read_file, list_directory, resolve
194 from ...._http import fetch_github
195
196 def callback(output: Output) -> None:
197 print(output)
198
199 output_fn = options.pop("output_fn", callback)
200
201 def value_or_default(key: str) -> Any:
202 return options.pop(key, default_options[key])
203
204 # when passing the args into the rust layer, any kwargs with None values
205 # will cause an error, so we need to filter them out.
206 args = {}
207 if name := value_or_default("name"):
208 args["name"] = name
209
210 if target_profile := value_or_default("target_profile"):
211 args["target_profile"] = target_profile
212 if output_semantics := value_or_default("output_semantics"):
213 args["output_semantics"] = output_semantics
214
215 if search_path := value_or_default("search_path"):
216 args["search_path"] = search_path
217 if shots := value_or_default("shots"):
218 args["shots"] = shots
219 if seed := value_or_default("seed"):
220 args["seed"] = seed
221
222 return run_qasm_program(
223 qasm,
224 output_fn,
225 None,
226 None,
227 None,
228 read_file,
229 list_directory,
230 resolve,
231 fetch_github,
232 **args,
233 )
234