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

Use Mollist2 as internal system information data structure #103

Merged
merged 13 commits into from
Aug 20, 2020
Merged

Conversation

liwt31
Copy link
Collaborator

@liwt31 liwt31 commented Jul 15, 2020

This the first commit for #100 . The majority of this commit is to use MolList2 as the only way to construct MPO. Now Mpo.__init__ is by default constructing MPOs using the general algorithm. The biggest modification apart from deleting old code is to correct the zero-point energies due to different definitions between MolList and MolList2. The quasiboson algorithm #25 , chain representation #90 , and hybrid TDH algorithm #61 are removed.

Apart from issues mentioned in #100, there are other things that are in the todo list:

  • The removal of hybrid TDH is not complete. Basically only the tests are removed. (Done in 4dd41ff)
  • The Bogoliubov formulation should be updated to work with MolList2. Although the algorithm is not necessary I'd like to keep the example. (The example is removed in 5d6b3a9)
  • The finite temperature SBM won't work because thermal propagation calculates electron occupations which are not defined for SBM. This is considered to be a side-effect of using "e" to represent spins. (Fixed in 5d6b3a9)

These things will be addressed in future commits.

Lastly, I observed that the svd compression test in test_mp.py won't pass for Mpo. I have to increase the bond dimension a little bit to let it pass. I tried to figure out why but I best speculation is that svd compression will stick into some local minimum when compressing the new Mpo structure by MolList2. After all, it is not easy to understand why Mpdm will work while Mpo will not, and the distance metric is actually rather strict for large tensor. I'll keep an eye on this though.

@liwt31
Copy link
Collaborator Author

liwt31 commented Jul 16, 2020

The second commit 9293d9d makes MolList a subclass of MolList2 and renames it to HolsteinModel. Now every mol_list in Reno is essentially MolList2.

  • Every if-else clause that distinguishes MolList and MolList2 is simplified and only the branch with MolList2 is retained.
  • The ModelTranslator mechanism is deleted because in principle every model should be converted to the general model first.
    • Currently, the SBM model is implemented in the HolsteinModel and it'll be moved to another class class SpinBosonModel(MolList2) in the future. (update: done in 778525c)
    • class VibronicModel(MolList2) should also be implemented. (update: done in e112ab9)
  • Tests based on cross-validation between different schemes and between MolList and MolList2 are deleted. The MPO constructed by MolList2 should be tested against qutip in small systems in the future, and this will be a part of this PR. (Done in 38ab90b)
  • I found scipy.linalg.norm sometimes produces a nan with perfectly normal input, so I changed all of them to np.linalg.norm.

I've thought of retaining MolList by making it just a wrapper for HolsteinModel for maximum backward compatibility but finally decided not to do so. But I'm happy to make one if you think it's necessary.

@liwt31
Copy link
Collaborator Author

liwt31 commented Jul 16, 2020

In e112ab9 the VibronicModel is implemented. The condition for MPO svd compression test in test_mp.py is further relaxed to make the test pass. I haven't found a clue why the test cannot pass yet. But the fact that in my machine the atol=1e-4 condition will work while in Travis it will not may indicate it's a numerical issue.

liwt31 added 3 commits July 17, 2020 10:07
Implement `SpinBosonModel` as a subclass of `MolList2`. Original interface in `HolsteinModel` is removed. Original `SpinBosonModel` in `sbm/sbm.py` is renamed to `SpinBosonDynamics`.
@liwt31
Copy link
Collaborator Author

liwt31 commented Jul 19, 2020

In the build for 4dd41ff I found a performance regression on finite temperature CV DMRG:

============================ slowest test durations ============================
89.19s call     renormalizer/cv/tests/test_abs.py::test_ft_abs

After some closer examination, It is concluded the regression is caused by the zero-point energy included in MolList2, and it is fixed by d6d6332. I don't understand what's going on @jiangtong1000 if you are interested you can have a look.

@liwt31
Copy link
Collaborator Author

liwt31 commented Jul 28, 2020

Contents of the last three commits:

  • MolList2 is renamed to Model. Almost all variables similar to mol_list or mlist is renamed to model.
  • A new interface is designed for Model.
    • The model dict in the original MolList2 is replaced by ham_terms list. The list elements are Op class (an augmented version of the original Op class). The representation is essentially sum-of-product.
    • The order argument is removed because basis now carries information on the DoF it contains.
  • The new interface relies on the enhancement of Op and BasisSet. Both the class is moved from utils directory to model directory.
    • In Op the DoF it represents must be specified via the dof argument. The arguments accept any hashable object, which means we can use simple int or tuple to represent the DoF instead of "e_1" etc.
    • In BasisSet the DoF also has to be specified.
  • VibronicModel introduced in e112ab9 is removed because drastic changes have been made to BasisSet and Op.

The new interface generally reduces boilerplate when constructing a new model, and most importantly the new interface is natural and consistent for different model and basis. An example in 1d-Heisenberg.ipynb:

ham_terms = []
for ispin in range(nspin-1):
    op1 = Op("sigma_z sigma_z", [ispin, ispin+1], 1.0/4)
    op2 = Op("sigma_+ sigma_-", [ispin, ispin+1], 1.0/2)
    op3 = Op("sigma_- sigma_+", [ispin, ispin+1], 1.0/2)
    ham_terms.extend([op1, op2, op3])

# set the spin order and local basis
basis = [BasisHalfSpin(i) for i in range(nspin)]

# construct Hamiltonian MPO
model = Model(basis, ham_terms)
mpo = Mpo(model)

By now the PR is almost complete. I understand the content could be overwhelming. I could introduce the changes in a coding group meeting if necessary.

@jjren
Copy link
Collaborator

jjren commented Jul 28, 2020

It's a rather big update. Thanks, Weitang!

@liwt31
Copy link
Collaborator Author

liwt31 commented Jul 28, 2020

Now the only problem is the SVD compress test for MPO in test_mp.py. My opinion is still that it is not a big issue and I'm happy to be corrected.

@liwt31 liwt31 changed the title [Work in Progress] Use Mollist2 as internal system information data structure Use Mollist2 as internal system information data structure Jul 28, 2020
self.qbtrunc = qbtrunc
self.base = int(round(n_phys_dim ** (1.0 / nqboson)))
self.hartree = hartree

if hartree:
Copy link
Collaborator

@jjren jjren Aug 10, 2020

Choose a reason for hiding this comment

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

Should we remove the hartree part?
The Hatree (SCF, TDH) is actually limited to Holstein model.
I think the same calculations could be carried out with DMRG/TD-DMRG with M=1.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed. I think it's OK to remove them.

@@ -161,7 +154,7 @@ def e0(self):

@property
def is_simple(self):
return self.force3rd == (0.0, 0.0) and self.nqboson == 1 and self.omega[0] == self.omega[1]
return self.force3rd == (0.0, 0.0) and self.omega[0] == self.omega[1]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the force3d is not needed anymore.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is now only used in TDH module. This feature will be removed along with the module.

mt.sigmaqn = np.zeros(mt.pdim_prod, dtype=np.int)
else:
mt.sigmaqn = self._get_sigmaqn(idx)
mt.sigmaqn = np.zeros_like(mt.sigmaqn)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is use_dummy_qn necessary or not now? we could set sigmaqn=0 to achieve the same purpose, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure about this. I'll try to remove this to see if it is necessary or not.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it would be more consistent to use sigmaqn=0 if not considering the quantum number.



@pytest.mark.parametrize("nsites", [5, 10])
# More states make MPO representation not efficient
Copy link
Collaborator

Choose a reason for hiding this comment

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

More sites?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Exactly

self.symbol: str = symbol
# replace " + " so that " " can be used to split the symbol ("b^\dagger + b")
# the logic of Op is based on multiplication of symbols. So special treatment on addition is inevitable
self.split_symbol : List[str] = [s.replace("plus", " + ") for s in symbol.replace(" + ", "plus").split(" ")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think it is better to replace " + " in defining "b^\dagger + b" with "b^\dagger+b"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well, this is a possible improvement but I usually prefer b^\dagger + b in writing LaTeX. In the near future, I don't think the change is necessary

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK.

Comment on lines 129 to 137
# Construct mapping from easy-to-manipulate integer to actual Op
# There has to be an Identity for the algorithm to work
# even if Identity is not in the table
primary_ops = {0: Op.identity()}
# unique operators with DoF names taken into consideration
# The inclusion of DoF names is necessary fo multi-dof basis.
unique_op: Set[Op] = set(np.array(table).ravel())
if primary_ops[0] in unique_op:
unique_op.remove(primary_ops[0])
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why Op.identity is necessary here (restricting the key to be 0).
Though the left-most in_ops is an identity operator physically, it is technically just a placeholder with qn=0 and corresponds to an additional column np.zeros attached to op_table to make the logic more consistent when looping from 0 to N-1.
For example primary_ops = {0:xxx, 1: op.indentity()} will also obtain the correct answer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The key could be anything. The problem is that if the operator "identity" is not present in the model, there will be actually no primary_ops key for the operator "identity". This is not a serious issue because the algorithm still works as you've pointed out, but I feel somewhat unhappy with the situation. I agree that the resulting code is ugly and hard to grasp. I'll clean it up and add a few comments on the code.

Copy link
Collaborator

@jjren jjren Aug 14, 2020

Choose a reason for hiding this comment

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

ok. If you think it is necessary to include operator "identity" in primary_ops, I think the key should be 0, to be consistent with the first and the last artificial column.

@liwt31
Copy link
Collaborator Author

liwt31 commented Aug 16, 2020

In this commit, the TDH module is removed. The removal of use_dummy_qn is not as easy as the removal of the TDH module. Apart from cases when qn is not preserved, the attribute is also used in the conj_trans method for MpDm as a result of the simple qn design (only one set of qn available). To remove conj_trans I also choose to remove the MpDmFull and SuperMPO class. MpDmFull relies on conj_trans and the dissipation dynamics are rarely used.

After this PR I think it'll be appropriate to list the obsolete features in the project wiki for future reference.

@jjren
Copy link
Collaborator

jjren commented Aug 20, 2020

I‘m ok with this PR. The qn problem in mpdm could be fixed in the future.
I plan to update the qn system in the near future to support more quantum numbers by replacing the single int with a tuple.
After that, mpdm could be characterized by the qn of <bra| and qn of |ket>. The transpose operation could work then.

@liwt31
Copy link
Collaborator Author

liwt31 commented Aug 20, 2020

The outdated CI information sees to be a persisted issue. I'm merging this PR because CI results have been manually checked.

@liwt31 liwt31 merged commit c6ea6d8 into master Aug 20, 2020
@jjren jjren deleted the clean_up_mpo branch October 8, 2020 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants