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

HEOM Parity support #2261

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1a2e5d0
Added FitBath and OhmicBath classes, fix spelling on solver_base
gsuarezr Oct 11, 2023
27ed772
notebooks as example
gsuarezr Oct 11, 2023
3ba219d
formatting
gsuarezr Oct 11, 2023
40a914d
added tests
gsuarezr Oct 13, 2023
3edecc0
Merge branch 'qutip:master' into master
gsuarezr Oct 19, 2023
f51033a
Make fit classes inherit, which broke tests, refactored functions and…
gsuarezr Oct 19, 2023
c880375
minor refactoring
gsuarezr Oct 24, 2023
7080102
replaced tests to match new classes
gsuarezr Oct 24, 2023
390c596
added parity support changed prints to __str__
gsuarezr Oct 25, 2023
fa563b5
deleted innecessary comments and code
gsuarezr Oct 25, 2023
c6a7d4e
Rewrote summary methods,added comments,eliminated unnecessary code
gsuarezr Oct 26, 2023
5c03d92
Rewrote summary methods,added comments,eliminated unnecessary code
gsuarezr Oct 26, 2023
5f0003c
moving parity to another branch
gsuarezr Oct 27, 2023
22cdee6
Merge branch 'qutip:master' into parity
gsuarezr Oct 27, 2023
f16d52d
added parity support
gsuarezr Oct 27, 2023
fc794bc
Merge branch 'qutip:master' into parity
gsuarezr Nov 9, 2023
26c49ed
moved example notebook to tutorials
gsuarezr Nov 8, 2023
9699687
removed mpmath
gsuarezr Nov 9, 2023
77f23a4
removed bosonic fitting tests
gsuarezr Nov 9, 2023
2c74559
removed changes from other branches
gsuarezr Nov 9, 2023
b9d8093
codeclimate sugestions
gsuarezr Nov 9, 2023
3fe627e
refactoring according to codeclimate
gsuarezr Nov 13, 2023
65a8299
added changelog entry
gsuarezr Nov 13, 2023
ca7095a
revert unrelated changes
gsuarezr Nov 27, 2023
a79a305
revert unrelated changes 2
gsuarezr Nov 27, 2023
5ee816a
revert unrelated changes 3
gsuarezr Nov 27, 2023
aaa45f1
revert unrelated changes 4
gsuarezr Nov 27, 2023
27c878d
Merge branch 'qutip:master' into parity
gsuarezr Dec 11, 2023
4172aef
Merge branch 'qutip:master' into parity
gsuarezr Dec 19, 2023
4b5cfc1
make odd parity keyword only
gsuarezr Dec 20, 2023
e7f1360
Apply suggestions from code review
gsuarezr Dec 20, 2023
87a23a0
Merge branch 'qutip:master' into parity
gsuarezr Jan 15, 2024
01058af
added simple test
gsuarezr Feb 11, 2024
dd1f104
check broken tests
gsuarezr Feb 15, 2024
adaca75
daily
gsuarezr Feb 19, 2024
a5ce6cc
set use_mkl=False so tests pass for python 3.10
gsuarezr Feb 19, 2024
ce69039
code review suggestions
gsuarezr Mar 4, 2024
e004b44
Merge branch 'master' into parity
gsuarezr Mar 4, 2024
07d1c38
missed import
gsuarezr Mar 4, 2024
41ddaba
missed comma
gsuarezr Mar 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/2261.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
It adds odd parity support to HEOM's fermionic solver
31 changes: 22 additions & 9 deletions qutip/solver/heom/bofin_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class HierarchyADOs:
labels: list of tuples
A list of the ADO labels within the hierarchy.
"""

def __init__(self, exponents, max_depth):
self.exponents = exponents
self.max_depth = max_depth
Expand Down Expand Up @@ -347,6 +348,7 @@ class HierarchyADOsState:
See :class:`HierarchyADOs` for a full list of the available attributes
and methods.
"""

def __init__(self, rho, ados, ado_state):
self.rho = rho
self._ado_state = ado_state
Expand Down Expand Up @@ -566,6 +568,17 @@ class HEOMSolver(Solver):
If multiple baths are given, they must all be either fermionic
or bosonic baths.

odd_parity : Bool
For fermionic baths only. Default is "False". If set to "True",
the RHS construction differs slightly (it implies the RHS is acting on
an initial density operator which has odd parity in terms of its
representation in fermionic annhilation operators). In other words, a
state with a well defined number of excitations has even parity, but
one with superpositions of the number of excitations has odd parity.
This is normally prohibited in 'normal' electronic systems, where
charge is conserved, but is useful for the construction of
electron spectrum (under linear response assumptions).

Copy link
Member

@nwlambert nwlambert Mar 1, 2024

Choose a reason for hiding this comment

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

Simon asked us to help distill and clarify this a little bit; how about:

" For fermionic baths only. Default is "False". "Parity" refers to the parity of the initial system state used with the HEOM. An example of an odd parity state is one made from applying an odd number of fermionic creation operators to a physical density operator. Physical systems have even parity, but allowing the generalization to odd-parity states allows one to calculate useful physical quantities like the system power spectrum or density of states. The form of the HEOM differs depending on the parity of the initial system state, so if this option is set to "True", a different RHS is constructed, which can then be used with a system state of odd parity. "

max_depth : int
The maximum depth of the heirarchy (i.e. the maximum number of bath
exponent "excitations" to retain).
Expand Down Expand Up @@ -602,9 +615,10 @@ class HEOMSolver(Solver):
"state_data_type": "dense",
}

def __init__(self, H, bath, max_depth, *, options=None):
def __init__(self, H, bath, max_depth, *, odd_parity=False, options=None):
_time_start = time()

# we call bool here because odd_parity will be used in arithmetic
self.odd_parity = bool(odd_parity)
if not isinstance(H, (Qobj, QobjEvo)):
raise TypeError("The Hamiltonian (H) must be a Qobj or QobjEvo")

Expand Down Expand Up @@ -753,11 +767,9 @@ def _grad_prev_fermionic(self, he_n, k):
]

n_excite = sum(he_fermionic_n)
sign1 = (-1) ** (n_excite + 1)

gsuarezr marked this conversation as resolved.
Show resolved Hide resolved
n_excite_before_m = sum(he_fermionic_n[:k])
sign2 = (-1) ** (n_excite_before_m)

sign1 = (-1) ** (n_excite + 1 - self.odd_parity)
sign2 = (-1)**(n_excite_before_m + self.odd_parity)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you put these lines back where they were so that there is a small block of code for the calculation of each sign instead of interleaving the two calculations?

sigma_bar_k = k + self.ados.sigma_bar_k_offset[k]

if self.ados.exponents[k].type == BathExponent.types["+"]:
Expand Down Expand Up @@ -800,10 +812,9 @@ def _grad_next_fermionic(self, he_n, k):
for i, exp in zip(he_n, self.ados.exponents)
]
n_excite = sum(he_fermionic_n)
sign1 = (-1) ** (n_excite + 1)

n_excite_before_m = sum(he_fermionic_n[:k])
sign2 = (-1) ** (n_excite_before_m)
sign1 = (-1) ** (n_excite + 1 - self.odd_parity)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

sign2 = (-1)**(n_excite_before_m + self.odd_parity)

if self.ados.exponents[k].type == BathExponent.types["+"]:
if sign1 == -1:
Expand Down Expand Up @@ -1233,6 +1244,7 @@ class HSolverDL(HEOMSolver):
operator). See :meth:`BosonicBath.combine` for details.
Keyword only. Default: True.
"""

def __init__(
self, H_sys, coup_op, coup_strength, temperature,
N_cut, N_exp, cut_freq, *, bnd_cut_approx=False, options=None,
Expand Down Expand Up @@ -1281,6 +1293,7 @@ class _GatherHEOMRHS:
nhe : int
The number of ADOs in the hierarchy.
"""

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you again undo these unrelated changes?

def __init__(self, f_idx, block, nhe):
self._block_size = block
self._n_blocks = nhe
Expand Down
50 changes: 47 additions & 3 deletions qutip/tests/solver/heom/test_bofin_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from qutip import (
basis, destroy, expect, liouvillian, qeye, sigmax, sigmaz,
tensor, Qobj, QobjEvo
tensor, Qobj, QobjEvo, fcreate, fdestroy
Copy link
Contributor

Choose a reason for hiding this comment

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

fcreate appears to be unused. Could you remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I shouldn't have included it

)
from qutip.core import data as _data
from qutip.solver.heom.bofin_baths import (
Expand Down Expand Up @@ -54,8 +54,8 @@ def assert_raises_steady_state_time_dependent(hsolver):
with pytest.raises(ValueError) as err:
hsolver.steady_state()
assert str(err.value) == (
"A steady state cannot be determined for a time-dependent"
" system"
"A steady state cannot be determined for a time-dependent"
Copy link
Contributor

Choose a reason for hiding this comment

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

This can stay -- it's a valid indentation fix.

" system"
)


Expand Down Expand Up @@ -232,6 +232,7 @@ class DrudeLorentzPureDephasingModel:
""" Analytic Drude-Lorentz pure-dephasing model for testing the HEOM
solver.
"""

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove these too.

def __init__(self, lam, gamma, T, Nk):
self.lam = lam
self.gamma = gamma
Expand Down Expand Up @@ -289,6 +290,7 @@ class UnderdampedPureDephasingModel:
""" Analytic Drude-Lorentz pure-dephasing model for testing the HEOM
solver.
"""

def __init__(self, lam, gamma, w0, T, Nk):
self.lam = lam
self.gamma = gamma
Expand Down Expand Up @@ -331,6 +333,7 @@ class BosonicMode:
""" A description of a bosonic mode for inclusion in a
DiscreteLevelCurrentModel.
"""

def __init__(self, N, Lambda, Omega, gamma_b):
self.N = N
self.Lambda = Lambda
Expand All @@ -356,6 +359,7 @@ class DiscreteLevelCurrentModel:
""" Analytic discrete level current model for testing the HEOM solver
with a fermionic bath (and optionally a bosonic mode).
"""

def __init__(self, gamma, W, T, lmax, theta=2., e1=1., bosonic_mode=None):
# single fermion
self.e1 = e1 # energy
Expand Down Expand Up @@ -1127,6 +1131,46 @@ def test_solving_with_step(self):

assert states[-1] == ado_state.extract(0)

def test_parity(self):
depth = 2
Nk = 2
# system: two fermions
N = 2
d_1 = fdestroy(N, 0)
d_2 = fdestroy(N, 1)
# bath params:
mu = 0. # chemical potential
Gamma = 1 # coupling strenght
W = 2.5 # bath width
# system params:
# coulomb repulsion
U = 3 * np.pi * Gamma
# impurity energy
w0 = - U / 2.
beta = 1 / (0.2 * Gamma) # Inverse Temperature
H = w0 * (d_1.dag() * d_1 + d_2.dag()
* d_2) + U * d_1.dag() * d_1 * d_2.dag() * d_2

L = liouvillian(H)
bath1 = LorentzianPadeBath(
Q=d_1, gamma=2 * Gamma, w=W, mu=mu, T=1 / beta, Nk=Nk,
tag="Lead 1")
bath2 = LorentzianPadeBath(
Q=d_2, gamma=2 * Gamma, w=W, mu=mu, T=1 / beta, Nk=Nk,
tag="Lead 2")
resultHEOMPade = HEOMSolver(L, [bath1, bath2], depth, odd_parity=True)
rhoss, _ = resultHEOMPade.steady_state(use_mkl=False)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain why you needed to pass use_mkl=False here? I think we would expect it to automatically do something sensible for users in all cases and we don't appear to have had to use it in any of the other HEOM solver tests yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used used_mkl=False, because if I don't for the test fails for python 3.10 with no cython (only in that setup it passes in all others), the test fails with Exception: Zero pivot, numerical factorization or iterative refinement problem. I found this old thread #791 suggesting that sometimes mkl solvers give some trouble so I decided to set it to false

rhoss = rhoss.full()
expected_odd = np.diag([-0.18472, 0.68472, 0.68472, -0.18472])
expected = np.diag([0.10623, 0.39376, 0.39376, 0.10623])
assert np.isclose(rhoss, expected_odd,atol=1e-5).all()
resultHEOMPade = HEOMSolver(L, [bath1, bath2], depth, odd_parity=False)
rhoss, _ = resultHEOMPade.steady_state(use_mkl=False)
rhoss = rhoss.full()
assert np.isclose(rhoss, expected,atol=1e-5).all()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
resultHEOMPade = HEOMSolver(L, [bath1, bath2], depth, odd_parity=True)
rhoss, _ = resultHEOMPade.steady_state(use_mkl=False)
rhoss = rhoss.full()
expected_odd = np.diag([-0.18472, 0.68472, 0.68472, -0.18472])
expected = np.diag([0.10623, 0.39376, 0.39376, 0.10623])
assert np.isclose(rhoss, expected_odd,atol=1e-5).all()
resultHEOMPade = HEOMSolver(L, [bath1, bath2], depth, odd_parity=False)
rhoss, _ = resultHEOMPade.steady_state(use_mkl=False)
rhoss = rhoss.full()
assert np.isclose(rhoss, expected,atol=1e-5).all()
solver = HEOMSolver(L, [bath1, bath2], depth, odd_parity=True)
rhoss, _ = solver.steady_state(use_mkl=False)
rhoss = rhoss.full()
expected_odd = np.diag([-0.18472, 0.68472, 0.68472, -0.18472])
np.testing.assert_allclose(rhoss, expected_odd, atol=1e-5)
solver = HEOMSolver(L, [bath1, bath2], depth, odd_parity=False)
rhoss, _ = solver.steady_state(use_mkl=False)
rhoss = rhoss.full()
expected = np.diag([0.10623, 0.39376, 0.39376, 0.10623])
np.testing.assert_allclose(rhoss, expected, atol=1e-5)




Copy link
Contributor

Choose a reason for hiding this comment

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

There appear to be too many blank lines here.


class TestHeomsolveFunction:
@pytest.mark.parametrize(['evo'], [
Expand Down