Classical expressions (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 QuantumCircuit.if_test()
(IfElseOp
) and QuantumCircuit.while_loop()
(WhileLoopOp
), and in the
target of QuantumCircuit.switch()
(SwitchCaseOp
).
참고
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.
Expressions (qiskit.circuit.classical.expr
)#
The necessary components for building expressions are all exported from the
expr
namespace within qiskit.circuit.classical
, so you can
choose whether to use qualified access (for example 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 the representation of the expression system 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 Construction.
Representation#
The expression system is based on tree representation. All nodes in the tree are final (uninheritable) instances of the abstract base class:
- class qiskit.circuit.classical.expr.Expr[소스]#
Root base class of all nodes in the expression tree. The base case should never be instantiated directly.
This must not be subclassed by users; subclasses form the internal data of the representation of expressions, and it does not make sense to add more outside of Qiskit library code.
All subclasses are responsible for setting their
type
attribute in their__init__
, and should not call the parent initialiser.
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 Var
node and associating a Type
with it.
Similarly, literals used in comparison (such as integers) should be lifted to Value
nodes
with associated types.
The operations traditionally associated with pre-, post- or infix operators in programming are
represented by the Unary
and Binary
nodes as appropriate. These each take an
operation type code, which are exposed as enumerations inside each class as Unary.Op
and Binary.Op
respectively.
- final class qiskit.circuit.classical.expr.Unary(op, operand, type)[소스]#
A unary expression.
- 매개변수:
- class Op(value)[소스]#
Enumeration of the opcodes for unary operations.
The bitwise negation
BIT_NOT
takes a single bit or an unsigned integer of known width, and returns a value of the same type.The logical negation
LOGIC_NOT
takes an input that is implicitly coerced to a Boolean, and returns a Boolean.- BIT_NOT = 1#
Bitwise negation.
~operand
.
- LOGIC_NOT = 2#
Logical negation.
!operand
.
- final class qiskit.circuit.classical.expr.Binary(op, left, right, type)[소스]#
A binary expression.
- 매개변수:
- class Op(value)[소스]#
Enumeration of the opcodes for binary operations.
The bitwise operations
BIT_AND
,BIT_OR
andBIT_XOR
apply to two operands of the same type, which must be a single bit or an unsigned integer of fixed width. The resultant type is the same as the two input types.The logical operations
LOGIC_AND
andLOGIC_OR
first implicitly coerce their arguments to Booleans, and then apply the logical operation. The resultant type is always Boolean.The binary mathematical relations
EQUAL
,NOT_EQUAL
,LESS
,LESS_EQUAL
,GREATER
andGREATER_EQUAL
take unsigned integers (with an implicit cast to make them the same width), and return a Boolean.- BIT_AND = 1#
Bitwise “and”.
lhs & rhs
.
- BIT_OR = 2#
Bitwise “or”.
lhs | rhs
.
- BIT_XOR = 3#
Bitwise “exclusive or”.
lhs ^ rhs
.
- LOGIC_AND = 4#
Logical “and”.
lhs && rhs
.
- LOGIC_OR = 5#
Logical “or”.
lhs || rhs
.
- EQUAL = 6#
Numeric equality.
lhs == rhs
.
- NOT_EQUAL = 7#
Numeric inequality.
lhs != rhs
.
- LESS = 8#
Numeric less than.
lhs < rhs
.
- LESS_EQUAL = 9#
Numeric less than or equal to.
lhs <= rhs
- GREATER = 10#
Numeric greater than.
lhs > rhs
.
- GREATER_EQUAL = 11#
Numeric greater than or equal to.
lhs >= rhs
.
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.
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 Value
instances, and any required promotions can be resolved into
suitable 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 Var
or Value
objects, and will resolve any required implicit casts on your behalf.
- qiskit.circuit.classical.expr.lift(value, /, type=None)[소스]#
Lift the given Python
value
to aValue
orVar
.If an explicit
type
is given, the typing in the output will reflect that.예제
Lifting simple circuit objects to be
Var
instances:>>> from qiskit.circuit import Clbit, ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.lift(Clbit()) Var(<clbit>, Bool()) >>> expr.lift(ClassicalRegister(3, "c")) Var(ClassicalRegister(3, "c"), Uint(3))
The type of the return value can be influenced, if the given value could be interpreted losslessly as the given type (use
cast()
to perform a full set of casting operations, include lossy ones):>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr, types >>> expr.lift(ClassicalRegister(3, "c"), types.Uint(5)) Var(ClassicalRegister(3, "c"), Uint(5)) >>> expr.lift(5, types.Uint(4)) Value(5, Uint(4))
- 반환 형식:
You can manually specify casts in cases where the cast is allowed in explicit form, but may be
lossy (such as the cast of a higher precision Uint
to a lower precision one).
- qiskit.circuit.classical.expr.cast(operand, type, /)[소스]#
Create an explicit cast from the given value to the given type.
예제
Add an explicit cast node that explicitly casts a higher precision type to a lower precision one:
>>> from qiskit.circuit.classical import expr, types >>> value = expr.value(5, types.Uint(32)) >>> expr.cast(value, types.Uint(8)) Cast(Value(5, types.Uint(32)), types.Uint(8), implicit=False)
- 반환 형식:
There are helper constructor functions for each of the unary operations.
- qiskit.circuit.classical.expr.bit_not(operand, /)[소스]#
Create a bitwise ‘not’ expression node from the given value, resolving any implicit casts and lifting the value into a
Value
node if required.예제
Bitwise negation of a
ClassicalRegister
:>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.bit_not(ClassicalRegister(3, "c")) Unary(Unary.Op.BIT_NOT, Var(ClassicalRegister(3, 'c'), Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.logic_not(operand, /)[소스]#
Create a logical ‘not’ expression node from the given value, resolving any implicit casts and lifting the value into a
Value
node if required.예제
Logical negation of a
ClassicalRegister
:>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.logic_not(ClassicalRegister(3, "c")) Unary(Unary.Op.LOGIC_NOT, Cast(Var(ClassicalRegister(3, 'c'), Uint(3)), Bool(), implicit=True), Bool())
- 반환 형식:
Similarly, the binary operations and relations have helper functions defined.
- qiskit.circuit.classical.expr.bit_and(left, right, /)[소스]#
Create a bitwise ‘and’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Bitwise ‘and’ of a classical register and an integer literal:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.bit_and(ClassicalRegister(3, "c"), 0b111) Binary(Binary.Op.BIT_AND, Var(ClassicalRegister(3, 'c'), Uint(3)), Value(7, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.bit_or(left, right, /)[소스]#
Create a bitwise ‘or’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Bitwise ‘or’ of a classical register and an integer literal:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.bit_or(ClassicalRegister(3, "c"), 0b101) Binary(Binary.Op.BIT_OR, Var(ClassicalRegister(3, 'c'), Uint(3)), Value(5, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.bit_xor(left, right, /)[소스]#
Create a bitwise ‘exclusive or’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Bitwise ‘exclusive or’ of a classical register and an integer literal:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.bit_xor(ClassicalRegister(3, "c"), 0b101) Binary(Binary.Op.BIT_XOR, Var(ClassicalRegister(3, 'c'), Uint(3)), Value(5, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.logic_and(left, right, /)[소스]#
Create a logical ‘and’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Logical ‘and’ of two classical bits:
>>> from qiskit.circuit import Clbit >>> from qiskit.circuit.classical import expr >>> expr.logical_and(Clbit(), Clbit()) Binary(Binary.Op.LOGIC_AND, Var(<clbit 0>, Bool()), Var(<clbit 1>, Bool()), Bool())
- 반환 형식:
- qiskit.circuit.classical.expr.logic_or(left, right, /)[소스]#
Create a logical ‘or’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Logical ‘or’ of two classical bits
>>> from qiskit.circuit import Clbit >>> from qiskit.circuit.classical import expr >>> expr.logical_and(Clbit(), Clbit()) Binary(Binary.Op.LOGIC_OR, Var(<clbit 0>, Bool()), Var(<clbit 1>, Bool()), Bool())
- 반환 형식:
- qiskit.circuit.classical.expr.equal(left, right, /)[소스]#
Create an ‘equal’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Equality between a classical register and an integer:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.equal(ClassicalRegister(3, "c"), 7) Binary(Binary.Op.EQUAL, Var(ClassicalRegister(3, "c"), Uint(3)), Value(7, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.not_equal(left, right, /)[소스]#
Create a ‘not equal’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Inequality between a classical register and an integer:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.not_equal(ClassicalRegister(3, "c"), 7) Binary(Binary.Op.NOT_EQUAL, Var(ClassicalRegister(3, "c"), Uint(3)), Value(7, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.less(left, right, /)[소스]#
Create a ‘less than’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Query if a classical register is less than an integer:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.less(ClassicalRegister(3, "c"), 5) Binary(Binary.Op.LESS, Var(ClassicalRegister(3, "c"), Uint(3)), Value(5, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.less_equal(left, right, /)[소스]#
Create a ‘less than or equal to’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Query if a classical register is less than or equal to another:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.less(ClassicalRegister(3, "a"), ClassicalRegister(3, "b")) Binary(Binary.Op.LESS_EQUAL, Var(ClassicalRegister(3, "a"), Uint(3)), Var(ClassicalRegister(3, "b"), Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.greater(left, right, /)[소스]#
Create a ‘greater than’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Query if a classical register is greater than an integer:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.less(ClassicalRegister(3, "c"), 5) Binary(Binary.Op.GREATER, Var(ClassicalRegister(3, "c"), Uint(3)), Value(5, Uint(3)), Uint(3))
- 반환 형식:
- qiskit.circuit.classical.expr.greater_equal(left, right, /)[소스]#
Create a ‘greater than or equal to’ expression node from the given value, resolving any implicit casts and lifting the values into
Value
nodes if required.예제
Query if a classical register is greater than or equal to another:
>>> from qiskit.circuit import ClassicalRegister >>> from qiskit.circuit.classical import expr >>> expr.less(ClassicalRegister(3, "a"), ClassicalRegister(3, "b")) Binary(Binary.Op.GREATER_EQUAL, Var(ClassicalRegister(3, "a"), Uint(3)), Var(ClassicalRegister(3, "b"), Uint(3)), Uint(3))
- 반환 형식:
Qiskit’s legacy method for specifying equality conditions for use in conditionals is to use a
two-tuple of a Clbit
or ClassicalRegister
and an integer. This represents an
exact equality condition, and there are no ways to specify any other relations. The helper function
lift_legacy_condition()
converts this legacy format into the new expression syntax.
- qiskit.circuit.classical.expr.lift_legacy_condition(condition, /)[소스]#
Lift a legacy two-tuple equality condition into a new-style
Expr
.예제
Taking an old-style conditional instruction and getting an
Expr
from its condition:from qiskit.circuit import ClassicalRegister from qiskit.circuit.library import HGate from qiskit.circuit.classical import expr cr = ClassicalRegister(2) instr = HGate().c_if(cr, 3) lifted = expr.lift_legacy_condition(instr.condition)
- 반환 형식:
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.
- class qiskit.circuit.classical.expr.ExprVisitor(*args, **kwds)[소스]#
Base class for visitors to the
Expr
tree. Subclasses should override whichever of thevisit_*
methods that they are able to handle, and should be organised such that non-existent methods will never be called.
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 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.
For the convenience of simple visitors that only need to inspect the variables in an expression and
not the general structure, the iterator method iter_vars()
is provided.
- qiskit.circuit.classical.expr.iter_vars(node)[소스]#
Get an iterator over the
Var
nodes referenced at any level in the givenExpr
.예제
Print out the name of each
ClassicalRegister
encountered:from qiskit.circuit import ClassicalRegister from qiskit.circuit.classical import expr cr1 = ClassicalRegister(3, "a") cr2 = ClassicalRegister(3, "b") for node in expr.iter_vars(expr.bit_and(expr.bit_not(cr1), cr2)): if isinstance(node.var, ClassicalRegister): print(node.var.name)
Two expressions can be compared for direct structural equality by using the built-in Python ==
operator. In general, though, one might want to compare two expressions slightly more semantically,
allowing that the Var
nodes inside them are bound to different memory-location descriptions
between two different circuits. In this case, one can use structurally_equivalent()
with two
suitable “key” functions to do the comparison.
- qiskit.circuit.classical.expr.structurally_equivalent(left, right, left_var_key=None, right_var_key=None)[소스]#
Do these two expressions have exactly the same tree structure, up to some key function for the
Var
objects?In other words, are these two expressions the exact same trees, except we compare the
Var.var
fields by calling the appropriate*_var_key
function on them, and comparing that output for equality. This function does not allow any semantic “equivalences” such as asserting thata == b
is equivalent tob == a
; the evaluation order of the operands could, in general, cause such a statement to be false (consider hypotheticalextern
functions that access global state).There’s no requirements on the key functions, except that their outputs should have general
__eq__
methods. If a key function returnsNone
, the variable will be used verbatim instead.- 매개변수:
left_var_key (Callable[[Any], Any] | None) – a callable whose output should be used when comparing
Var.var
attributes. If this argument isNone
or its output isNone
for a given variable inleft
, the variable will be used verbatim.right_var_key (Callable[[Any], Any] | None) – same as
left_var_key
, but used on the variables inright
instead.
- 반환 형식:
예제
Comparing two expressions for structural equivalence, with no remapping of the variables. These are different because the different
Clbit
instances compare differently:>>> from qiskit.circuit import Clbit >>> from qiskit.circuit.classical import expr >>> left_bits = [Clbit(), Clbit()] >>> right_bits = [Clbit(), Clbit()] >>> left = expr.logic_and(expr.logic_not(left_bits[0]), left_bits[1]) >>> right = expr.logic_and(expr.logic_not(right_bits[0]), right_bits[1]) >>> expr.structurally_equivalent(left, right) False
Comparing the same two expressions, but this time using mapping functions that associate the bits with simple indices:
>>> left_key = {var: i for i, var in enumerate(left_bits)}.get >>> right_key = {var: i for i, var in enumerate(right_bits)}.get >>> expr.structurally_equivalent(left, right, left_key, right_key) True
Typing (qiskit.circuit.classical.types
)#
The type system of the expression tree is exposed through this module. This is inherently linked to
the expression system in the expr
module, as most expressions can only be
understood with the context of the types that they act on.
All types inherit from an abstract base class:
- class qiskit.circuit.classical.types.Type[소스]#
Root base class of all nodes in the type tree. The base case should never be instantiated directly.
This must not be subclassed by users; subclasses form the internal data of the representation of expressions, and it does not make sense to add more outside of Qiskit library code.
Types should be considered immutable objects, and you must not mutate them. It is permissible to
reuse a Type
that you take from another object without copying it, and generally this will
be the best approach for performance. Type
objects are designed to be small amounts of
data, and it’s best to point to the same instance of the data where possible rather than
heap-allocating a new version of the same thing. Where possible, the class constructors will return
singleton instances to facilitate this.
The two different types available are for Booleans (corresponding to Clbit
and the
literals True
and False
), and unsigned integers (corresponding to
ClassicalRegister
and Python integers).
- final class qiskit.circuit.classical.types.Bool[소스]#
The Boolean type. This has exactly two values:
True
andFalse
.
Note that Uint
defines a family of types parametrised by their width; it is not one single
type, which may be slightly different to the ‘classical’ programming languages you are used to.
There are some functions on these types exposed here as well. These are mostly expected to be used only in manipulations of the expression tree; users who are building expressions using the user-facing construction interface should not need to use these.
The type system is equipped with a partial ordering, where \(a < b\) is interpreted as “\(a\) is a strict subtype of \(b\)”. Note that the partial ordering is a subset of the directed graph that describes the allowed explicit casting operations between types. The partial ordering defines when one type may be lossless directly interpreted as another.
The low-level interface to querying the subtyping relationship is the order()
function.
- qiskit.circuit.classical.types.order(left, right, /)[소스]#
Get the ordering relationship between the two types as an enumeration value.
예제
Compare two
Uint
types of different widths:>>> from qiskit.circuit.classical import types >>> types.order(types.Uint(8), types.Uint(16)) Ordering.LESS
Compare two types that have no ordering between them:
>>> types.order(types.Uint(8), types.Bool()) Ordering.NONE
- 반환 형식:
The return value is an enumeration Ordering
that describes what, if any, subtyping
relationship exists between the two types.
- class qiskit.circuit.classical.types.Ordering(value)[소스]#
Enumeration listing the possible relations between two types. Types only have a partial ordering, so it’s possible for two types to have no sub-typing relationship.
Note that the sub-/supertyping relationship is not the same as whether a type can be explicitly cast from one to another.
Some helper methods are then defined in terms of this low-level order()
primitive:
- qiskit.circuit.classical.types.is_subtype(left, right, /, strict=False)[소스]#
Does the relation \(\text{left} \le \text{right}\) hold? If there is no ordering relation between the two types, then this returns
False
. Ifstrict
, then the equality is also forbidden.예제
Check if one type is a subclass of another:
>>> from qiskit.circuit.classical import types >>> types.is_subtype(types.Uint(8), types.Uint(16)) True
Check if one type is a strict subclass of another:
>>> types.is_subtype(types.Bool(), types.Bool()) True >>> types.is_subtype(types.Bool(), types.Bool(), strict=True) False
- 반환 형식:
- qiskit.circuit.classical.types.is_supertype(left, right, /, strict=False)[소스]#
Does the relation \(\text{left} \ge \text{right}\) hold? If there is no ordering relation between the two types, then this returns
False
. Ifstrict
, then the equality is also forbidden.예제
Check if one type is a superclass of another:
>>> from qiskit.circuit.classical import types >>> types.is_supertype(types.Uint(8), types.Uint(16)) False
Check if one type is a strict superclass of another:
>>> types.is_supertype(types.Bool(), types.Bool()) True >>> types.is_supertype(types.Bool(), types.Bool(), strict=True) False
- 반환 형식:
- qiskit.circuit.classical.types.greater(left, right, /)[소스]#
Get the greater of the two types, assuming that there is an ordering relation between them. Technically, this is a slightly restricted version of the concept of the ‘meet’ of the two types in that the return value must be one of the inputs. In practice in the type system there is no concept of a ‘sum’ type, so the ‘meet’ exists if and only if there is an ordering between the two types, and is equal to the greater of the two types.
- 반환:
The greater of the two types.
- 예외 발생:
TypeError – if there is no ordering relation between the two types.
- 반환 형식:
예제
Find the greater of two
Uint
types:>>> from qiskit.circuit.classical import types >>> types.greater(types.Uint(8), types.Uint(16)) types.Uint(16)