microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/qdk_package

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

275lines · modecode

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