Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix qml.center with linear combinations #6049

Merged
merged 20 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@

<h3>Bug fixes 🐛</h3>

* Fixed a bug in `qml.pauli.dla.center` that omitted elements from the center if they were
dwierichs marked this conversation as resolved.
Show resolved Hide resolved
linear combinations of input elements.
[(#6049)](https://github.com/PennyLaneAI/pennylane/pull/6049)

* Fixed a bug in `qml.SPSAOptimizer` that ignored keyword arguments in the objective function.
[(#6027)](https://github.com/PennyLaneAI/pennylane/pull/6027)

Expand Down Expand Up @@ -242,7 +246,7 @@

This release contains contributions from (in alphabetical order):
Guillermo Alonso,
Utkarsh Azad
Utkarsh Azad,
Astral Cai,
Yushao Chen,
Gabriel Bottrill,
Expand All @@ -261,4 +265,5 @@ Vincent Michaud-Rioux,
Anurav Modak,
Mudit Pandey,
Erik Schultheis,
nate stemen.
nate stemen,
David Wierichs,
59 changes: 37 additions & 22 deletions pennylane/pauli/dla/center.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""A function to compute the center of a Lie algebra"""
from itertools import combinations
from typing import Union

import numpy as np
from scipy.linalg import null_space

from pennylane.operation import Operator
from pennylane.pauli import PauliSentence, PauliWord
from pennylane.pauli.dla import structure_constants


def center(
Expand All @@ -33,9 +34,11 @@
.. math:: \mathfrak{\xi}(\mathfrak{g}) := \{h \in \mathfrak{g} | [h, h_i]=0 \ \forall h_i \in \mathfrak{g} \}

Args:
g (List[Union[Operator, PauliSentence, PauliWord]]): List of operators for which to find the center.
pauli (bool): Indicates whether it is assumed that :class:`~.PauliSentence` or :class:`~.PauliWord` instances are input and returned.
This can help with performance to avoid unnecessary conversions to :class:`~pennylane.operation.Operator`
g (List[Union[Operator, PauliSentence, PauliWord]]): List of operators that spans
the algebra for which to find the center.
pauli (bool): Indicates whether it is assumed that :class:`~.PauliSentence` or
:class:`~.PauliWord` instances are input and returned. This can help with performance
to avoid unnecessary conversions to :class:`~pennylane.operation.Operator`
and vice versa. Default is ``False``.

Returns:
Expand All @@ -54,25 +57,37 @@
In this case, just ``X(0)``.

>>> qml.center(g)
[X(0)]
[-0.9999999999999999 * X(0)]

"""
if len(g) < 2:
# A length-zero list has zero center, a length-one list has full center
return g

adjoint_repr = structure_constants(g, pauli)
# Compute kernel for adjoint rep of each DLA element
kernels = [null_space(ad_x) for ad_x in adjoint_repr]
# If any kernels vanishes, their overlap vanishes as well
if any(k.shape[1] == 0 for k in kernels):
return []

Check warning on line 72 in pennylane/pauli/dla/center.py

View check run for this annotation

Codecov / codecov/patch

pennylane/pauli/dla/center.py#L72

Added line #L72 was not covered by tests
dwierichs marked this conversation as resolved.
Show resolved Hide resolved
# Compute the complements of the kernels
supports = [null_space(k.T) for k in kernels]
# Combine all complements
combined_support = np.hstack(supports)
if combined_support.shape[1] == 0:
return g
# Compute the complement of the combined complements: It is the intersection of the kernels
center_coefficients = null_space(combined_support.T)

# Construct operators from numerical output and convert to desired format
res = [sum(c * x for c, x in zip(c_coeffs, g)) for c_coeffs in center_coefficients.T]
dwierichs marked this conversation as resolved.
Show resolved Hide resolved

have_paulis = isinstance(g[0], (PauliWord, PauliSentence))
dwierichs marked this conversation as resolved.
Show resolved Hide resolved
if pauli or have_paulis:
_ = [el.simplify() for el in res]
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
if not pauli:
res = [el.operation() for el in res]
else:
res = [el.simplify() for el in res]

if not pauli:
g = [o.pauli_rep for o in g]

d = len(g)
commutators = np.zeros((d, d), dtype=int)
for (j, op1), (k, op2) in combinations(enumerate(g), r=2):
res = op1.commutator(op2)
res.simplify()
if res != PauliSentence({}):
commutators[j, k] = 1 # dummy value to indicate operators dont commute
commutators[k, j] = 1

mask = np.all(commutators == 0, axis=0)
res = list(np.array(g)[mask])

if not pauli:
res = [op.operation() for op in res]
return res
20 changes: 16 additions & 4 deletions tests/pauli/dla/test_center.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
"""Tests for pennylane/dla/center.py functionality"""

import numpy as np
import pytest

import pennylane as qml
Expand Down Expand Up @@ -60,19 +61,30 @@ def test_center_pauli_word_pauli_True(pauli):
qml.pauli.PauliWord({0: "X"}),
qml.pauli.PauliWord({0: "X", 1: "X"}),
qml.pauli.PauliWord({1: "Y"}),
qml.pauli.PauliWord({0: "X", 1: "Z"}),
dwierichs marked this conversation as resolved.
Show resolved Hide resolved
]
x0_pw = qml.pauli.PauliWord({0: "X"})
center = qml.center(ops, pauli=pauli)
assert isinstance(center, list)
assert len(center) == 1
if pauli:
assert qml.center(ops, pauli=pauli) == [qml.pauli.PauliWord({0: "X"})]
assert isinstance(center[0], qml.pauli.PauliSentence) and len(center[0]) == 1
assert x0_pw in center[0]
assert np.isclose(center[0][x0_pw], -1)
else:
assert qml.center(ops, pauli=pauli) == [qml.X(0)]
assert isinstance(center[0], qml.ops.op_math.SProd)
assert qml.equal(center[0], -1 * qml.X(0))
dwierichs marked this conversation as resolved.
Show resolved Hide resolved


c = 1 / np.sqrt(2)

GENERATOR_CENTERS = (
([qml.X(0), qml.X(0) @ qml.X(1), qml.Y(1)], [qml.X(0)]),
([qml.X(0) @ qml.X(1), qml.Y(1), qml.X(0)], [qml.X(0)]),
([qml.X(0), qml.X(0) @ qml.X(1), qml.Y(1)], [-1 * qml.X(0)]),
([qml.X(0) @ qml.X(1), qml.Y(1), qml.X(0)], [-1 * qml.X(0)]),
([qml.X(0) @ qml.X(1), qml.Y(1), qml.X(1)], []),
([qml.X(0) @ qml.X(1), qml.Y(1), qml.Z(0)], []),
([p(0) @ p(1) for p in [qml.X, qml.Y, qml.Z]], [p(0) @ p(1) for p in [qml.X, qml.Y, qml.Z]]),
([qml.X(0), qml.X(1), sum(p(0) @ p(1) for p in [qml.Y, qml.Z])], [c * qml.X(0) + c * qml.X(1)]),
dwierichs marked this conversation as resolved.
Show resolved Hide resolved
)


Expand Down
Loading