Skip to content

Commit

Permalink
Add importer for OpenQASM 3 (#9347)
Browse files Browse the repository at this point in the history
* Add importer for OpenQASM 3

This adds an interface for importing OpenQASM 3 into Qiskit Terra.
Right now, this is provided only as an optional dependency (the
`qasm3-import` optional), using the package `qiskit_qasm3_import`, which
in turn depends on the ANTLR runtime `antlr4-python3-runtime`.

The importer code may in the future be vendored into Terra, and will
likely be replaced completely by a more efficient importer in the
future, once a more concrete strategy has been decided for handling of
classical components in quantum programs.

* Expand capabilities section of release note

* Expand OQ3 import documentation

* Relax exact pin in optional requirement

* Remove superfluous word

* Update link to Qiskit org

Co-authored-by: Matthew Treinish <[email protected]>
  • Loading branch information
jakelishman and mtreinish authored Jan 19, 2023
1 parent 179c29c commit b2311c4
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 24 deletions.
179 changes: 156 additions & 23 deletions qiskit/qasm3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,126 @@
# that they have been altered from the originals.

"""
==========================
Qasm (:mod:`qiskit.qasm3`)
==========================
================================
OpenQASM 3 (:mod:`qiskit.qasm3`)
================================
.. currentmodule:: qiskit.qasm3
.. autosummary::
:toctree: ../stubs/
Qiskit provides some tools for converting between `OpenQASM 3 <https://openqasm.com>`__
representations of quantum programs, and the :class:`.QuantumCircuit` class. These will continue to
evolve as Qiskit's support for the dynamic-circuit capabilities expressed by OpenQASM 3 increases.
Exporter
dumps
dump
Exporting to OpenQASM 3
=======================
The high-level functions are simply :func:`dump` and :func:`dumps`, which respectively export to a
file (given as a filename) and to a Python string.
.. autofunction:: dump
.. autofunction:: dumps
Both of these exporter functions are single-use wrappers around the main :class:`Exporter` class.
For more complex exporting needs, including dumping multiple circuits in a single session, it may be
more convenient or faster to use the complete interface.
.. autoclass:: Exporter
:members:
All of these interfaces will raise :exc:`QASM3ExporterError` on failure.
.. autoexception:: QASM3ExporterError
Importing from OpenQASM 3
=========================
Currently only two high-level functions are offered, as Qiskit support for importing from OpenQASM 3
is in its infancy, and the implementation is expected to change significantly. The two functions
are :func:`load` and :func:`loads`, which are direct counterparts of :func:`dump` and :func:`dumps`,
respectively loading a program indirectly from a named file and directly from a given string.
.. note::
While we are still in the exploratory release period, to use either function, the package
``qiskit_qasm3_import`` must be installed. This can be done by installing Qiskit Terra with the
``qasm3-import`` extra, such as by:
.. code-block:: text
pip install qiskit-terra[qasm3-import]
We expect that this functionality will eventually be merged into core Terra, and no longer
require an optional import, but we do not yet have a timeline for this.
.. autofunction:: load
.. autofunction:: loads
Both of these two functions raise :exc:`QASM3ImporterError` on failure.
.. autoexception:: QASM3ImporterError
For example, we can define a quantum program using OpenQASM 3, and use :func:`loads` to directly
convert it into a :class:`.QuantumCircuit`:
.. plot::
:include-source:
import qiskit.qasm3
program = \"\"\"
OPENQASM 3.0;
include "stdgates.inc";
input float[64] a;
qubit[3] q;
bit[2] mid;
bit[3] out;
let aliased = q[0:1];
gate my_gate(a) c, t {
gphase(a / 2);
ry(a) c;
cx c, t;
}
gate my_phase(a) c {
ctrl @ inv @ gphase(a) c;
}
my_gate(a * 2) aliased[0], q[{1, 2}][0];
measure q[0] -> mid[0];
measure q[1] -> mid[1];
while (mid == "00") {
reset q[0];
reset q[1];
my_gate(a) q[0], q[1];
my_phase(a - pi/2) q[1];
mid[0] = measure q[0];
mid[1] = measure q[1];
}
if (mid[0]) {
let inner_alias = q[{0, 1}];
reset inner_alias;
}
out = measure q;
\"\"\"
circuit = qiskit.qasm3.loads(program)
circuit.draw("mpl")
"""

from qiskit.utils import optionals as _optionals
from .exporter import Exporter
from .exceptions import QASM3Error, QASM3ExporterError
from .exceptions import QASM3Error, QASM3ImporterError, QASM3ExporterError


def dumps(circuit, **kwargs) -> str:
"""Serialize a :class:`~qiskit.circuit.QuantumCircuit` object in an OpenQASM3 string.
.. note::
This is a quick interface to the main :obj:`.Exporter` interface. All keyword arguments to
this function are inherited from the constructor of that class, and if you have multiple
circuits to export, it will be faster to create an :obj:`.Exporter` instance, and use its
:obj:`.Exporter.dumps` method.
Args:
circuit (QuantumCircuit): Circuit to serialize.
**kwargs: Arguments for the :obj:`.Exporter` constructor.
Expand All @@ -53,17 +145,58 @@ def dump(circuit, stream, **kwargs) -> None:
"""Serialize a :class:`~qiskit.circuit.QuantumCircuit` object as a OpenQASM3 stream to file-like
object.
.. note::
This is a quick interface to the main :obj:`.Exporter` interface. All keyword arguments to
this function are inherited from the constructor of that class, and if you have multiple
circuits to export, it will be faster to create an :obj:`.Exporter` instance, and use its
:obj:`.Exporter.dump` method.
Args:
circuit (QuantumCircuit): Circuit to serialize.
stream (TextIOBase): stream-like object to dump the OpenQASM3 serialization
**kwargs: Arguments for the :obj:`.Exporter` constructor.
"""
Exporter(**kwargs).dump(circuit, stream)


@_optionals.HAS_QASM3_IMPORT.require_in_call("loading from OpenQASM 3")
def load(filename: str):
"""Load an OpenQASM 3 program from the file ``filename``.
Args:
filename: the filename to load the program from.
Returns:
QuantumCircuit: a circuit representation of the OpenQASM 3 program.
Raises:
QASM3ImporterError: if the OpenQASM 3 file is invalid, or cannot be represented by a
:class:`.QuantumCircuit`.
"""

import qiskit_qasm3_import

with open(filename, "r") as fptr:
program = fptr.read()
try:
return qiskit_qasm3_import.parse(program)
except qiskit_qasm3_import.ConversionError as exc:
raise QASM3ImporterError(str(exc)) from exc


@_optionals.HAS_QASM3_IMPORT.require_in_call("loading from OpenQASM 3")
def loads(program: str):
"""Load an OpenQASM 3 program from the given string.
Args:
program: the OpenQASM 3 program.
Returns:
QuantumCircuit: a circuit representation of the OpenQASM 3 program.
Raises:
QASM3ImporterError: if the OpenQASM 3 file is invalid, or cannot be represented by a
:class:`.QuantumCircuit`.
"""

import qiskit_qasm3_import

try:
return qiskit_qasm3_import.parse(program)
except qiskit_qasm3_import.ConversionError as exc:
raise QASM3ImporterError(str(exc)) from exc
4 changes: 4 additions & 0 deletions qiskit/qasm3/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ class QASM3Error(QiskitError):

class QASM3ExporterError(QASM3Error):
"""An error raised during running the OpenQASM 3 exporter."""


class QASM3ImporterError(QASM3Error):
"""An error raised during the OpenQASM 3 importer."""
8 changes: 8 additions & 0 deletions qiskit/utils/optionals.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@
- Various LaTeX-based visualizations, especially the circuit drawers, need access to the
`pylatexenc <https://github.com/phfaist/pylatexenc>`__ project to work correctly.
* - .. py:data:: HAS_QASM3_IMPORT
- The functions :func:`.qasm3.load` and :func:`.qasm3.loads` for importing OpenQASM 3 programs
into :class:`.QuantumCircuit` instances use `an external importer package
<https://qiskit.github.io/qiskit-qasm3-import>`__.
* - .. py:data:: HAS_SEABORN
- Qiskit Terra provides several visualisation tools in the :mod:`.visualization` module. Some
of these are built using `Seaborn <https://seaborn.pydata.org/>`__, which must be installed
Expand Down Expand Up @@ -264,6 +269,9 @@
name="pylatexenc",
install="pip install pylatexenc",
)
HAS_QASM3_IMPORT = _LazyImportTester(
"qiskit_qasm3_import", install="pip install qiskit_qasm3_import"
)
HAS_SEABORN = _LazyImportTester("seaborn", install="pip install seaborn")
HAS_SKLEARN = _LazyImportTester(
{"sklearn.linear_model": ("Ridge", "Lasso")},
Expand Down
48 changes: 48 additions & 0 deletions releasenotes/notes/qasm3-import-0e7e01cb75aa6251.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
features:
- |
Support for importing OpenQASM 3 programs into Qiskit has been added. This can most easily be
accessed using the functions :func:`.qasm3.loads` and :func:`.qasm3.load`, to load a program
directly from a string and indirectly from a filename, respectively. For example, one can now
do::
from qiskit import qasm3
circuit = qasm3.loads("""
OPENQASM 3.0;
include "stdgates.inc";
qubit q;
qubit[5] qr;
bit c;
bit[5] cr;
h q;
c = measure q;
if (c) {
h qr[0];
cx qr[0], qr[1];
cx qr[0], qr[2];
cx qr[0], qr[3];
cx qr[0], qr[4];
} else {
h qr[4];
cx qr[4], qr[3];
cx qr[4], qr[2];
cx qr[4], qr[1];
cx qr[4], qr[0];
}
cr = measure qr;
""")
This will load the program into a :class:`.QuantumCircuit` instance in the variable ``circuit``.
Not all OpenQASM 3 features are supported at first, because Qiskit does not yet have a way to
represent advanced classical data processing. The capabilities of the importer will increase
along with the capabilities of the rest of Qiskit. The initial feature set of the importer is
approximately the same set of features that would be output by the exporter (:func:`.qasm3.dump`
and :func:`.qasm3.dumps`).
Note that Qiskit's support of OpenQASM 3 is not meant to provide a totally lossless
representation of :class:`.QuantumCircuit`\ s. For that, consider using :mod:`qiskit.qpy`.
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ scikit-quant<=0.7;platform_system != 'Windows'
jax;platform_system != 'Windows'
jaxlib;platform_system != 'Windows'
docplex
qiskit-qasm3-import; python_version>='3.8'
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
)


qasm3_import_extras = [
"qiskit-qasm3-import>=0.1.0",
]
visualization_extras = [
"matplotlib>=3.3",
"ipywidgets>=7.3.0",
Expand Down Expand Up @@ -81,14 +84,15 @@
include_package_data=True,
python_requires=">=3.7",
extras_require={
"qasm3-import": qasm3_import_extras,
"visualization": visualization_extras,
"bip-mapper": bip_requirements,
"crosstalk-pass": z3_requirements,
"csp-layout-pass": csp_requirements,
"toqm": toqm_requirements,
# Note: 'all' only includes extras that are stable and work on the majority of Python
# versions and OSes supported by Terra. You have to ask for anything else explicitly.
"all": visualization_extras + z3_requirements + csp_requirements,
"all": visualization_extras + z3_requirements + csp_requirements + qasm3_import_extras,
},
project_urls={
"Bug Tracker": "https://github.com/Qiskit/qiskit-terra/issues",
Expand Down
Loading

0 comments on commit b2311c4

Please sign in to comment.