lie_closure(generators, max_iterations=10000, verbose=False, pauli=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 \(\mathcal{G} = \{G_1, .. , G_N\}\). For such generators, one computes all nested commutators \([G_i, [G_j, .., [G_k, G_\ell]]]\) until no new operators are generated from commutation. All these operators together form the DLA, see e.g. section IIB of arXiv:2308.01432.

  • generators (Iterable[Union[PauliWord, PauliSentence, Operator]]) – 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 or PauliWord instances are input and returned. This can help with performance to avoid unnecessary conversions to Operator and vice versa. Default is False.

  • tol (float) – Numerical tolerance for the linear independence check used in PauliVSpace.


a basis of either PauliSentence or Operator instances that is closed under commutators (Lie closure).

Return type

Union[list[PauliSentence], list[Operator]]


Let us walk through a simple example of computing the Lie closure of the generators of the transverse field Ising model on two qubits.

>>> ops = [X(0) @ X(1), Z(0), Z(1)]

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),
 -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.

Note that by default, lie_closure returns PennyLane operators. Internally we use the more efficient representation in terms of PauliSentence by making use of the op.pauli_rep attribute of operators composed of Pauli operators. If desired, this format can be returned by using the keyword pauli=True. In that case, the input is also assumed to be a PauliSentence 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])