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

Symbolic Hamiltonian with constants #1389

Merged
merged 4 commits into from
Jul 16, 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
35 changes: 20 additions & 15 deletions src/qibo/hamiltonians/hamiltonians.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ def __init__(self, form=None, nqubits=None, symbol_map={}, backend=None):
self.nqubits = nqubits

@property
def dense(self):
"""Creates the equivalent :class:`qibo.hamiltonians.MatrixHamiltonian`."""
def dense(self) -> "MatrixHamiltonian":
"""Creates the equivalent Hamiltonian matrix."""
if self._dense is None:
log.warning(
"Calculating the dense form of a symbolic Hamiltonian. "
Expand Down Expand Up @@ -380,8 +380,9 @@ def form(self, form):

@property
def terms(self):
"""List of :class:`qibo.core.terms.HamiltonianTerm` objects
of which the Hamiltonian is a sum of.
"""List of terms of which the Hamiltonian is a sum of.
Terms will be objects of type :class:`qibo.core.terms.HamiltonianTerm`.
"""
if self._terms is None:
# Calculate terms based on ``self.form``
Expand All @@ -407,7 +408,10 @@ def terms(self, terms):

@property
def matrix(self):
"""Returns the full ``(2 ** nqubits, 2 ** nqubits)`` matrix representation."""
"""Returns the full matrix representation.
Consisting of ``(2 ** nqubits, 2 ** nqubits)`` elements.
"""
return self.dense.matrix

def eigenvalues(self, k=6):
Expand Down Expand Up @@ -495,17 +499,16 @@ def _get_symbol_matrix(self, term):

return result

def _calculate_dense_from_form(self):
"""Calculates equivalent :class:`qibo.core.hamiltonians.Hamiltonian` using symbolic form.
def _calculate_dense_from_form(self) -> Hamiltonian:
"""Calculates equivalent Hamiltonian using symbolic form.
Useful when the term representation is not available.
"""
matrix = self._get_symbol_matrix(self.form)
return Hamiltonian(self.nqubits, matrix, backend=self.backend)

def _calculate_dense_from_terms(self):
"""Calculates equivalent :class:`qibo.core.hamiltonians.Hamiltonian`
using the term representation.
"""
def _calculate_dense_from_terms(self) -> Hamiltonian:
"""Calculates equivalent Hamiltonian using the term representation."""
if 2 * self.nqubits > len(EINSUM_CHARS): # pragma: no cover
# case not tested because it only happens in large examples
raise_error(NotImplementedError, "Not enough einsum characters.")
Expand Down Expand Up @@ -547,7 +550,6 @@ def expectation_from_samples(self, freq, qubit_map=None):
raise_error(NotImplementedError, "Z^k is not implemented since Z^2=I.")
keys = list(freq.keys())
counts = np.array(list(freq.values())) / sum(freq.values())
coeff = list(self.form.as_coefficients_dict().values())
qubits = []
for term in terms:
qubits_term = []
Expand All @@ -566,8 +568,8 @@ def expectation_from_samples(self, freq, qubit_map=None):
if subk.count(1) % 2 == 1:
expval_k = -1
expval_q += expval_k * counts[i]
expval += expval_q * float(coeff[j])
return expval
expval += expval_q * self.terms[j].coefficient.real
Copy link
Member Author

@alecandido alecandido Jul 12, 2024

Choose a reason for hiding this comment

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

Any clue why SymbolicTerm coefficient are stored as complex values?

self.coefficient = complex(coefficient)

@andrea-pasquale?

O.e. I believe they are terms in a Hamiltonian, that is hermitian, and I imagine in general a superposition of other Hermitian terms, with real coefficients.
Though you could also have anti-hermitian with imaginary coefficients, or sums with the Hermitian conjugate. So, maybe it's just here that, since terms made of only Z are required, the coefficients are expected to be real...

Copy link
Member

Choose a reason for hiding this comment

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

I think terms here may not necessarily be hermitian. A term is a product of symbols and it happens that the symbols we provide are the Paulis which are all hermitian, however the user can also define their own symbol using custom matrix. So there may be a case where you have non-hermitian terms and complex coefficients resulting to a hermitian Hamiltonian.

The constant, on the other hand, probably needs to be real in every case, since it is multiplying the idenity that is hermitian.

return expval + self.constant.real

def __add__(self, o):
if isinstance(o, self.__class__):
Expand Down Expand Up @@ -682,7 +684,10 @@ def __mul__(self, o):
return new_ham

def apply_gates(self, state, density_matrix=False):
"""Applies gates corresponding to the Hamiltonian terms to a given state.
"""Applies gates corresponding to the Hamiltonian terms.
Gates are applied to the given state.
Helper method for ``__matmul__``.
"""
total = 0
Expand Down
11 changes: 11 additions & 0 deletions tests/test_hamiltonians_symbolic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import pytest
import sympy
from pytest import approx

from qibo import Circuit, gates, hamiltonians
from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector
Expand Down Expand Up @@ -385,3 +386,13 @@ def test_trotter_hamiltonian_operation_errors(backend):
h2 = hamiltonians.XXZ(3, dense=False, backend=backend)
with pytest.raises(NotImplementedError):
h = h1 @ h2


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)

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