-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
QASM3 exporter #6565
QASM3 exporter #6565
Changes from all commits
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 |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// OpenQASM 3 standard gate library | ||
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. Is this dependent on the answer to #6492 (comment) ? 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. good point. I will add that also as a dependency. This is not strictly needed, i added as a way to test the possibility to add different inc files. If there is no access to |
||
|
||
// phase gate | ||
gate p(lambda) a { ctrl @ gphase(lambda) a; } | ||
|
||
// Pauli gate: bit-flip or NOT gate | ||
gate x a { U(pi, 0, pi) a; } | ||
// Pauli gate: bit and phase flip | ||
gate y a { U(pi, pi/2, pi/2) a; } | ||
// Pauli gate: phase flip | ||
gate z a { p(pi) a; } | ||
|
||
// Clifford gate: Hadamard | ||
gate h a { U(pi/2, 0, pi) a; } | ||
// Clifford gate: sqrt(Z) or S gate | ||
gate s a { pow(1/2) @ z a; } | ||
// Clifford gate: inverse of sqrt(Z) | ||
gate sdg a { inv @ pow(1/2) @ z a; } | ||
|
||
// sqrt(S) or T gate | ||
gate t a { pow(1/2) @ s a; } | ||
// inverse of sqrt(S) | ||
gate tdg a { inv @ pow(1/2) @ s a; } | ||
|
||
// sqrt(NOT) gate | ||
gate sx a { pow(1/2) @ x a; } | ||
|
||
// Rotation around X-axis | ||
gate rx(theta) a { U(theta, -pi/2, pi/2) a; } | ||
// rotation around Y-axis | ||
gate ry(theta) a { U(theta, 0, 0) a; } | ||
// rotation around Z axis | ||
gate rz(lambda) a { gphase(-lambda/2); U(0, 0, lambda) a; } | ||
|
||
// controlled-NOT | ||
gate cx c, t { ctrl @ x c, t; } | ||
// controlled-Y | ||
gate cy a, b { ctrl @ y a, b; } | ||
// controlled-Z | ||
gate cz a, b { ctrl @ z a, b; } | ||
// controlled-phase | ||
gate cp(lambda) a, b { ctrl @ p(lambda) a, b; } | ||
// controlled-rx | ||
gate crx(theta) a, b { ctrl @ rx(theta) a, b; } | ||
// controlled-ry | ||
gate cry(theta) a, b { ctrl @ ry(theta) a, b; } | ||
// controlled-rz | ||
gate crz(theta) a, b { ctrl @ rz(theta) a, b; } | ||
// controlled-H | ||
gate ch a, b { ctrl @ h a, b; } | ||
|
||
// swap | ||
gate swap a, b { cx a, b; cx b, a; cx a, b; } | ||
|
||
// Toffoli | ||
gate ccx a, b, c { ctrl @ ctrl @ x a, b, c; } | ||
// controlled-swap | ||
gate cswap a, b, c { ctrl @ swap a, b, c; } | ||
|
||
// four parameter controlled-U gate with relative phase | ||
gate cu(theta, phi, lambda, gamma) c, t { p(gamma) c; ctrl @ U(theta, phi, lambda) c, t; } | ||
|
||
// Gates for OpenQASM 2 backwards compatibility | ||
// CNOT | ||
gate CX c, t { ctrl @ U(pi, 0, pi) c, t; } | ||
// phase gate | ||
gate phase(lambda) q { U(0, 0, lambda) q; } | ||
// controlled-phase | ||
gate cphase(lambda) a, b { ctrl @ phase(lambda) a, b; } | ||
// identity or idle gate | ||
gate id a { U(0, 0, 0) a; } | ||
// IBM Quantum experience gates | ||
gate u1(lambda) q { U(0, 0, lambda) q; } | ||
gate u2(phi, lambda) q { gphase(-(phi+lambda)/2); U(pi/2, phi, lambda) q; } | ||
gate u3(theta, phi, lambda) q { gphase(-(phi+lambda)/2); U(theta, phi, lambda) q; } |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,68 @@ | ||||||
# This code is part of Qiskit. | ||||||
# | ||||||
# (C) Copyright IBM 2021. | ||||||
# | ||||||
# This code is licensed under the Apache License, Version 2.0. You may | ||||||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||||||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||||||
# | ||||||
# Any modifications or derivative works of this code must retain this | ||||||
# copyright notice, and modified files need to carry a notice indicating | ||||||
# that they have been altered from the originals. | ||||||
|
||||||
""" | ||||||
========================== | ||||||
Qasm (:mod:`qiskit.qasm3`) | ||||||
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. Shouldn't this be
Suggested change
|
||||||
========================== | ||||||
|
||||||
.. currentmodule:: qiskit.qasm3 | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: ../stubs/ | ||||||
|
||||||
Exporter | ||||||
dumps | ||||||
dump | ||||||
""" | ||||||
|
||||||
from .exporter import Exporter | ||||||
|
||||||
|
||||||
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. | ||||||
|
||||||
Returns: | ||||||
str: The OpenQASM3 serialization | ||||||
""" | ||||||
return Exporter(**kwargs).dumps(circuit) | ||||||
|
||||||
|
||||||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty temporary: just like the QASM 2 exporter, we probably will want a way for gates to define their QASM 3 representation, but with QASM 3 being far more complex, the structure of exporting now goes "Terra -> AST -> string". So gates have to return something that can be made into an AST object. For the time being, while we're vendoring our own AST, we return that. In the future, once we've shifted to using the reference AST, which crucially will include a "string -> AST" parser, we'll be able to allow gates to return their QASM 3 definition as a string, and in those cases we'll be able to use the parser to raise it to a full AST node.
Having the definitions in proper AST representation is nice because we get a separation of concerns between AST generation and text output, and we can optionally do much more compiler-like things, like verifying that the instruction is valid, and that the signature is consistent with (say) another definition given in an include file.
The CX gate here is mostly just an example - we may want to move this to a different gate, or just define a new test gate in a unit test, since in reality
cx
should be mapped to builtin names most of the time.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. The problem with
cx
is that has no definition in Qiskit. Not having a_define_qasm3
means thatcx
is opaque when the standard include is missing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The QASM 3 exporter should be able to do something sensible for it without a
_define_qasm3
method - it's a simple control gate, and we probably should have some way of quickly working out that it can be defined byctrl @ x q0, q1
by inspecting the Terra definition. It'll be a nuisance for us (we have many controlled gates) and users (Terra can't use information they've already given) if we don't handle it automatically.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, I'd be happy if the way we do it is to define
_define_qasm3()
onControlledGate
, and have the inherited to all members, but that starts to get slightly more complicated, because thenControlledGate._define_qasm3
will need to have a way to query the exporter for how the inner gate is defined. That's an example of what you were worried about when we called - there needs to be a two-way flow of information in that case. One way to do that might be by a sort of co-routine approach - we could allow_define_qasm3
toyield
arbitrary Terra objects, and have the caller feed their AST representations back in withgenerator.send
.Something like:
and the caller does something along the lines of (forgive me - I never get the flow right the first time with co-routines):
This allows
_define_qasm3
toyield
an arbitrary number of collections of objects back to the exporter, which will parse them into AST, then pass them back in parsed form. It then just loops round doing that til the original_define_qasm3
returns its final value. This would have problems with object cycles, but I'm not sure if we're allowing QASM 3 to be recursive anyway, but if so, we can do a similar thing todeepcopy
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My longer term question here is about the interface we move to. I get that this is temporary given the current state of the upstream qasm ast and the lack of a qiskit parser. But I feel longer term the place we want to be in a
__qasm3__
attribute that just returns a string of the qasm representation of the gate.The thing I want to avoid is the pattern of having
_qasm()
and_assemble()
on every standard gate. For things in qiskit's standard lib we should have enough context to know how to deal with them without having to hard code a bunch of stuff in the classes. But, having something that's opt-in for objects where we don't have that context or in other situations makes sense.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I don't think we should need a
_qasm()
method on any standard gate - we should be able to set up the types enough that this is never necessary. I didn't originally think we were going to merge with this AST still attached toCX
- I thought it was mostly just for testing purposes, and we were planning to move it into a custom test gate.We definitely do need something opt-in, that isn't just static, because there are problems when the exporter needs to munge the output names to avoid conflicts in custom gates. It's ok if the custom gate only relies on standard gates with invariant names (it's easy enough for us to regex-capture the name the gate has returned), but if it relies on other custom Terra instructions, it needs to know how to call them in QASM in order to output the correct call. If not, we might mistake it for a different instruction.