Skip to content

Commit

Permalink
modified __add__ for Observable and Hamiltonian to support addi…
Browse files Browse the repository at this point in the history
…ng `0` (#2603)

* modified __add__ for Observable and Hamiltonian to support adding 0, and wrote unit tests

* Update changelog-dev.md

* Update doc/releases/changelog-dev.md

Co-authored-by: antalszava <antalszava@gmail.com>

* modified __iadd__ for Hamiltonian and added more observables for tests

* modified test_hamiltonian_add_zero and test_add_zero

* format

Co-authored-by: antalszava <antalszava@gmail.com>
  • Loading branch information
Qi-Hu and antalszava committed Jun 3, 2022
1 parent 2b07d22 commit 43fc4f6
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 2 deletions.
11 changes: 9 additions & 2 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

<h3>New features since last release</h3>

* Support adding `Observable` objects to the integer `0`.
[(#2603)](https://github.com/PennyLaneAI/pennylane/pull/2603)

This allows us to directly sum a list of observables as follows:
```
H = sum([qml.PauliX(i) for i in range(10)])
```

* Parameter broadcasting within operations and tapes was introduced.
[(#2575)](https://github.com/PennyLaneAI/pennylane/pull/2575)
[(#2590)](https://github.com/PennyLaneAI/pennylane/pull/2590)
Expand Down Expand Up @@ -363,6 +371,5 @@
This release contains contributions from (in alphabetical order):

Amintor Dusko, Chae-Yeun Park, Christian Gogolin, Christina Lee, David Wierichs, Edward Jiang, Guillermo Alonso-Linaje,
Jay Soni, Juan Miguel Arrazola, Maria Schuld, Mikhail Andrenkov, Romain Moyard, Samuel Banning, Soran Jahangiri,
Jay Soni, Juan Miguel Arrazola, Maria Schuld, Mikhail Andrenkov, Romain Moyard, Qi Hu, Samuel Banning, Soran Jahangiri,
Utkarsh Azad, WingCode

5 changes: 5 additions & 0 deletions pennylane/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
import copy
import itertools
import functools
import numbers
import warnings
from enum import IntEnum
from scipy.sparse import kron, eye, coo_matrix
Expand Down Expand Up @@ -1659,12 +1660,16 @@ def compare(self, other):

def __add__(self, other):
r"""The addition operation between Observables/Tensors/qml.Hamiltonian objects."""
if isinstance(other, numbers.Number) and other == 0:
return self
if isinstance(other, qml.Hamiltonian):
return other + self
if isinstance(other, (Observable, Tensor)):
return qml.Hamiltonian([1, 1], [self, other], simplify=True)
raise ValueError(f"Cannot add Observable and {type(other)}")

__radd__ = __add__

def __mul__(self, a):
r"""The scalar multiplication operation between a scalar and an Observable/Tensor."""
if isinstance(a, (int, float)):
Expand Down
9 changes: 9 additions & 0 deletions pennylane/ops/qubit/hamiltonian.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""
# pylint: disable=too-many-arguments,too-many-instance-attributes
import itertools
import numbers
from copy import copy
from collections.abc import Iterable

Expand Down Expand Up @@ -571,6 +572,9 @@ def __add__(self, H):
ops = self.ops.copy()
self_coeffs = copy(self.coeffs)

if isinstance(H, numbers.Number) and H == 0:
return self

if isinstance(H, Hamiltonian):
coeffs = qml.math.concatenate([self_coeffs, copy(H.coeffs)], axis=0)
ops.extend(H.ops.copy())
Expand All @@ -585,6 +589,8 @@ def __add__(self, H):

raise ValueError(f"Cannot add Hamiltonian and {type(H)}")

__radd__ = __add__

def __mul__(self, a):
r"""The scalar multiplication operation between a scalar and a Hamiltonian."""
if isinstance(a, (int, float)):
Expand All @@ -604,6 +610,9 @@ def __sub__(self, H):

def __iadd__(self, H):
r"""The inplace addition operation between a Hamiltonian and a Hamiltonian/Tensor/Observable."""
if isinstance(H, numbers.Number) and H == 0:
return self

if isinstance(H, Hamiltonian):
self._coeffs = qml.math.concatenate([self._coeffs, H.coeffs], axis=0)
self._ops.extend(H.ops.copy())
Expand Down
48 changes: 48 additions & 0 deletions tests/ops/qubit/test_hamiltonian.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,34 @@
),
]

add_zero_hamiltonians = [
qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]),
qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]),
qml.Hamiltonian(
[1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]
),
]

iadd_zero_hamiltonians = [
# identical hamiltonians
(
qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]),
qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]),
),
(
qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]),
qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]),
),
(
qml.Hamiltonian(
[1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]
),
qml.Hamiltonian(
[1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]
),
),
]

sub_hamiltonians = [
(
qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]),
Expand Down Expand Up @@ -723,6 +751,16 @@ def test_hamiltonian_add(self, H1, H2, H):
"""Tests that Hamiltonians are added correctly"""
assert H.compare(H1 + H2)

@pytest.mark.parametrize("H", add_zero_hamiltonians)
def test_hamiltonian_add_zero(self, H):
"""Tests that Hamiltonians can be added to zero"""
assert H.compare(H + 0)
assert H.compare(0 + H)
assert H.compare(H + 0.0)
assert H.compare(0.0 + H)
assert H.compare(H + 0e1)
assert H.compare(0e1 + H)

@pytest.mark.parametrize(("coeff", "H", "res"), mul_hamiltonians)
def test_hamiltonian_mul(self, coeff, H, res):
"""Tests that scalars and Hamiltonians are multiplied correctly"""
Expand Down Expand Up @@ -764,6 +802,16 @@ def test_hamiltonian_iadd(self, H1, H2, H):
H1 += H2
assert H.compare(H1)

@pytest.mark.parametrize(("H1", "H2"), iadd_zero_hamiltonians)
def test_hamiltonian_iadd_zero(self, H1, H2):
"""Tests in-place addition between Hamiltonians and zero"""
H1 += 0
assert H1.compare(H2)
H1 += 0.0
assert H1.compare(H2)
H1 += 0e1
assert H1.compare(H2)

@pytest.mark.parametrize(("coeff", "H", "res"), mul_hamiltonians)
def test_hamiltonian_imul(self, coeff, H, res):
"""Tests that scalars and Hamiltonians are multiplied inline correctly"""
Expand Down
30 changes: 30 additions & 0 deletions tests/test_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import warnings

import pytest
from scipy.sparse import csr_matrix

import numpy as np
from pennylane import numpy as pnp
from numpy.linalg import multi_dot
Expand All @@ -27,6 +29,7 @@
from pennylane.operation import Tensor, operation_derivative, Operator, Operation

from gate_data import I, X, CNOT, Toffoli, SWAP, II
from pennylane.ops import cv
from pennylane.wires import Wires


Expand Down Expand Up @@ -1478,6 +1481,23 @@ def test_sparse_matrix_error(self):
),
]

add_zero_obs = [
qml.PauliX(0),
qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2),
qml.PauliX(0) @ qml.Hadamard(2),
# qml.Projector(np.array([1, 1]), wires=[0, 1]),
# qml.SparseHamiltonian(csr_matrix(np.array([[1, 0], [-1.5, 0]])), 1),
# CVObservables
qml.Identity(1),
cv.NumberOperator(wires=[1]),
cv.TensorN(wires=[1]),
cv.X(wires=[1]),
cv.P(wires=[1]),
# cv.QuadOperator(1.234, wires=0),
# cv.FockStateProjector([1,2,3], wires=[0, 1, 2]),
cv.PolyXP(np.array([1.0, 2.0, 3.0]), wires=[0]),
]

mul_obs = [
(qml.PauliZ(0), 3, qml.Hamiltonian([3], [qml.PauliZ(0)])),
(qml.PauliZ(0) @ qml.Identity(1), 3, qml.Hamiltonian([3], [qml.PauliZ(0)])),
Expand Down Expand Up @@ -1580,6 +1600,16 @@ def test_addition(self, obs1, obs2, obs):
"""Tests addition between Tensors and Observables"""
assert obs.compare(obs1 + obs2)

@pytest.mark.parametrize("obs", add_zero_obs)
def test_add_zero(self, obs):
"""Tests adding Tensors and Observables to zero"""
assert obs.compare(obs + 0)
assert obs.compare(0 + obs)
assert obs.compare(obs + 0.0)
assert obs.compare(0.0 + obs)
assert obs.compare(obs + 0e1)
assert obs.compare(0e1 + obs)

@pytest.mark.parametrize(("coeff", "obs", "res_obs"), mul_obs)
def test_scalar_multiplication(self, coeff, obs, res_obs):
"""Tests scalar multiplication of Tensors and Observables"""
Expand Down

0 comments on commit 43fc4f6

Please sign in to comment.