Skip to content

Commit

Permalink
Create public experimental interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jakelishman committed Jan 17, 2024
1 parent 9d7ccc6 commit 2b7f36f
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 12 deletions.
24 changes: 12 additions & 12 deletions crates/qasm3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ fn restore_include_path(include_path: Option<&OsStr>) {
///
/// Args:
/// source (str): the program source in a Python string.
/// custom_instructions (Iterable[CustomGate]): Python constructors to use for particular named
/// gates. If not supplied, Qiskit will use its own standard-library constructors for
/// gates defined in the OpenQASM 3.0 standard-library file ``stdgates.inc``.
/// custom_gates (Iterable[CustomGate]): Python constructors to use for particular named gates.
/// If not supplied, Qiskit will use its own standard-library constructors for gates
/// defined in the OpenQASM 3.0 standard-library file ``stdgates.inc``.
/// include_path (Iterable[str]): the path to search when resolving ``include`` statements.
/// If not given, Qiskit will arrange for this to point to a location containing
/// ``stdgates.inc`` only. Paths are tried in the sequence order.
Expand All @@ -95,12 +95,12 @@ fn restore_include_path(include_path: Option<&OsStr>) {
/// In the case of a parsing error, most of the error messages are printed to the terminal
/// and formatted, for better legibility.
#[pyfunction]
#[pyo3(pass_module, signature = (source, /, *, custom_instructions=None, include_path=None))]
#[pyo3(pass_module, signature = (source, /, *, custom_gates=None, include_path=None))]
pub fn loads(
module: &PyModule,
py: Python,
source: String,
custom_instructions: Option<Vec<circuit::PyGate>>,
custom_gates: Option<Vec<circuit::PyGate>>,
include_path: Option<Vec<OsString>>,
) -> PyResult<circuit::PyCircuit> {
let old_path = set_include_path(module, include_path.as_deref())?;
Expand All @@ -112,7 +112,7 @@ pub fn loads(
"errors during parsing; see printed errors",
));
}
let gates = match custom_instructions {
let gates = match custom_gates {
Some(gates) => gates
.into_iter()
.map(|gate| (gate.name().to_owned(), gate))
Expand All @@ -137,9 +137,9 @@ pub fn loads(
/// opened it is consumed in Python space, whereas filenames are opened and consumed in
/// Rust space; there might be slightly different performance characteristics, depending on
/// your system and how the streams are buffered by default.
/// custom_instructions (Iterable[CustomGate]): Python constructors to use for particular named
/// gates. If not supplied, Qiskit will use its own standard-library constructors for
/// gates defined in the OpenQASM 3.0 standard-library file ``stdgates.inc``.
/// custom_gates (Iterable[CustomGate]): Python constructors to use for particular named gates.
/// If not supplied, Qiskit will use its own standard-library constructors for gates
/// defined in the OpenQASM 3.0 standard-library file ``stdgates.inc``.
/// include_path (Iterable[str]): the path to search when resolving ``include`` statements.
/// If not given, Qiskit will arrange for this to point to a location containing
/// ``stdgates.inc`` only. Paths are tried in the sequence order.
Expand All @@ -160,13 +160,13 @@ pub fn loads(
#[pyfunction]
#[pyo3(
pass_module,
signature = (pathlike_or_filelike, /, *, custom_instructions=None, include_path=None),
signature = (pathlike_or_filelike, /, *, custom_gates=None, include_path=None),
)]
pub fn load(
module: &PyModule,
py: Python,
pathlike_or_filelike: &PyAny,
custom_instructions: Option<Vec<circuit::PyGate>>,
custom_gates: Option<Vec<circuit::PyGate>>,
include_path: Option<Vec<OsString>>,
) -> PyResult<circuit::PyCircuit> {
let source =
Expand All @@ -183,7 +183,7 @@ pub fn load(
QASM3ImporterError::new_err(format!("failed to read file '{:?}': {:?}", &path, err))
})?
};
loads(module, py, source, custom_instructions, include_path)
loads(module, py, source, custom_gates, include_path)
}

/// Create a suitable sequence for use with the ``custom_gates`` of :func:`load` and :func:`loads`,
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ select = [
extension-pkg-allow-list = [
"numpy",
"qiskit._accelerate",
"qiskit._qasm2",
"qiskit._qasm3",
# We can't allow pylint to load qiskit._qasm2 because it's not able to
# statically resolve the cyclical load of the exception and it bugs out.
"retworkx",
Expand Down
23 changes: 23 additions & 0 deletions qiskit/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@
is not present, but will raise :exc:`OptionalDependencyImportWarning` to let you know about it.
.. autoexception:: OptionalDependencyImportWarning
When experimental features are being used, Qiskit will raise :exc:`ExperimentalWarning`.
.. autoexception:: ExperimentalWarning
Filtering warnings
------------------
Python has built-in mechanisms to filter warnings, described in the documentation of the
:mod:`warnings` module. You can use these subclasses in your warning filters from within Python to
silence warnings you are not interested in. For example, if you are knowingly using experimental
features and are comfortable that they make break in later versions, you can silence
:exc:`ExperimentalWarning` like this:
import warnings
from qiskit.exceptions import ExperimentalWarning
warnings.filterwarnings("ignore", category=ExperimentalWarning)
"""

from typing import Optional
Expand Down Expand Up @@ -124,3 +143,7 @@ class OptionalDependencyImportWarning(QiskitWarning):
"""Raised when an optional library raises errors during its import."""

# Not a subclass of `ImportWarning` because those are hidden by default.


class ExperimentalWarning(QiskitWarning):
"""Raised when an experimental feature is being used."""
69 changes: 69 additions & 0 deletions qiskit/qasm3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,60 @@
\"\"\"
circuit = qiskit.qasm3.loads(program)
circuit.draw("mpl")
Experimental import interface
-----------------------------
The import functions given above rely on the ANTLR-based reference parser from the OpenQASM project
itself, which is more intended as a language reference than a performant parser. You need to have
the extension ``qiskit-qasm3-import`` installed to use it.
Qiskit is developing a native parser, written in Rust, which is available as part of the core Qiskit
package. This parser is still in its early experimental stages, so is missing features and its
interface is changing and expanding, but it is typically orders of magnitude more performant for the
subset of OpenQASM 3 it currently supports.
You can use the experimental interface immediately, with similar functions to the main interface
above:
.. autofunction:: load_experimental
.. autofunction:: loads_experimental
These two functions both raise :exc:`.ExperimentalWarning`; you can disable this warning by doing::
import warnings
from qiskit.exceptions import ExperimentalWarning
warnings.filterwarnings("ignore", category=ExperimentalWarning, module="qiskit.qasm3")
These two functions allow for specifying include paths as an iterable of paths, and for specifying
custom Python constructors to use for particular gates. These custom constructors are specified by
using the :class:`CustomGate` object:
.. autoclass:: CustomGate
In ``custom_gates`` is not given, Qiskit will attempt to use its standard-library gate objects for
the gates defined in OpenQASM 3 standard library file ``stdgates.inc``. This sequence of gates is
available on this module, if you wish to build on top of it:
.. py:data:: STDGATES_INC_GATES
A tuple of :class:`CustomGate` objects specifying the Qiskit constructors to use for the
``stdgates.inc`` include file.
"""

import functools
import warnings

from qiskit import _qasm3
from qiskit.exceptions import ExperimentalWarning
from qiskit.utils import optionals as _optionals

from .experimental import ExperimentalFeatures
from .exporter import Exporter
from .exceptions import QASM3Error, QASM3ImporterError, QASM3ExporterError
from qiskit._qasm3 import CustomGate, STDGATES_INC_GATES


def dumps(circuit, **kwargs) -> str:
Expand Down Expand Up @@ -253,3 +300,25 @@ def loads(program: str):
return qiskit_qasm3_import.parse(program)
except qiskit_qasm3_import.ConversionError as exc:
raise QASM3ImporterError(str(exc)) from exc


@functools.wraps(_qasm3.loads)
def loads_experimental(source, /, *, custom_gates=None, include_path=None):
"""<overridden by functools.wraps>"""
warnings.warn(
"This is an experimental native version of the OpenQASM 3 importer."
" Beware that its interface might change, and it might be missing features.",
category=ExperimentalWarning,
)
return _qasm3.loads(source, custom_gates=custom_gates, include_path=include_path)


@functools.wraps(_qasm3.load)
def load_experimental(pathlike_or_filelike, /, *, custom_gates=None, include_path=None):
"""<overridden by functools.wraps>"""
warnings.warn(
"This is an experimental native version of the OpenQASM 3 importer."
" Beware that its interface might change, and it might be missing features.",
category=ExperimentalWarning,
)
return _qasm3.load(pathlike_or_filelike, custom_gates=custom_gates, include_path=include_path)

0 comments on commit 2b7f36f

Please sign in to comment.