Source code for pennylane.pauli.dla.structure_constants
# Copyright 2024 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A function to compute the adjoint representation of a Lie algebra"""
from itertools import combinations
from typing import Union
import numpy as np
from pennylane.operation import Operator
from pennylane.typing import TensorLike
from ..pauli_arithmetic import PauliSentence, PauliWord
def _all_commutators(ops):
commutators = {}
for (j, op1), (k, op2) in combinations(enumerate(ops), r=2):
res = op1.commutator(op2)
if res != PauliSentence({}):
commutators[(j, k)] = res
return commutators
[docs]def structure_constants(
g: list[Union[Operator, PauliWord, PauliSentence]], pauli: bool = False
) -> TensorLike:
r"""
Compute the structure constants that make up the adjoint representation of a Lie algebra.
Given a DLA :math:`\{iG_1, iG_2, .. iG_d \}` of dimension :math:`d`,
the structure constants yield the decomposition of all commutators in terms of DLA elements,
.. math:: [i G_\alpha, i G_\beta] = \sum_{\gamma = 0}^{d-1} f^\gamma_{\alpha, \beta} iG_\gamma.
The adjoint representation :math:`\left(\text{ad}(iG_\gamma)\right)_{\alpha, \beta} = f^\gamma_{\alpha, \beta}` is given by those structure constants,
which can be computed via
.. math:: f^\gamma_{\alpha, \beta} = \frac{\text{tr}\left(i G_\gamma \cdot \left[i G_\alpha, i G_\beta \right] \right)}{\text{tr}\left( iG_\gamma iG_\gamma \right)}.
Note that this is just the projection of the commutator on the DLA element :math:`iG_\gamma` via the trace inner product.
The inputs are assumed to be orthogonal. However, we neither assume nor enforce normalization of the DLA elements
:math:`G_\alpha`, hence the normalization
factor :math:`\text{tr}\left( iG_\gamma iG_\gamma \right)` in the projection.
Args:
g (List[Union[Operator, PauliWord, PauliSentence]]): The (dynamical) Lie algebra for which we want to compute
its adjoint representation. DLAs can be generated by a set of generators via :func:`~lie_closure`.
pauli (bool): Indicates whether it is assumed that :class:`~.PauliSentence` or :class:`~.PauliWord` instances are input.
This can help with performance to avoid unnecessary conversions to :class:`~pennylane.operation.Operator`
and vice versa. Default is ``False``.
Returns:
TensorLike: The adjoint representation of shape ``(d, d, d)``, corresponding to indices ``(gamma, alpha, beta)``.
.. seealso:: :func:`~lie_closure`, :func:`~center`, :class:`~pennylane.pauli.PauliVSpace`, `Demo: Introduction to Dynamical Lie Algebras for quantum practitioners <https://pennylane.ai/qml/demos/tutorial_liealgebra/>`__
**Example**
Let us generate the DLA of the transverse field Ising model using :func:`~lie_closure`.
>>> n = 2
>>> gens = [X(i) @ X(i+1) for i in range(n-1)]
>>> gens += [Z(i) for i in range(n)]
>>> dla = qml.lie_closure(gens)
>>> print(dla)
[X(0) @ X(1), Z(0), Z(1), -1.0 * (Y(0) @ X(1)), -1.0 * (X(0) @ Y(1)), -1.0 * (Y(0) @ Y(1))]
The dimension of the DLA is :math:`d = 6`. Hence, the structure constants have shape ``(6, 6, 6)``.
>>> adjoint_rep = qml.structure_constants(dla)
>>> adjoint_rep.shape
(6, 6, 6)
The structure constants tell us the commutation relation between operators in the DLA via
.. math:: [i G_\alpha, i G_\beta] = \sum_{\gamma = 0}^{d-1} f^\gamma_{\alpha, \beta} iG_\gamma.
Let us confirm those with an example. Take :math:`[iG_1, iG_3] = [iZ_0, -iY_0 X_1] = -i 2 X_0 X_1 = -i 2 G_0`, so
we should have :math:`f^0_{1, 3} = -2`, which is indeed the case.
>>> adjoint_rep[0, 1, 3]
-2.0
We can also look at the overall adjoint action of the first element :math:`G_0 = X_{0} \otimes X_{1}` of the DLA on other elements.
In particular, at :math:`\left(\text{ad}(iG_0)\right)_{\alpha, \beta} = f^0_{\alpha, \beta}`, which corresponds to the following matrix.
>>> adjoint_rep[0]
array([[ 0., 0., 0., 0., 0., 0.],
[-0., 0., 0., -2., 0., 0.],
[-0., 0., 0., 0., -2., 0.],
[-0., 2., -0., 0., 0., 0.],
[-0., -0., 2., 0., 0., 0.],
[ 0., -0., -0., -0., -0., 0.]])
Note that we neither enforce nor assume normalization by default.
"""
if any((op.pauli_rep is None) for op in g):
raise ValueError(
f"Cannot compute adjoint representation of non-pauli operators. Received {g}."
)
if not pauli:
g = [op.pauli_rep for op in g]
commutators = _all_commutators(g)
rep = np.zeros((len(g), len(g), len(g)), dtype=float)
for i, op in enumerate(g):
for (j, k), res in commutators.items():
value = (1j * (op @ res).trace()).real
value = value / (op @ op).trace() # v = ∑ (v · e_j / ||e_j||^2) * e_j
rep[i, j, k] = value
rep[i, k, j] = -value
return rep
_modules/pennylane/pauli/dla/structure_constants
Download Python script
Download Notebook
View on GitHub