qml.lie_closure¶
- lie_closure(generators, *, max_iterations=10000, verbose=False, pauli=False, matrix=False, tol=None)[source]¶
Compute the dynamical Lie algebra from a set of generators.
The Lie closure, pronounced “Lee” closure, is a way to compute the so-called dynamical Lie algebra (DLA) of a set of generators G={G1,..,GN}. For such generators, one computes all nested commutators [Gi,[Gj,..,[Gk,Gℓ]]] until no new operators are generated from commutation. All these operators together form the DLA, see e.g. section IIB of arXiv:2308.01432.
- Parameters
generators (Iterable[Union[PauliWord, PauliSentence, Operator, TensorLike]]) – generating set for which to compute the Lie closure.
max_iterations (int) – maximum depth of nested commutators to consider. Default is
10000
.verbose (bool) – whether to print out progress updates during Lie closure calculation. Default is
False
.pauli (bool) – Indicates whether it is assumed that
PauliSentence
orPauliWord
instances are input and returned. This can help with performance to avoid unnecessary conversions toOperator
and vice versa. Default isFalse
.matrix (bool) – Whether or not matrix representations should be used and returned in the Lie closure computation. This can help speed up the computation when using sums of Paulis with many terms. Default is
False
.tol (float) – Numerical tolerance for the linear independence check used in
PauliVSpace
.
- Returns
A basis of either
PauliSentence
,Operator
, ornp.ndarray
instances that is closed under commutators (Lie closure).- Return type
Union[list[
PauliSentence
], list[Operator
], np.ndarray]
See also
structure_constants()
,center()
,PauliVSpace
, Demo: Introduction to Dynamical Lie Algebras for quantum practitionersExample
>>> ops = [X(0) @ X(1), Z(0), Z(1)] >>> dla = qml.lie_closure(ops)
Let us walk through what happens in this simple example of computing the Lie closure of these generators (the transverse field Ising model on two qubits). A first round of commutators between all elements yields:
>>> qml.commutator(X(0) @ X(1), Z(0)) -2j * (Y(0) @ X(1)) >>> qml.commutator(X(0) @ X(1), Z(1)) -2j * (X(0) @ Y(1))
A next round of commutators between all elements further yields the new operator
Y(0) @ Y(1)
.>>> qml.commutator(X(0) @ Y(1), Z(0)) -2j * (Y(0) @ Y(1))
After that, no new operators emerge from taking nested commutators and we have the resulting DLA. This can be done in short via
lie_closure
as follows.>>> ops = [X(0) @ X(1), Z(0), Z(1)] >>> dla = qml.lie_closure(ops) >>> print(dla) [X(1) @ X(0), Z(0), Z(1), -1.0 * (Y(0) @ X(1)), -1.0 * (X(0) @ Y(1)), -1.0 * (Y(0) @ Y(1))]
Note that we normalize by removing the factors of 2i, though minus signs are left intact.
Usage Details
Note that by default,
lie_closure
returns PennyLane operators. Internally we use the more efficient representation in terms ofPauliSentence
by making use of theop.pauli_rep
attribute of operators composed of Pauli operators. If desired, this format can be returned by using the keywordpauli=True
. In that case, the input is also assumed to be aPauliSentence
instance.>>> ops = [ ... PauliSentence({PauliWord({0: "X", 1: "X"}): 1.}), ... PauliSentence({PauliWord({0: "Z"}): 1.}), ... PauliSentence({PauliWord({1: "Z"}): 1.}), ... ] >>> dla = qml.lie_closure(ops, pauli=True) >>> print(dla) [1.0 * X(0) @ X(1), 1.0 * Z(0), 1.0 * Z(1), -1.0 * Y(0) @ X(1), -1.0 * X(0) @ Y(1), -1.0 * Y(0) @ Y(1)] >>> type(dla[0]) pennylane.pauli.pauli_arithmetic.PauliSentence
In the case of sums of Pauli operators with many terms, it is often faster to use the matrix representation of the operators rather than the semi-analytic
PauliSentence
orOperator
representation. We can force this by using thematrix
keyword. The resultingdla
is anp.ndarray
of dimension(dim_g, 2**n, 2**n)
, wheredim_g
is the dimension of the DLA andn
the number of qubits.>>> dla = qml.lie_closure(ops, matrix=True) >>> dla.shape (6, 4, 4)
You can retrieve a semi-analytic representation again by using
pauli_decompose()
.>>> dla_ops = [qml.pauli_decompose(op) for op in dla]