Skip to content

Commit

Permalink
implement SpinBosonModel
Browse files Browse the repository at this point in the history
Implement `SpinBosonModel` as a subclass of `MolList2`. Original interface in `HolsteinModel` is removed. Original `SpinBosonModel` in `sbm/sbm.py` is renamed to `SpinBosonDynamics`.
  • Loading branch information
liwt31 committed Jul 17, 2020
1 parent e112ab9 commit 778525c
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 69 deletions.
9 changes: 7 additions & 2 deletions doc/source/model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ Model

Define the system
=================
Use the class :class:`~renormalizer.model.MolList2`, :class:`~renormalizer.model.HolsteinModel`
or :class:`~renormalizer.model.VibronicModel` to define the calculated system.
Use the class :class:`~renormalizer.model.MolList2`, :class:`~renormalizer.model.HolsteinModel`,
:class:`~renormalizer.model.VibronicModel` or :class:`~renormalizer.model.SpinBosonModel`
to define the calculated system.

.. autoclass:: renormalizer.model.mlist.MolList2
:members:
Expand All @@ -19,3 +20,7 @@ Use the class :class:`~renormalizer.model.MolList2`, :class:`~renormalizer.model
.. autoclass:: renormalizer.model.mlist.VibronicModel
:members:
:inherited-members:

.. autoclass:: renormalizer.model.mlist.SpinBosonModel
:members:
:inherited-members:
4 changes: 2 additions & 2 deletions example/sbm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

from renormalizer.sbm import SpinBosonModel, param2mollist
from renormalizer.sbm import SpinBosonDynamics, param2mollist
from renormalizer.utils import Quantity, CompressConfig, EvolveConfig


Expand All @@ -14,5 +14,5 @@

compress_config = CompressConfig(threshold=1e-4)
evolve_config = EvolveConfig(adaptive=True, guess_dt=0.1)
sbm = SpinBosonModel(mol_list, Quantity(0), compress_config=compress_config, evolve_config=evolve_config, dump_dir="./", job_name="sbm")
sbm = SpinBosonDynamics(mol_list, Quantity(0), compress_config=compress_config, evolve_config=evolve_config, dump_dir="./", job_name="sbm")
sbm.evolve(evolve_dt=0.1, evolve_time=20)
2 changes: 1 addition & 1 deletion renormalizer/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

from renormalizer.model.phonon import Phonon
from renormalizer.model.mol import Mol
from renormalizer.model.mlist import MolList2, construct_j_matrix, load_from_dict, HolsteinModel, VibronicModel
from renormalizer.model.mlist import MolList2, load_from_dict, HolsteinModel, VibronicModel, SpinBosonModel
85 changes: 63 additions & 22 deletions renormalizer/model/mlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,7 @@ def __init__(self, mol_list: List[Mol], j_matrix: Union[Quantity, np.ndarray, N
nv = 0
for imol, mol in enumerate(mol_list):
order[f"e_{imol}"] = idx
if not mol.sbm:
basis.append(ba.BasisSimpleElectron())
else:
basis.append(ba.BasisHalfSpin())
basis.append(ba.BasisSimpleElectron())
idx += 1
for iph, ph in enumerate(mol.dmrg_phs):
order[f"v_{nv}"] = idx
Expand Down Expand Up @@ -232,15 +229,8 @@ def __init__(self, mol_list: List[Mol], j_matrix: Union[Quantity, np.ndarray, N
for imol in range(mol_num):
for jmol in range(mol_num):
if imol == jmol:
if mol_list[imol].sbm:
model[(f"e_{imol}",)] = \
[(Op(r"sigma_z", 0), mol_list[imol].elocalex)]
model[(f"e_{imol}",)].append(
(Op("sigma_x", 0), mol_list[imol].tunnel)
)
else:
model[(f"e_{imol}",)] = \
[(Op(r"a^\dagger a", 0), mol_list[imol].elocalex + mol_list[imol].dmrg_e0)]
model[(f"e_{imol}",)] = \
[(Op(r"a^\dagger a", 0), mol_list[imol].elocalex + mol_list[imol].dmrg_e0)]
else:
model[(f"e_{imol}", f"e_{jmol}")] = \
[(Op(r"a^\dagger", 1), Op("a", -1), j_matrix[imol, jmol])]
Expand All @@ -257,18 +247,14 @@ def __init__(self, mol_list: List[Mol], j_matrix: Union[Quantity, np.ndarray, N
# vibration potential part
for imol, mol in enumerate(mol_list):
for iph, ph in enumerate(mol.dmrg_phs):
if mol_list[imol].sbm:
op_str = r"sigma_z"
else:
op_str = r"a^\dagger a"
if np.allclose(ph.omega[0], ph.omega[1]):
model[(f"e_{imol}", f"{mapping[(imol,iph)]}")] = [
(Op(op_str, 0), Op("x", 0), -ph.omega[1] ** 2 * ph.dis[1]),
(Op(r"a^\dagger a", 0), Op("x", 0), -ph.omega[1] ** 2 * ph.dis[1]),
]
else:
model[(f"e_{imol}", f"{mapping[(imol,iph)]}")] = [
(Op(op_str, 0), Op("x^2", 0), 0.5 * (ph.omega[1] ** 2 - ph.omega[0] ** 2)),
(Op(op_str, 0), Op("x", 0), -ph.omega[1] ** 2 * ph.dis[1]),
(Op(r"a^\dagger a", 0), Op("x^2", 0), 0.5 * (ph.omega[1] ** 2 - ph.omega[0] ** 2)),
(Op(r"a^\dagger a", 0), Op("x", 0), -ph.omega[1] ** 2 * ph.dis[1]),
]


Expand Down Expand Up @@ -338,8 +324,19 @@ class VibronicModel(MolList2):
sub-key (one-to-one map between the sub-key and tuple).
The model_translator is ModelTranslator.vibronic_model
for example:
``{"I": {("v_1"):[(Op,factor),]}, ("e_i","e_j") : {"J":factor, ("v_0",):[(Op, factor), (Op, factor)],
("v_1","v_2"):[(Op1, Op2, factor), (Op1, Op2, factor)]...}...}``
::
{
"I" : {("v_1"):[(Op,factor),]},
("e_i","e_j") : {
"J":factor,
("v_0",):[(Op, factor), (Op, factor)],
("v_1","v_2"):[(Op1, Op2, factor), (Op1, Op2, factor)]
...
}
...
}
"""
def __init__(self, order: Union[Dict, List], basis: Union[Dict, List], model: Dict, dipole: Dict = None):
new_model = defaultdict(list)
Expand Down Expand Up @@ -371,6 +368,50 @@ def __init__(self, order: Union[Dict, List], basis: Union[Dict, List], model: Di

super().__init__(order, basis, new_model, dipole)


class SpinBosonModel(MolList2):
r"""
Spin-Boson model
.. math::
\hat{H} = \epsilon \sigma_z + \Delta \sigma_x
+ \frac{1}{2} \sum_i(p_i^2+\omega^2_i q_i^2)
+ \sigma_z \sum_i c_i q_i
"""
def __init__(self, epsilon: Quantity, delta: Quantity, ph_list: List[Phonon], dipole: float=None):

self.epsilon = epsilon.as_au()
self.delta = delta.as_au()
self.ph_list = ph_list

order = {}
basis = []
model = {}

order[f"e_0"] = 0
basis.append(ba.BasisHalfSpin())
for iph, ph in enumerate(ph_list):
order[f"v_{iph}"] = iph+1
basis.append(ba.BasisSHO(ph.omega[0], ph.n_phys_dim))

# spin
model[(f"e_0",)] = [(Op(r"sigma_z", 0), self.epsilon),(Op("sigma_x", 0), self.delta)]
# vibration energy and potential
for iph, ph in enumerate(ph_list):
assert ph.is_simple
model[(f"v_{iph}",)] = [
(Op("p^2", 0), 0.5),
(Op("x^2", 0), 0.5 * ph.omega[0] ** 2)
]
model[(f"e_0", f"v_{iph}")] = [
(Op("sigma_z", 0), Op("x", 0), -ph.omega[1] ** 2 * ph.dis[1]),
]
if dipole is None:
dipole = 0
super().__init__(order, basis, model, dipole={"e_0": dipole})


def construct_j_matrix(mol_num, j_constant, periodic):
# nearest neighbour interaction
j_constant_au = j_constant.as_au()
Expand Down
8 changes: 1 addition & 7 deletions renormalizer/model/mol.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ class Mol:
phonon information : ph
"""

def __init__(self, elocalex, ph_list: List[Phonon], dipole=None, heatbath=False, tunnel=Quantity(0)):
def __init__(self, elocalex, ph_list: List[Phonon], dipole=None, heatbath=False):
self.elocalex = elocalex.as_au()
self.dipole = dipole
self.tunnel = tunnel.as_au()
if len(ph_list) == 0:
raise ValueError("No phonon mode in phonon list")
self.dmrg_phs = [ph for ph in ph_list if not ph.hartree]
Expand Down Expand Up @@ -92,10 +91,6 @@ def no_qboson(self):
def phs(self):
return self.dmrg_phs + self.hartree_phs

@property
def sbm(self):
return not np.allclose(self.tunnel, 0)

@property
def gs_zpe(self):
e = 0.
Expand All @@ -115,7 +110,6 @@ def to_dict(self):
info_dict["elocalex"] = self.elocalex
info_dict["dipole"] = self.dipole
info_dict["reorganization energy in a.u."] = self.reorganization_energy
info_dict["tunnel"] = self.tunnel
info_dict["dmrg phonon modes"] = self.n_dmrg_phs
if self.n_hartree_phs:
info_dict["dmrg phonon modes"] = self.n_dmrg_phs
Expand Down
1 change: 0 additions & 1 deletion renormalizer/model/phonon.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ def split(self, n=2, width: Quantity=Quantity(10, "cm-1")) -> List["Phonon"]:
phonons.append(ph)
return phonons


def to_dict(self):
info_dict = OrderedDict()
info_dict["omega"] = self.omega
Expand Down
43 changes: 19 additions & 24 deletions renormalizer/mps/tests/test_sbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,23 @@
import qutip
import pytest

from renormalizer.model import Phonon, Mol, HolsteinModel
from renormalizer.model import Phonon, Mol, SpinBosonModel
from renormalizer.mps import Mps, Mpo, MpDm, ThermalProp
from renormalizer.utils import Quantity, CompressConfig, EvolveConfig


def get_mol():
def get_model():
nphonons = 5
ph_levels = 2

delta = 1
epsilon = 1
delta = 1

ph_list = [Phonon.simple_phonon(Quantity(1), Quantity(1), ph_levels)] * nphonons
m = Mol(Quantity(epsilon), ph_list, tunnel=Quantity(-delta))
return m
return SpinBosonModel(Quantity(epsilon), Quantity(delta), ph_list)

def test_zt():
mol = get_mol()
mol_list = HolsteinModel([mol], Quantity(0), )
mol_list = get_model()

mps = Mps.ground_state(mol_list, False)
mps.compress_config = CompressConfig(threshold=1e-6)
Expand All @@ -36,14 +34,13 @@ def test_zt():
mps = mps.evolve(mpo, evolve_dt=dt)
time_series.append(time_series[-1] + dt)
spin.append(mps.expectation(Mpo.onsite(mol_list, "sigma_z")))
qutip_res = get_qutip_zt(mol, time_series)
qutip_res = get_qutip_zt(mol_list, time_series)
assert np.allclose(qutip_res, spin, atol=1e-3)


@pytest.mark.xfail(reason="thermal prop calculate electron occupations (not valid for spin)")
def test_ft():
mol = get_mol()
mol_list = HolsteinModel([mol], Quantity(0), )
mol_list = get_model()
mpo = Mpo(mol_list)
impdm = MpDm.max_entangled_gs(mol_list)
impdm.compress_config = CompressConfig(threshold=1e-6)
Expand All @@ -62,30 +59,28 @@ def test_ft():
mpdm = mpdm.evolve(mpo, evolve_dt=dt)
time_series.append(time_series[-1] + dt)
spin.append(mpdm.expectation(Mpo.onsite(mol_list, "sigma_z")))
qutip_res = get_qutip_ft(mol, temperature, time_series)
qutip_res = get_qutip_ft(mol_list, temperature, time_series)
assert np.allclose(qutip_res, spin, atol=1e-3)


def get_qutip_operator(mol):
def get_qutip_operator(mol_list):
blist = []
for i, ph1 in enumerate(mol.dmrg_phs):
for i, ph1 in enumerate(mol_list.ph_list):
basis = [qutip.identity(2)]
for j, ph2 in enumerate(mol.dmrg_phs):
for j, ph2 in enumerate(mol_list.ph_list):
if j == i:
state = qutip.destroy(ph1.n_phys_dim)
else:
state = qutip.identity(ph2.n_phys_dim)
basis.append(state)
blist.append(qutip.tensor(basis))

ph_iden = [qutip.identity(ph.n_phys_dim) for ph in mol.dmrg_phs]
ph_iden = [qutip.identity(ph.n_phys_dim) for ph in mol_list.ph_list]

sigma_x = qutip.tensor([qutip.sigmax()] + ph_iden)
sigma_z = qutip.tensor([qutip.sigmaz()] + ph_iden)
delta = mol.tunnel
epsilon = mol.elocalex
terms = [-delta * sigma_x, epsilon * sigma_z]
for i, ph in enumerate(mol.dmrg_phs):
terms = [mol_list.delta * sigma_x, mol_list.epsilon * sigma_z]
for i, ph in enumerate(mol_list.ph_list):
g = ph.coupling_constant
terms.append(ph.omega[0] * blist[i].dag() * blist[i])
terms.append(ph.omega[0] * g * sigma_z * (blist[i].dag() + blist[i]))
Expand All @@ -94,15 +89,15 @@ def get_qutip_operator(mol):
return H, sigma_x, sigma_z


def get_qutip_zt(mol, time_series):
H, _, sigma_z = get_qutip_operator(mol)
init_state = qutip.tensor([qutip.basis(2)] + [qutip.basis(ph.n_phys_dim) for ph in mol.dmrg_phs])
def get_qutip_zt(mol_list, time_series):
H, _, sigma_z = get_qutip_operator(mol_list)
init_state = qutip.tensor([qutip.basis(2)] + [qutip.basis(ph.n_phys_dim) for ph in mol_list.ph_list])
result = qutip.mesolve(H, init_state, time_series, e_ops=[sigma_z])
return result.expect[0]


def get_qutip_ft(mol, temperature, time_series):
H, sigma_x, sigma_z = get_qutip_operator(mol)
def get_qutip_ft(mol_list, temperature, time_series):
H, sigma_x, sigma_z = get_qutip_operator(mol_list)
init_state = sigma_x * (-temperature.to_beta() * H).expm().unit() * sigma_x.dag()
result = qutip.mesolve(H, init_state, time_series, e_ops=[sigma_z])
return result.expect[0]
2 changes: 1 addition & 1 deletion renormalizer/sbm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

from renormalizer.sbm.lib import SpectralDensityFunction, param2mollist
from renormalizer.sbm.sbm import SpinBosonModel
from renormalizer.sbm.sbm import SpinBosonDynamics
5 changes: 2 additions & 3 deletions renormalizer/sbm/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import scipy.special
import scipy.optimize

from renormalizer.model import Phonon, Mol, HolsteinModel
from renormalizer.model import Phonon, Mol, SpinBosonModel
from renormalizer.utils import Quantity


Expand Down Expand Up @@ -160,5 +160,4 @@ def param2mollist(alpha: float, raw_delta: Quantity, omega_c: Quantity, renormal
omega_list, displacement_list = sdf.trapz(n_phonons, 0.0, max_omega.as_au())

ph_list = [Phonon.simplest_phonon(o, d) for o,d in zip(omega_list, displacement_list)]
mol = Mol(Quantity(0), ph_list, tunnel=delta)
return HolsteinModel([mol], None, )
return SpinBosonModel(Quantity(0), delta, ph_list)
2 changes: 1 addition & 1 deletion renormalizer/sbm/sbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
logger = logging.getLogger(__name__)


class SpinBosonModel(TdMpsJob):
class SpinBosonDynamics(TdMpsJob):

def __init__(self, mol_list: MolList2, temperature: Quantity, compress_config=None, evolve_config=None, dump_dir=None, job_name=None):
self.mol_list = mol_list
Expand Down
9 changes: 4 additions & 5 deletions renormalizer/sbm/tests/test_sbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from renormalizer.model import Phonon, Mol
from renormalizer.sbm import SpinBosonModel, param2mollist, SpectralDensityFunction
from renormalizer.sbm import SpinBosonDynamics, param2mollist, SpectralDensityFunction
from renormalizer.utils import Quantity, EvolveConfig, EvolveMethod
from renormalizer.mps.tests.test_sbm import get_qutip_zt

Expand All @@ -16,8 +16,7 @@ def test_sdf():
omega_list, displacement_list = sdf.trapz(200, 0.0, 50)

ph_list = [Phonon.simplest_phonon(o, d) for o,d in zip(omega_list, displacement_list)]
mol = Mol(Quantity(0), ph_list, tunnel=Quantity(1))
mol_reor = mol.reorganization_energy
mol_reor = sum(ph.reorganization_energy.as_au() for ph in ph_list)

assert mol_reor == pytest.approx(alpha * omega_c.as_au() / 2, abs=0.005)

Expand All @@ -37,8 +36,8 @@ def test_sbm_zt(alpha):
mol_list = param2mollist(alpha, raw_delta, raw_omega_c, 5, n_phonons)

evolve_config = EvolveConfig(method=EvolveMethod.tdvp_ps, adaptive=True, guess_dt=0.1)
sbm = SpinBosonModel(mol_list, Quantity(0), evolve_config=evolve_config)
sbm = SpinBosonDynamics(mol_list, Quantity(0), evolve_config=evolve_config)
sbm.evolve(nsteps=20, evolve_time=20)
spin1 = sbm.sigma_z
spin2 = get_qutip_zt(mol_list[0], sbm.evolve_times)
spin2 = get_qutip_zt(mol_list, sbm.evolve_times)
assert np.allclose(spin1, spin2, atol=1e-3)

0 comments on commit 778525c

Please sign in to comment.