Skip to content

Commit

Permalink
Merge pull request #1517 from qiboteam/symbolic
Browse files Browse the repository at this point in the history
Fix bug in `SymbolicHamiltonian.__mul__`
  • Loading branch information
renatomello authored Nov 11, 2024
2 parents fb5311e + 64f6e2e commit a8e7edd
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 86 deletions.
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
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
)

0 comments on commit a8e7edd

Please sign in to comment.