Skip to content

Commit d52446a

Browse files
authored
Add Python evaluation API (#2345)
1 parent 8fb656a commit d52446a

File tree

19 files changed

+1923
-164
lines changed

19 files changed

+1923
-164
lines changed

compiler/qsc/src/packages.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub fn prepare_package_store(
8181
capabilities: TargetCapabilityFlags,
8282
package_graph_sources: PackageGraphSources,
8383
) -> BuildableProgram {
84-
let (std_id, mut package_store) = crate::compile::package_store_with_stdlib(capabilities);
84+
let (std_id, qasm_id, mut package_store) = crate::qasm::package_store_with_qasm(capabilities);
8585

8686
let mut canonical_package_identifier_to_package_id_mapping = FxHashMap::default();
8787

@@ -156,6 +156,7 @@ pub fn prepare_package_store(
156156
.map(|pkg| (pkg, Some(alias.clone())))
157157
})
158158
.chain(std::iter::once((std_id, None)))
159+
.chain(std::iter::once((qasm_id, Some("QasmStd".into()))))
159160
.collect::<Vec<_>>();
160161

161162
BuildableProgram {

language_service/src/completion/tests.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -2976,7 +2976,7 @@ fn package_aliases() {
29762976
label: "MyDep",
29772977
kind: Module,
29782978
sort_text: Some(
2979-
"0600MyDep",
2979+
"0700MyDep",
29802980
),
29812981
detail: None,
29822982
additional_text_edits: None,
@@ -3006,7 +3006,7 @@ fn package_alias_members() {
30063006
label: "Other",
30073007
kind: Module,
30083008
sort_text: Some(
3009-
"0700Other",
3009+
"0800Other",
30103010
),
30113011
detail: None,
30123012
additional_text_edits: None,
@@ -3053,7 +3053,7 @@ fn dependency_namespace_members() {
30533053
label: "Sub",
30543054
kind: Module,
30553055
sort_text: Some(
3056-
"0700Sub",
3056+
"0800Sub",
30573057
),
30583058
detail: None,
30593059
additional_text_edits: None,
@@ -3151,7 +3151,7 @@ fn member_completion_in_imported_namespace_from_dependency() {
31513151
label: "Bar",
31523152
kind: Module,
31533153
sort_text: Some(
3154-
"0700Bar",
3154+
"0800Bar",
31553155
),
31563156
detail: None,
31573157
additional_text_edits: None,
@@ -3204,7 +3204,7 @@ fn aliased_namespace_in_dependency() {
32043204
label: "Bar",
32053205
kind: Module,
32063206
sort_text: Some(
3207-
"0700Bar",
3207+
"0800Bar",
32083208
),
32093209
detail: None,
32103210
additional_text_edits: None,

pip/qsharp/_native.pyi

+123-38
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,42 @@ class Interpreter:
293293
"""
294294
...
295295

296+
def import_qasm(
297+
self,
298+
source: str,
299+
output_fn: Callable[[Output], None],
300+
read_file: Callable[[str], Tuple[str, str]],
301+
list_directory: Callable[[str], List[Dict[str, str]]],
302+
resolve_path: Callable[[str, str], str],
303+
fetch_github: Callable[[str, str, str, str], str],
304+
**kwargs
305+
) -> Any:
306+
"""
307+
Imports OpenQASM source code into the active Q# interpreter.
308+
309+
Args:
310+
source (str): An OpenQASM program or fragment.
311+
output_fn: The function to handle the output of the execution.
312+
read_file: A callable that reads a file and returns its content and path.
313+
list_directory: A callable that lists the contents of a directory.
314+
resolve_path: A callable that resolves a file path given a base path and a relative path.
315+
fetch_github: A callable that fetches a file from GitHub.
316+
**kwargs: Additional keyword arguments to pass to the execution.
317+
- name (str): The name of the program. This is used as the entry point for the program.
318+
- search_path (Optional[str]): The optional search path for resolving file references.
319+
- output_semantics (OutputSemantics, optional): The output semantics for the compilation.
320+
- program_type (ProgramType, optional): The type of program compilation to perform.
321+
322+
Returns:
323+
value: The value returned by the last statement in the source code.
324+
325+
Raises:
326+
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
327+
QSharpError: If there is an error compiling the program.
328+
QSharpError: If there is an error evaluating the source code.
329+
"""
330+
...
331+
296332
class Result(Enum):
297333
"""
298334
A Q# measurement result.
@@ -375,75 +411,81 @@ def physical_estimates(logical_resources: str, params: str) -> str:
375411
"""
376412
...
377413

378-
def resource_estimate_qasm(
414+
def circuit_qasm_program(
379415
source: str,
380-
job_params: str,
381416
read_file: Callable[[str], Tuple[str, str]],
382417
list_directory: Callable[[str], List[Dict[str, str]]],
383418
resolve_path: Callable[[str, str], str],
384419
fetch_github: Callable[[str, str, str, str], str],
385420
**kwargs
386-
) -> str:
421+
) -> Circuit:
387422
"""
388-
Estimates the resource requirements for executing QASM source code.
423+
Synthesizes a circuit for an OpenQASM program.
389424
390425
Note:
391426
This call while exported is not intended to be used directly by the user.
392427
It is intended to be used by the Python wrapper which will handle the
393428
callbacks and other Python specific details.
394429
395430
Args:
396-
source (str): The QASM source code to estimate the resource requirements for.
397-
job_params (str): The parameters for the job.
431+
source (str): An OpenQASM program. Alternatively, a callable can be provided,
432+
which must be an already imported global callable.
398433
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
399434
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
400435
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
401436
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
402437
**kwargs: Additional keyword arguments to pass to the execution.
403-
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
404-
- search_path (str): The optional search path for resolving imports.
438+
- name (str): The name of the program. This is used as the entry point for the program.
439+
- search_path (Optional[str]): The optional search path for resolving file references.
405440
Returns:
406-
str: The estimated resource requirements for executing the QASM source code.
441+
Circuit: The synthesized circuit.
442+
443+
Raises:
444+
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
445+
QSharpError: If there is an error evaluating the program.
446+
QSharpError: If there is an error synthesizing the circuit.
407447
"""
408448
...
409449

410-
def run_qasm(
450+
def compile_qasm_program_to_qir(
411451
source: str,
412-
output_fn: Callable[[Output], None],
413452
read_file: Callable[[str], Tuple[str, str]],
414453
list_directory: Callable[[str], List[Dict[str, str]]],
415454
resolve_path: Callable[[str, str], str],
416455
fetch_github: Callable[[str, str, str, str], str],
417456
**kwargs
418-
) -> Any:
457+
) -> str:
419458
"""
420-
Executes QASM source code using the specified target profile.
459+
Compiles the OpenQASM source code into a program that can be submitted to a
460+
target as QIR (Quantum Intermediate Representation).
421461
422462
Note:
423463
This call while exported is not intended to be used directly by the user.
424464
It is intended to be used by the Python wrapper which will handle the
425465
callbacks and other Python specific details.
426466
427467
Args:
428-
source (str): The QASM source code to execute.
429-
output_fn (Callable[[Output], None]): The function to handle the output of the execution.
430-
read_file (Callable[[str], Tuple[str, str]]): The function to read a file and return its contents.
431-
list_directory (Callable[[str], List[Dict[str, str]]]): The function to list the contents of a directory.
432-
resolve_path (Callable[[str, str], str]): The function to resolve a path given a base path and a relative path.
433-
fetch_github (Callable[[str, str, str, str], str]): The function to fetch a file from GitHub.
434-
**kwargs: Additional keyword arguments to pass to the execution.
435-
- target_profile (TargetProfile): The target profile to use for execution.
436-
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
437-
- search_path (str): The optional search path for resolving imports.
438-
- shots (int): The number of shots to run the program for. Defaults to 1.
439-
- seed (int): The seed to use for the random number generator.
468+
source (str): The OpenQASM source code to estimate the resource requirements for.
469+
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
470+
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
471+
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
472+
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
473+
**kwargs: Additional keyword arguments to pass to the compilation when source program is provided.
474+
- name (str): The name of the circuit. This is used as the entry point for the program.
475+
- target_profile (TargetProfile): The target profile to use for code generation.
476+
- search_path (Optional[str]): The optional search path for resolving file references.
477+
- output_semantics (OutputSemantics, optional): The output semantics for the compilation.
440478
441479
Returns:
442-
Any: The result of the execution.
480+
str: The converted QIR code as a string.
481+
482+
Raises:
483+
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
484+
QSharpError: If there is an error compiling the program.
443485
"""
444486
...
445487

446-
def compile_qasm_to_qir(
488+
def compile_qasm_to_qsharp(
447489
source: str,
448490
read_file: Callable[[str], Tuple[str, str]],
449491
list_directory: Callable[[str], List[Dict[str, str]]],
@@ -452,57 +494,100 @@ def compile_qasm_to_qir(
452494
**kwargs
453495
) -> str:
454496
"""
455-
Converts a Qiskit QuantumCircuit to QIR (Quantum Intermediate Representation).
497+
Converts a OpenQASM program to Q#.
456498
457499
Note:
458500
This call while exported is not intended to be used directly by the user.
459501
It is intended to be used by the Python wrapper which will handle the
460502
callbacks and other Python specific details.
461503
462504
Args:
463-
source (str): The QASM source code to estimate the resource requirements for.
505+
source (str): The OpenQASM source code to estimate the resource requirements for.
464506
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
465507
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
466508
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
467509
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
468510
**kwargs: Additional keyword arguments to pass to the execution.
469511
- name (str): The name of the circuit. This is used as the entry point for the program.
470-
- entry_expr (str, optional): The entry expression for the QIR conversion. Defaults to None.
471-
- target_profile (TargetProfile): The target profile to use for code generation.
472512
- search_path (Optional[str]): The optional search path for resolving file references.
473513
474514
Returns:
475-
str: The converted QIR code as a string.
515+
str: The converted Q# code as a string.
476516
"""
477517
...
478518

479-
def compile_qasm_to_qsharp(
519+
def resource_estimate_qasm_program(
480520
source: str,
521+
job_params: str,
481522
read_file: Callable[[str], Tuple[str, str]],
482523
list_directory: Callable[[str], List[Dict[str, str]]],
483524
resolve_path: Callable[[str, str], str],
484525
fetch_github: Callable[[str, str, str, str], str],
485526
**kwargs
486527
) -> str:
487528
"""
488-
Converts a Qiskit QuantumCircuit to Q#.
529+
Estimates the resource requirements for executing OpenQASM source code.
489530
490531
Note:
491532
This call while exported is not intended to be used directly by the user.
492533
It is intended to be used by the Python wrapper which will handle the
493534
callbacks and other Python specific details.
494535
495536
Args:
496-
source (str): The QASM source code to estimate the resource requirements for.
537+
source (str): The OpenQASM source code to estimate the resource requirements for.
538+
job_params (str): The parameters for the job.
497539
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
498540
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
499541
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
500542
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
501543
**kwargs: Additional keyword arguments to pass to the execution.
502-
- name (str): The name of the circuit. This is used as the entry point for the program.
503-
- search_path (Optional[str]): The optional search path for resolving file references.
544+
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
545+
- search_path (str): The optional search path for resolving imports.
546+
Returns:
547+
str: The estimated resource requirements for executing the OpenQASM source code.
548+
"""
549+
...
550+
551+
def run_qasm_program(
552+
source: str,
553+
output_fn: Callable[[Output], None],
554+
noise: Optional[Tuple[float, float, float]],
555+
read_file: Callable[[str], Tuple[str, str]],
556+
list_directory: Callable[[str], List[Dict[str, str]]],
557+
resolve_path: Callable[[str, str], str],
558+
fetch_github: Callable[[str, str, str, str], str],
559+
**kwargs
560+
) -> Any:
561+
"""
562+
Runs the given OpenQASM program for the given number of shots.
563+
Each shot uses an independent instance of the simulator.
564+
565+
Note:
566+
This call while exported is not intended to be used directly by the user.
567+
It is intended to be used by the Python wrapper which will handle the
568+
callbacks and other Python specific details.
569+
570+
Args:
571+
source (str): The OpenQASM source code to execute.
572+
output_fn (Callable[[Output], None]): The function to handle the output of the execution.
573+
noise: The noise to use in simulation.
574+
read_file (Callable[[str], Tuple[str, str]]): The function to read a file and return its contents.
575+
list_directory (Callable[[str], List[Dict[str, str]]]): The function to list the contents of a directory.
576+
resolve_path (Callable[[str, str], str]): The function to resolve a path given a base path and a relative path.
577+
fetch_github (Callable[[str, str, str, str], str]): The function to fetch a file from GitHub.
578+
**kwargs: Additional keyword arguments to pass to the execution.
579+
- target_profile (TargetProfile): The target profile to use for execution.
580+
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
581+
- search_path (str): The optional search path for resolving imports.
582+
- output_semantics (OutputSemantics, optional): The output semantics for the compilation.
583+
- shots (int): The number of shots to run the program for. Defaults to 1.
584+
- seed (int): The seed to use for the random number generator.
504585
505586
Returns:
506-
str: The converted Q# code as a string.
587+
Any: The result of the execution.
588+
589+
Raises:
590+
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
591+
QSharpError: If there is an error interpreting the input.
507592
"""
508593
...

pip/qsharp/_qsharp.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Licensed under the MIT License.
33

44
from . import telemetry_events, code
5-
from ._native import (
5+
from ._native import ( # type: ignore
66
Interpreter,
77
TargetProfile,
88
StateDumpData,
@@ -29,8 +29,8 @@
2929
import types
3030
from time import monotonic
3131

32-
_interpreter = None
33-
_config = None
32+
_interpreter: Union["Interpreter", None] = None
33+
_config: Union["Config", None] = None
3434

3535
# Check if we are running in a Jupyter notebook to use the IPython display function
3636
_in_jupyter = False
@@ -481,11 +481,12 @@ def run(
481481
a List of ShotResults is returned.
482482
483483
:raises QSharpError: If there is an error interpreting the input.
484+
:raises ValueError: If the number of shots is less than 1.
484485
"""
485486
ipython_helper()
486487

487488
if shots < 1:
488-
raise QSharpError("The number of shots must be greater than 0.")
489+
raise ValueError("The number of shots must be greater than 0.")
489490

490491
telemetry_events.on_run(shots)
491492
start_time = monotonic()
@@ -635,16 +636,23 @@ def circuit(
635636
:raises QSharpError: If there is an error synthesizing the circuit.
636637
"""
637638
ipython_helper()
639+
start = monotonic()
640+
telemetry_events.on_circuit()
638641
if isinstance(entry_expr, Callable) and hasattr(entry_expr, "__global_callable"):
639642
if len(args) == 1:
640643
args = args[0]
641644
elif len(args) == 0:
642645
args = None
643-
return get_interpreter().circuit(
646+
res = get_interpreter().circuit(
644647
callable=entry_expr.__global_callable, args=args
645648
)
646649
else:
647-
return get_interpreter().circuit(entry_expr, operation)
650+
res = get_interpreter().circuit(entry_expr, operation)
651+
652+
durationMs = (monotonic() - start) * 1000
653+
telemetry_events.on_circuit_end(durationMs)
654+
655+
return res
648656

649657

650658
def estimate(
@@ -666,7 +674,7 @@ def estimate(
666674
ipython_helper()
667675

668676
def _coerce_estimator_params(
669-
params: Optional[Union[Dict[str, Any], List, EstimatorParams]] = None
677+
params: Optional[Union[Dict[str, Any], List, EstimatorParams]] = None,
670678
) -> List[Dict[str, Any]]:
671679
if params is None:
672680
params = [{}]

0 commit comments

Comments
 (0)