-
Notifications
You must be signed in to change notification settings - Fork 3k
Add importer for OpenQASM 3 #9347
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
Changes from 11 commits
9184649
131909b
cc8548c
45f996a
531b7ff
e5b409a
66e3810
fc9931a
85e0f5a
baf9af6
f121fa4
95366fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Comment on lines
+67
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not have any high-level disagreement with these function names on their own. It is however worth pointing out that this API breaks from the current OpenQASM2 APIs - https://qiskit.org/documentation/stubs/qiskit.qasm.Qasm.html#qiskit.qasm.Qasm. I wonder if it might make sense to begin renaming the older API as This could be follow-up work.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I was on holiday, I actually wrote a whole new OpenQASM 2 parser for Qiskit, with the majority in Rust. It was mostly for my own learning experience, but since it's like 10x faster than the current Qiskit one (with no external Python dependencies), I might make a PR changing it. With or without though, though, I think we should move to a new
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, Jake, I'd be hesitant to add more work and potential breaks to the existing QASM2 parser for those that are using it given we're planning on migrating away from it but the pathway suggested for the qasm2 module seems good.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, agree that there's no reason to break users' workflows. My rough intent was to keep |
||
|
|
||
| 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. | ||
|
|
@@ -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() | ||
|
jlapeyre marked this conversation as resolved.
|
||
| 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 | ||
| 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``. | ||
|
jakelishman marked this conversation as resolved.
|
||
|
|
||
| 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`. | ||
Uh oh!
There was an error while loading. Please reload this page.