Skip to content

Commit

Permalink
[Bug fix] bind_new_parameters for LinearCombination (#5431)
Browse files Browse the repository at this point in the history
Fix problem from discussion in
#5216 (comment)

[sc-59515]

---------

Co-authored-by: lillian542 <[email protected]>
Co-authored-by: Pietropaolo Frisoni <[email protected]>
Co-authored-by: Mudit Pandey <[email protected]>
Co-authored-by: Christina Lee <[email protected]>
Co-authored-by: albi3ro <[email protected]>
Co-authored-by: Utkarsh <[email protected]>
Co-authored-by: Astral Cai <[email protected]>
Co-authored-by: lillian542 <[email protected]>
Co-authored-by: Alex Preciado <[email protected]>
Co-authored-by: Thomas R. Bromley <[email protected]>
Co-authored-by: Vincent Michaud-Rioux <[email protected]>
Co-authored-by: Josh Izaac <[email protected]>
Co-authored-by: Nathan Killoran <[email protected]>
Co-authored-by: Matthew Silverman <[email protected]>
Co-authored-by: Mikhail Andrenkov <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
17 people authored Apr 5, 2024
1 parent d17b2ca commit 93e2e12
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 16 deletions.
33 changes: 27 additions & 6 deletions pennylane/ops/functions/bind_new_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@ def bind_new_parameters_identity(op: Identity, params: Sequence[TensorLike]):
return qml.Identity(*params, wires=op.wires)


@bind_new_parameters.register
def bind_new_parameters_linear_combination(
op: qml.ops.LinearCombination, params: Sequence[TensorLike]
):
new_coeffs, new_ops = [], []
i = 0
for o in op.ops:
new_coeffs.append(params[i])
i += 1
if o.data:
sub_data = params[i : i + len(o.data)]
new_ops.append(bind_new_parameters(o, sub_data))
i += len(sub_data)
else:
new_ops.append(o)

new_H = qml.ops.LinearCombination(new_coeffs, new_ops)

if op.grouping_indices is not None:
new_H.grouping_indices = op.grouping_indices

return new_H


@bind_new_parameters.register
def bind_new_parameters_composite_op(op: CompositeOp, params: Sequence[TensorLike]):
new_operands = []
Expand Down Expand Up @@ -198,12 +222,9 @@ def bind_new_parameters_pow(op: Pow, params: Sequence[TensorLike]):
return Pow(bind_new_parameters(op.base, params), op.scalar)


@bind_new_parameters.register(qml.ops.Hamiltonian)
@bind_new_parameters.register(qml.ops.LinearCombination)
def bind_new_parameters_hamiltonian(
op: Union[qml.ops.Hamiltonian, qml.ops.LinearCombination], params: Sequence[TensorLike]
):
new_H = qml.Hamiltonian(params, op.ops)
@bind_new_parameters.register
def bind_new_parameters_hamiltonian(op: qml.ops.Hamiltonian, params: Sequence[TensorLike]):
new_H = qml.ops.Hamiltonian(params, op.ops)
if op.grouping_indices is not None:
new_H.grouping_indices = op.grouping_indices
return new_H
Expand Down
2 changes: 1 addition & 1 deletion pennylane/ops/op_math/controlled_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""
This submodule contains controlled operators based on the ControlledOp class.
"""

# pylint: disable=no-value-for-parameter
import warnings
from typing import Iterable
from functools import lru_cache
Expand Down
116 changes: 107 additions & 9 deletions tests/ops/functions/test_bind_new_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import pytest
from gate_data import X, Y, Z, I, GELL_MANN

import numpy as np
import pennylane as qml

from pennylane.ops.functions import bind_new_parameters
Expand Down Expand Up @@ -184,28 +185,33 @@ def test_controlled_sequence():
assert qml.equal(new_op.base, qml.RX(0.5, wires=3))


@pytest.mark.parametrize(
"H, new_coeffs, expected_H",
[
with qml.operation.disable_new_opmath_cm():
TEST_BIND_LEGACY_HAMILTONIAN = [
(
qml.Hamiltonian(
qml.ops.Hamiltonian(
[1.1, 2.1, 3.1],
[Tensor(qml.PauliZ(0), qml.PauliX(1)), qml.Hadamard(1), qml.PauliY(0)],
),
[1.2, 2.2, 3.2],
qml.Hamiltonian(
qml.ops.Hamiltonian(
[1.2, 2.2, 3.2],
[Tensor(qml.PauliZ(0), qml.PauliX(1)), qml.Hadamard(1), qml.PauliY(0)],
),
),
(
qml.Hamiltonian([1.6, -1], [qml.Hermitian(X, wires=1), qml.PauliX(1)]),
qml.ops.Hamiltonian([1.6, -1], [qml.Hermitian(X, wires=1), qml.PauliX(1)]),
[-1, 1.6],
qml.Hamiltonian([-1, 1.6], [qml.Hermitian(X, wires=1), qml.PauliX(1)]),
qml.ops.Hamiltonian([-1, 1.6], [qml.Hermitian(X, wires=1), qml.PauliX(1)]),
),
],
]


@pytest.mark.usefixtures("use_legacy_opmath")
@pytest.mark.parametrize(
"H, new_coeffs, expected_H",
TEST_BIND_LEGACY_HAMILTONIAN,
)
def test_hamiltonian(H, new_coeffs, expected_H):
def test_hamiltonian_legacy_opmath(H, new_coeffs, expected_H):
"""Test that `bind_new_parameters` with `Hamiltonian` returns a new
operator with the new parameters without mutating the original
operator."""
Expand All @@ -215,6 +221,98 @@ def test_hamiltonian(H, new_coeffs, expected_H):
assert new_H is not H


TEST_BIND_LINEARCOMBINATION = [
( # LinearCombination with only data being the coeffs
qml.ops.LinearCombination(
[1.1, 2.1, 3.1],
[qml.prod(qml.PauliZ(0), qml.X(1)), qml.Hadamard(1), qml.Y(0)],
),
[1.2, 2.2, 3.2],
qml.ops.LinearCombination(
[1.2, 2.2, 3.2],
[qml.prod(qml.PauliZ(0), qml.X(1)), qml.Hadamard(1), qml.Y(0)],
),
),
( # LinearCombination with Hermitian that carries extra data
qml.ops.LinearCombination(
[1.6, -1], [qml.Hermitian(np.array([[0.0, 1.0], [1.0, 0.0]]), wires=1), qml.X(1)]
),
[-1, np.array([[1.0, 1.0], [1.0, 1.0]]), 1.6],
qml.ops.LinearCombination(
[-1, 1.6], [qml.Hermitian(np.array([[1.0, 1.0], [1.0, 1.0]]), wires=1), qml.X(1)]
),
),
( # LinearCombination with prod that contains Hermitian that carries extra data
qml.ops.LinearCombination(
[1.6, -1],
[
qml.prod(qml.X(0), qml.Hermitian(np.array([[0.0, 1.0], [1.0, 0.0]]), wires=1)),
qml.X(1),
],
),
[-1, np.array([[1.0, 1.0], [1.0, 1.0]]), 1.6],
qml.ops.LinearCombination(
[-1, 1.6],
[
qml.prod(qml.X(0), qml.Hermitian(np.array([[1.0, 1.0], [1.0, 1.0]]), wires=1)),
qml.X(1),
],
),
),
( # LinearCombination with prod that contains Hermitian that carries extra data
qml.ops.LinearCombination(
[1.6, -1],
[
qml.prod(qml.X(0), qml.Hermitian(np.array([[0.0, 1.0], [1.0, 0.0]]), wires=1)),
qml.X(1),
],
),
[-1, np.array([[1.0, 1.0], [1.0, 1.0]]), 1.6],
qml.ops.LinearCombination(
[-1, 1.6],
[
qml.prod(qml.X(0), qml.Hermitian(np.array([[1.0, 1.0], [1.0, 1.0]]), wires=1)),
qml.X(1),
],
),
),
( # LinearCombination with Projector that carries extra data and prod that contains Hermitian that carries extra data
qml.ops.LinearCombination(
[1.0, 1.6, -1],
[
qml.Projector(np.array([1.0, 0.0]), 0),
qml.prod(qml.X(0), qml.Hermitian(np.array([[0.0, 1.0], [1.0, 0.0]]), wires=1)),
qml.X(1),
],
),
[-1.0, np.array([0.0, 1.0]), -1, np.array([[1.0, 1.0], [1.0, 1.0]]), 1.6],
qml.ops.LinearCombination(
[-1.0, -1, 1.6],
[
qml.Projector(np.array([0.0, 1.0]), 0),
qml.prod(qml.X(0), qml.Hermitian(np.array([[1.0, 1.0], [1.0, 1.0]]), wires=1)),
qml.X(1),
],
),
),
]


@pytest.mark.parametrize(
"H, new_coeffs, expected_H",
TEST_BIND_LINEARCOMBINATION,
)
def test_linear_combination(H, new_coeffs, expected_H):
"""Test that `bind_new_parameters` with `LinearCombination` returns a new
operator with the new parameters without mutating the original
operator."""
new_H = bind_new_parameters(H, new_coeffs)

assert qml.equal(new_H, expected_H)
assert new_H is not H


@pytest.mark.usefixtures("use_legacy_and_new_opmath")
def test_hamiltonian_grouping_indices():
"""Test that bind_new_parameters with a Hamiltonian preserves the grouping indices."""
H = qml.Hamiltonian([1.0, 2.0], [qml.PauliX(0), qml.PauliX(1)])
Expand Down

0 comments on commit 93e2e12

Please sign in to comment.