Permalink
Browse files

added magnetic field to general hubbard model (#475)

* added magnetic field to general hubbard model

* added self to author lists

* renamed field_operator to magnetic_field_operator

* made magnetic field operator indexing-independent

added test for equivalence of magnetic field parameter in special and
general cases

* fixed bug in test

* renamed difference_operator to number_difference_operator
  • Loading branch information...
bryano authored and kevinsung committed Oct 5, 2018
1 parent 8a79033 commit e64c17a8043b2ad0a60695b581293bf903ad91a3
View
1 NOTICE
@@ -24,6 +24,7 @@ Xinle Liu (Google)
Sam McArdle (Oxford)
Matthew Neeley (Google)
Thomas O'Brien (Leiden University)
Bryan O'Gorman (UC Berkeley, NASA)
Isil Ozfidan (D-Wave Systems)
Max Radin (UC Santa Barbara)
Jhonathan Romero (Harvard)
View
@@ -139,6 +139,7 @@ Authors
`Sam McArdle <https://github.com/sammcardle30>`__ (Oxford),
`Matthew Neeley <https://github.com/maffoo>`__ (Google),
`Thomas O'Brien <https://github.com/obriente>`__ (Leiden University),
`Bryan O'Gorman <https://ti.arc.nasa.gov/profile/bogorman>`__ (UC Berkeley, NASA),
`Isil Ozfidan <https://github.com/conta877>`__ (D-Wave Systems),
`Max Radin <https://github.com/max-radin>`__ (UC Santa Barbara),
`Jhonathan Romero <https://github.com/jromerofontalvo>`__ (Harvard),
@@ -16,7 +16,7 @@
from collections import namedtuple
from openfermion.ops import FermionOperator
from openfermion.utils import SpinPairs
from openfermion.utils import (SpinPairs, Spin)
TunnelingParameter = namedtuple('TunnelingParameter',
('edge_type', 'dofs', 'coefficient'))
@@ -39,6 +39,8 @@ def tunneling_operator(i, j, coefficient):
return (FermionOperator(((i, 1), (j, 0)), coefficient) +
FermionOperator(((j, 1), (i, 0)), coefficient.conjugate()))
def number_difference_operator(i, j, coefficient=1.):
return number_operator(i, coefficient) - number_operator(j, coefficient)
class FermiHubbardModel:
r"""A general, parameterized Fermi-Hubbard model.
@@ -96,6 +98,9 @@ class FermiHubbardModel:
\\
&- \sum_{a} \mu_a
\sum_i \sum_{\sigma} n_{i, a, \sigma}
\\
&- h \sum_{i} \sum_{a}
\left(n_{i, a, \uparrow} - n_{i, a, \downarrow}\right)
\end{align}
where
@@ -115,7 +120,8 @@ class FermiHubbardModel:
- :math:`U_{a, b}^{(\mathrm{nghbr, \pm})}` is the Coulomb potential
betwen spin orbitals on neighborings sites with the same (+) or
different (-) spins,
- :math:`\mu_{a}` is the chemical potential.
- :math:`\mu_{a}` is the chemical potential, and
- :math:`h` is the magnetic field.
One can also construct the Hamiltonian for the spinless model, which
has the form
@@ -157,7 +163,8 @@ class FermiHubbardModel:
def __init__(self, lattice,
tunneling_parameters=None,
interaction_parameters=None,
potential_parameters=None
potential_parameters=None,
magnetic_field=0
):
r"""A Hubbard model defined on a lattice.
@@ -169,6 +176,7 @@ def __init__(self, lattice,
Number, int?]], optional): The interaction parameters.
potential_parameters (Iterable[Tuple[int, Number]], optional): The
potential parameters.
magnetic_field (Number, optional): The magnetic field. Default is 0.
Each group of parameters is specified as an iterable of tuples.
@@ -232,6 +240,8 @@ def __init__(self, lattice,
- :math:`i` runs over the sites of the lattice;
- :math:`a` is the degree of freedom ``dof``; and
- :math:`\mu` is the ``coefficient``.
In the spinless model, the magnetic field is ignored.
"""
self.lattice = lattice
@@ -242,6 +252,7 @@ def __init__(self, lattice,
interaction_parameters)
self.potential_parameters = self.parse_potential_parameters(
potential_parameters)
self.magnetic_field = magnetic_field
@@ -333,8 +344,20 @@ def potential_terms(self):
terms += number_operator(i, -param.coefficient)
return terms
def field_terms(self):
terms = FermionOperator()
if self.lattice.spinless or not self.magnetic_field:
return terms
for site_index in self.lattice.site_indices:
for dof in self.lattice.dof_indices:
i = self.lattice.to_spin_orbital_index(site_index, dof, Spin.UP)
j = self.lattice.to_spin_orbital_index(site_index, dof, Spin.DOWN)
terms += number_difference_operator(i, j, -self.magnetic_field)
return terms
def hamiltonian(self):
return (self.tunneling_terms() +
self.interaction_terms() +
self.potential_terms())
self.potential_terms() +
self.field_terms())
@@ -25,36 +25,38 @@
def fermi_hubbard_from_general(x_dimension, y_dimension, tunneling, coulomb,
chemical_potential=0.,
periodic=True, spinless=False):
periodic=True, spinless=False, magnetic_field=0):
lattice = HubbardSquareLattice(x_dimension, y_dimension,
periodic=periodic, spinless=spinless)
interaction_edge_type = 'neighbor' if spinless else 'onsite'
model = FermiHubbardModel(lattice,
tunneling_parameters=(('neighbor', (0, 0), tunneling),),
interaction_parameters=((interaction_edge_type, (0, 0), coulomb),),
potential_parameters=((0, chemical_potential),))
potential_parameters=((0, chemical_potential),),
magnetic_field=magnetic_field)
return model.hamiltonian()
@pytest.mark.parametrize(
'x_dimension,y_dimension,tunneling,coulomb,' +
'chemical_potential,spinless,periodic',
'chemical_potential,spinless,periodic,magnetic_field',
itertools.product(
range(1, 4), range(1, 4),
(random.uniform(0, 2.),), (random.uniform(0, 2.),),
(random.uniform(0, 2.),), (True, False), (True, False))
(random.uniform(0, 2.),), (True, False), (True, False),
(random.uniform(-1, 1),))
)
def test_fermi_hubbard_square_special_general_equivalance(
def test_fermi_hubbard_square_special_general_equivalence(
x_dimension, y_dimension, tunneling, coulomb,
chemical_potential, spinless, periodic):
chemical_potential, spinless, periodic, magnetic_field):
hubbard_model_special = fermi_hubbard(
y_dimension, x_dimension, tunneling, coulomb,
chemical_potential=chemical_potential, spinless=spinless,
periodic=periodic)
periodic=periodic, magnetic_field=magnetic_field)
hubbard_model_general = fermi_hubbard_from_general(
x_dimension, y_dimension, tunneling, coulomb,
chemical_potential=chemical_potential, spinless=spinless,
periodic=periodic)
periodic=periodic, magnetic_field=magnetic_field)
assert hubbard_model_special == hubbard_model_general
def random_parameters(lattice, probability=0.5, distinguish_edges=False):
@@ -81,15 +83,21 @@ def random_parameters(lattice, probability=0.5, distinguish_edges=False):
(dof, random.uniform(-1, 1))
for dof in lattice.dof_indices
if random.random() <= probability]
if random.random() <= probability:
parameters['magnetic_field'] = random.uniform(-1, 1)
return parameters
def test_fermi_hubbard_default_parameters():
lattice = HubbardSquareLattice(3, 3)
model = FermiHubbardModel(lattice)
assert model.tunneling_parameters == []
assert model.interaction_parameters == []
assert model.potential_parameters == []
assert model.magnetic_field == 0
def test_fermi_hubbard_bad_parameters():
lattice = HubbardSquareLattice(3, 3)
@@ -151,8 +159,16 @@ def test_fermi_hubbard_square_lattice_random_parameters(
assert len(term) == 2
assert len(spin_orbitals) == 1
spin_orbital = spin_orbitals.pop()
_, dof, _ = lattice.from_spin_orbital_index(spin_orbital)
parameter = (dof, -coefficient)
_, dof, spin_index = lattice.from_spin_orbital_index(spin_orbital)
potential_coefficient = -coefficient
if not lattice.spinless:
spin = (-1) ** spin_index
potential_coefficient -= (
((-1) ** spin_index) *
parameters.get('magnetic_field', 0))
if not potential_coefficient:
continue
parameter = (dof, potential_coefficient)
assert parameter in parameters['potential_parameters']
terms_per_parameter['potential', parameter] += 1
edge_type_to_n_site_pairs = {
@@ -19,7 +19,7 @@
from ._grid import Grid
from ._lattice import (HubbardSquareLattice, SpinPairs)
from ._lattice import (HubbardSquareLattice, SpinPairs, Spin)
from ._lcu_util import (lambda_norm,
preprocess_lcu_coefficients_for_reversible_sampling)
@@ -11,11 +11,16 @@
# limitations under the License.
import abc
from enum import Enum
from enum import Enum, IntEnum
import itertools
from six import add_metaclass
class Spin(IntEnum):
UP = 0
DOWN = 1
class SpinPairs(Enum):
"""The spin orbitals corresponding to a pair of spatial orbitals."""
@@ -42,6 +47,9 @@ class HubbardLattice:
And the following methods:
site_pairs_iter(edge_type: Hashable) -> Iterable[Tuple[int, int]]: Iterable
over pairs of sites corresponding to the given edge type.
For 'spinful' lattices, the ``spin_indices`` ``0`` and ``1`` correspond to
'up' and 'down', respectively.
"""
@abc.abstractproperty
@@ -16,8 +16,11 @@
import pytest
import random
from openfermion.utils import (HubbardSquareLattice, SpinPairs)
from openfermion.utils import (HubbardSquareLattice, SpinPairs, Spin)
def test_spin():
lattice = HubbardSquareLattice(3, 3)
assert tuple(lattice.spin_indices) == (Spin.UP, Spin.DOWN)
@pytest.mark.parametrize(
"x_dimension,y_dimension,n_dofs,spinless,periodic",

0 comments on commit e64c17a

Please sign in to comment.