-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial representation of classical expressions
This adds a method of representing a classical type system and expression tree in Qiskit. This initial version contains only simple bitwise, logical and relational operations on Booleans and sized unsigned integers. This is largely in line with https://github.com/Qiskit/RFCs/blob/0dd13344/0010-simple-classical-representations.md Some of the details surrounding construction and representation have been changed slightly from that design, but the type system described is the same. In particular, this commit slightly modifies the representation of the tree; instead of individual tree items for each unary and binary operation, they are part of the `Unary` and `Binary` nodes with a respective enumeration discriminator in the node. This was done to make visitors to the expression tree require less boilerplate and be easier to maintain by exploiting the natural similarities between all these operations of the same type. One can still discriminate on the particular operations by using a jump table based on the enumeration values, if desired. Secondly, a series of constructor functions was added that is separate to the representation form. This is for both efficiency and user convenience. The tree form is _purely_ a data format; it does no type-checking on creation so that manipulations of the tree do not need to pay runtime costs when the manipulators are the ones who will be ensuring that the type system is fulfilled anyway. The more user friendly constructors _do_ resolve all this type information as part of their construction. They also infer typing information for Qiskit scalar values, and insert casts as required. Last, the tree has a `Cast` node added that is additional to the description in the RFC. This was done to make life easier for consumers of the tree: they do not need to be aware of all the possible implicit promotions that are required to make various unary and binary operations valid. This will reduce complexity of consumer code and reduce the chance of bugs caused by different paths handling the casts differently.
- Loading branch information
1 parent
fbd64d9
commit 69c902a
Showing
12 changed files
with
1,832 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.. _qiskit-circuit-classical: | ||
|
||
.. automodule:: qiskit.circuit.classical | ||
:no-members: | ||
:no-inherited-members: | ||
:no-special-members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ Qiskit Terra API Reference | |
|
||
circuit | ||
circuit_library | ||
circuit_classical | ||
compiler | ||
execute | ||
visualization | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2023. | ||
# | ||
# 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. | ||
|
||
""" | ||
======================================================= | ||
Classical expressions (:mod:`qiskit.circuit.classical`) | ||
======================================================= | ||
This module contains an exploratory representation of runtime operations on classical values during | ||
circuit execution. | ||
Currently, only simple expressions on bits and registers that result in a Boolean value are | ||
supported, and these are only valid for use in the conditions of :meth:`.QuantumCircuit.if_test` | ||
(:class:`.IfElseOp`) and :meth:`.QuantumCircuit.while_loop` (:class:`.WhileLoopOp`), and in the | ||
target of :meth:`.QuantumCircuit.switch` (:class:`.SwitchCaseOp`). | ||
.. note:: | ||
This is an exploratory module, and while we will commit to the standard Qiskit deprecation | ||
policy within it, please be aware that the module will be deliberately limited in scope at the | ||
start, and early versions may not evolve cleanly into the final version. It is possible that | ||
various components of this module will be replaced (subject to deprecations) instead of improved | ||
into a new form. | ||
The type system and expression tree will be expanded over time, and it is possible that the | ||
allowed types of some operations may need to change between versions of Qiskit as the classical | ||
processing capabilities develop. | ||
.. automodule:: qiskit.circuit.classical.expr | ||
.. automodule:: qiskit.circuit.classical.types | ||
""" | ||
|
||
from . import types, expr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2023. | ||
# | ||
# 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. | ||
|
||
""" | ||
================================================== | ||
Expressions (:mod:`qiskit.circuit.classical.expr`) | ||
================================================== | ||
The necessary components for building expressions are all exported from the | ||
:mod:`~.qiskit.circuit.classical.expr` namespace within :mod:`qiskit.circuit.classical`, so you can | ||
choose whether to use qualified access (for example :class:`.expr.Value`) or import the names you | ||
need directly and call them without the prefix. | ||
There are two pathways for constructing expressions. The classes that form :ref:`the | ||
representation of the expression system <circuit-classical-expressions-expr-representation>` | ||
have constructors that perform zero type checking; it is up to the caller to ensure that they | ||
are building valid objects. For a more user-friendly interface to direct construction, there | ||
are helper functions associated with most of the classes that do type validation and inference. | ||
These are described below, in :ref:`circuit-classical-expressions-expr-construction`. | ||
.. _circuit-classical-expressions-expr-representation: | ||
Representation | ||
============== | ||
The expression system is based on tree representation. All nodes in the tree are final | ||
(uninheritable) instances of the abstract base class: | ||
.. autoclass:: Expr | ||
These objects are mutable and should not be reused in a different location without a copy. | ||
The entry point from general circuit objects to the expression system is by wrapping the object | ||
in a :class:`Value` node and associating a :class:`~.types.Type` with it. | ||
.. autoclass:: Value | ||
The operations traditionally associated with pre-, post- or infix operators in programming are | ||
represented by the :class:`Unary` and :class:`Binary` nodes as appropriate. These each take an | ||
operation type code, which are exposed as enumerations inside each class as :class:`Unary.Op` | ||
and :class:`Binary.Op` respectively. | ||
.. autoclass:: Unary | ||
:members: Op | ||
:member-order: bysource | ||
.. autoclass:: Binary | ||
:members: Op | ||
:member-order: bysource | ||
When constructing expressions, one must ensure that the types are valid for the operation. | ||
Attempts to construct expressions with invalid types will raise a regular Python ``TypeError``. | ||
Expressions in this system are defined to act only on certain sets of types. However, values | ||
may be cast to a suitable supertype in order to satisfy the typing requirements. In these | ||
cases, a node in the expression tree is used to represent the promotion. In all cases where | ||
operations note that they "implicitly cast" or "coerce" their arguments, the expression tree | ||
must have this node representing the conversion. | ||
.. autoclass:: Cast | ||
.. _circuit-classical-expressions-expr-construction: | ||
Construction | ||
============ | ||
Constructing the tree representation directly is verbose and easy to make a mistake with the | ||
typing. In many cases, much of the typing can be inferred, scalar values can automatically | ||
be promoted to :class:`Value` instances, and any required promotions can be resolved into | ||
suitable :class:`Cast` nodes. | ||
The functions and methods described in this section are a more user-friendly way to build the | ||
expression tree, while staying close to the internal representation. All these functions will | ||
automatically lift valid Python scalar values into corresponding :class:`Value` objects, and | ||
will resolve any required implicit casts on your behalf. | ||
.. autofunction:: value | ||
You can manually specify casts in cases where the cast is allowed in explicit form, but may be | ||
losslses (such as the cast of a higher precision :class:`~.types.Uint` to a lower precision one). | ||
.. autofunction:: cast | ||
There are helper constructor functions for each of the unary operations. | ||
.. autofunction:: bit_not | ||
.. autofunction:: logic_not | ||
Similarly, the binary operations and relations have helper functions defined. | ||
.. autofunction:: bit_and | ||
.. autofunction:: bit_or | ||
.. autofunction:: logic_and | ||
.. autofunction:: logic_or | ||
.. autofunction:: equal | ||
.. autofunction:: not_equal | ||
.. autofunction:: less | ||
.. autofunction:: less_equal | ||
.. autofunction:: greater | ||
.. autofunction:: greater_equal | ||
Qiskit's legacy method for specifying equality conditions for use in conditionals is to use a | ||
two-tuple of a :class:`.Clbit` or :class:`.ClassicalRegister` and an integer. This represents an | ||
exact equality condition, and there are no ways to specify any other relations. The helper function | ||
:func:`lift_legacy` converts this legacy format into the new expression syntax. | ||
.. autofunction:: lift_legacy | ||
Working with the expression tree | ||
================================ | ||
A typical consumer of the expression tree wants to recursively walk through the tree, potentially | ||
statefully, acting on each node differently depending on its type. This is naturally a | ||
double-dispatch problem; the logic of 'what is to be done' is likely stateful and users should be | ||
free to define their own operations, yet each node defines 'what is being acted on'. We enable this | ||
double dispatch by providing a base visitor class for the expression tree. | ||
.. autoclass:: ExprVisitor | ||
:members: | ||
:undoc-members: | ||
Consumers of the expression tree should subclass the visitor, and override the ``visit_*`` methods | ||
that they wish to handle. Any non-overridden methods will call :meth:`~ExprVisitor.visit_generic`, | ||
which unless overridden will raise a ``RuntimeError`` to ensure that you are aware if new nodes | ||
have been added to the expression tree that you are not yet handling. | ||
""" | ||
|
||
__all__ = [ | ||
"ExprVisitor", | ||
"Expr", | ||
"Value", | ||
"Cast", | ||
"Unary", | ||
"Binary", | ||
"value", | ||
"cast", | ||
"bit_not", | ||
"logic_not", | ||
"bit_and", | ||
"bit_or", | ||
"bit_xor", | ||
"logic_and", | ||
"logic_or", | ||
"equal", | ||
"not_equal", | ||
"less", | ||
"less_equal", | ||
"greater", | ||
"greater_equal", | ||
"lift_legacy", | ||
] | ||
|
||
from .expr import ExprVisitor, Expr, Value, Cast, Unary, Binary | ||
from .constructors import ( | ||
value, | ||
cast, | ||
bit_not, | ||
logic_not, | ||
bit_and, | ||
bit_or, | ||
bit_xor, | ||
logic_and, | ||
logic_or, | ||
equal, | ||
not_equal, | ||
less, | ||
less_equal, | ||
greater, | ||
greater_equal, | ||
lift_legacy, | ||
) |
Oops, something went wrong.