Skip to content

Add Python evaluation API #2345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/qsc/src/packages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub fn prepare_package_store(
capabilities: TargetCapabilityFlags,
package_graph_sources: PackageGraphSources,
) -> BuildableProgram {
let (std_id, mut package_store) = crate::compile::package_store_with_stdlib(capabilities);
let (std_id, qasm_id, mut package_store) = crate::qasm::package_store_with_qasm(capabilities);

let mut canonical_package_identifier_to_package_id_mapping = FxHashMap::default();

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

BuildableProgram {
Expand Down
10 changes: 5 additions & 5 deletions language_service/src/completion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2976,7 +2976,7 @@ fn package_aliases() {
label: "MyDep",
kind: Module,
sort_text: Some(
"0600MyDep",
"0700MyDep",
),
detail: None,
additional_text_edits: None,
Expand Down Expand Up @@ -3006,7 +3006,7 @@ fn package_alias_members() {
label: "Other",
kind: Module,
sort_text: Some(
"0700Other",
"0800Other",
),
detail: None,
additional_text_edits: None,
Expand Down Expand Up @@ -3053,7 +3053,7 @@ fn dependency_namespace_members() {
label: "Sub",
kind: Module,
sort_text: Some(
"0700Sub",
"0800Sub",
),
detail: None,
additional_text_edits: None,
Expand Down Expand Up @@ -3151,7 +3151,7 @@ fn member_completion_in_imported_namespace_from_dependency() {
label: "Bar",
kind: Module,
sort_text: Some(
"0700Bar",
"0800Bar",
),
detail: None,
additional_text_edits: None,
Expand Down Expand Up @@ -3204,7 +3204,7 @@ fn aliased_namespace_in_dependency() {
label: "Bar",
kind: Module,
sort_text: Some(
"0700Bar",
"0800Bar",
),
detail: None,
additional_text_edits: None,
Expand Down
161 changes: 123 additions & 38 deletions pip/qsharp/_native.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,42 @@ class Interpreter:
"""
...

def import_qasm(
self,
source: str,
output_fn: Callable[[Output], None],
read_file: Callable[[str], Tuple[str, str]],
list_directory: Callable[[str], List[Dict[str, str]]],
resolve_path: Callable[[str, str], str],
fetch_github: Callable[[str, str, str, str], str],
**kwargs
) -> Any:
"""
Imports OpenQASM source code into the active Q# interpreter.

Args:
source (str): An OpenQASM program or fragment.
output_fn: The function to handle the output of the execution.
read_file: A callable that reads a file and returns its content and path.
list_directory: A callable that lists the contents of a directory.
resolve_path: A callable that resolves a file path given a base path and a relative path.
fetch_github: A callable that fetches a file from GitHub.
**kwargs: Additional keyword arguments to pass to the execution.
- name (str): The name of the program. This is used as the entry point for the program.
- search_path (Optional[str]): The optional search path for resolving file references.
- output_semantics (OutputSemantics, optional): The output semantics for the compilation.
- program_type (ProgramType, optional): The type of program compilation to perform.

Returns:
value: The value returned by the last statement in the source code.

Raises:
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
QSharpError: If there is an error compiling the program.
QSharpError: If there is an error evaluating the source code.
"""
...

class Result(Enum):
"""
A Q# measurement result.
Expand Down Expand Up @@ -375,75 +411,81 @@ def physical_estimates(logical_resources: str, params: str) -> str:
"""
...

def resource_estimate_qasm(
def circuit_qasm_program(
source: str,
job_params: str,
read_file: Callable[[str], Tuple[str, str]],
list_directory: Callable[[str], List[Dict[str, str]]],
resolve_path: Callable[[str, str], str],
fetch_github: Callable[[str, str, str, str], str],
**kwargs
) -> str:
) -> Circuit:
"""
Estimates the resource requirements for executing QASM source code.
Synthesizes a circuit for an OpenQASM program.

Note:
This call while exported is not intended to be used directly by the user.
It is intended to be used by the Python wrapper which will handle the
callbacks and other Python specific details.

Args:
source (str): The QASM source code to estimate the resource requirements for.
job_params (str): The parameters for the job.
source (str): An OpenQASM program. Alternatively, a callable can be provided,
which must be an already imported global callable.
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
**kwargs: Additional keyword arguments to pass to the execution.
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
- search_path (str): The optional search path for resolving imports.
- name (str): The name of the program. This is used as the entry point for the program.
- search_path (Optional[str]): The optional search path for resolving file references.
Returns:
str: The estimated resource requirements for executing the QASM source code.
Circuit: The synthesized circuit.

Raises:
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
QSharpError: If there is an error evaluating the program.
QSharpError: If there is an error synthesizing the circuit.
"""
...

def run_qasm(
def compile_qasm_program_to_qir(
source: str,
output_fn: Callable[[Output], None],
read_file: Callable[[str], Tuple[str, str]],
list_directory: Callable[[str], List[Dict[str, str]]],
resolve_path: Callable[[str, str], str],
fetch_github: Callable[[str, str, str, str], str],
**kwargs
) -> Any:
) -> str:
"""
Executes QASM source code using the specified target profile.
Compiles the OpenQASM source code into a program that can be submitted to a
target as QIR (Quantum Intermediate Representation).

Note:
This call while exported is not intended to be used directly by the user.
It is intended to be used by the Python wrapper which will handle the
callbacks and other Python specific details.

Args:
source (str): The QASM source code to execute.
output_fn (Callable[[Output], None]): The function to handle the output of the execution.
read_file (Callable[[str], Tuple[str, str]]): The function to read a file and return its contents.
list_directory (Callable[[str], List[Dict[str, str]]]): The function to list the contents of a directory.
resolve_path (Callable[[str, str], str]): The function to resolve a path given a base path and a relative path.
fetch_github (Callable[[str, str, str, str], str]): The function to fetch a file from GitHub.
**kwargs: Additional keyword arguments to pass to the execution.
- target_profile (TargetProfile): The target profile to use for execution.
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
- search_path (str): The optional search path for resolving imports.
- shots (int): The number of shots to run the program for. Defaults to 1.
- seed (int): The seed to use for the random number generator.
source (str): The OpenQASM source code to estimate the resource requirements for.
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
**kwargs: Additional keyword arguments to pass to the compilation when source program is provided.
- name (str): The name of the circuit. This is used as the entry point for the program.
- target_profile (TargetProfile): The target profile to use for code generation.
- search_path (Optional[str]): The optional search path for resolving file references.
- output_semantics (OutputSemantics, optional): The output semantics for the compilation.

Returns:
Any: The result of the execution.
str: The converted QIR code as a string.

Raises:
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
QSharpError: If there is an error compiling the program.
"""
...

def compile_qasm_to_qir(
def compile_qasm_to_qsharp(
source: str,
read_file: Callable[[str], Tuple[str, str]],
list_directory: Callable[[str], List[Dict[str, str]]],
Expand All @@ -452,57 +494,100 @@ def compile_qasm_to_qir(
**kwargs
) -> str:
"""
Converts a Qiskit QuantumCircuit to QIR (Quantum Intermediate Representation).
Converts a OpenQASM program to Q#.

Note:
This call while exported is not intended to be used directly by the user.
It is intended to be used by the Python wrapper which will handle the
callbacks and other Python specific details.

Args:
source (str): The QASM source code to estimate the resource requirements for.
source (str): The OpenQASM source code to estimate the resource requirements for.
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
**kwargs: Additional keyword arguments to pass to the execution.
- name (str): The name of the circuit. This is used as the entry point for the program.
- entry_expr (str, optional): The entry expression for the QIR conversion. Defaults to None.
- target_profile (TargetProfile): The target profile to use for code generation.
- search_path (Optional[str]): The optional search path for resolving file references.

Returns:
str: The converted QIR code as a string.
str: The converted Q# code as a string.
"""
...

def compile_qasm_to_qsharp(
def resource_estimate_qasm_program(
source: str,
job_params: str,
read_file: Callable[[str], Tuple[str, str]],
list_directory: Callable[[str], List[Dict[str, str]]],
resolve_path: Callable[[str, str], str],
fetch_github: Callable[[str, str, str, str], str],
**kwargs
) -> str:
"""
Converts a Qiskit QuantumCircuit to Q#.
Estimates the resource requirements for executing OpenQASM source code.

Note:
This call while exported is not intended to be used directly by the user.
It is intended to be used by the Python wrapper which will handle the
callbacks and other Python specific details.

Args:
source (str): The QASM source code to estimate the resource requirements for.
source (str): The OpenQASM source code to estimate the resource requirements for.
job_params (str): The parameters for the job.
read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path.
list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory.
resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path.
fetch_github (Callable[[str, str, str, str], str]): A callable that fetches a file from GitHub.
**kwargs: Additional keyword arguments to pass to the execution.
- name (str): The name of the circuit. This is used as the entry point for the program.
- search_path (Optional[str]): The optional search path for resolving file references.
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
- search_path (str): The optional search path for resolving imports.
Returns:
str: The estimated resource requirements for executing the OpenQASM source code.
"""
...

def run_qasm_program(
source: str,
output_fn: Callable[[Output], None],
noise: Optional[Tuple[float, float, float]],
read_file: Callable[[str], Tuple[str, str]],
list_directory: Callable[[str], List[Dict[str, str]]],
resolve_path: Callable[[str, str], str],
fetch_github: Callable[[str, str, str, str], str],
**kwargs
) -> Any:
"""
Runs the given OpenQASM program for the given number of shots.
Each shot uses an independent instance of the simulator.

Note:
This call while exported is not intended to be used directly by the user.
It is intended to be used by the Python wrapper which will handle the
callbacks and other Python specific details.

Args:
source (str): The OpenQASM source code to execute.
output_fn (Callable[[Output], None]): The function to handle the output of the execution.
noise: The noise to use in simulation.
read_file (Callable[[str], Tuple[str, str]]): The function to read a file and return its contents.
list_directory (Callable[[str], List[Dict[str, str]]]): The function to list the contents of a directory.
resolve_path (Callable[[str, str], str]): The function to resolve a path given a base path and a relative path.
fetch_github (Callable[[str, str, str, str], str]): The function to fetch a file from GitHub.
**kwargs: Additional keyword arguments to pass to the execution.
- target_profile (TargetProfile): The target profile to use for execution.
- name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'.
- search_path (str): The optional search path for resolving imports.
- output_semantics (OutputSemantics, optional): The output semantics for the compilation.
- shots (int): The number of shots to run the program for. Defaults to 1.
- seed (int): The seed to use for the random number generator.

Returns:
str: The converted Q# code as a string.
Any: The result of the execution.

Raises:
QasmError: If there is an error generating, parsing, or analyzing the OpenQASM source.
QSharpError: If there is an error interpreting the input.
"""
...
22 changes: 15 additions & 7 deletions pip/qsharp/_qsharp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Licensed under the MIT License.

from . import telemetry_events, code
from ._native import (
from ._native import ( # type: ignore
Interpreter,
TargetProfile,
StateDumpData,
Expand All @@ -29,8 +29,8 @@
import types
from time import monotonic

_interpreter = None
_config = None
_interpreter: Union["Interpreter", None] = None
_config: Union["Config", None] = None

# Check if we are running in a Jupyter notebook to use the IPython display function
_in_jupyter = False
Expand Down Expand Up @@ -481,11 +481,12 @@ def run(
a List of ShotResults is returned.

:raises QSharpError: If there is an error interpreting the input.
:raises ValueError: If the number of shots is less than 1.
"""
ipython_helper()

if shots < 1:
raise QSharpError("The number of shots must be greater than 0.")
raise ValueError("The number of shots must be greater than 0.")

telemetry_events.on_run(shots)
start_time = monotonic()
Expand Down Expand Up @@ -635,16 +636,23 @@ def circuit(
:raises QSharpError: If there is an error synthesizing the circuit.
"""
ipython_helper()
start = monotonic()
telemetry_events.on_circuit()
if isinstance(entry_expr, Callable) and hasattr(entry_expr, "__global_callable"):
if len(args) == 1:
args = args[0]
elif len(args) == 0:
args = None
return get_interpreter().circuit(
res = get_interpreter().circuit(
callable=entry_expr.__global_callable, args=args
)
else:
return get_interpreter().circuit(entry_expr, operation)
res = get_interpreter().circuit(entry_expr, operation)

durationMs = (monotonic() - start) * 1000
telemetry_events.on_circuit_end(durationMs)

return res


def estimate(
Expand All @@ -666,7 +674,7 @@ def estimate(
ipython_helper()

def _coerce_estimator_params(
params: Optional[Union[Dict[str, Any], List, EstimatorParams]] = None
params: Optional[Union[Dict[str, Any], List, EstimatorParams]] = None,
) -> List[Dict[str, Any]]:
if params is None:
params = [{}]
Expand Down
Loading
Loading