Gates#
Quantum logic gates provide the building blocks for describing quantum operations, which are usually either (unitary) interactions between two or more (sub)systems, or (linear) transformations of any number of systems. In Qhronology, they are represented by instances of the QuantumGate base class,
from qhronology.quantum.gates import QuantumGate
and its derivatives (subclasses),
from qhronology.quantum.gates import Unitary, Pauli, GellMann, Rotation, Phase, Diagonal, Swap, Summation, Not, Hadamard, Fourier, Measurement
These objects describe a distinct vertical slice (sometimes called a “layer” or “cycle”) in the quantum circuit picturalism, and so include information about the locations of both control and anticontrol nodes, in addition to the presence of any empty wires. They also possess other metadata associated with the gate such as parameter values, symbolic assumptions, and algebraic substitutions.
Facilities to combine gates together are also provided by the package and take two forms: “interleaved” compositions via the GateInterleave class, and “stacked” compositions via the GateStack class:
from qhronology.quantum.gates import GateInterleave, GateStack
Both of these classes concern the creation of more complex spatial (“vertical”) gate structures. Temporal (“horizontal”) compositions (i.e., gates wired in serial) as single object instances on the other hand are not supported, as this can be achieved simply by combining the individual components sequentially in a circuit.
Main class#
- class QuantumGate(
- spec: mat | arr | list[list[num | expr | str]] | None,
- targets: list[int] | None = None,
- controls: list[int] | None = None,
- anticontrols: list[int] | None = None,
- num_systems: int | None = None,
- dim: int | None = None,
- numerical: bool | None = None,
- array: bool | None = None,
- symbols: dict | None = None,
- substitutions: list[tuple[num | expr | str, num | expr | str]] | None = None,
- simplification: bool | None = None,
- conjugation: bool | None = None,
- exponent: num | expr | str | None = None,
- coefficient: num | expr | str | None = None,
- label: str | None = None,
- notation: str | None = None,
- family: str | None = None,
Bases:
QuantumObjectA class for creating quantum gates and storing their metadata.
This class forms the base upon which all quantum gates are built. Instances of this base class and its derivatives (subclasses) provide complete descriptions of quantum gates. This means that they describe a complete vertical column (sometimes called a “slice”, “layer”, or “cycle”) in the quantum circuitry picturalism, including control nodes, anticontrol nodes, empty wires, and the (unitary) gate operator itself. The details of any algebraic symbols, mathematical substitutions, and visualization labels are also recorded. Note that, unlike the internal matrix representations contained within instances of the
QuantumStateclass (and its derivatives), the matrix representations of subclass instances ofQuantumGateare not mutable.- Parameters:
spec (
mat | arr | list[list[num | expr | str]]) –The specification of the quantum gate’s matrix representation in a standard
dim-dimensional basis. Can be one of:a SymPy matrix (
mat)a NumPy array (
arr)a list of lists of numerical, symbolic, or string expressions that collectively describe a matrix (
list[list[num | expr | str]])
Defaults to the single-system
dim-dimensional identity operator.targets (
list[int]) – The numerical indices of the subsystems on which the gate’s core operator resides. Defaults tolist(range(0, count_systems(to_matrix(spec), dim))).controls (
list[int]) – The numerical indices of the subsystems on which control nodes reside. Defaults to[].anticontrols (
list[int]) – The numerical indices of the subsystems on which anticontrol nodes reside. Defaults to[].num_systems (
int) – The (total) number of systems which the gate spans. Must be a non-negative integer. Defaults tomax(targets + controls + anticontrols + [count_systems(to_matrix(spec), dim)]) + 1.dim (
int) – The dimensionality of the quantum gate’s Hilbert space. Must be a non-negative integer. Defaults to2.numerical (
bool) – Whether to cast the gate’s matrix elements as floating-point values (True) (if possible) or exact values (False). Defaults toFalse.array (
bool) – Whether to cast the gate’s matrix as a NumPy array (True) or SymPy matrix (False). Defaults toFalse.symbols (
dict[sym | str,dict[str,Any]]) – A dictionary in which the keys are individual symbols (usually found within the gate specificationspec) and the values are dictionaries of their respective SymPy keyword-argumentassumptions. Defaults to{}.substitutions (
list[tuple[num | expr | str,num | expr | str]]) – A list of 2-tuples of substitutions to be applied to the gate. All instances of the expression in each tuple’s first element are replaced by the expression in the respective second element. This uses the same format as the SymPysubs()method. Defaults to[].simplification (
bool) – Whether to perform mathematical simplification on the gate when it is called. IfFalse, does not simplify. Defaults toFalse.conjugation (
bool) – Whether to perform Hermitian conjugation on the gate when it is called. IfFalse, does not conjugate. Defaults toFalse.exponent (
num | expr | str) – A numerical or string representation of a scalar value to which gate’s operator (residing ontargets) is exponentiated. Must be a non-negative integer. Defaults to1.coefficient (
num | expr | str) – A numerical or string representation of a scalar value by which the gate’s matrix (occupyingtargets) is multiplied. Performed after exponentiation. Useful for multiplying the gate by a phase factor. Defaults to1.label (
str) – The unformatted string used to represent the gate in mathematical expressions. Defaults to"U".notation (
str) – The formatted string used to represent the gate in mathematical expressions. When notNone, overrides the value passed tolabel. Not intended to be set by the user in most cases. Defaults toNone.family (
str) – A string expressing the kind of block element for which the gate is to be visualized. Not intended to be set by the user. Defaults to"GATE".
Note
The indices specified in
targets,controls, andanticontrolsmust be distinct.Examples
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> U = QuantumGate(spec=unitary, label="U") >>> U.output() Matrix([ [U[0, 0], U[0, 1]], [U[1, 0], U[1, 1]]]) >>> U.diagram()
>>> unitary = sp.MatrixSymbol("U", 3, 3).as_mutable() >>> U3 = QuantumGate(spec=unitary, dim=3, label="U") >>> U3.output() Matrix([ [U[0, 0], U[0, 1], U[0, 2]], [U[1, 0], U[1, 1], U[1, 2]], [U[2, 0], U[2, 1], U[2, 2]]]) >>> U3.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> UI = QuantumGate( ... spec=unitary, ... targets=[0], ... num_systems=2, ... label="U", ... ) >>> UI.output() Matrix([ [U[0, 0], 0, U[0, 1], 0], [ 0, U[0, 0], 0, U[0, 1]], [U[1, 0], 0, U[1, 1], 0], [ 0, U[1, 0], 0, U[1, 1]]]) >>> UI.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> IU = QuantumGate( ... spec=unitary, ... targets=[1], ... num_systems=2, ... label="U", ... ) >>> IU.output() Matrix([ [U[0, 0], U[0, 1], 0, 0], [U[1, 0], U[1, 1], 0, 0], [ 0, 0, U[0, 0], U[0, 1]], [ 0, 0, U[1, 0], U[1, 1]]]) >>> IU.diagram()
>>> unitary = sp.MatrixSymbol("U", 4, 4).as_mutable() >>> UU = QuantumGate( ... spec=unitary, ... targets=[0, 1], ... num_systems=2, ... label="U", ... ) >>> UU.output() Matrix([ [U[0, 0], U[0, 1], U[0, 2], U[0, 3]], [U[1, 0], U[1, 1], U[1, 2], U[1, 3]], [U[2, 0], U[2, 1], U[2, 2], U[2, 3]], [U[3, 0], U[3, 1], U[3, 2], U[3, 3]]]) >>> UU.diagram()
>>> unitary = sp.MatrixSymbol("U", 2**2, 2**2).as_mutable() >>> U = QuantumGate( ... spec=unitary, ... targets=[0, 2], ... label="U", ... ) >>> U.output() Matrix([ [U[0, 0], U[0, 1], 0, 0, U[0, 2], U[0, 3], 0, 0], [U[1, 0], U[1, 1], 0, 0, U[1, 2], U[1, 3], 0, 0], [ 0, 0, U[0, 0], U[0, 1], 0, 0, U[0, 2], U[0, 3]], [ 0, 0, U[1, 0], U[1, 1], 0, 0, U[1, 2], U[1, 3]], [U[2, 0], U[2, 1], 0, 0, U[2, 2], U[2, 3], 0, 0], [U[3, 0], U[3, 1], 0, 0, U[3, 2], U[3, 3], 0, 0], [ 0, 0, U[2, 0], U[2, 1], 0, 0, U[2, 2], U[2, 3]], [ 0, 0, U[3, 0], U[3, 1], 0, 0, U[3, 2], U[3, 3]]]) >>> U.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> CU = QuantumGate( ... spec=unitary, ... targets=[1], ... controls=[0], ... label="U", ... ) >>> CU.output() Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, U[0, 0], U[0, 1]], [0, 0, U[1, 0], U[1, 1]]]) >>> CU.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> UC = QuantumGate( ... spec=unitary, ... targets=[0], ... controls=[1], ... label="U", ... ) >>> UC.output() Matrix([ [1, 0, 0, 0], [0, U[0, 0], 0, U[0, 1]], [0, 0, 1, 0], [0, U[1, 0], 0, U[1, 1]]]) >>> UC.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> AU = QuantumGate( ... spec=unitary, ... targets=[1], ... anticontrols=[0], ... label="U", ... ) >>> AU.output() Matrix([ [U[0, 0], U[0, 1], 0, 0], [U[1, 0], U[1, 1], 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1]]) >>> AU.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> CCU = QuantumGate( ... spec=unitary, ... targets=[2], ... controls=[0, 1], ... label="U", ... ) >>> CCU.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, U[0, 0], U[0, 1]], [0, 0, 0, 0, 0, 0, U[1, 0], U[1, 1]]]) >>> CCU.diagram()
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> AUC = QuantumGate( ... spec=unitary, ... targets=[1], ... controls=[2], ... anticontrols=[0], ... label="U", ... ) >>> AUC.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0], [0, U[0, 0], 0, U[0, 1], 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, U[1, 0], 0, U[1, 1], 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1]]) >>> AUC.diagram()
Constructor argument properties#
- property QuantumGate.spec: mat | arr | list[list[num | expr | str]] | list[tuple[num | expr | str, int | list[int]]]#
The specification of the quantum object. Provides a description of the object’s values in a standard
dim-dimensional basis.
- property QuantumGate.targets: list[int]#
The numerical indices of the subsystems on which the gate’s core operator resides.
- property QuantumGate.controls: list[int]#
The numerical indices of the subsystems on which control nodes reside.
For example, a controlled-\(\Unitary\) gate in \(\Dimension\) dimensions takes the form
(294)#\[\begin{split}\begin{aligned} \Control^{\indices{0}} \Unitary^{\indices{1}} &= \sum\limits_{k=0}^{\Dimension - 1} \ket{k}\bra{k}\otimes\Unitary^{k} \\ &= \ket{0}\bra{0}\otimes\Identity + \ket{1}\bra{1}\otimes\Unitary + \ket{2}\bra{2}\otimes\Unitary^{2} + \ldots + \ket{\Dimension - 1}\bra{\Dimension - 1}\otimes\Unitary^{\Dimension - 1} \end{aligned}\end{split}\]
- property QuantumGate.anticontrols: list[int]#
The numerical indices of the subsystems on which anticontrol nodes reside.
For example, an anticontrolled-\(\Unitary\) gate in \(\Dimension\) dimensions takes the form
(295)#\[\begin{split}\begin{aligned} \Anticontrol^{\indices{0}} \Unitary^{\indices{1}} &= \sum\limits_{k=0}^{\Dimension - 1} \ket{k}\bra{k}\otimes\Unitary^{\Dimension - 1 - k} \\ &= \ket{0}\bra{0}\otimes\Unitary^{\Dimension - 1} + \ket{1}\bra{1}\otimes\Unitary^{\Dimension - 2} + \ket{2}\bra{2}\otimes\Unitary^{\Dimension - 3} + \ldots + \ket{\Dimension - 1}\bra{\Dimension - 1}\otimes\Identity \end{aligned}\end{split}\]
- property QuantumGate.num_systems: int#
The number of systems that the gate spans. Must be a non-negative integer.
- property QuantumGate.dim: int#
The dimensionality of the quantum object. Must be a non-negative integer.
- property QuantumGate.numerical: bool#
Whether to cast the object’s matrix elements as floating-point values or exact values.
- property QuantumGate.array: bool#
Whether to cast the object’s matrix as a NumPy array or SymPy matrix.
- property QuantumGate.symbols: dict[sym | str, dict[str, Any]]#
A dictionary in which the keys are individual symbols (contained within the object’s matrix representation) and the values are dictionaries of their respective SymPy keyword-argument
assumptions(“predicates”). A full list of currently supported predicates, and their defaults, is as follows:"algebraic":True"commutative":True"complex":True"extended_negative":False"extended_nonnegative":True"extended_nonpositive":False"extended_nonzero":True"extended_positive":True"extended_real":True"finite":True"hermitian":True"imaginary":False"infinite":False"integer":True"irrational":False"negative":False"noninteger":False"nonnegative":True"nonpositive":False"nonzero":True"positive":True"rational":True"real":True"transcendental":False"zero":False
- property QuantumGate.substitutions: list[tuple[num | expr | str, num | expr | str]]#
A list of 2-tuples of substitutions to be applied to the object’s matrix representation.
- property QuantumGate.simplification: bool#
Whether to perform mathematical simplification on the object when it is called.
- property QuantumGate.conjugation: bool#
Whether to perform Hermitian conjugation on the object when it is called.
- property QuantumGate.exponent: num | expr | str#
A numerical or string representation of a scalar value specifying the value to which the gate’s matrix representation is exponentiated.
For any gate with a matrix representation \(\op{A}\) that is involutory, i.e., \(\op{A}^2 = \Identity\) (where \(\Identity\) is the identity matrix), its exponentiation is performed as described below. First, we have the identity,
(296)#\[\exp[\eye z \op{A}] = \cos(z)\Identity + \eye\sin(z)\op{A},\]for any \(z \in \Complexes\). In the case of \(z = -\frac{\pi}{2}\), this becomes
(297)#\[\exp\Bigl[-\eye\frac{\pi}{2}\op{A}\Bigr] = -\eye\op{A},\]which can be rearranged to give
(298)#\[\begin{split}\begin{aligned} \op{A} &= \eye \exp\Bigl[-\eye\frac{\pi}{2}\op{A}\Bigr] \\ &= \exp\Bigl[\eye\frac{\pi}{2}\Bigr] \cdot \exp\Bigl[-\eye\frac{\pi}{2}\op{A}\Bigr]. \end{aligned}\end{split}\]Simply taking this expression to an arbitrary power \(p \in \mathbb{C}\) thus yields the identity
(299)#\[\begin{split}\begin{aligned} \op{A}^p &= \exp\Bigl[\eye\frac{\pi}{2} p\Bigr] \cdot \exp\Bigl[-\eye\frac{\pi}{2} p \op{A}\Bigr] \\ &= \exp\Bigl[\eye\frac{\pi}{2} p\Bigr] \Bigl[\cos\Bigl(\frac{\pi}{2} p\Bigr) \Identity - \eye \sin\Bigl(\frac{\pi}{2} p\Bigr) \op{A}\Bigr] \\ &= \frac{1 + \e^{\eye \pi p}}{2} \Identity + \frac{1 - \e^{\eye \pi p}}{2} \op{A}. \end{aligned}\end{split}\]
- property QuantumGate.coefficient: num | expr | str#
A numerical or string representation of a scalar value by which the gate’s matrix (occupying
targets) is multiplied.
- property QuantumGate.label: str#
The unformatted string used to represent the object in mathematical expressions. Must have a non-zero length.
- property QuantumGate.notation: str#
The formatted string used to represent the object in mathematical expressions. When set, overrides the value of the
labelproperty. Must have a non-zero length. Not intended to be set by the user in most cases.
- property QuantumGate.family: str | list[str]#
The code of the block element that the object is to be visualized as. Not intended to be set by the user.
Read-only properties#
- property QuantumGate.current: mat | arr#
The current (unprocessed) matrix representation of the quantum gate.
- property QuantumGate.systems: list[int]#
Read-only property containing an ordered list of the numerical indices of the object’s systems.
Methods#
- QuantumGate.matrix(
- numerical: bool | None = None,
- array: bool | None = None,
Compute the unprocessed matrix representation of the gate.
- Parameters:
numerical (
bool) – Whether to cast the matrix elements as floating-point values (True) (if possible) or exact values (False). Defaults to the value ofself.numerical.array (
bool) – Whether to cast the matrix as a NumPy array (True) or SymPy matrix (False). Defaults to the value ofself.array.
- Returns:
mat | arr– The unprocessed matrix representation of the gate.
- QuantumGate.output(
- numerical: bool | None = None,
- array: bool | None = None,
- substitutions: list[tuple[num | expr | str, num | expr | str]] | None = None,
- simplification: bool | None = None,
- conjugation: bool | None = None,
- exponent: bool | num | expr | str | None = None,
- coefficient: bool | num | expr | str | None = None,
Compute the processed matrix representation of the constructed gate (including any controls, anticontrols, and empty systems).
- Parameters:
numerical (
bool) – Whether to cast the matrix elements as floating-point values (True) (if possible) or exact values (False). Defaults to the value ofself.numerical.array (
bool) – Whether to cast the matrix as a NumPy array (True) or SymPy matrix (False). Defaults to the value ofself.array.substitutions (
list[tuple[num | expr | str,num | expr | str]]) – Algebraic substitutions to be applied to the gate. Defaults to the value ofself.substitutions.simplification (
bool) – Whether to perform mathematical simplification on the gate. IfFalse, does not simplify. Defaults to the value ofself.simplification.conjugation (
bool) – Whether to perform Hermitian conjugation on the gate. IfFalse, does not conjugate. Defaults to the value ofself.conjugation.exponent (
bool | num | expr | str) – The scalar value by which the gate’s matrix representation is exponentiated. IfFalse, does not exponentiate. Defaults to the value ofself.exponent.coefficient (
bool | num | expr | str) – The scalar value by which the gate’s matrix representation is multiplied. IfFalse, does not multiply the gate by the coefficient. Defaults to the value ofself.coefficient.
- Returns:
mat | arr– The processed matrix representation of the constructed gate.
- QuantumGate.print(
- delimiter: str | None = None,
- product: bool | None = None,
- return_string: bool | None = None,
- numerical: bool | None = None,
- substitutions: list[tuple[num | expr | str, num | expr | str]] | None = None,
- simplification: bool | None = None,
- conjugation: bool | None = None,
- exponent: bool | num | expr | str | None = None,
- coefficient: bool | num | expr | str | None = None,
Print or return a mathematical expression of the quantum gate as a string.
Note that this method is essentially a wrapper on the
output()method, and so includes its arguments.- Parameters:
delimiter (
str) – A string containing the character(s) with which to delimit (i.e., separate) the values in the ket and/or bra terms in the mathematical expression. Defaults to",".product (
bool) – Whether to represent the mathematical expression using tensor products. Only applies if the gate is a multipartite composition. Defaults toFalse.return_string (
bool) – Whether to return the mathematical expression as a string. Defaults toFalse.numerical (
bool) – Whether to cast the matrix elements as floating-point values (True) (if possible) or exact values (False). Defaults to the value ofself.numerical.substitutions (
list[tuple[num | expr | str,num | expr | str]]) – Algebraic substitutions to be applied to the gate. Defaults to the value ofself.substitutions.simplification (
bool) – Whether to perform mathematical simplification on the gate. IfFalse, does not simplify. Defaults to the value ofself.simplification.conjugation (
bool) – Whether to perform Hermitian conjugation on the gate. IfFalse, does not conjugate. Defaults to the value ofself.conjugation.exponent (
bool | num | expr | str) – The scalar value by which the gate is exponentiated. IfFalse, does not exponentiate. Defaults to the value ofself.exponent.coefficient (
bool | num | expr | str) – The scalar value by which the gate is multiplied. IfFalse, does not multiply the gate by the coefficient. Defaults to the value ofself.coefficient.
- Returns:
None– Returned ifreturn_stringisFalse.str– The constructed mathematical expression. Returned ifreturn_stringisTrue.
Examples
>>> unitary = sp.MatrixSymbol("U", 2, 2).as_mutable() >>> U = QuantumGate( ... spec=unitary, ... dim=2, ... label="U", ... ) >>> U.output() Matrix([ [U[0, 0], U[0, 1]], [U[1, 0], U[1, 1]]]) >>> U.print() U = U[0, 0]|0⟩⟨0| + U[0, 1]|0⟩⟨1| + U[1, 0]|1⟩⟨0| + U[1, 1]|1⟩⟨1|
>>> unitary = sp.MatrixSymbol("U", 4, 4).as_mutable() >>> UU = QuantumGate( ... spec=unitary, ... targets=[0, 1], ... dim=2, ... label="UU", ... ) >>> UU.output() Matrix([ [U[0, 0], U[0, 1], U[0, 2], U[0, 3]], [U[1, 0], U[1, 1], U[1, 2], U[1, 3]], [U[2, 0], U[2, 1], U[2, 2], U[2, 3]], [U[3, 0], U[3, 1], U[3, 2], U[3, 3]]]) >>> UU.print() UU = U[0, 0]|0,0⟩⟨0,0| + U[0, 1]|0,0⟩⟨0,1| + U[0, 2]|0,0⟩⟨1,0| + U[0, 3]|0,0⟩⟨1,1| + U[1, 0]|0,1⟩⟨0,0| + U[1, 1]|0,1⟩⟨0,1| + U[1, 2]|0,1⟩⟨1,0| + U[1, 3]|0,1⟩⟨1,1| + U[2, 0]|1,0⟩⟨0,0| + U[2, 1]|1,0⟩⟨0,1| + U[2, 2]|1,0⟩⟨1,0| + U[2, 3]|1,0⟩⟨1,1| + U[3, 0]|1,1⟩⟨0,0| + U[3, 1]|1,1⟩⟨0,1| + U[3, 2]|1,1⟩⟨1,0| + U[3, 3]|1,1⟩⟨1,1| >>> UU.print(product=True) UU = U[0, 0]|0⟩⟨0|⊗|0⟩⟨0| + U[0, 1]|0⟩⟨0|⊗|0⟩⟨1| + U[0, 2]|0⟩⟨1|⊗|0⟩⟨0| + U[0, 3]|0⟩⟨1|⊗|0⟩⟨1| + U[1, 0]|0⟩⟨0|⊗|1⟩⟨0| + U[1, 1]|0⟩⟨0|⊗|1⟩⟨1| + U[1, 2]|0⟩⟨1|⊗|1⟩⟨0| + U[1, 3]|0⟩⟨1|⊗|1⟩⟨1| + U[2, 0]|1⟩⟨0|⊗|0⟩⟨0| + U[2, 1]|1⟩⟨0|⊗|0⟩⟨1| + U[2, 2]|1⟩⟨1|⊗|0⟩⟨0| + U[2, 3]|1⟩⟨1|⊗|0⟩⟨1| + U[3, 0]|1⟩⟨0|⊗|1⟩⟨0| + U[3, 1]|1⟩⟨0|⊗|1⟩⟨1| + U[3, 2]|1⟩⟨1|⊗|1⟩⟨0| + U[3, 3]|1⟩⟨1|⊗|1⟩⟨1|
- QuantumGate.diagram(
- pad: tuple[int, int] | None = None,
- sep: tuple[int, int] | None = None,
- style: str | None = None,
- return_string: bool | None = None,
Print or return a circuit diagram of the quantum object as a multiline string.
- Parameters:
pad (
tuple[int,int]) – A 2-tuple of non-negative integers specifying intra-gate padding (i.e., the horizontal and vertical interior paddings between the content at the centre of each gate (e.g., label) and its outer edge (e.g., block border)). Defaults to(1, 0).sep (
tuple[int,int]) – A 2-tuple of non-negative integers specifying inter-gate separation (i.e., the horizontal and vertical exterior separation distances between the edges of neighbouring gates). Defaults to(1, 1).style (
str) – A string specifying the style for the circuit visualization to take. Can be any of"ascii","unicode", or"unicode_alt". Defaults to"unicode".return_string (
bool) – Whether to return the assembled diagram as a multiline string. Defaults toFalse.
- Returns:
None– Returned ifreturn_stringisFalse.str– The rendered circuit diagram. Returned ifreturn_stringisTrue.
Note
The quality of the semigraphical diagrams depends greatly on the typeface with which they are visualized. For best results, monospace (fixed-width) fonts with good Unicode coverage should be used.
Examples
For usage examples, please see those of the
QuantumGateclass and its subclasses (sec:docs_gates_subclassesSubclasses).
- QuantumGate.decompose(
- gates: list[QuantumGate],
- additional_nodes: tuple[int, int, int] | None = None,
- depth: int | list[int] | None = None,
- num_find: int | None = None,
- only_targets: bool | None = None,
- include_empty: bool | None = None,
- preserve_structure: bool | None = None,
- numerical: bool | None = None,
- array: bool | None = None,
Decompose the gate into one or more sequences of gates of a specified set. By default (
only_targets = True), only the component of the gate that lies on the its target systems, i.e., the operator returned by thematrix()method, is decomposed. If decomposing eitherGateStackorGateInterleave, theonly_targetsargument is forced toFalse.- Parameters:
gates (
list[QuantumGate]) – A list ofQuantumGateinstances with which the gate is to be decomposed. Can include any subclass ofQuantumGate, including user-defined gates. The order in which the gates are given in the list can (in the case of multiple possible decompositions) change the order in which the sequences are found. Note that not every gate given in this list will necessarily appear in any found sequence.additional_nodes (
tuple[int,int,int]) – A 3-tuple of non-negative integers specifying the maximum number of additional target, control, and anticontrol nodes to endow to each gate ingates. Defaults to(0, 0, 0).depth (
int | list[int]) – The circuit depth up to which the decomposition is performed. Must be a positive integer or a list of positive integers. Use the typelist[int]to search for decompositions at specific circuit depths. If0, begins the search at a depth of 1 and increases indefinitely. Defaults to0.num_find (
int) – The (maximum) number of sequences to find. If0, exhaustively searches all possible sequences within the search space. Defaults to1.only_targets (
bool) – Whether to decompose only the component of the gate on the targets (True), or alternatively the entire gate (False), including all control and anticontrol nodes. Forced toFalseif decomposingGateStackorGateInterleave, otherwise defaults toTrue.include_empty (
bool) – Whether to include empty systems (empty wires) between the first and last target systems in the decomposition. Defaults toFalse.preserve_structure (
bool) – Whether to preserve the node structure (i.e., the relative positions of the targets, controls, and anticontrols) of the gates given ingates. Defaults toFalse.numerical (
bool) – Whether to perform the decomposition numerically with floating-point values (True) (if possible) or exact values (False). Defaults to the value ofself.numerical.array (
bool) – Whether to perform the decomposition using NumPy arrays (True) or SymPy matrices (False). Defaults to the value ofself.array.
- Returns:
list[list[QuantumGate]]– A list of found decomposition seqeuences. Will be empty if no sequences are found.
Note
Currently, this method decomposes any given gate using brute force, which is extremely inefficient. The speed at which decompositions are found depends entirely on the size of the search space, which increases exponentially with:
The number of gates in
gatesThe number of additional target, control, and anticontrol nodes specified in
additional_nodesThe search depth
The number of systems
The gate dimensionality
Future work on this method could involve the implementation of heuristics with which the decomposition can be performed more efficiently.
Examples
>>> SWAP = Swap() >>> SWAP.diagram()
>>> decomposition = SWAP.decompose( ... gates=[Not(targets=[0], controls=[1])] ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... pad=(1, 0), ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> CSWAP = Swap(targets=[0, 2], controls=[1]) >>> CSWAP.diagram(sep=(2, 1))
>>> decomposition = CSWAP.decompose( ... gates=[Not()], ... additional_nodes=(0, 2, 0), ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> decomposition = CSWAP.decompose( ... gates=[Not()], ... additional_nodes=(0, 2, 0), ... only_targets=False, ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> CNOT = Not(targets=[2], controls=[0]) >>> CNOT.diagram(sep=(2, 1))
>>> decomposition = CNOT.decompose( ... gates=[Not(targets=[1], controls=[0])], ... only_targets=False, ... preserve_structure=True, ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> n = 4 >>> CXXXX = Not(targets=list(range(1, n + 1)), controls=[0]) >>> CXXXX.diagram(sep=(2, 1))
>>> decomposition = CXXXX.decompose(gates=[Not()])[0] >>> QuantumCircuit(gates=decomposition).diagram( ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> iSWAP = QuantumGate( ... spec=[ ... [1, 0, 0, 0], ... [0, 0, sp.I, 0], ... [0, sp.I, 0, 0], ... [0, 0, 0, 1], ... ], ... label="iSWAP", ... ) >>> iSWAP.diagram()
>>> decomposition = iSWAP.decompose( ... gates=[ ... Not(targets=[0], controls=[1]), ... Phase(exponent=sp.Rational(1, 2), label="S"), ... ], ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... pad=(1, 0), ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> SWAP = Swap(targets=[0, 3]) >>> SWAP.diagram(sep=(2, 1))
>>> decomposition = SWAP.decompose( ... gates=[Swap()], ... preserve_structure=True, ... include_empty=True, ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... sep=(2, 1), ... force_separation=True, ... visible={"gates"}, ... )
>>> phased_identity = Pauli(index=0, coefficient=-sp.I, label="-iI") >>> phased_identity.diagram()
>>> decomposition = phased_identity.decompose( ... gates=[Pauli(index=1), Pauli(index=2), Pauli(index=3)] ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... pad=(1, 0), ... uniform_spacing=True, ... visible={"gates"}, ... )
>>> n = 5 >>> phase_power = Phase(phase="w", exponent=n, label=f"P^{n}") >>> phase_power.diagram()
>>> decomposition = phase_power.decompose( ... gates=[Phase(phase="w")], ... only_targets=False, ... )[0] >>> QuantumCircuit(gates=decomposition).diagram( ... pad=(1, 0), ... uniform_spacing=True, ... visible={"gates"}, ... )
>>> paulis = [Pauli(index=i) for i in range(1,4)] >>> XYZ = GateStack(*paulis) >>> XYZ.diagram()
>>> decomposition = XYZ.decompose(gates=paulis)[0] >>> QuantumCircuit(gates=decomposition).diagram( ... pad=(1, 0), ... uniform_spacing=True, ... visible={"gates"}, ... )
Subclasses#
Most of the canonical gates used in standard quantum computing theory are implemented in Qhronology as an assortment of subclasses of the QuantumGate class. It is important to be aware of how, even though they share many of the same arguments and properties, the usage of these classes can differ greatly. This is especially true among a few distinct categorizations of gates, namely dimensionality and compositionality, which are summarized below:
Subclass |
Alias |
Dimensionality |
Compositionality |
|---|---|---|---|
|
fixed (qubits) |
unipartite |
|
|
fixed (qubits) |
unipartite |
|
|
fixed (qutrits) |
unipartite |
|
|
fixed (qubits) |
unipartite |
|
|
variable (qudits) |
unipartite |
|
|
variable (qudits) |
unipartite |
|
|
variable (qudits) |
bipartite |
|
|
variable (qudits) |
unipartite |
|
|
fixed (qubits) |
unipartite |
|
|
variable (qudits) |
unipartite, multipartite |
|
|
variable (qudits) |
unipartite, multipartite |
|
|
variable (qudits) |
multipartite |
For the classes of fixed dimensionality, their constructors do not take dim as an argument, nor can the associated property be set. For the classes describing unipartite gates, more than one system can still be targeted, in which case the gate’s elementary matrix will simply be duplicated onto each system. Note that the Swap class can only describe bipartite gates, and so is not multipartite for any general number of systems. Also note that gates of the Measurement class can act on systems of any dimension but do not themselves possess a dimensionality.
Please be aware that the documentation of these subclasses included hereafter consists only properties and methods that are either new or modified from the base class QuantumGate.
Note
In all of these subclasses, the spec property should not be set.
- class Unitary(
- *args,
- parameters: tuple[num | expr | str, num | expr | str, num | expr | str] | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating \(2\)-dimensional unitary gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.A square matrix \(\Unitary\) is said to be unitary if its matrix inverse is equal to its conjugate tranpose (Hermitian conjugate). Mathematically, this is the condition
(300)#\[\Unitary^\dagger = \Unitary^{-1},\]which equivalently means that any unitary matrix satisfies
(301)#\[\Unitary^\dagger \Unitary = \Unitary \Unitary^\dagger = \Identity,\]where \(\Identity\) is the identity matrix. Evidently, the inverse of a unitary matrix is another unitary matrix. Additionally, the product of any two unitary matrices is unitary, and so the set of all unitary matrices consitutes a group, called the unitary group (written \(\GroupUnitary(n)\) for the group of \(n \times n\) unitary matrices), with the group operation being matrix multiplication.
Unitary gates for qubits correspond to \(2 \times 2\) unitary matrices, with one parametrization of such matrices being
(302)#\[\begin{split}\Unitary(\theta,\phi,\lambda) = \begin{bmatrix} \cos(\theta/2) & -\e^{\eye\lambda}\sin(\theta/2) \\ \e^{\eye\phi}\sin(\theta/2) & \e^{\eye(\phi + \lambda)}\cos(\theta/2) \end{bmatrix},\end{split}\]where the parameters (
parameters) \(\theta, \phi, \lambda \in \Reals\) can be interpreted as angles. Note that this form is \(2\pi\)-periodic in each of its three parameters, and specifies any element of \(\GroupUnitary(2)\) (up to a global phase).This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.parameters (
tuple[num | expr | str,num | expr | str,num | expr | str]) – A 3-tuple of scalar values corresponding to the parameters \((\theta, \phi, \lambda)\) in the given definition. Defaults to(0, 0, 0).**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
The unitary gate is defined only for \(2\)-dimensional (i.e., binary/qubit) systems. This means that the constructor does not take
dimas an argument, nor can the associated property be set.Examples
>>> U = Unitary(parameters=("θ", "φ", "λ")) >>> U.output() Matrix([ [ cos(θ/2), -exp(I*λ)*sin(θ/2)], [exp(I*φ)*sin(θ/2), exp(I*(λ + φ))*cos(θ/2)]]) >>> U.diagram()
>>> I = Unitary(parameters=(0, 0, 0), label="I") >>> I.output() Matrix([ [1, 0], [0, 1]]) >>> I.diagram()
>>> H = Unitary(parameters=(sp.pi/2, 0, sp.pi), label="H") >>> H.output() Matrix([ [sqrt(2)/2, sqrt(2)/2], [sqrt(2)/2, -sqrt(2)/2]]) >>> H.diagram()
- property parameters: tuple[num | expr | str, num | expr | str, num | expr | str]
The 3-tuple of parameter values.
- class Pauli(
- *args,
- index: int,
- **kwargs,
Bases:
QuantumGateA subclass for creating Pauli gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Pauli gates correspond to the Pauli operators \(\Pauli_i\), which is a set of three unitary operators that have the following representations as \(2 \times 2\) matrices:
(303)#\[\begin{split}\begin{aligned} \Pauli_1 &= \Pauli_x \equiv \ket{0}\bra{1} + \ket{1}\bra{0} = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}, \\ \Pauli_2 &= \Pauli_y \equiv -\eye \ket{0}\bra{1} + \eye \ket{1}\bra{0} = \begin{bmatrix} 0 & -\eye \\ \eye & 0 \end{bmatrix}, \\ \Pauli_3 &= \Pauli_z \equiv \ket{0}\bra{0} - \ket{1}\bra{1} = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}. \end{aligned}\end{split}\]Indexed by \(i\) (
index), this set is often extended to include the \(2\)-dimensional identity matrix for \(i=0\).This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.index (
int) –The index of the desired Pauli matrix. Can take the following values:
0(\(2\)-dimensional identity matrix \(\Identity\))1(Pauli-\(X\) \(\Pauli_x\))2(Pauli-\(Y\) \(\Pauli_y\))3(Pauli-\(Z\) \(\Pauli_z\))
**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
The Pauli gates are defined only for \(2\)-dimensional (i.e., binary/qubit) systems. This means that the constructor does not take
dimas an argument, nor can the associated property be set.Examples
>>> X = Pauli(index=1) >>> X.output() Matrix([ [0, 1], [1, 0]]) >>> X.diagram()
>>> Y = Pauli(index=2) >>> Y.output() Matrix([ [0, -I], [I, 0]]) >>> Y.diagram()
>>> Z = Pauli(index=3) >>> Z.output() Matrix([ [1, 0], [0, -1]]) >>> Z.diagram()
>>> I = Pauli(index=0) >>> I.output() Matrix([ [1, 0], [0, 1]]) >>> I.diagram()
>>> S = Pauli(index=3, exponent=sp.Rational(1, 2), label="S") >>> S.output() Matrix([ [1, 0], [0, I]]) >>> S.diagram()
>>> ZZ = Pauli(index=3, targets=[0, 1], label="Z⊗Z") >>> ZZ.output() Matrix([ [1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) >>> ZZ.diagram()
>>> CZ = Pauli(index=3, targets=[1], controls=[0]) >>> CZ.output() Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]) >>> CZ.diagram()
>>> R_xx = Pauli( ... index=1, ... targets=[0, 1], ... exponent="θ/pi", ... coefficient="exp(-I*θ/2)", ... label="R_xx(θ)", ... ) >>> R_xx.output(simplification=True) Matrix([ [ cos(θ/2), 0, 0, -I*sin(θ/2)], [ 0, cos(θ/2), -I*sin(θ/2), 0], [ 0, -I*sin(θ/2), cos(θ/2), 0], [-I*sin(θ/2), 0, 0, cos(θ/2)]]) >>> R_xx.diagram()
- property index: int
The index of the desired Pauli matrix.
- class GellMann(
- *args,
- index: int,
- **kwargs,
Bases:
QuantumGateA subclass for creating Gell-Mann gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Gell-Mann gates correspond to the Gell-Mann operators \(\GellMann_i\), which is a set of eight non-unitary operators that have the following representations as \(3 \times 3\) matrices:
(304)#\[\begin{split}\begin{aligned} &\GellMann_1 \equiv \ket{0}\bra{1} + \ket{1}\bra{0} = \begin{bmatrix} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix}, \\ &\GellMann_3 \equiv \ket{0}\bra{0} - \ket{1}\bra{1} = \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & 0 \end{bmatrix}, \\ &\GellMann_5 \equiv -\eye\ket{0}\bra{2} + \eye\ket{2}\bra{0} = \begin{bmatrix} 0 & 0 & -\eye \\ 0 & 0 & 0 \\ \eye & 0 & 0 \end{bmatrix}, \\ &\GellMann_7 \equiv -\eye\ket{2}\bra{3} + \eye\ket{3}\bra{2} = \begin{bmatrix} 0 & 0 & 0 \\ 0 & 0 & -\eye \\ 0 & \eye & 0 \end{bmatrix}, \end{aligned} \quad \begin{aligned} &\GellMann_2 \equiv -\eye\ket{0}\bra{1} + \eye \ket{1}\bra{0} = \begin{bmatrix} 0 & -\eye & 0 \\ \eye & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix}, \\ &\GellMann_4 \equiv \ket{0}\bra{2} + \ket{2}\bra{0} = \begin{bmatrix} 0 & 0 & 1 \\ 0 & 0 & 0 \\ 1 & 0 & 0 \end{bmatrix}, \\ &\GellMann_6 \equiv \ket{2}\bra{3} + \ket{3}\bra{2} = \begin{bmatrix} 0 & 0 & 0 \\ 0 & 0 & 1 \\ 0 & 1 & 0 \end{bmatrix}, \\ &\GellMann_8 \equiv \frac{1}{\sqrt{3}}\bigl(\ket{0}\bra{0} + \ket{1}\bra{1} - 2\ket{2}\bra{2}\bigr) = \frac{1}{\sqrt{3}}\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & -2 \end{bmatrix}. \end{aligned}\end{split}\]Indexed by \(i\) (
index), this set is often extended to include the \(3\)-dimensional identity matrix for \(i=0\).This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.index (
int) –The index of the desired Gell-Mann matrix. Can take the following values:
0(\(3\)-dimensional identity matrix \(\Identity\))1(\(\GellMann_1\))2(\(\GellMann_2\))3(\(\GellMann_3\))4(\(\GellMann_4\))5(\(\GellMann_5\))6(\(\GellMann_6\))7(\(\GellMann_7\))8(\(\GellMann_8\))
**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
The Gell-Mann gates are defined only for \(3\)-dimensional (i.e., ternary/qutrit) systems. This means that the constructor does not take
dimas an argument, nor can the associated property be set.Examples
>>> L = GellMann(index=8) >>> L.output() Matrix([ [sqrt(3)/3, 0, 0], [ 0, sqrt(3)/3, 0], [ 0, 0, -2*sqrt(3)/3]]) >>> L.diagram()
>>> I = GellMann(index=0) >>> I.output() Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]) >>> I.diagram()
- property index: int
The index of the desired Gell-Mann matrix.
- class Rotation(
- *args,
- axis: int,
- angle: num | expr | str | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating rotation gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.The elementary rotation gates correspond to the rotation operators \(\Rotation_i\), which is a set of three unitary operators that have the following representations as \(2 \times 2\) matrices:
(305)#\[\begin{split}\begin{aligned} \Rotation_1 &= \Rotation_x = \e^{-\eye\Pauli_{x}\theta/2} = \begin{bmatrix} \cos(\theta/2) & -\eye\sin(\theta/2) \\ -\eye\sin(\theta/2) & \cos(\theta/2) \end{bmatrix}, \\ \Rotation_2 &= \Rotation_y = \e^{-\eye\Pauli_{y}\theta/2} = \begin{bmatrix} \cos(\theta/2) & -\sin(\theta/2) \\ \sin(\theta/2) & \cos(\theta/2) \end{bmatrix}, \\ \Rotation_3 &= \Rotation_z = \e^{-\eye\Pauli_{z}\theta/2} = \begin{bmatrix} \e^{-\eye\theta/2} & 0 \\ 0 & \e^{\eye\theta/2} \end{bmatrix}. \end{aligned}\end{split}\]Indexed by \(i\) (
index), each matrix in this set is parameterized by the rotation angle (angle) \(\theta \in \Reals\).These are fundamentally single-system gates, and so a copy of the specified gate is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.axis (
int) –The index corresponding to the axis of the desired rotation matrix. Can take the following values:
1(\(x\)-rotation \(\Rotation_x\))2(\(y\)-rotation \(\Rotation_y\))3(\(z\)-rotation \(\Rotation_z\))
angle (
num | expr | str) – The rotation angle. Defaults to0.**kwargs –
Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
The rotation gates are defined only for \(2\)-dimensional (i.e., binary/qubit) systems. This means that the constructor does not take
dimas an argument, nor can the associated property be set.Examples
>>> R_x = Rotation(axis=1, angle="θ", label="R_x") >>> R_x.output() Matrix([ [ cos(θ/2), -I*sin(θ/2)], [-I*sin(θ/2), cos(θ/2)]]) >>> R_x.diagram()
>>> R_y = Rotation(axis=2, angle="φ", label="R_y") >>> R_y.output() Matrix([ [cos(φ/2), -sin(φ/2)], [sin(φ/2), cos(φ/2)]]) >>> R_y.diagram()
>>> R_z = Rotation(axis=3, angle="t", label="R_z") >>> R_z.output() Matrix([ [exp(-I*t/2), 0], [ 0, exp(I*t/2)]]) >>> R_z.diagram()
- property axis: int
The index corresponding to the axis of the desired rotation matrix.
- property angle: num | expr | str
The rotation angle.
- class Phase(
- *args,
- phase: num | expr | str | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating phase gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Phase gates correspond to phase operators \(\Phase\), which, in \(\Dimension\) dimensions, can be represented by \(\Dimension \times \Dimension\) diagonal matrices of the form
(306)#\[\begin{split}\begin{aligned} \Phase(\omega) &= \sum\limits_{k=0}^{\Dimension - 1} \omega^k \ket{k}\bra{k} \\ &= \begin{bmatrix} 1 & 0 & 0 & \ldots & 0 \\ 0 & \omega & 0 & \ldots & 0 \\ 0 & 0 & \omega^2 & \ldots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \ldots & \omega^{\Dimension - 1} \end{bmatrix} \end{aligned}\end{split}\]where \(\omega \in \Complexes\) is the phase parameter (
phase).This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.phase (
num | expr | str) – The phase parameter. Defaults to the unit root given bysp.exp(2 * sp.pi * sp.I / self.dim).**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Examples
>>> P = Phase() >>> P.output() Matrix([ [1, 0], [0, -1]]) >>> P.diagram()
>>> S = Phase(exponent=sp.Rational(1, 2), label="S") >>> S.output() Matrix([ [1, 0], [0, I]]) >>> S.diagram()
>>> T = Phase(exponent=sp.Rational(1, 4), label="T") >>> T.output() Matrix([ [1, 0], [0, (-1)**(1/4)]]) >>> T.diagram()
>>> P3 = Phase(dim=3) >>> P3.output() Matrix([ [1, 0, 0], [0, exp(2*I*pi/3), 0], [0, 0, exp(-2*I*pi/3)]]) >>> P3.diagram()
>>> W = Phase(phase="w", dim=3, label="W") >>> W.output() Matrix([ [1, 0, 0], [0, w, 0], [0, 0, w**2]]) >>> W.diagram()
- property phase: num | expr | str
The phase parameter.
- class Diagonal(
- *args,
- entries: dict[int | list[int], num | expr | str],
- exponentiation: bool | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating diagonal gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Diagonal gates correspond to diagonal operators \(\Diagonal\), which, in \(\Dimension\) dimensions, can be represented by \(\Dimension \times \Dimension\) diagonal matrices of the form
(307)#\[\begin{split}\begin{aligned} \Diagonal(\lambda_0, \lambda_1, \ldots, \lambda_{\Dimension - 1}) &= \sum\limits_{k=0}^{\Dimension - 1} \lambda_k\ket{k}\bra{k} \\ &= \begin{bmatrix} \lambda_0 & 0 & 0 & \ldots & 0 \\ 0 & \lambda_1 & 0 & \ldots & 0 \\ 0 & 0 & \lambda_2 & \ldots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \ldots & \lambda_{\Dimension - 1} \end{bmatrix} \end{aligned}\end{split}\]where \(\{\lambda_k : \lambda_k \in \Complexes, \; \abs{\lambda_k} = 1\}_{k=0}^{\Dimension - 1}\) are the main diagonal entries (
entries).This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.entries (
dict[int | list[int],num | expr | str]) – A dictionary in which the keys are number-basis specifications (integer or list of integers) and the values are scalars.exponentiation (
bool) – Whether to exponentiate (with imaginary unit) the values given inentries. Defaults toFalse.**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
Basis elements that are unspecified in the
entriesargument all have a corresponding matrix element of1, regardless of the value ofexponentiation.Examples
>>> D = Diagonal(entries={0: "u", 1: "v"}) >>> D.output() Matrix([ [u, 0], [0, v]]) >>> D.diagram()
>>> D3 = Diagonal( ... entries={0: "a", 1: "b", 2: "c"}, ... dim=3, ... ) >>> D3.output() Matrix([ [a, 0, 0], [0, b, 0], [0, 0, c]]) >>> D3.diagram()
>>> P = Diagonal( ... entries={1: "p"}, ... exponentiation=True, ... symbols={"p": {"real": True}}, ... label="P", ... ) >>> P.output() Matrix([ [1, 0], [0, exp(I*p)]]) >>> P.diagram()
- property entries: dict[int | list[int], num | expr | str]
A dictionary in which the keys are number-basis specifications (integer or list of integers) and the values are scalars.
- property exponentiation: bool
Whether to exponentiate (with imaginary unit) the values given in
entries.
- class Swap(
- *args,
- **kwargs,
Bases:
QuantumGateA subclass for creating SWAP (exchange) gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.SWAP gates correspond to SWAP operators \(\Swap\), which, between two systems \(A\) and \(B\) (each of \(\Dimension\) dimensions), can be represented by the \(\Dimension^2 \times \Dimension^2\) matrix
(308)#\[\Swap^{\indices{A,B}} = \sum\limits_{j,k=0}^{\Dimension - 1} {\ket{j}\bra{k}}^{\indices{A}} \otimes {\ket{k}\bra{j}}^{\indices{B}}.\]- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.targets (
list[int,int]) – A list of exactly two indices corresponding to the systems to be swapped. Is an argument of the superclassQuantumGate, so can be specified positionally in*args. Defaults to[0, 1]**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Examples
>>> S = Swap(targets=[0, 1]) >>> S.output() Matrix([ [1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) >>> S.diagram()
>>> S3 = Swap(targets=[0, 1], dim=3) >>> S3.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1]]) >>> S3.diagram()
>>> SIS = Swap(targets=[0, 2]) >>> SIS.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]]) >>> SIS.diagram()
>>> CSWAP = Swap(targets=[1, 2], controls=[0]) >>> CSWAP.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]]) >>> CSWAP.diagram()
>>> SCS = Swap(targets=[0, 2], controls=[1]) >>> SCS.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]]) >>> SCS.diagram()
>>> RSWAP = Swap( ... targets=[0, 1], ... exponent=sp.Rational(1, 2), ... label="√SWAP", ... family="GATE", ... ) >>> RSWAP.output() Matrix([ [1, 0, 0, 0], [0, 1/2 + I/2, 1/2 - I/2, 0], [0, 1/2 - I/2, 1/2 + I/2, 0], [0, 0, 0, 1]]) >>> RSWAP.diagram()
>>> PSWAP = Swap( ... targets=[0, 1], ... exponent="p", ... symbols={"p": {"real": True}}, ... label="SWAP^p", ... family="GATE", ... ) >>> PSWAP.output() Matrix([ [1, 0, 0, 0], [0, exp(I*pi*p)/2 + 1/2, 1/2 - exp(I*pi*p)/2, 0], [0, 1/2 - exp(I*pi*p)/2, exp(I*pi*p)/2 + 1/2, 0], [0, 0, 0, 1]]) >>> PSWAP.diagram()
- class Summation(
- *args,
- shift: int | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating SUM (summation) gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.The SUM gate is essentially a generalization of the NOT gate to \(\Dimension\)-dimensional systems. It corresponds to the SUM operator \(\SUM\), which can be represented by \(\Dimension \times \Dimension\) matrices of the form
(309)#\[\SUM(n) = \sum\limits_{k=0}^{\Dimension - 1} \ket{k \oplus n}\bra{k},\]where \(n \in \Integers_{\geq 0}\) is the shift parameter (
shift), and \(k \oplus n \equiv k + n \mathrel{\mathrm{mod}} \Dimension\).The case of \(n = 1\) is known as the shift operator, and represents a (non-Hermitian) generalization of the Pauli-\(X\) operator \(\Pauli_x\) to \(\Dimension\) dimensions.
This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.shift (
int) – The shift parameter. Must be a non-negative integer. Defaults to1.**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Examples
>>> SUM = Summation(shift=1) >>> SUM.output() Matrix([ [0, 1], [1, 0]]) >>> SUM.diagram()
>>> SUM3 = Summation(shift=1, dim=3) >>> SUM3.output() Matrix([ [0, 0, 1], [1, 0, 0], [0, 1, 0]]) >>> SUM3.diagram()
- property shift: int
The shift parameter.
- class Not(
- *args,
- **kwargs,
Bases:
SummationA subclass for creating NOT (logical negation or “bit-flip”) gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.The NOT gate is essentially a specialization of the SUM gate to \(2\)-dimensional systems. In the computational basis, it corresponds to the NOT operator \(\NOT\), which is exactly equivalent to the Pauli-\(X\) operator \(\Pauli_x\), and so has the matrix representation
(310)#\[\begin{split}\begin{aligned} \NOT &= \ket{0}\bra{1} + \ket{1}\bra{0} \\ &= \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}. \end{aligned}\end{split}\]As such, this class exists purely to simplify access to this operation.
This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
NOT gates are defined only for \(2\)-dimensional (i.e., binary/qubit) systems. This means that the constructor does not take
dimas an argument, nor can the associated property be set.Examples
>>> N = Not() >>> N.output() Matrix([ [0, 1], [1, 0]]) >>> N.diagram()
>>> NN = Not(targets=[0, 1]) >>> NN.output() Matrix([ [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]) >>> NN.diagram()
>>> CNOT = Not(targets=[1], controls=[0]) >>> CNOT.output() Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) >>> CNOT.diagram()
>>> ANOT = Not(targets=[1], anticontrols=[0]) >>> ANOT.output() Matrix([ [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) >>> ANOT.diagram()
>>> CCNOT = Not(targets=[2], controls=[0, 1]) >>> CCNOT.output() Matrix([ [1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]]) >>> CCNOT.diagram()
>>> RNOT = Not( ... exponent=sp.Rational(1, 2), ... label="√NOT", ... family="GATE", ... ) >>> RNOT.output() Matrix([ [1/2 + I/2, 1/2 - I/2], [1/2 - I/2, 1/2 + I/2]]) >>> RNOT.diagram()
- class Hadamard(
- *args,
- **kwargs,
Bases:
QuantumGateA subclass for creating Hadamard gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Hadamard gates correspond to Hadamard operators \(\Hadamard\), which, for qubits (\(2\) dimensions), may be represented by the \(2 \times 2\) matrix
(311)#\[\begin{split}\begin{aligned} \Hadamard &= \frac{1}{\sqrt{2}}\sum\limits_{j,k=0}^{1} (-1)^{jk} \ket{j}\bra{k} \\ &= \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix}. \end{aligned}\end{split}\]For qudits (\(\Dimension\) dimensions), this can be generalized to the following \(\Dimension \times \Dimension\) form,
(312)#\[\begin{split}\begin{aligned} \Hadamard_\Dimension &= \frac{1}{\sqrt{\Dimension}}\sum\limits_{j,k=0}^{\Dimension - 1} \omega_\Dimension^{k(\Dimension - j)} \ket{j}\bra{k} \\ &= \begin{bmatrix} 1 & 1 & 1 & \ldots & 1 \\ 1 & \omega^{\Dimension - 1} & \omega^{2(\Dimension - 1)} & \ldots & \omega^{(\Dimension - 1)^2} \\ 1 & \omega^{\Dimension - 2} & \omega^{2(\Dimension - 2)} & \ldots & \omega^{(\Dimension - 1)(\Dimension - 2)} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \omega & \omega^{2} & \ldots & \omega^{\Dimension - 1} \end{bmatrix} \end{aligned}\end{split}\]where \(\omega_\Dimension \equiv \e^{2\pi\eye/\Dimension}\).
This is fundamentally a single-system gate, and so a copy is placed on each of the subsystems corresponding to the indices in the
targetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Examples
>>> H = Hadamard() >>> H.output() Matrix([ [sqrt(2)/2, sqrt(2)/2], [sqrt(2)/2, -sqrt(2)/2]]) >>> H.diagram()
>>> HH = Hadamard(targets=[0, 1], label="H⊗H") >>> HH.output() Matrix([ [1/2, 1/2, 1/2, 1/2], [1/2, -1/2, 1/2, -1/2], [1/2, 1/2, -1/2, -1/2], [1/2, -1/2, -1/2, 1/2]]) >>> HH.diagram()
>>> H3 = Hadamard(dim=3) >>> H3.output() Matrix([ [sqrt(3)/3, sqrt(3)/3, sqrt(3)/3], [sqrt(3)/3, sqrt(3)*exp(-2*I*pi/3)/3, sqrt(3)*exp(2*I*pi/3)/3], [sqrt(3)/3, sqrt(3)*exp(2*I*pi/3)/3, sqrt(3)*exp(-2*I*pi/3)/3]]) >>> H3.diagram()
- class Fourier(
- *args,
- composite: bool | None = None,
- reverse: bool | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating Fourier (quantum [discrete] Fourier transform [QFT]) gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Fourier gates correspond to Fourier operators \(\QFT\), which, for a single system in \(\Dimension\) dimensions, can be represented by the \(\Dimension \times \Dimension\) matrix
(313)#\[\begin{split}\begin{aligned} \QFT &= \frac{1}{\sqrt{\Dimension}} \sum\limits_{j,k=0}^{\Dimension - 1} \omega_{\Dimension}^{jk} \ket{j}\bra{k} \\ &= \frac{1}{\sqrt{\Dimension}} \begin{bmatrix} 1 & 1 & 1 & 1 & \ldots & 1 \\ 1 & \omega & \omega^2 & \omega^3 & \ldots & \omega^{\Dimension - 1} \\ 1 & \omega^2 & \omega^4 & \omega^6 & \ldots & \omega^{2(\Dimension - 1)} \\ 1 & \omega^3 & \omega^6 & \omega^9 & \ldots & \omega^{3(\Dimension - 1)} \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \omega^{\Dimension - 1} & \omega^{2(\Dimension - 1)} & \omega^{3(\Dimension - 1)} & \ldots & \omega^{(\Dimension - 1)(\Dimension - 1)} \\ \end{bmatrix} \end{aligned}\end{split}\]where \(\omega \equiv \omega_{\Dimension} \equiv \e^{2\pi\eye/\Dimension}\).
In the case of \(N\) systems, it is easier to characterize the multipartite Fourier operator \(\QFT_N\) not by its matrix form but by the transformation it imposes, to which its action on the basis state \(\bigotimes\limits_{\ell=1}^{N} \ket{j_\ell} \equiv \ket{j_1, \ldots, j_N}\) (where \(j_\ell \in \Integers_{0}^{\Dimension - 1}\)) is
(314)#\[\ket{j_1, \ldots, j_N} \stackrel{\QFT_N}{\longrightarrow} \frac{1}{\sqrt{\Dimension^N}} \bigotimes\limits_{\ell=1}^{N} \sum\limits_{k_\ell=0}^{\Dimension - 1} \e^{2\pi\eye j k_\ell \Dimension^{-\ell}} \ket{k_\ell}\]where \(j \equiv \sum\limits_{\ell=1}^{N} j_\ell \Dimension^{N - \ell}\).
If
compositeisTrue, the composite form \(\QFT_N\) is applied to the subsystems specified bytargetsin:ascending order if
reverseisFalsedescending order if
reverseisTrue
If
compositeisFalse, a copy of the elementary form \(\QFT\) is placed on each of the subsystems corresponding to the indices in thetargetsproperty.- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.composite (
bool) – Whether the composite (multipartite) Fourier gate is to be used. IfFalse, copies of the elementary Fourier gate are placed on each index specified intargets. Defaults toTrue.reverse (
bool) – Whether to reverse the order in which the composite (multipartite) Fourier gate is applied. Has no effect whencompositeisFalse. Defaults toFalse.**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Examples
>>> F = Fourier() >>> F.output() Matrix([ [sqrt(2)/2, sqrt(2)/2], [sqrt(2)/2, -sqrt(2)/2]]) >>> F.diagram()
>>> F3 = Fourier(dim=3) >>> F3.output() Matrix([ [sqrt(3)/3, sqrt(3)/3, sqrt(3)/3], [sqrt(3)/3, sqrt(3)*exp(-2*I*pi/3)/3, sqrt(3)*exp(2*I*pi/3)/3], [sqrt(3)/3, sqrt(3)*exp(2*I*pi/3)/3, sqrt(3)*exp(-2*I*pi/3)/3]]) >>> F3.diagram()
>>> FF = Fourier(targets=[0, 1]) >>> FF.output() Matrix([ [1/2, 1/2, 1/2, 1/2], [1/2, -1/2, 1/2, -1/2], [1/2, I/2, -1/2, -I/2], [1/2, -I/2, -1/2, I/2]]) >>> FF.diagram()
- property composite: bool
Whether the composite (multipartite) Fourier gate is to be used.
- property reverse: bool
Whether to reverse the order in which the composite (multipartite) Fourier gate is applied. Has no effect when
self.compositeisFalse.
- class Measurement(
- *args,
- operators: list[mat | arr | QuantumObject],
- observable: bool | None = None,
- **kwargs,
Bases:
QuantumGateA subclass for creating measurement gates and storing their metadata.
This is built upon the
QuantumGateclass, and so inherits all of its attributes, properties, and methods.Instances of this class each describe a (non-linear) operation in which the input state (\(\op{\rho}\)) is quantum-mechanically measured (against the forms in specified in
operators) and subsequently mutated according to its predicted post-measurement form (i.e., the sum of all possible measurement outcomes). This yields the transformed states:When
observableisFalse(operatorsis a list of Kraus operators or projectors \(\Kraus_i\)):
(315)#\[\op{\rho}^\prime = \sum_i \Kraus_i \op{\rho} \Kraus_i^\dagger\]When
observableisTrue(operatorsis a list of observables \(\Observable_i\)):
(316)#\[\op{\rho}^\prime = \sum_i \trace[\Observable_i \op{\rho}] \Observable_i\]The items in the list
operatorscan also be vectors (e.g., \(\ket{\xi_i}\)), in which case each is converted into its corresponding matrix representation (e.g., \(\Kraus_i = \ket{\xi_i}\bra{\xi_i}\)) prior to any measurement(s).Note also that this method does not check for validity of supplied POVMs or the completeness of sets of observables, nor does it renormalize the post-measurement state.
- Parameters:
*args – Positional arguments, passed directly to the constructor
__init__of the superclassQuantumGate.operators (
list[mat | arr | QuantumObject]) – The operator(s) with which to perform the measurement. These would typically be a (complete) set of Kraus operators forming a POVM, a (complete) set of (orthogonal) projectors forming a PVM, or a set of observables constituting a complete basis for the relevant state space.observable (
bool) – Whether to treat the items inoperatorsas observables (as opposed to Kraus operators or projectors). Defaults toFalse.**kwargs – Arbitrary keyword arguments, passed directly to the constructor
__init__of the superclassQuantumGate.
Note
In quantum mechanics, measurement operations constitute (in general) non-linear and non-unitary transformations of (normalized) state vectors and density operators. As such, they cannot be represented by matrices, and so the
matrixproperty therefore does not return a valid representation of the measurement operation.Note
The
targetsargument must be specified as a list of numerical indices of the subsystem(s) to be measured. The number of indices must match the number of systems spanned by all given operators.Examples
>>> from qhronology.mechanics.matrices import ket >>> basis_vectors = [ket(i) for i in [0, 1]] >>> M_basis = Measurement(operators=basis_vectors, observable=False) >>> M_basis.diagram()
>>> pauli_matrices = [Pauli(index=i) for i in [1, 2, 3]] >>> M_pauli = Measurement( ... operators=pauli_matrices, ... observable=True, ... targets=[0], ... num_systems=2, ... ) >>> M_pauli.diagram()
>>> from qhronology.mechanics.matrices import ket >>> plus = (ket(0) + ket(1)) / sp.sqrt(2) >>> minus = (ket(0) - ket(1)) / sp.sqrt(2) >>> M_pm = Measurement( ... operators=[plus, minus], ... observable=False, ... targets=[1], ... num_systems=2, ... ) >>> M_pm.diagram()
- property operators: list[mat | arr | QuantumObject]
The operator(s) with which to perform the measurement.
- property observable: bool
Whether to treat the items in the
operatorsproperty as observables (as opposed to Kraus operators or projectors).
Combinations#
- class GateInterleave(
- *gates: QuantumGate,
- merge: bool | None = None,
- num_systems: int | None = None,
- numerical: bool | None = None,
- array: bool | None = None,
- label: str | None = None,
- notation: str | None = None,
Bases:
QuantumGateCompose two or more
QuantumGateinstances together by interleaving them.This is achieved by multiplying the gates’ matrix representations. For example, for gates described by the multipartite operators \(\op{A} \otimes \Identity\) and \(\Identity \otimes \op{B}\), their interleaved composition is
(317)#\[(\op{A} \otimes \Identity) \cdot (\Identity \otimes \op{B}) = \op{A} \otimes \op{B}.\]While this is a subclass of
QuantumGate, all of its inherited properties, except for those corresponding to arguments in its constructor, are read-only. This is because they are calculated from their corresponding properties in the individual instances contained within thegatesproperty.- Parameters:
*gates (
QuantumGate) –QuantumGateinstances to be interleaved.merge (
bool) – Whether to merge the gates together diagrammatically. Defaults toFalse.num_systems (
int) – The (total) number of systems which the gate spans. Must be a non-negative integer. Defaults tomax([gate.num_systems for gate in gates]).numerical (
bool) – Whether to cast the gate’s matrix elements as floating-point values (True) (if possible) or exact values (False). Defaults toFalse.array (
bool) – Whether to cast the gate’s matrix as a NumPy array (True) or SymPy matrix (False). Defaults toFalse.label (
str) – The unformatted string used to represent the gate in mathematical expressions. Defaults to"⊗".join([gate.label for gate in gates]).notation (
str) – The formatted string used to represent the gate in mathematical expressions. When notNone, overrides the value passed tolabel. Not intended to be set by the user in most cases. Defaults toNone.
Note
Care should be taken to ensure that gates passed to this class all have the same
num_systemsvalue and do not have overlappingtargets,controls, andanticontrolsproperties.Note
The resulting visualization (using the inherited
diagram()method or in circuit diagrams) may not be accurate in every case. However, the composed matrix should still be correct.Examples
>>> HI = Hadamard(targets=[0], num_systems=2) >>> IH = Hadamard(targets=[1], num_systems=2) >>> HH = GateInterleave(HI, IH, merge=True) >>> HH.output() Matrix([ [1/2, 1/2, 1/2, 1/2], [1/2, -1/2, 1/2, -1/2], [1/2, 1/2, -1/2, -1/2], [1/2, -1/2, -1/2, 1/2]]) >>> HH.diagram(sep=(1, 2))
>>> XII = Pauli(index=1, targets=[0], num_systems=3) >>> IYI = Pauli(index=2, targets=[1], num_systems=3) >>> IIZ = Pauli(index=3, targets=[2], num_systems=3) >>> XYZ = GateInterleave(XII, IYI, IIZ) >>> XYZ.output() Matrix([ [0, 0, 0, 0, 0, 0, -I, 0], [0, 0, 0, 0, 0, 0, 0, I], [0, 0, 0, 0, I, 0, 0, 0], [0, 0, 0, 0, 0, -I, 0, 0], [0, 0, -I, 0, 0, 0, 0, 0], [0, 0, 0, I, 0, 0, 0, 0], [I, 0, 0, 0, 0, 0, 0, 0], [0, -I, 0, 0, 0, 0, 0, 0]]) >>> XYZ.diagram(sep=(1, 2))
>>> CNII = Not(targets=[1], controls=[0], num_systems=4) >>> IINC = Not(targets=[2], controls=[3], num_systems=4) >>> CNNC = GateInterleave(CNII, IINC) >>> CNNC.diagram()
- property gates: list[QuantumGate]
Variable-length list of
QuantumGateinstances to be composited.
- property merge: bool
Whether to merge the gates together diagrammatically.
- class GateStack(
- *gates: QuantumGate,
- merge: bool | None = None,
- num_systems: int | None = None,
- numerical: bool | None = None,
- array: bool | None = None,
- label: str | None = None,
- notation: str | None = None,
Bases:
GateInterleaveCompose two or more
QuantumGateinstances together by “stacking” them vertically.This is achieved by computing the tensor product of the gates’ matrix representations. For example, for gates described by the multipartite operators \(\op{A} \otimes \Identity\) and \(\Identity \otimes \op{B}\), their stacked composition is
(318)#\[(\op{A} \otimes \Identity) \otimes (\Identity \otimes \op{B}) = \op{A} \otimes \Identity \otimes \Identity \otimes \op{B}.\]This class is derived from the
QuantumGateclass, and so should be used in much the same way.- Parameters:
*gates (
QuantumGate) –QuantumGateinstances to be stacked.merge (
bool) – Whether to merge the gates together diagrammatically. Defaults toFalse.num_systems (
int) – The (total) number of systems which the gate spans. Must be a non-negative integer. Defaults tosum([gate.num_systems for gate in gates]).numerical (
bool) – Whether to cast the gate’s matrix elements as floating-point values (True) (if possible) or exact values (False). Defaults toFalse.array (
bool) – Whether to cast the gate’s matrix as a NumPy array (True) or SymPy matrix (False). Defaults toFalse.label (
str) – The unformatted string used to represent the gate in mathematical expressions. Defaults to"⊗".join([gate.label for gate in gates]).notation (
str) – The formatted string used to represent the gate in mathematical expressions. When notNone, overrides the value passed tolabel. Not intended to be set by the user in most cases. Defaults toNone.
Examples
>>> X = Pauli(index=1) >>> Y = Pauli(index=2) >>> Z = Pauli(index=3) >>> XYZ = GateStack(X, Y, Z) >>> XYZ.output() Matrix([ [0, 0, 0, 0, 0, 0, -I, 0], [0, 0, 0, 0, 0, 0, 0, I], [0, 0, 0, 0, I, 0, 0, 0], [0, 0, 0, 0, 0, -I, 0, 0], [0, 0, -I, 0, 0, 0, 0, 0], [0, 0, 0, I, 0, 0, 0, 0], [I, 0, 0, 0, 0, 0, 0, 0], [0, -I, 0, 0, 0, 0, 0, 0]]) >>> XYZ.diagram(sep=(1, 2))
>>> gates = [ ... Not( ... targets=[(i + 1) % 2], ... controls=[i % 2], ... ) ... for i in range(0, 4) ... ] >>> CNOTs = GateStack(*gates) >>> CNOTS.diagram()
>>> labels = ("x", "y", "z") >>> gates = [ ... Rotation( ... axis=i, ... angle="θ", ... label=f"R_{labels[i - 1]}", ... ) ... for i in range(1, 4) ... ] >>> Rotations = GateStack(*gates) >>> Rotations.diagram(sep=(1, 1))