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 bug in SymbolicHamiltonian.__mul__ #1517

Merged
merged 2 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions src/qibo/hamiltonians/hamiltonians.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,9 @@ def __mul__(self, o):
new_ham.constant = self.constant * o
if self._dense is not None:
new_ham.dense = o * self._dense

new_ham.nqubits = self.nqubits

return new_ham

def apply_gates(self, state, density_matrix=False):
Expand Down
151 changes: 65 additions & 86 deletions tests/test_hamiltonians_symbolic.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""Test methods of :class:`qibo.core.hamiltonians.SymbolicHamiltonian`."""
"""Test methods of :class:`qibo.core.SymbolicHamiltonian`."""

import numpy as np
import pytest
import sympy
from pytest import approx

from qibo import Circuit, gates, hamiltonians
from qibo import Circuit, gates
from qibo.hamiltonians import TFIM, XXZ, Hamiltonian, SymbolicHamiltonian
from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector
from qibo.symbols import I, Y, Z
from qibo.symbols import I, Symbol, X, Y, Z


def symbolic_tfim(nqubits, h=1.0):
Expand All @@ -22,37 +22,29 @@ def symbolic_tfim(nqubits, h=1.0):

def test_symbolic_hamiltonian_errors(backend):
# Wrong type of Symbol matrix
from qibo.symbols import Symbol

with pytest.raises(TypeError):
s = Symbol(0, "test")
# Wrong type of symbolic expression
with pytest.raises(TypeError):
ham = hamiltonians.SymbolicHamiltonian("test", backend=backend)
ham = SymbolicHamiltonian("test", backend=backend)
# Passing form with symbol that is not in ``symbol_map``
from qibo import matrices

Z, X = sympy.Symbol("Z"), sympy.Symbol("X")
symbol_map = {Z: (0, matrices.Z)}
with pytest.raises(ValueError):
ham = hamiltonians.SymbolicHamiltonian(
Z * X, symbol_map=symbol_map, backend=backend
)
ham = SymbolicHamiltonian(Z * X, symbol_map=symbol_map, backend=backend)
# Invalid operation in Hamiltonian expresion
ham = hamiltonians.SymbolicHamiltonian(
sympy.cos(Z), symbol_map=symbol_map, backend=backend
)
ham = SymbolicHamiltonian(sympy.cos(Z), symbol_map=symbol_map, backend=backend)
with pytest.raises(TypeError):
dense = ham.dense


@pytest.mark.parametrize("nqubits", [3, 4])
@pytest.mark.parametrize("calcterms", [False, True])
def test_symbolictfim_hamiltonian_to_dense(backend, nqubits, calcterms):
final_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1), backend=backend
)
target_ham = hamiltonians.TFIM(nqubits, h=1, backend=backend)
final_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1), backend=backend)
target_ham = TFIM(nqubits, h=1, backend=backend)
if calcterms:
_ = final_ham.terms
backend.assert_allclose(final_ham.matrix, target_ham.matrix, atol=1e-15)
Expand All @@ -61,14 +53,12 @@ def test_symbolictfim_hamiltonian_to_dense(backend, nqubits, calcterms):
@pytest.mark.parametrize("nqubits", [3, 4])
@pytest.mark.parametrize("calcterms", [False, True])
def test_symbolicxxz_hamiltonian_to_dense(backend, nqubits, calcterms):
from qibo.symbols import X, Y, Z

sham = sum(X(i) * X(i + 1) for i in range(nqubits - 1))
sham += sum(Y(i) * Y(i + 1) for i in range(nqubits - 1))
sham += 0.5 * sum(Z(i) * Z(i + 1) for i in range(nqubits - 1))
sham += X(0) * X(nqubits - 1) + Y(0) * Y(nqubits - 1) + 0.5 * Z(0) * Z(nqubits - 1)
final_ham = hamiltonians.SymbolicHamiltonian(sham, backend=backend)
target_ham = hamiltonians.XXZ(nqubits, backend=backend)
final_ham = SymbolicHamiltonian(sham, backend=backend)
target_ham = XXZ(nqubits, backend=backend)
if calcterms:
_ = final_ham.terms
backend.assert_allclose(final_ham.matrix, target_ham.matrix, atol=1e-15)
Expand All @@ -79,20 +69,16 @@ def test_symbolicxxz_hamiltonian_to_dense(backend, nqubits, calcterms):
@pytest.mark.parametrize("calcdense", [False, True])
def test_symbolic_hamiltonian_scalar_mul(backend, nqubits, calcterms, calcdense):
"""Test multiplication of Trotter Hamiltonian with scalar."""
local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
target_ham = 2 * hamiltonians.TFIM(nqubits, h=1.0, backend=backend)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
target_ham = 2 * TFIM(nqubits, h=1.0, backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
_ = local_ham.dense
local_dense = (2 * local_ham).dense
backend.assert_allclose(local_dense.matrix, target_ham.matrix)

local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
Expand All @@ -106,20 +92,16 @@ def test_symbolic_hamiltonian_scalar_mul(backend, nqubits, calcterms, calcdense)
@pytest.mark.parametrize("calcdense", [False, True])
def test_symbolic_hamiltonian_scalar_add(backend, nqubits, calcterms, calcdense):
"""Test addition of Trotter Hamiltonian with scalar."""
local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
target_ham = 2 + hamiltonians.TFIM(nqubits, h=1.0, backend=backend)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
target_ham = 2 + TFIM(nqubits, h=1.0, backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
_ = local_ham.dense
local_dense = (2 + local_ham).dense
backend.assert_allclose(local_dense.matrix, target_ham.matrix)

local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
Expand All @@ -133,21 +115,17 @@ def test_symbolic_hamiltonian_scalar_add(backend, nqubits, calcterms, calcdense)
@pytest.mark.parametrize("calcdense", [False, True])
def test_symbolic_hamiltonian_scalar_sub(backend, nqubits, calcterms, calcdense):
"""Test subtraction of Trotter Hamiltonian with scalar."""
local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
target_ham = 2 - hamiltonians.TFIM(nqubits, h=1.0, backend=backend)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
target_ham = 2 - TFIM(nqubits, h=1.0, backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
_ = local_ham.dense
local_dense = (2 - local_ham).dense
backend.assert_allclose(local_dense.matrix, target_ham.matrix)

target_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) - 2
local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
target_ham = TFIM(nqubits, h=1.0, backend=backend) - 2
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
Expand All @@ -163,57 +141,61 @@ def test_symbolic_hamiltonian_operator_add_and_sub(
backend, nqubits, calcterms, calcdense
):
"""Test addition and subtraction between Trotter Hamiltonians."""
local_ham1 = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
local_ham2 = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=0.5), backend=backend
)
local_ham1 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
local_ham2 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=0.5), backend=backend)
if calcterms:
_ = local_ham1.terms
_ = local_ham2.terms
if calcdense:
_ = local_ham1.dense
_ = local_ham2.dense
local_ham = local_ham1 + local_ham2
target_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + hamiltonians.TFIM(
target_ham = TFIM(nqubits, h=1.0, backend=backend) + TFIM(
nqubits, h=0.5, backend=backend
)
dense = local_ham.dense
backend.assert_allclose(dense.matrix, target_ham.matrix)

local_ham1 = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
local_ham2 = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=0.5), backend=backend
)
local_ham1 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
local_ham2 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=0.5), backend=backend)
if calcterms:
_ = local_ham1.terms
_ = local_ham2.terms
if calcdense:
_ = local_ham1.dense
_ = local_ham2.dense
local_ham = local_ham1 - local_ham2
target_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) - hamiltonians.TFIM(
target_ham = TFIM(nqubits, h=1.0, backend=backend) - TFIM(
nqubits, h=0.5, backend=backend
)
dense = local_ham.dense
backend.assert_allclose(dense.matrix, target_ham.matrix)

# Test multiplication and sum
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Worth also keeping in mind for tutorials even :)

target = XXZ(nqubits, backend=backend)
term_1 = SymbolicHamiltonian(
X(0) * X(1) + X(1) * X(2) + X(0) * X(2), backend=backend
)
term_2 = SymbolicHamiltonian(
Y(0) * Y(1) + Y(1) * Y(2) + Y(0) * Y(2), backend=backend
)
term_3 = SymbolicHamiltonian(
Z(0) * Z(1) + Z(1) * Z(2) + Z(0) * Z(2), backend=backend
)
hamiltonian = term_1 + term_2 + 0.5 * term_3
matrix = hamiltonian.dense.matrix

backend.assert_allclose(matrix, target.matrix)


@pytest.mark.parametrize("nqubits", [5])
@pytest.mark.parametrize("calcterms", [False, True])
@pytest.mark.parametrize("calcdense", [False, True])
def test_symbolic_hamiltonian_hamiltonianmatmul(backend, nqubits, calcterms, calcdense):
local_ham1 = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
local_ham2 = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=0.5), backend=backend
)
dense_ham1 = hamiltonians.TFIM(nqubits, h=1.0, backend=backend)
dense_ham2 = hamiltonians.TFIM(nqubits, h=0.5, backend=backend)
local_ham1 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
local_ham2 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=0.5), backend=backend)
dense_ham1 = TFIM(nqubits, h=1.0, backend=backend)
dense_ham2 = TFIM(nqubits, h=0.5, backend=backend)
if calcterms:
_ = local_ham1.terms
_ = local_ham2.terms
Expand All @@ -234,10 +216,8 @@ def test_symbolic_hamiltonian_matmul(backend, nqubits, density_matrix, calcterms
if density_matrix
else random_statevector(2**nqubits, backend=backend)
)
local_ham = hamiltonians.SymbolicHamiltonian(
symbolic_tfim(nqubits, h=1.0), backend=backend
)
dense_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
dense_ham = TFIM(nqubits, h=1.0, backend=backend)
if calcterms:
_ = local_ham.terms
local_matmul = local_ham @ state
Expand All @@ -251,15 +231,12 @@ def test_symbolic_hamiltonian_matmul(backend, nqubits, density_matrix, calcterms
def test_symbolic_hamiltonian_state_expectation(
backend, nqubits, normalize, calcterms, calcdense
):
local_ham = (
hamiltonians.SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend)
+ 2
)
local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + 2
if calcterms:
_ = local_ham.terms
if calcdense:
_ = local_ham.dense
dense_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + 2
dense_ham = TFIM(nqubits, h=1.0, backend=backend) + 2

state = random_statevector(2**nqubits, backend=backend)

Expand All @@ -280,17 +257,17 @@ def test_symbolic_hamiltonian_state_expectation_different_nqubits(
):
expr = symbolic_tfim(3, h=1.0)
if give_nqubits:
local_ham = hamiltonians.SymbolicHamiltonian(expr, nqubits=5, backend=backend)
local_ham = SymbolicHamiltonian(expr, nqubits=5, backend=backend)
else:
local_ham = hamiltonians.SymbolicHamiltonian(expr, backend=backend)
local_ham = SymbolicHamiltonian(expr, backend=backend)
if calcterms:
_ = local_ham.terms
if calcdense:
_ = local_ham.dense

dense_ham = hamiltonians.TFIM(3, h=1.0, backend=backend)
dense_ham = TFIM(3, h=1.0, backend=backend)
dense_matrix = np.kron(backend.to_numpy(dense_ham.matrix), np.eye(4))
dense_ham = hamiltonians.Hamiltonian(5, dense_matrix, backend=backend)
dense_ham = Hamiltonian(5, dense_matrix, backend=backend)

state = random_statevector(2**5, backend=backend)

Expand All @@ -314,8 +291,8 @@ def test_hamiltonian_expectation_from_samples(backend):
backend.set_seed(0)
obs0 = 2 * Z(0) * Z(1) + Z(0) * Z(2)
obs1 = 2 * Z(0) * Z(1) + Z(0) * Z(2) * I(3)
h0 = hamiltonians.SymbolicHamiltonian(obs0, backend=backend)
h1 = hamiltonians.SymbolicHamiltonian(obs1, backend=backend)
h0 = SymbolicHamiltonian(obs0, backend=backend)
h1 = SymbolicHamiltonian(obs1, backend=backend)
c = Circuit(4)
c.add(gates.RX(0, np.random.rand()))
c.add(gates.RX(1, np.random.rand()))
Expand All @@ -337,7 +314,7 @@ def test_symbolic_hamiltonian_abstract_symbol_ev(backend, density_matrix, calcte

matrix = np.random.random((2, 2))
form = X(0) * Symbol(1, matrix) + Symbol(0, matrix) * X(1)
local_ham = hamiltonians.SymbolicHamiltonian(form, backend=backend)
local_ham = SymbolicHamiltonian(form, backend=backend)
if calcterms:
_ = local_ham.terms

Expand All @@ -353,8 +330,8 @@ def test_symbolic_hamiltonian_abstract_symbol_ev(backend, density_matrix, calcte

def test_trotter_hamiltonian_operation_errors(backend):
"""Test errors in ``SymbolicHamiltonian`` addition and subtraction."""
h1 = hamiltonians.SymbolicHamiltonian(symbolic_tfim(3, h=1.0), backend=backend)
h2 = hamiltonians.SymbolicHamiltonian(symbolic_tfim(4, h=1.0), backend=backend)
h1 = SymbolicHamiltonian(symbolic_tfim(3, h=1.0), backend=backend)
h2 = SymbolicHamiltonian(symbolic_tfim(4, h=1.0), backend=backend)
with pytest.raises(RuntimeError):
h = h1 + h2
with pytest.raises(RuntimeError):
Expand All @@ -373,7 +350,7 @@ def test_trotter_hamiltonian_operation_errors(backend):
h = h1 @ "test"
with pytest.raises(NotImplementedError):
h = h1 @ np.ones((2, 2, 2, 2))
h2 = hamiltonians.XXZ(3, dense=False, backend=backend)
h2 = XXZ(3, dense=False, backend=backend)
with pytest.raises(NotImplementedError):
h = h1 @ h2

Expand All @@ -382,7 +359,9 @@ def test_symbolic_hamiltonian_with_constant(backend):
c = Circuit(1)
c.add(gates.H(0))
c.add(gates.M(0))
h = hamiltonians.SymbolicHamiltonian(1e6 - Z(0), backend=backend)
h = SymbolicHamiltonian(1e6 - Z(0), backend=backend)

result = c.execute(nshots=10000)
assert float(result.expectation_from_samples(h)) == approx(1e6, rel=1e-5, abs=0.0)
assert float(result.expectation_from_samples(h)) == pytest.approx(
1e6, rel=1e-5, abs=0.0
)