diff --git a/.gitignore b/.gitignore index be36bef..d583a15 100644 --- a/.gitignore +++ b/.gitignore @@ -88,7 +88,7 @@ CMakeCache.txt CMakeFiles CMakeScripts Testing -Makefile +build/Makefile cmake_install.cmake install_manifest.txt compile_commands.json diff --git a/.gitmodules b/.gitmodules index 98a6d70..9b2cb70 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,7 @@ path = ext/armadillo url = https://gitlab.com/conradsnicta/armadillo-code.git branch = 11.4.x +[submodule "ext/trompeloeil"] + path = ext/trompeloeil + url = /~https://github.com/rollbear/trompeloeil.git + branch = v43 diff --git a/Dockerfile b/Dockerfile index 7980791..cf35848 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,6 +37,12 @@ RUN cd $PKGDIR/catch2 && \ cmake -Bbuild -H. -DBUILD_TESTING=OFF && \ cmake --build build/ --target install +# Install trompeloeil +RUN cd $PKGDIR/trompeloeil && \ + mkdir build && cd build && \ + cmake -G "Unix Makefiles" .. && \ + cmake --build . --target install + RUN apt-get -y clean && \ rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index 7961934..d13f4d0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Installation ``` * Run the container: ``` - docker run --rm -it -v $(pwd)/tests:/home/tests ghcr.io/chiamin/hybridleads:latest + docker run --rm -it -v $(pwd):/home ghcr.io/chiamin/hybridleads:latest ``` **Note**: replace the image name `ghcr.io/chiamin/hybridleads:latest` by `hybridleads` if you're building the image by yourself. * Package version: @@ -27,6 +27,7 @@ Installation * `ITensor v3.1.11` * `armadillo 11.4.x` * `Catch2 v3.2.0` + * `trompeloeil v43` * Environment variables: The compiling flags for ITensor, * `CCCOM="g++ -m64 -std=c++17 -fconcepts -fPIC"` @@ -53,8 +54,9 @@ cmake -B build make -C build ``` -The resulting executable is ```test.exe``` (in ```tests/build/```). +The resulting executables are ```test_*.exe``` (in ```tests/build/```). +If mocking is needed in the unit test, one may consider [trompeloeil](/~https://github.com/rollbear/trompeloeil). Run other main executables -------------------------- @@ -65,8 +67,28 @@ make -e ``` to use those flags. -Then, for instance, one can run the executable ```itdvp/itdvp.exe``` by +Then, for instance, one can run the executable ```hybridleads/itdvp/itdvp.exe``` by ``` ./itdvp.exe input ``` -with the parameters been assigned in ```itdvp/input```. +with the parameters been assigned in ```itdvp/input```, +see [here](https://www.itensor.org/docs.cgi?vers=cppv3&page=formulas/input) for the more detailed documentation on input files. + +Dependency Management +--------------------- +Apart from the known package managers, like [conan](https://conan.io/) or [vcpkg](https://vcpkg.io/), here we adopt git submodule to do the dependency management for few reasons, (i) `ITensor` is unavailable on both ecosystems, and (ii) `itensor.utility` is a personal project and is also unavailable on both ecosystems. Git submodule is a compromised solution. + +1. To add a new submodule + + ``` + git submodule add -b {branch_name} {git_repo_url} {folder_name} + ``` + The optional directory name should be placed under `ext/`, i.e. `{folder_name}` should be something like `ext/{repo_name}`. + + If the above command fails, one could also try the manual checkout to specify the branch or tag afterwards. + +2. To update an existig submodule to its newest version + ``` + git submodule update --remote {repo_name} + ``` + or manually checkout to newer tag or version within the directory where the submodule seats in. diff --git a/check/Corr.h b/check/Corr.h deleted file mode 100644 index 4f68106..0000000 --- a/check/Corr.h +++ /dev/null @@ -1,219 +0,0 @@ -#ifndef __CORR_H_CMC__ -#define __CORR_H_CMC__ -#include "itensor/all.h" -#include "IUtility.h" -using namespace iutility; - -template -Mat exact_corr (const MPS& psi) -{ - int N = length (psi); - auto sites = SiteType (siteInds(psi)); - auto corr = Mat (N,N); - for(int i = 1; i <= N; i++) - for(int j = 1; j <= N; j++) - { - AutoMPO ampo (sites); - ampo += 1.,"Cdag",i,"C",j; - auto C = toMPO (ampo); - if constexpr (is_same_v ) - corr(i-1,j-1) = inner (psi, C, psi); - else - corr(i-1,j-1) = innerC (psi, C, psi); - } - return corr; -} - -template -Mat exact_corr2 (const MPS& psi) -{ - int N = length (psi); - auto sites = SiteType (siteInds(psi)); - auto corr = Mat (N,N); - auto phi = psi; - for(int i = 1; i <= N; i++) - for(int j = 1; j <= N; j++) - { - phi.ref(j) *= sites.op("C",j); - phi.ref(j).noPrime("Site"); - phi.ref(i) *= sites.op("Cdag",i); - phi.ref(i).noPrime("Site"); - if constexpr (is_same_v ) - corr(i-1,j-1) = inner (psi, phi); - else - corr(i-1,j-1) = innerC (psi, phi); - } - return corr; -} - -inline ITensor multSite (ITensor A, ITensor const& B) -{ - A *= B; - A.noPrime(); - return A; -} - -inline Index make_index (int m) -{ - //return Index (QN({"Nf",0}), m, Out, "Mix"); - return Index (m, "Mix"); -} - -ITensor make_expand_proj (Index ii, int m_extra=1) -{ - ii.dag(); - // Make projector between the original space (m) and the enlarged space (m+m_extra) - Index inew = make_index (dim(ii) + m_extra); - auto P_expand = ITensor (ii, inew); - for(int i = 1; i <= dim(ii); i++) - P_expand.set (ii=i, inew=i, 1.); - return P_expand; -} - -template -class Cdag_Set -{ - public: - Cdag_Set () {} - Cdag_Set (const MPS& psi, int ell, string spin_str=""); - void to_right (const MPS& psi, int ell, Args const& args=Args::global(), string spin_str=""); - Vec apply_C (const MPS& psi, int j, string spin_str) const; - int m () const { return dim(_si); } - - private: - vector _Us, _Proj; - ITensor _V; - int _ilast; - Index _si; // The "site" (left) index of _V - SiteType _sites; -}; - -template -Cdag_Set :: Cdag_Set (const MPS& psi, int ell, string spin_str) -: _ilast (0) -, _sites (siteInds(psi)) -{ - if (orthoCenter(psi) <= ell) { - cout << "Error: Cdag_Set :: init: orthogonality center must be > ell" << endl; - cout << " " << orthoCenter(psi) << ", " << ell << endl; - throw; - } - - _V = multSite (_sites.op("Cdag"+spin_str, ell), multSite (_sites.op("F",ell), psi(ell))); - _V *= prime (dag(psi(ell)), rightLinkIndex(psi,ell)); - - // Make a dummy site-index to C - _si = make_index (1); - _V *= setElt(_si=1); -} - -template -void Cdag_Set :: to_right (const MPS& psi, int ell, Args const& args, string spin_str) -{ - if (orthoCenter(psi) <= ell) { - cout << "Error: Cdag_Set :: to_right: orthogonality center must be > ell" << endl; - cout << " " << orthoCenter(psi) << ", " << ell << endl; - throw; - } - - // Apply the i-th transfer matrix - _V *= prime (dag(psi(ell)), "Link"); - _V *= multSite (_sites.op("F",ell), psi(ell)); - - // Compute the element for c_{i=ell} - ITensor c_l = multSite (_sites.op("Cdag"+spin_str,ell), multSite(_sites.op("F",ell), psi(ell))); - c_l *= prime (dag(psi(ell)), rightLinkIndex(psi,ell)); - - // ------- Add c_l to Cdag ------- - // - ITensor P_ex = make_expand_proj (_si); - _Proj.push_back (P_ex); - - _V *= P_ex; // expand _V - - // Add c_l into _V - Index si = findIndex (_V, "Mix"); - int m = dim(si); - _V += c_l * setElt(si=m); - // ------------------------------------------- - - // Truncate by SVD; keep the UV form - _Us.emplace_back (si); - ITensor Vtmp, D; - svd (_V, _Us.back(), D, Vtmp, args); - _V = D * Vtmp; - _si = commonIndex (_V, _Us.back()); - - _ilast++; -} - -template -Vec Cdag_Set :: apply_C (const MPS& psi, int j, string spin_str) const -{ - // Apply Cj - ITensor V = _V * multSite (_sites.op("C"+spin_str,j), psi(j)); - V *= prime (dag(psi(j)), rightLinkIndex (psi,j-1)); - - // Contract back _Us to get CidagCj - Vec cdagc (_ilast+1); - for(int i = _ilast; i > 0; i--) - { - int im = i-1; - V *= _Us.at(im); - const Index& ii = V.inds()(1); - cdagc(i) = eltT (V, ii=dim(ii)); - - V *= dag(_Proj.at(im)); // Project to left space (the column of U) - } - - Index si = V.inds()(1); - cdagc(0) = eltT (V, si=1); - - return cdagc; -} - -template -Mat Measure_corr (MPS psi, Args const& args=Args::global(), string spin_str="") -{ - int i1 = args.getInt("corr_ibeg",1); - int i2 = args.getInt("corr_iend",length(psi)); - int N = i2 - i1 + 1; - - auto corr = Mat (N,N); - Cdag_Set Cdag; - - auto sp = SiteType (siteInds(psi)); - int mmax = 0; - for(int j = i1; j <= i2; j++) - { - psi.position(j); - int jc = j - i1; - - // Diagonal - ITensor tmp = multSite (sp.op("N"+spin_str,j), psi(j)); - ITensor ci = tmp * dag(psi(j)); - corr(jc,jc) = eltT (ci); - - if (j == i1) continue; - else if (j == i1+1) - Cdag = Cdag_Set (psi, i1, spin_str); // Initialize C - else - Cdag.to_right (psi, j-1, args, spin_str); // Extend C to the next site - - // Off-diagonal - auto cdagc = Cdag.apply_C (psi, j, spin_str); - for(int i = i1; i < j; i++) - { - int ic = i - i1; - auto c = cdagc (ic); - corr(ic,jc) = c; - corr(jc,ic) = conjT (c); - } - - if (Cdag.m() > mmax) - mmax = Cdag.m(); - } - cout << "max dim in Measure_corr = " << mmax << endl; - return corr; -} -#endif diff --git a/check/MyObserver.h b/check/MyObserver.h deleted file mode 100644 index e21f842..0000000 --- a/check/MyObserver.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __MYOBSERVER_H_CMC__ -#define __MYOBSERVER_H_CMC__ -#include "itensor/all.h" -using namespace itensor; - -template -class MyObserver : public DMRGObserver -{ - public: - MyObserver (const SitesType& sites, const MPS& psi, const Args& args = Args::global()) - : DMRGObserver (psi, args) - , _sites (sites) - , _ns (length(psi)+1,0.) - , _Npar (0.) - { - _write = args.getBool ("Write",false); - _write_minm = args.getInt ("out_minm",0); - _out_dir = args.getString("out_dir","."); - _ConserveNf = args.getBool ("ConserveNf",true); - } - - void measure (const Args& args = Args::global()); - - Real Npar () const { return _Npar; } - - private: - bool _write; - string _out_dir; // empty string "" if not write - int _write_minm; - vector _iDel, _jDel; - vector _Delta; - bool _ConserveNf; - SitesType _sites; - - vector _ns; - Real _Npar; -}; - -Real Onsite_mea (const ITensor& A, const ITensor& op) -{ - ITensor re = A * op; - re.noPrime ("Site"); - re *= dag(A); - return re.real(); -} - -template -void MyObserver :: measure (const Args& args) -{ - DMRGObserver::measure (args); - - // Define your measurements below - // Call psi() to access the MPS - // - auto N = length(psi()); - auto b = args.getInt("AtBond",1); - auto sw = args.getInt("Sweep",0); - auto ha = args.getInt("HalfSweep",0); - auto energy = args.getReal("Energy",0); - - // On-site measure - int oc = orthoCenter(psi()); - if (oc == N || ha == 2) // measure during the second half of sweep - { - // Density - ITensor n_op = _sites.op("N",oc); - Real ni = Onsite_mea (psi().A(oc), n_op); - cout << "\tn " << oc << " = " << ni << endl; - } - - if (oc == 1 && ha == 2) - { - int m = args.getInt("MaxDim"); - // Write MPS - if (_write && m >= _write_minm) - { - writeToFile (_out_dir+"/psi"+"_m"+to_string(m)+".mps", psi()); - } - } -} - -#endif diff --git a/check/TDVPObserver.h b/check/TDVPObserver.h deleted file mode 100644 index a76300a..0000000 --- a/check/TDVPObserver.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef __TDVPOBSERVER_H_CMC__ -#define __TDVPOBSERVER_H_CMC__ -#include -#include -#include "itensor/all.h" -#include "Entanglement.h" -#include "ContainerUtility.h" -using namespace vectool; -using namespace iutility; - -template -class TDVPObserver : public DMRGObserver -{ - public: - TDVPObserver (const SitesType& sites, const MPS& psi, const Args& args=Args::global()) - : DMRGObserver (psi, args) - , _sites (sites) - , _ns (length(psi),0.) - , _Npar (0.) - , _specs (length(psi)) - { - _write = args.getBool ("Write",false); - _out_dir = args.getString("out_dir","."); - } - - void measure (const Args& args); - - Real Npar () const { return _Npar; } - auto const& ns () const { return _ns; } - const Spectrum& spec (int i) const { return _specs.at(i); } - - private: - bool _write; - string _out_dir; // empty string "" if not write - SitesType _sites; - - // Observables - vector _ns; - Real _Npar; - vector _specs; -}; - -template -void TDVPObserver :: measure (const Args& args) -{ - DMRGObserver::measure (args); - - cout << scientific << setprecision(14); - // Define your measurements below - // Call psi() to access the MPS - // - auto N = length(psi()); - auto b = args.getInt("AtBond"); - auto sw = args.getInt("Sweep"); - auto ha = args.getInt("HalfSweep"); - auto energy = args.getReal("Energy",0); - - if (b != N) - _specs.at(b) = spectrum(); - - int oc = orthoCenter(psi()); - int nc = args.getInt("NumCenter"); - // measure during the second half of sweep - if ((nc == 2 && oc == N) || ha == 2) - { - // Density - ITensor n_tmp = psi().A(oc) * _sites.op("N",oc); - n_tmp.noPrime("Site"); - n_tmp *= dag(psi().A(oc)); - Real ni = toReal (n_tmp); - cout << "\tn " << oc << " = " << ni << endl; - _ns.at(oc-1) = ni; - - // Entanglement entropy - Real S = EntangEntropy (spectrum()); - cout << "\tentang entropy " << oc << " = " << S << endl; - } - - // At the end of a sweep - if (oc == 1 && ha == 2 && b == 1) - { - for(int i = 1; i < N; i++) - { - cout << "\tbond dim " << i << " = " << dim(rightLinkIndex (psi(), i)) << endl; - } - - if (_write) - { - cout << "write MPS" << endl; - writeToFile (_out_dir+"/psi.mps", psi()); - } - } -} -#endif diff --git a/check/check.cc b/check/check.cc deleted file mode 100644 index 28cc6ea..0000000 --- a/check/check.cc +++ /dev/null @@ -1,406 +0,0 @@ -#include "itensor/all.h" -#include "ReadInput.h" -#include "IUtility.h" -#include "MyObserver.h" -#include "MPSUtility.h" -#include "tdvp.h" -#include "TDVPObserver.h" -#include "Corr.h" -//#include "/home/chiamin/Project/2021/topological_qubit/code/mixedbasis/git/SingleParticle.h" -using namespace itensor; -using namespace std; - -Matrix Hamilt_k (int L, Real t, Real mu, Real damp_fac=1., bool damp_left=true, bool verbose=false) -{ - cout << "L = " << L << endl; - Matrix H (L,L); - for(int i = 0; i < L; i++) - { - H(i,i) = -mu; - if (i != L-1) - { - int damp_dist = (damp_left ? L-2-i : i); - Real ti = t * pow (damp_fac, damp_dist); - H(i,i+1) = -ti; - H(i+1,i) = -ti; - if (verbose) - cout << "Hk, t " << i << " = " << ti << endl; - } - } - return H; -} - -int get_Npar (const Matrix& Hk, Real mu) -{ - Matrix U; - Vector ens; - diagHermitian (Hk, U, ens); - - int N = 0; - for(int i = 0; i < ens.size(); i++) - { - auto en = ens(i); - if (en < mu) - N += 1; - } - return N; -} - -vector n_product_state (int L, int Np) -{ - vector nstr (L); - for(int i = 0; i < L; i++) - { - string state; - if (i % 2 == 0 and Np > 0) - { - nstr.at(i) = "Occ"; - Np--; - } - else - nstr.at(i) = "Emp"; - } - if (Np > 0) - { - for(int i = 0; i < L; i++) - if (Np > 0 and nstr.at(i) == "Emp") - { - nstr.at(i) = "Occ"; - Np--; - } - } - return nstr; -} - -void set_initstate (InitState& init, int Np, int i1, int i2) -{ - int L = i2 - i1 + 1; - if (Np < 0 or Np > L) - { - cout << "Error: not valid Np: " << Np << endl; - cout << "L = " << L << endl; - throw; - } - auto nstr = n_product_state (L, Np); - for(int i = i1; i <= i2; i++) - init.set (i, nstr.at(i-i1)); -} - -MPS make_initstate (const Fermion& sites, int Np) -{ - int N = length(sites); - int Npar = Np; - InitState init (sites); - for(int i = 1; i <= N; i++) - { - string state; - if (i % 2 == 0 && Np-- > 0) - state = "Occ"; - else - state = "Emp"; - init.set (i, state); - cout << i << ": " << state << endl; - } - if (Np > 0) - { - for(int i = 1; i <= N; i += 2) - if (Np-- > 0) - init.set (i,"Occ"); - } - auto psi = MPS (init); - - auto Nmpo = Make_NMPO (sites); - auto Ntot = inner (psi,Nmpo,psi); - if (Ntot != Npar) - { - cout << "particle number not match:" << Ntot << " " << Npar << endl; - throw; - } - cout << "Ntot = " << Ntot << endl; - return psi; -} - -AutoMPO set_H (const SiteSet& sites, int L_lead, int L_device, Real t, Real mu, Real V, bool periodic=false) -{ - int N = length(sites); - AutoMPO ampo (sites); - for(int i = 1; i <= N; ++i) - { - if (i != N) - { - ampo += -t,"Cdag",i,"C",i+1; - ampo += -t,"Cdag",i+1,"C",i; - cout << "H t " << i << " " << t << endl; - } - if (mu != 0.) - ampo += -mu,"N",i; - } - if (periodic) - { - ampo += -t,"Cdag",N,"C",1; - ampo += -t,"Cdag",1,"C",N; - cout << "H t " << N << " " << t << endl; - } - - int Ri = L_lead + L_device; - for(int i = L_lead+1; i <= Ri; i++) - for(int j = L_lead+1; j < i; j++) - { - ampo += V,"N",i,"N",j; - } - return ampo; -} - -AutoMPO set_H (const SiteSet& sites, int L_lead, int L_device, - Real t_lead, Real t_device, Real t_contact, - Real muL, Real muS, Real muR, Real V, bool periodic=false) -{ - int N = length (sites); - AutoMPO ampo (sites); - for(int i = 1; i <= N; ++i) - { - if (i != N) - { - Real t; - if (i == L_lead or i == L_lead+L_device) - t = t_contact; - else if (i < L_lead or i > L_lead+L_device) - t = t_lead; - else - t = t_device; - ampo += -t,"Cdag",i,"C",i+1; - ampo += -t,"Cdag",i+1,"C",i; - cout << "H t " << i << " " << t << endl; - } - { - Real mu; - if (i <= L_lead) - mu = muL; - else if (i <= L_lead+L_device) - mu = muS; - else - mu = muR; - ampo += -mu,"N",i; - cout << "H mu " << i << " " << mu << endl; - } - } - if (periodic) - { - ampo += -t_lead,"Cdag",N,"C",1; - ampo += -t_lead,"Cdag",1,"C",N; - cout << "H t " << N << " " << t_lead << endl; - } - - int Ri = L_lead + L_device; - for(int i = L_lead+1; i <= Ri; i++) - for(int j = L_lead+1; j < i; j++) - { - ampo += V,"N",i,"N",j; - cout << "H V " << V << " " << i << " " << j << endl; - } - return ampo; -} - -AutoMPO set_H_SC (const SiteSet& sites, int L_lead, int L_device, - Real t_lead, Real t_device, Real t_contact, - Real muL, Real muS, Real muR, Real V, Real Delta) -{ - int N = length (sites); - AutoMPO ampo (sites); - for(int i = 1; i <= N; ++i) - { - if (i != N) - { - Real t; - if (i == L_lead or i == L_lead+L_device) - t = t_contact; - else if (i < L_lead or i > L_lead+L_device) - t = t_lead; - else - t = t_device; - ampo += -t,"Cdag",i,"C",i+1; - ampo += -t,"Cdag",i+1,"C",i; - cout << "H t " << i << " " << t << endl; - } - { - Real mu; - if (i <= L_lead) - mu = muL; - else if (i <= L_lead+L_device) - mu = muS; - else - mu = muR; - ampo += -mu,"N",i; - cout << "H mu " << i << " " << mu << endl; - } - } - - int Ri = L_lead + L_device; - if (V != 0.) - { - for(int i = L_lead+1; i <= Ri; i++) - for(int j = L_lead+1; j < i; j++) - { - ampo += V,"N",i,"N",j; - cout << "H V " << V << " " << i << " " << j << endl; - } - } - // SC - if (Delta != 0.) - { - for(int i = L_lead+1; i < Ri; i++) - { - ampo += -Delta,"C",i,"C",i+1; - ampo += -Delta,"Cdag",i+1,"Cdag",i; - } - } - return ampo; -} - -template -ITensor print_wf (const MPSType& psi) -{ - ITensor pp (1.); - vector iis; - for(int i = 1; i <= length(psi); i++) - { - pp *= psi(i); - auto is = findIndex (psi(i), "Site,0"); - iis.push_back (is); - if constexpr (is_same_v ) - iis.push_back (prime(is)); - } - pp.permute (iis); - PrintData(pp); - return pp; -} - -Real den (const SiteSet& sites, const MPS& psi, int i) -{ - AutoMPO ampo (sites); - ampo += 1.,"N",i; - auto n = toMPO (ampo); - return inner(psi,n,psi); -} - -Real den (const SiteSet& sites, const MPS& psi, int i1, int i2) -{ - Real n = 0.; - for(int i = i1; i <= i2; i++) - n += den (sites, psi, i); - return n; -} - -int main(int argc, char* argv[]) -{ - string infile = argv[1]; - InputGroup input (infile,"basic"); - - auto L_lead = input.getInt("L_lead"); - auto L_device = input.getInt("L_device"); - int L = 2*L_lead + L_device; - auto t_lead = input.getReal("t_lead"); - auto t_device = input.getReal("t_device"); - auto t_contact = input.getReal("t_contact"); - auto mu_leadL = input.getReal("mu_leadL"); - auto mu_leadR = input.getReal("mu_leadR"); - auto mu_device = input.getReal("mu_device"); - auto V = input.getReal("V"); - auto Delta = input.getReal("Delta"); - - auto do_write = input.getYesNo("write_to_file"); - auto out_dir = input.getString("outdir","."); - auto out_minm = input.getInt("out_minm",0); - auto ConserveQNs = input.getYesNo("ConserveQNs",true); - auto ConserveNf = input.getYesNo("ConserveNf",true); - auto WriteDim = input.getInt("WriteDim",-1); - auto sweeps = iutility::Read_sweeps (infile, "DMRG"); - - auto mu_biasL = input.getReal("mu_biasL"); - auto mu_biasS = input.getReal("mu_biasS"); - auto mu_biasR = input.getReal("mu_biasR"); - auto dt = input.getReal("dt"); - auto time_steps = input.getInt("time_steps"); - auto NumCenter = input.getInt("NumCenter"); - auto Truncate = input.getYesNo("Truncate"); - auto sweepst = iutility::Read_sweeps (infile, "TDVP"); - - cout << "device site = " << L_lead+1 << " " << (L_lead + L_device) << endl; - cout << setprecision(14); - // Site set - using SitesType = Fermion; - auto sites = SitesType (L, {"ConserveQNs",ConserveQNs,"ConserveNf",ConserveNf}); - - // ----------- Getting initial state ---------------- - // Compute particle numbers - auto Hk_L = Hamilt_k (L_lead, t_lead, mu_leadL); - auto Hk_R = Hamilt_k (L_lead, t_lead, mu_leadR); - auto Hk_S = Hamilt_k (L_device, t_device, mu_device); - - int Np_L = get_Npar (Hk_L, mu_leadL+mu_biasL); - int Np_R = get_Npar (Hk_R, mu_leadR+mu_biasR); - int Np_S = get_Npar (Hk_S, mu_device+mu_biasS); - cout << "Np L,R,S = " << Np_L << " " << Np_R << " " << Np_S << endl; - - // Initialze MPS for DMRG - InitState init (sites); - set_initstate (init, Np_L, 1, L_lead); - set_initstate (init, Np_S, L_lead+1, L_lead+L_device); - set_initstate (init, Np_R, L_lead+L_device+1, L); - auto psi = MPS (init); - psi.position(1); - - cout << "Np_L = " << den (sites, psi, 1, L_lead) << endl; - cout << "Np_S = " << den (sites, psi, L_lead+1, L_lead+L_device) << endl; - cout << "Np_R = " << den (sites, psi, L_lead+L_device+1, L) << endl; - cout << "Ntot = " << den (sites, psi, 1, L) << endl; - - // Make initial-state Hamiltonian MPO - auto ampo = set_H (sites, L_lead, L_device, t_lead, t_device, 0., mu_leadL+mu_biasL, mu_device+mu_biasS, mu_leadR+mu_biasR, V); - auto H = toMPO (ampo); - cout << "MPO dim = " << maxLinkDim(H) << endl; - - // DMRG to find the initial state - Real en0 = dmrg (psi, H, sweeps); // Get ground state - cout << "Initial energy = " << en0 << endl; - - cout << "Np_L = " << den (sites, psi, 1, L_lead) << endl; - cout << "Np_S = " << den (sites, psi, L_lead+1, L_lead+L_device) << endl; - cout << "Np_R = " << den (sites, psi, L_lead+L_device+1, L) << endl; - cout << "Ntot = " << den (sites, psi, 1, L) << endl; - // -------------------------------------------------------------------------- - - // ------------------ Time evolution --------------------- - // Define time-evolution Hamiltonian MPO - ampo = set_H (sites, L_lead, L_device, t_lead, t_device, t_contact, mu_leadL, mu_device, mu_leadR, V); - auto Ht = toMPO (ampo); - - // Define current operator MPO - int N = length (psi); - vector spec_links = {L_lead, L_lead+L_device}; - vector JMPOs (N); - for(int i = 1; i < L; i++) - { - AutoMPO ampoj (sites); - ampoj += 1.,"Cdag",i,"C",i+1; - JMPOs.at(i) = toMPO(ampoj); - } - - // Time evolution by using TDVP - Args args_tdvp = {"Quiet",true,"NumCenter",NumCenter,"DoNormalize",true,"Truncate",Truncate}; - auto obs = TDVPObserver (sites, psi); - for(int step = 1; step <= time_steps; step++) - { - cout << "step = " << step << endl; - tdvp (psi, Ht, 1_i*dt, sweepst, obs, args_tdvp); - - // Measure currents by MPO - for(int j = 1; j < L; j++) - { - auto Jtmp = innerC (psi, JMPOs.at(j), psi); - auto J = -2. * imag(Jtmp); - cout << "\t*current spec " << j << " " << j+1 << " = " << J << endl; - } - } - return 0; -} diff --git a/check/tdvp.h b/check/tdvp.h deleted file mode 100644 index 1ae52c6..0000000 --- a/check/tdvp.h +++ /dev/null @@ -1,416 +0,0 @@ -#ifndef __ITENSOR_TDVP_H -#define __ITENSOR_TDVP_H - -#include "itensor/iterativesolvers.h" -#include "itensor/mps/localmposet.h" -#include "itensor/mps/sweeps.h" -#include "itensor/mps/DMRGObserver.h" -#include "itensor/util/cputime.h" - -namespace itensor { - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& PH, - Cplx t, - const Sweeps& sweeps, - const Args& args = Args::global()); - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& PH, - Cplx t, - const Sweeps& sweeps, - DMRGObserver & obs, - Args args = Args::global()); - -// -// Available TDVP methods: -// second order integrator: sweep left-to-right and right-to-left -// - -// -//TDVP with an MPO -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - const Sweeps& sweeps, - const Args& args = Args::global()) - { - LocalMPO PH(H,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,args); - return energy; - } - -// -//TDVP with an MPO and custom DMRGObserver -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - const Sweeps& sweeps, - DMRGObserver & obs, - const Args& args = Args::global()) - { - LocalMPO PH(H,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - -// -//TDVP with an MPO and boundary tensors LH, RH -// LH - H1 - H2 - ... - HN - RH -//(ok if one or both of LH, RH default constructed) -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - ITensor const& LH, - ITensor const& RH, - const Sweeps& sweeps, - const Args& args = Args::global()) - { - LocalMPO PH(H,LH,RH,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,args); - return energy; - } - -// -//TDVP with an MPO and boundary tensors LH, RH -//and a custom observer -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - ITensor const& LH, - ITensor const& RH, - const Sweeps& sweeps, - DMRGObserver& obs, - const Args& args = Args::global()) - { - LocalMPO PH(H,LH,RH,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - -// -//TDVP with a set of MPOs (lazily summed) -//(H vector is 0-indexed) -// -Real inline -tdvp(MPS& psi, - std::vector const& Hset, - Cplx t, - const Sweeps& sweeps, - const Args& args = Args::global()) - { - LocalMPOSet PH(Hset,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,args); - return energy; - } - -// -//TDVP with a set of MPOs and a custom DMRGObserver -//(H vector is 0-indexed) -// -Real inline -tdvp(MPS & psi, - std::vector const& Hset, - Cplx t, - const Sweeps& sweeps, - DMRGObserver& obs, - const Args& args = Args::global()) - { - LocalMPOSet PH(Hset,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - - -// -// TDVPWorker -// - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& PH, - Cplx t, - Sweeps const& sweeps, - Args const& args) - { - DMRGObserver obs(psi,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - -vector reach_max_dim (const MPS& psi, int maxdim) -{ - int N = length(psi); - vector re (N); - for(int b = 1; b < N; b++) - { - int m = dim (rightLinkIndex (psi, b)); - bool reach_max = (m >= maxdim ? true : false); - re.at(b) = reach_max; - } - return re; -} - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& H, - Cplx t, - Sweeps const& sweeps, - DMRGObserver& obs, - Args args) -{ - // Truncate blocks of degenerate singular values (or not) - args.add("RespectDegenerate",args.getBool("RespectDegenerate",true)); - - const bool silent = args.getBool("Silent",false); - if(silent) - { - args.add("Quiet",true); - args.add("PrintEigs",false); - args.add("NoMeasure",true); - args.add("DebugLevel",-1); - } - const bool quiet = args.getBool("Quiet",false); - const int debug_level = args.getInt("DebugLevel",(quiet ? -1 : 0)); - const bool mixNumCenter = args.getBool("mixNumCenter",false); - - const int N = length(psi); - Real energy = NAN; - - auto halfSweep = args.getString("HalfSweep",""); - if (halfSweep == "toLeft") - psi.position(N); - else - psi.position(1); - - args.add("DebugLevel",debug_level); - - for(int sw = 1; sw <= sweeps.nsweep(); ++sw) - { - int numCenter = args.getInt("NumCenter",2); - - args.add("Truncate",true); - - cpu_time sw_time; - args.add("Sweep",sw); - args.add("NSweep",sweeps.nsweep()); - args.add("Cutoff",sweeps.cutoff(sw)); - args.add("MinDim",sweeps.mindim(sw)); - args.add("MaxDim",sweeps.maxdim(sw)); - args.add("MaxIter",sweeps.niter(sw)); - - vector is_maxdim; - if (mixNumCenter) - { - is_maxdim = reach_max_dim (psi, args.getInt("MaxDim")); - } - - if(!H.doWrite() - && args.defined("WriteDim") - && sweeps.maxdim(sw) >= args.getInt("WriteDim")) - { - if(!quiet) - { - println("\nTurning on write to disk, write_dir = ", - args.getString("WriteDir","./")); - } - - //psi.doWrite(true); - H.doWrite(true,args); - } - - // 0, 1 and 2-site wavefunctions - ITensor phi0,phi1; - Spectrum spec; - for(int b = 1, ha = 1; ha <= 2; ) - { - if (halfSweep == "toRight" && ha == 2) - continue; - else if (halfSweep == "toLeft" && ha == 1) - continue; - - if(!quiet) - printfln("Sweep=%d, HS=%d, Bond=%d/%d",sw,ha,b,(N-1)); - - // Forward propagation - H.numCenter(numCenter); - H.position(b,psi); - - if(numCenter == 2) - phi1 = psi(b)*psi(b+1); - else if(numCenter == 1) - phi1 = psi(b); - - applyExp(H,phi1,-t/2,args); - - if(args.getBool("DoNormalize",true)) - phi1 /= norm(phi1); - - if(numCenter == 2) - spec = psi.svdBond(b,phi1,(ha==1 ? Fromleft : Fromright),H,args); - else if(numCenter == 1) - psi.ref(b) = phi1; - - // Calculate energy - ITensor H_phi1; - H.product(phi1,H_phi1); - energy = real(eltC(dag(phi1)*H_phi1)); - - - // mixed Nc - if (mixNumCenter) - { - int maxdim = sweeps.maxdim(sw); - if (ha == 1) - { - if (numCenter == 2 and b < N-1 and !is_maxdim.at(b) and is_maxdim.at(b+1)) - { - phi1 = psi(b+1); - numCenter = 1; - b += 1; - } - } - else if (ha == 2) - { - if (numCenter == 2 and b > 1 and !is_maxdim.at(b) and is_maxdim.at(b-1)) - { - phi1 = psi(b); - numCenter = 1; - } - } - } - - // Backward propagation - if((ha == 1 && b+numCenter-1 != N) || (ha == 2 && b != 1)) - { - auto b1 = (ha == 1 ? b+1 : b); - - if(numCenter == 2) - { - phi0 = psi(b1); - } - else if(numCenter == 1) - { - Index l; - if(ha == 1) l = commonIndex(psi(b),psi(b+1)); - else l = commonIndex(psi(b-1),psi(b)); - ITensor U,S,V(l); - spec = svd(phi1,U,S,V,args); - psi.ref(b) = U; - phi0 = S*V; - } - - H.numCenter(numCenter-1); - H.position(b1,psi); - - applyExp(H,phi0,+t/2,args); - - if(args.getBool("DoNormalize",true)) - phi0 /= norm(phi0); - - if(numCenter == 2) - { - psi.ref(b1) = phi0; - } - if(numCenter == 1) - { - if(ha == 1) - { - psi.ref(b+1) *= phi0; - psi.leftLim(b); - psi.rightLim(b+2); - } - else - { - psi.ref(b-1) *= phi0; - psi.leftLim(b-2); - psi.rightLim(b); - } - } - - // Calculate energy - ITensor H_phi0; - H.product(phi0,H_phi0); - energy = real(eltC(dag(phi0)*H_phi0)); - } - - if(!quiet) - { - printfln(" Truncated to Cutoff=%.1E, Min_dim=%d, Max_dim=%d", - sweeps.cutoff(sw), - sweeps.mindim(sw), - sweeps.maxdim(sw) ); - printfln(" Trunc. err=%.1E, States kept: %s", - spec.truncerr(), - showDim(linkIndex(psi,b)) ); - } - - obs.lastSpectrum(spec); - - args.add("AtBond",b); - args.add("HalfSweep",ha); - args.add("Energy",energy); - args.add("Truncerr",spec.truncerr()); - - obs.measure(args); - - // Next sweep - if (mixNumCenter) - { - int maxdim = sweeps.maxdim(sw); - if (ha == 1) - { - if (numCenter == 1 and b != N and is_maxdim.at(b) and !is_maxdim.at(b+1)) - { - numCenter = 2; - } - } - else if (ha == 2) - { - if (numCenter == 1 and b > 2 and is_maxdim.at(b-1) and !is_maxdim.at(b-2)) - { - numCenter = 2; - b -= 1; - } - } - } - sweepnext(b,ha,N,{"NumCenter=",numCenter}); - } //for loop over b - - if(!silent) - { - auto sm = sw_time.sincemark(); - printfln(" Sweep %d/%d CPU time = %s (Wall time = %s)", - sw,sweeps.nsweep(),showtime(sm.time),showtime(sm.wall)); - } - - if(obs.checkDone(args)) break; - - } //for loop over sw - - if(args.getBool("DoNormalize",true)) - { - //if(numCenter==1) psi.position(1); - psi.normalize(); - } - - return energy; -} - -} //namespace itensor - -#endif diff --git a/dmrg/MyObserver.h b/dmrg/MyObserver.h deleted file mode 100644 index e89cb74..0000000 --- a/dmrg/MyObserver.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef __MYOBSERVER_H_CMC__ -#define __MYOBSERVER_H_CMC__ -#include "itensor/all.h" -using namespace itensor; - -template -class MyObserver : public DMRGObserver -{ - public: - MyObserver (const SitesType& sites, const MPS& psi, const Args& args = Args::global()) - : DMRGObserver (psi, args) - , _sites (sites) - , _ns (length(psi)+1,0.) - , _Npar (0.) - { - _write = args.getBool ("Write",false); - _write_minm = args.getInt ("out_minm",0); - _out_dir = args.getString("out_dir","."); - _ConserveNf = args.getBool ("ConserveNf",true); - } - - void measure (const Args& args = Args::global()); - - Real Npar () const { return _Npar; } - - private: - bool _write; - string _out_dir; // empty string "" if not write - int _write_minm; - vector _iDel, _jDel; - vector _Delta; - bool _ConserveNf; - SitesType _sites; - - vector _ns; - Real _Npar; -}; - -Real Onsite_mea (const ITensor& A, const ITensor& op) -{ - ITensor re = A * op; - re.noPrime ("Site"); - re *= dag(A); - return re.real(); -} - -template -void MyObserver :: measure (const Args& args) -{ - DMRGObserver::measure (args); - - // Define your measurements below - // Call psi() to access the MPS - // - auto N = length(psi()); - auto b = args.getInt("AtBond",1); - auto sw = args.getInt("Sweep",0); - auto ha = args.getInt("HalfSweep",0); - auto energy = args.getReal("Energy",0); - - // On-site measure - int oc = orthoCenter(psi()); -/* if (oc == N || ha == 2) // measure during the second half of sweep - { - ITensor nup_op = _sites.op("Nup",oc), - ndn_op = _sites.op("Ndn",oc); - Real n_up = Onsite_mea (psi().A(oc), nup_op), - n_dn = Onsite_mea (psi().A(oc), ndn_op); - - Real ni = n_up + n_dn, - sz = 0.5*(n_up-n_dn); - - _Npar += ni - _ns.at(oc); - _ns.at(oc) = ni; - - cout << " Measure on site " << oc << endl; - cout << " n_up = " << n_up << endl; - cout << " n_dn = " << n_dn << endl; - cout << " N_tot = " << ni << endl; - cout << " sz = " << sz << endl; - }*/ - - if (oc == 1 && ha == 2) - { - int m = args.getInt("MaxDim"); - // Write MPS - if (_write && m >= _write_minm) - { - writeToFile (_out_dir+"/psi"+"_m"+to_string(m)+".mps", psi()); - } - } -} - -#endif diff --git a/dmrg/run.cc b/dmrg/run.cc deleted file mode 100644 index 560ef21..0000000 --- a/dmrg/run.cc +++ /dev/null @@ -1,196 +0,0 @@ -#include "itensor/all.h" -#include "ReadInput.h" -#include "IUtility.h" -#include "MyObserver.h" -#include "../itdvp/GlobalIndices.h" -#include "GenMPO.h" -using namespace itensor; -using namespace std; - -template -MPO current_correlation (const SitesType& sites, int i) -{ - int N = length(sites); - AutoMPO ampo (sites); - if constexpr (is_same_v ) - { - ampo += -1_i,"Cdag",i,"C",i+1; - ampo += 1_i,"Cdag",i+1,"C",i; - ampo += 1_i,"Cdag",N-i,"C",N-i+1; - ampo += -1_i,"Cdag",N-i+1,"C",N-i; - } - - return toMPO (ampo); -} - -Cplx myinner (const MPO& mpo, const MPS& psi) -{ - auto L = ITensor(1.); - int N = length(psi); - for(int i = 1; i <= N; i++) - { - L *= psi(i) * mpo(i); - auto dagA = dag(psi(i)); - dagA.prime ("Site"); - if (i == 1) - { - auto ii = commonIndex (psi(i), psi(i+1), "Link"); - dagA.prime (ii); - } - else if (i == N) - { - auto ii = commonIndex (psi(i), psi(i-1), "Link"); - dagA.prime (ii); - } - else - { - dagA.prime ("Link"); - } - L *= dagA; - } - return eltC(L); -} - -MPS toMPS (const SiteSet& sites, - const ITensor& AL, const Index& iALl, const Index& iALr, const Index& iALs, - const ITensor& AR, const Index& iARl, const Index& iARr, const Index& iARs, - const ITensor& AC, const Index& iACl, const Index& iACr, const Index& iACs, - int oc=1) -{ - MPS psi (sites); - int N = length (psi); - - // Set tensors - vector ils(N+1), irs(N+1), iss(N+1); - for(int i = 1; i <= N; i++) - { - Index il, ir; - if (i < oc) - { - psi.ref(i) = AL; - ils.at(i) = iALl; - irs.at(i) = iALr; - iss.at(i) = iALs; - } - else if (i == oc) - { - psi.ref(i) = AC; - ils.at(i) = iACl; - irs.at(i) = iACr; - iss.at(i) = iACs; - } - else - { - psi.ref(i) = AR; - ils.at(i) = iARl; - irs.at(i) = iARr; - iss.at(i) = iARs; - } - assert (hasIndex (psi(i), ils.at(i))); - assert (hasIndex (psi(i), irs.at(i))); - } - - // Replace indices - auto inew = noPrime (sim (irs.at(1))); - psi.ref(1).replaceInds ({iss.at(1), irs.at(1)}, {sites(1), inew}); - irs.at(1) = inew; - for(int i = 2; i < N; i++) - { - inew = noPrime(sim(irs.at(i))); - psi.ref(i).replaceInds ({iss.at(i), ils.at(i), irs.at(i)}, {sites(i), irs.at(i-1), inew}); - ils.at(i) = irs.at(i-1); - irs.at(i) = inew; - } - psi.ref(N).replaceInds ({iss.at(N), ils.at(N)}, {sites(N), irs.at(N-1)}); - ils.at(N) = irs.at(N-1); - - // Check - for(int i = 1; i <= N; i++) - { - assert (hasIndex (psi(i), sites(i))); - if (i != N) - assert (commonIndex (psi(i), psi(i+1))); - } - - return psi; -} - -int main(int argc, char* argv[]) -{ - string infile = argv[1]; - InputGroup input (infile,"basic"); - - auto L = input.getInt("L"); - auto imp_site = input.getInt("imp_site"); - auto t = input.getReal("t"); - auto t_impL = input.getReal("t_impL"); - auto t_impR = input.getReal("t_impR"); - auto muL = input.getReal("muL"); - auto muR = input.getReal("muR"); - auto mu_imp = input.getReal("mu_imp"); - auto V = input.getReal("V"); - auto V_impL = input.getReal("V_impL"); - auto V_impR = input.getReal("V_impR"); - auto read_dir_L = input.getString("read_dir_L"); - auto read_dir_R = input.getString("read_dir_R"); - - auto WriteDim = input.getInt("WriteDim",-1); - auto do_write = input.getYesNo("write_to_file"); - auto out_dir = input.getString("outdir","."); - auto out_minm = input.getInt("out_minm",0); - auto H_file = input.getString("H_outfile","H.mpo"); - auto ConserveQNs = input.getYesNo("ConserveQNs",false); - auto sweeps = Read_sweeps (infile); - - // Read the tensors - ITensor AL_L, AR_L, AC_L, LW_L, RW_L; - ITensor AL_R, AR_R, AC_R, LW_R, RW_R; - readFromFile (read_dir_L+"/AL.itensor", AL_L); - readFromFile (read_dir_L+"/AR.itensor", AR_L); - readFromFile (read_dir_L+"/AC.itensor", AC_L); - readFromFile (read_dir_L+"/LW.itensor", LW_L); - readFromFile (read_dir_L+"/RW.itensor", RW_L); - readFromFile (read_dir_R+"/AL.itensor", AL_R); - readFromFile (read_dir_R+"/AR.itensor", AR_R); - readFromFile (read_dir_R+"/AC.itensor", AC_R); - readFromFile (read_dir_R+"/LW.itensor", LW_R); - readFromFile (read_dir_R+"/RW.itensor", RW_R); - GlobalIndices ISL, ISR; - ISL.read (read_dir_L+"/global.inds"); - ISR.read (read_dir_R+"/global.inds"); - - // Site set - using SitesType = Fermion; - auto sites = SitesType (L, {"ConserveQNs",ConserveQNs}); - - // Initialze MPS - MPS psi = toMPS (sites, AL_L, ISL.il(), prime(ISL.il(),2), ISL.is(), - AR_R, prime(ISR.ir(),2), ISR.ir(), ISR.is(), - AC_L, ISL.il(), ISL.ir(), ISL.is()); - psi.position(1); - assert (commonIndex (LW_L, psi(1))); - assert (commonIndex (RW_R, psi(L))); - - // Make MPO - MPO H = single_empurity_mpo (sites, imp_site, t, t_impL, t_impR, muL, muR, mu_imp, V, V_impL, V_impR); - to_inf_mpo (H, ISL.iwl(), ISR.iwr()); - assert (commonIndex (LW_L, H(1))); - assert (commonIndex (RW_R, H(L))); - - // Write to files - writeToFile (out_dir+"/"+H_file, H); - - // DMRG - MyObserver myobs (sites, psi, {"Write",do_write,"out_dir",out_dir,"out_minm",out_minm}); - dmrg (psi, H, LW_L, RW_R, sweeps, myobs, {"WriteDim",WriteDim}); -/* - int N = length(sites); - for(int i = 1; i <= N/2; i++) - { - auto J = current_correlation (sites, i); - auto j = myinner (J, psi); - cout << " " << i << " = " << j << endl; - } -*/ - return 0; -} diff --git a/ext/trompeloeil b/ext/trompeloeil new file mode 160000 index 0000000..ba406bf --- /dev/null +++ b/ext/trompeloeil @@ -0,0 +1 @@ +Subproject commit ba406bfd71caf63f2180990a390ff5518e570c21 diff --git a/hybridleads/check/Corr.h b/hybridleads/check/Corr.h new file mode 100644 index 0000000..f9a8fbf --- /dev/null +++ b/hybridleads/check/Corr.h @@ -0,0 +1,213 @@ +#ifndef __CORR_H_CMC__ +#define __CORR_H_CMC__ +#include "IUtility.h" +#include "itensor/all.h" +using namespace iutility; + +template +Mat exact_corr(const MPS& psi) { + int N = length(psi); + auto sites = SiteType(siteInds(psi)); + auto corr = Mat(N, N); + for (int i = 1; i <= N; i++) + for (int j = 1; j <= N; j++) { + AutoMPO ampo(sites); + ampo += 1., "Cdag", i, "C", j; + auto C = toMPO(ampo); + if constexpr (is_same_v) + corr(i - 1, j - 1) = inner(psi, C, psi); + else + corr(i - 1, j - 1) = innerC(psi, C, psi); + } + return corr; +} + +template +Mat exact_corr2(const MPS& psi) { + int N = length(psi); + auto sites = SiteType(siteInds(psi)); + auto corr = Mat(N, N); + auto phi = psi; + for (int i = 1; i <= N; i++) + for (int j = 1; j <= N; j++) { + phi.ref(j) *= sites.op("C", j); + phi.ref(j).noPrime("Site"); + phi.ref(i) *= sites.op("Cdag", i); + phi.ref(i).noPrime("Site"); + if constexpr (is_same_v) + corr(i - 1, j - 1) = inner(psi, phi); + else + corr(i - 1, j - 1) = innerC(psi, phi); + } + return corr; +} + +inline ITensor multSite(ITensor A, ITensor const& B) { + A *= B; + A.noPrime(); + return A; +} + +inline Index make_index(int m) { + // return Index (QN({"Nf",0}), m, Out, "Mix"); + return Index(m, "Mix"); +} + +ITensor make_expand_proj(Index ii, int m_extra = 1) { + ii.dag(); + // Make projector between the original space (m) and the enlarged space + // (m+m_extra) + Index inew = make_index(dim(ii) + m_extra); + auto P_expand = ITensor(ii, inew); + for (int i = 1; i <= dim(ii); i++) P_expand.set(ii = i, inew = i, 1.); + return P_expand; +} + +template +class Cdag_Set { + public: + Cdag_Set() {} + Cdag_Set(const MPS& psi, int ell, string spin_str = ""); + void to_right(const MPS& psi, int ell, Args const& args = Args::global(), + string spin_str = ""); + Vec apply_C(const MPS& psi, int j, string spin_str) const; + int m() const { return dim(_si); } + + private: + vector _Us, _Proj; + ITensor _V; + int _ilast; + Index _si; // The "site" (left) index of _V + SiteType _sites; +}; + +template +Cdag_Set::Cdag_Set(const MPS& psi, int ell, string spin_str) + : _ilast(0), _sites(siteInds(psi)) { + if (orthoCenter(psi) <= ell) { + cout << "Error: Cdag_Set :: init: orthogonality center " + "must be > ell" + << endl; + cout << " " << orthoCenter(psi) << ", " << ell << endl; + throw; + } + + _V = multSite(_sites.op("Cdag" + spin_str, ell), + multSite(_sites.op("F", ell), psi(ell))); + _V *= prime(dag(psi(ell)), rightLinkIndex(psi, ell)); + + // Make a dummy site-index to C + _si = make_index(1); + _V *= setElt(_si = 1); +} + +template +void Cdag_Set::to_right(const MPS& psi, int ell, + Args const& args, string spin_str) { + if (orthoCenter(psi) <= ell) { + cout << "Error: Cdag_Set :: to_right: orthogonality " + "center must be > ell" + << endl; + cout << " " << orthoCenter(psi) << ", " << ell << endl; + throw; + } + + // Apply the i-th transfer matrix + _V *= prime(dag(psi(ell)), "Link"); + _V *= multSite(_sites.op("F", ell), psi(ell)); + + // Compute the element for c_{i=ell} + ITensor c_l = multSite(_sites.op("Cdag" + spin_str, ell), + multSite(_sites.op("F", ell), psi(ell))); + c_l *= prime(dag(psi(ell)), rightLinkIndex(psi, ell)); + + // ------- Add c_l to Cdag ------- + // + ITensor P_ex = make_expand_proj(_si); + _Proj.push_back(P_ex); + + _V *= P_ex; // expand _V + + // Add c_l into _V + Index si = findIndex(_V, "Mix"); + int m = dim(si); + _V += c_l * setElt(si = m); + // ------------------------------------------- + + // Truncate by SVD; keep the UV form + _Us.emplace_back(si); + ITensor Vtmp, D; + svd(_V, _Us.back(), D, Vtmp, args); + _V = D * Vtmp; + _si = commonIndex(_V, _Us.back()); + + _ilast++; +} + +template +Vec Cdag_Set::apply_C(const MPS& psi, int j, + string spin_str) const { + // Apply Cj + ITensor V = _V * multSite(_sites.op("C" + spin_str, j), psi(j)); + V *= prime(dag(psi(j)), rightLinkIndex(psi, j - 1)); + + // Contract back _Us to get CidagCj + Vec cdagc(_ilast + 1); + for (int i = _ilast; i > 0; i--) { + int im = i - 1; + V *= _Us.at(im); + const Index& ii = V.inds()(1); + cdagc(i) = eltT(V, ii = dim(ii)); + + V *= dag(_Proj.at(im)); // Project to left space (the column of U) + } + + Index si = V.inds()(1); + cdagc(0) = eltT(V, si = 1); + + return cdagc; +} + +template +Mat Measure_corr(MPS psi, Args const& args = Args::global(), + string spin_str = "") { + int i1 = args.getInt("corr_ibeg", 1); + int i2 = args.getInt("corr_iend", length(psi)); + int N = i2 - i1 + 1; + + auto corr = Mat(N, N); + Cdag_Set Cdag; + + auto sp = SiteType(siteInds(psi)); + int mmax = 0; + for (int j = i1; j <= i2; j++) { + psi.position(j); + int jc = j - i1; + + // Diagonal + ITensor tmp = multSite(sp.op("N" + spin_str, j), psi(j)); + ITensor ci = tmp * dag(psi(j)); + corr(jc, jc) = eltT(ci); + + if (j == i1) + continue; + else if (j == i1 + 1) + Cdag = Cdag_Set(psi, i1, spin_str); // Initialize C + else + Cdag.to_right(psi, j - 1, args, spin_str); // Extend C to the next site + + // Off-diagonal + auto cdagc = Cdag.apply_C(psi, j, spin_str); + for (int i = i1; i < j; i++) { + int ic = i - i1; + auto c = cdagc(ic); + corr(ic, jc) = c; + corr(jc, ic) = conjT(c); + } + + if (Cdag.m() > mmax) mmax = Cdag.m(); + } + cout << "max dim in Measure_corr = " << mmax << endl; + return corr; +} +#endif diff --git a/hybridleads/check/MyObserver.h b/hybridleads/check/MyObserver.h new file mode 100644 index 0000000..e19802a --- /dev/null +++ b/hybridleads/check/MyObserver.h @@ -0,0 +1,77 @@ +#ifndef __MYOBSERVER_H_CMC__ +#define __MYOBSERVER_H_CMC__ +#include "itensor/all.h" +using namespace itensor; + +template +class MyObserver : public DMRGObserver { + public: + MyObserver(const SitesType& sites, const MPS& psi, + const Args& args = Args::global()) + : DMRGObserver(psi, args), + _sites(sites), + _ns(length(psi) + 1, 0.), + _Npar(0.) { + _write = args.getBool("Write", false); + _write_minm = args.getInt("out_minm", 0); + _out_dir = args.getString("out_dir", "."); + _ConserveNf = args.getBool("ConserveNf", true); + } + + void measure(const Args& args = Args::global()); + + Real Npar() const { return _Npar; } + + private: + bool _write; + string _out_dir; // empty string "" if not write + int _write_minm; + vector _iDel, _jDel; + vector _Delta; + bool _ConserveNf; + SitesType _sites; + + vector _ns; + Real _Npar; +}; + +Real Onsite_mea(const ITensor& A, const ITensor& op) { + ITensor re = A * op; + re.noPrime("Site"); + re *= dag(A); + return re.real(); +} + +template +void MyObserver::measure(const Args& args) { + DMRGObserver::measure(args); + + // Define your measurements below + // Call psi() to access the MPS + // + auto N = length(psi()); + auto b = args.getInt("AtBond", 1); + auto sw = args.getInt("Sweep", 0); + auto ha = args.getInt("HalfSweep", 0); + auto energy = args.getReal("Energy", 0); + + // On-site measure + int oc = orthoCenter(psi()); + if (oc == N || ha == 2) // measure during the second half of sweep + { + // Density + ITensor n_op = _sites.op("N", oc); + Real ni = Onsite_mea(psi().A(oc), n_op); + cout << "\tn " << oc << " = " << ni << endl; + } + + if (oc == 1 && ha == 2) { + int m = args.getInt("MaxDim"); + // Write MPS + if (_write && m >= _write_minm) { + writeToFile(_out_dir + "/psi" + "_m" + to_string(m) + ".mps", psi()); + } + } +} + +#endif diff --git a/hybridleads/check/TDVPObserver.h b/hybridleads/check/TDVPObserver.h new file mode 100644 index 0000000..ef043e1 --- /dev/null +++ b/hybridleads/check/TDVPObserver.h @@ -0,0 +1,89 @@ +#ifndef __TDVPOBSERVER_H_CMC__ +#define __TDVPOBSERVER_H_CMC__ +#include +#include + +#include "ContainerUtility.h" +#include "Entanglement.h" +#include "itensor/all.h" +using namespace vectool; +using namespace iutility; + +template +class TDVPObserver : public DMRGObserver { + public: + TDVPObserver(const SitesType& sites, const MPS& psi, + const Args& args = Args::global()) + : DMRGObserver(psi, args), + _sites(sites), + _ns(length(psi), 0.), + _Npar(0.), + _specs(length(psi)) { + _write = args.getBool("Write", false); + _out_dir = args.getString("out_dir", "."); + } + + void measure(const Args& args); + + Real Npar() const { return _Npar; } + auto const& ns() const { return _ns; } + const Spectrum& spec(int i) const { return _specs.at(i); } + + private: + bool _write; + string _out_dir; // empty string "" if not write + SitesType _sites; + + // Observables + vector _ns; + Real _Npar; + vector _specs; +}; + +template +void TDVPObserver::measure(const Args& args) { + DMRGObserver::measure(args); + + cout << scientific << setprecision(14); + // Define your measurements below + // Call psi() to access the MPS + // + auto N = length(psi()); + auto b = args.getInt("AtBond"); + auto sw = args.getInt("Sweep"); + auto ha = args.getInt("HalfSweep"); + auto energy = args.getReal("Energy", 0); + + if (b != N) _specs.at(b) = spectrum(); + + int oc = orthoCenter(psi()); + int nc = args.getInt("NumCenter"); + // measure during the second half of sweep + if ((nc == 2 && oc == N) || ha == 2) { + // Density + ITensor n_tmp = psi().A(oc) * _sites.op("N", oc); + n_tmp.noPrime("Site"); + n_tmp *= dag(psi().A(oc)); + Real ni = toReal(n_tmp); + cout << "\tn " << oc << " = " << ni << endl; + _ns.at(oc - 1) = ni; + + // Entanglement entropy + Real S = EntangEntropy(spectrum()); + cout << "\tentang entropy " << oc << " = " << S << endl; + } + + // At the end of a sweep + if (oc == 1 && ha == 2 && b == 1) { + for (int i = 1; i < N; i++) { + cout << "\tbond dim " << i << " = " << dim(rightLinkIndex(psi(), i)) + << endl; + } + + if (_write) { + cout << "write MPS" << endl; + writeToFile(_out_dir + "/psi.mps", psi()); + } + } +} +#endif diff --git a/check/analysis.py b/hybridleads/check/analysis.py similarity index 100% rename from check/analysis.py rename to hybridleads/check/analysis.py diff --git a/hybridleads/check/check.cc b/hybridleads/check/check.cc new file mode 100644 index 0000000..451b978 --- /dev/null +++ b/hybridleads/check/check.cc @@ -0,0 +1,365 @@ +#include "Corr.h" +#include "IUtility.h" +#include "MPSUtility.h" +#include "MyObserver.h" +#include "ReadInput.h" +#include "TDVPObserver.h" +#include "itensor/all.h" +#include "tdvp.h" +// #include +// "/home/chiamin/Project/2021/topological_qubit/code/mixedbasis/git/SingleParticle.h" +using namespace itensor; +using namespace std; + +Matrix Hamilt_k(int L, Real t, Real mu, Real damp_fac = 1., + bool damp_left = true, bool verbose = false) { + cout << "L = " << L << endl; + Matrix H(L, L); + for (int i = 0; i < L; i++) { + H(i, i) = -mu; + if (i != L - 1) { + int damp_dist = (damp_left ? L - 2 - i : i); + Real ti = t * pow(damp_fac, damp_dist); + H(i, i + 1) = -ti; + H(i + 1, i) = -ti; + if (verbose) cout << "Hk, t " << i << " = " << ti << endl; + } + } + return H; +} + +int get_Npar(const Matrix& Hk, Real mu) { + Matrix U; + Vector ens; + diagHermitian(Hk, U, ens); + + int N = 0; + for (int i = 0; i < ens.size(); i++) { + auto en = ens(i); + if (en < mu) N += 1; + } + return N; +} + +vector n_product_state(int L, int Np) { + vector nstr(L); + for (int i = 0; i < L; i++) { + string state; + if (i % 2 == 0 and Np > 0) { + nstr.at(i) = "Occ"; + Np--; + } else + nstr.at(i) = "Emp"; + } + if (Np > 0) { + for (int i = 0; i < L; i++) + if (Np > 0 and nstr.at(i) == "Emp") { + nstr.at(i) = "Occ"; + Np--; + } + } + return nstr; +} + +void set_initstate(InitState& init, int Np, int i1, int i2) { + int L = i2 - i1 + 1; + if (Np < 0 or Np > L) { + cout << "Error: not valid Np: " << Np << endl; + cout << "L = " << L << endl; + throw; + } + auto nstr = n_product_state(L, Np); + for (int i = i1; i <= i2; i++) init.set(i, nstr.at(i - i1)); +} + +MPS make_initstate(const Fermion& sites, int Np) { + int N = length(sites); + int Npar = Np; + InitState init(sites); + for (int i = 1; i <= N; i++) { + string state; + if (i % 2 == 0 && Np-- > 0) + state = "Occ"; + else + state = "Emp"; + init.set(i, state); + cout << i << ": " << state << endl; + } + if (Np > 0) { + for (int i = 1; i <= N; i += 2) + if (Np-- > 0) init.set(i, "Occ"); + } + auto psi = MPS(init); + + auto Nmpo = Make_NMPO(sites); + auto Ntot = inner(psi, Nmpo, psi); + if (Ntot != Npar) { + cout << "particle number not match:" << Ntot << " " << Npar << endl; + throw; + } + cout << "Ntot = " << Ntot << endl; + return psi; +} + +AutoMPO set_H(const SiteSet& sites, int L_lead, int L_device, Real t, Real mu, + Real V, bool periodic = false) { + int N = length(sites); + AutoMPO ampo(sites); + for (int i = 1; i <= N; ++i) { + if (i != N) { + ampo += -t, "Cdag", i, "C", i + 1; + ampo += -t, "Cdag", i + 1, "C", i; + cout << "H t " << i << " " << t << endl; + } + if (mu != 0.) ampo += -mu, "N", i; + } + if (periodic) { + ampo += -t, "Cdag", N, "C", 1; + ampo += -t, "Cdag", 1, "C", N; + cout << "H t " << N << " " << t << endl; + } + + int Ri = L_lead + L_device; + for (int i = L_lead + 1; i <= Ri; i++) + for (int j = L_lead + 1; j < i; j++) { + ampo += V, "N", i, "N", j; + } + return ampo; +} + +AutoMPO set_H(const SiteSet& sites, int L_lead, int L_device, Real t_lead, + Real t_device, Real t_contact, Real muL, Real muS, Real muR, + Real V, bool periodic = false) { + int N = length(sites); + AutoMPO ampo(sites); + for (int i = 1; i <= N; ++i) { + if (i != N) { + Real t; + if (i == L_lead or i == L_lead + L_device) + t = t_contact; + else if (i < L_lead or i > L_lead + L_device) + t = t_lead; + else + t = t_device; + ampo += -t, "Cdag", i, "C", i + 1; + ampo += -t, "Cdag", i + 1, "C", i; + cout << "H t " << i << " " << t << endl; + } + { + Real mu; + if (i <= L_lead) + mu = muL; + else if (i <= L_lead + L_device) + mu = muS; + else + mu = muR; + ampo += -mu, "N", i; + cout << "H mu " << i << " " << mu << endl; + } + } + if (periodic) { + ampo += -t_lead, "Cdag", N, "C", 1; + ampo += -t_lead, "Cdag", 1, "C", N; + cout << "H t " << N << " " << t_lead << endl; + } + + int Ri = L_lead + L_device; + for (int i = L_lead + 1; i <= Ri; i++) + for (int j = L_lead + 1; j < i; j++) { + ampo += V, "N", i, "N", j; + cout << "H V " << V << " " << i << " " << j << endl; + } + return ampo; +} + +AutoMPO set_H_SC(const SiteSet& sites, int L_lead, int L_device, Real t_lead, + Real t_device, Real t_contact, Real muL, Real muS, Real muR, + Real V, Real Delta) { + int N = length(sites); + AutoMPO ampo(sites); + for (int i = 1; i <= N; ++i) { + if (i != N) { + Real t; + if (i == L_lead or i == L_lead + L_device) + t = t_contact; + else if (i < L_lead or i > L_lead + L_device) + t = t_lead; + else + t = t_device; + ampo += -t, "Cdag", i, "C", i + 1; + ampo += -t, "Cdag", i + 1, "C", i; + cout << "H t " << i << " " << t << endl; + } + { + Real mu; + if (i <= L_lead) + mu = muL; + else if (i <= L_lead + L_device) + mu = muS; + else + mu = muR; + ampo += -mu, "N", i; + cout << "H mu " << i << " " << mu << endl; + } + } + + int Ri = L_lead + L_device; + if (V != 0.) { + for (int i = L_lead + 1; i <= Ri; i++) + for (int j = L_lead + 1; j < i; j++) { + ampo += V, "N", i, "N", j; + cout << "H V " << V << " " << i << " " << j << endl; + } + } + // SC + if (Delta != 0.) { + for (int i = L_lead + 1; i < Ri; i++) { + ampo += -Delta, "C", i, "C", i + 1; + ampo += -Delta, "Cdag", i + 1, "Cdag", i; + } + } + return ampo; +} + +template +ITensor print_wf(const MPSType& psi) { + ITensor pp(1.); + vector iis; + for (int i = 1; i <= length(psi); i++) { + pp *= psi(i); + auto is = findIndex(psi(i), "Site,0"); + iis.push_back(is); + if constexpr (is_same_v) iis.push_back(prime(is)); + } + pp.permute(iis); + PrintData(pp); + return pp; +} + +Real den(const SiteSet& sites, const MPS& psi, int i) { + AutoMPO ampo(sites); + ampo += 1., "N", i; + auto n = toMPO(ampo); + return inner(psi, n, psi); +} + +Real den(const SiteSet& sites, const MPS& psi, int i1, int i2) { + Real n = 0.; + for (int i = i1; i <= i2; i++) n += den(sites, psi, i); + return n; +} + +int main(int argc, char* argv[]) { + string infile = argv[1]; + InputGroup input(infile, "basic"); + + auto L_lead = input.getInt("L_lead"); + auto L_device = input.getInt("L_device"); + int L = 2 * L_lead + L_device; + auto t_lead = input.getReal("t_lead"); + auto t_device = input.getReal("t_device"); + auto t_contact = input.getReal("t_contact"); + auto mu_leadL = input.getReal("mu_leadL"); + auto mu_leadR = input.getReal("mu_leadR"); + auto mu_device = input.getReal("mu_device"); + auto V = input.getReal("V"); + auto Delta = input.getReal("Delta"); + + auto do_write = input.getYesNo("write_to_file"); + auto out_dir = input.getString("outdir", "."); + auto out_minm = input.getInt("out_minm", 0); + auto ConserveQNs = input.getYesNo("ConserveQNs", true); + auto ConserveNf = input.getYesNo("ConserveNf", true); + auto WriteDim = input.getInt("WriteDim", -1); + auto sweeps = iutility::Read_sweeps(infile, "DMRG"); + + auto mu_biasL = input.getReal("mu_biasL"); + auto mu_biasS = input.getReal("mu_biasS"); + auto mu_biasR = input.getReal("mu_biasR"); + auto dt = input.getReal("dt"); + auto time_steps = input.getInt("time_steps"); + auto NumCenter = input.getInt("NumCenter"); + auto Truncate = input.getYesNo("Truncate"); + auto sweepst = iutility::Read_sweeps(infile, "TDVP"); + + cout << "device site = " << L_lead + 1 << " " << (L_lead + L_device) << endl; + cout << setprecision(14); + // Site set + using SitesType = Fermion; + auto sites = + SitesType(L, {"ConserveQNs", ConserveQNs, "ConserveNf", ConserveNf}); + + // ----------- Getting initial state ---------------- + // Compute particle numbers + auto Hk_L = Hamilt_k(L_lead, t_lead, mu_leadL); + auto Hk_R = Hamilt_k(L_lead, t_lead, mu_leadR); + auto Hk_S = Hamilt_k(L_device, t_device, mu_device); + + int Np_L = get_Npar(Hk_L, mu_leadL + mu_biasL); + int Np_R = get_Npar(Hk_R, mu_leadR + mu_biasR); + int Np_S = get_Npar(Hk_S, mu_device + mu_biasS); + cout << "Np L,R,S = " << Np_L << " " << Np_R << " " << Np_S << endl; + + // Initialze MPS for DMRG + InitState init(sites); + set_initstate(init, Np_L, 1, L_lead); + set_initstate(init, Np_S, L_lead + 1, L_lead + L_device); + set_initstate(init, Np_R, L_lead + L_device + 1, L); + auto psi = MPS(init); + psi.position(1); + + cout << "Np_L = " << den(sites, psi, 1, L_lead) << endl; + cout << "Np_S = " << den(sites, psi, L_lead + 1, L_lead + L_device) << endl; + cout << "Np_R = " << den(sites, psi, L_lead + L_device + 1, L) << endl; + cout << "Ntot = " << den(sites, psi, 1, L) << endl; + + // Make initial-state Hamiltonian MPO + auto ampo = + set_H(sites, L_lead, L_device, t_lead, t_device, 0., mu_leadL + mu_biasL, + mu_device + mu_biasS, mu_leadR + mu_biasR, V); + auto H = toMPO(ampo); + cout << "MPO dim = " << maxLinkDim(H) << endl; + + // DMRG to find the initial state + Real en0 = dmrg(psi, H, sweeps); // Get ground state + cout << "Initial energy = " << en0 << endl; + + cout << "Np_L = " << den(sites, psi, 1, L_lead) << endl; + cout << "Np_S = " << den(sites, psi, L_lead + 1, L_lead + L_device) << endl; + cout << "Np_R = " << den(sites, psi, L_lead + L_device + 1, L) << endl; + cout << "Ntot = " << den(sites, psi, 1, L) << endl; + // -------------------------------------------------------------------------- + + // ------------------ Time evolution --------------------- + // Define time-evolution Hamiltonian MPO + ampo = set_H(sites, L_lead, L_device, t_lead, t_device, t_contact, mu_leadL, + mu_device, mu_leadR, V); + auto Ht = toMPO(ampo); + + // Define current operator MPO + int N = length(psi); + vector spec_links = {L_lead, L_lead + L_device}; + vector JMPOs(N); + for (int i = 1; i < L; i++) { + AutoMPO ampoj(sites); + ampoj += 1., "Cdag", i, "C", i + 1; + JMPOs.at(i) = toMPO(ampoj); + } + + // Time evolution by using TDVP + Args args_tdvp = {"Quiet", true, "NumCenter", NumCenter, + "DoNormalize", true, "Truncate", Truncate}; + auto obs = TDVPObserver(sites, psi); + for (int step = 1; step <= time_steps; step++) { + cout << "step = " << step << endl; + tdvp(psi, Ht, 1_i * dt, sweepst, obs, args_tdvp); + + // Measure currents by MPO + for (int j = 1; j < L; j++) { + auto Jtmp = innerC(psi, JMPOs.at(j), psi); + auto J = -2. * imag(Jtmp); + cout << "\t*current spec " << j << " " << j + 1 << " = " << J << endl; + } + } + return 0; +} diff --git a/check/input b/hybridleads/check/input similarity index 100% rename from check/input rename to hybridleads/check/input diff --git a/hybridleads/check/tdvp.h b/hybridleads/check/tdvp.h new file mode 100644 index 0000000..0ccbf08 --- /dev/null +++ b/hybridleads/check/tdvp.h @@ -0,0 +1,327 @@ +#ifndef __ITENSOR_TDVP_H +#define __ITENSOR_TDVP_H + +#include "itensor/iterativesolvers.h" +#include "itensor/mps/DMRGObserver.h" +#include "itensor/mps/localmposet.h" +#include "itensor/mps/sweeps.h" +#include "itensor/util/cputime.h" + +namespace itensor { + +template +Real TDVPWorker(MPS& psi, LocalOpT& PH, Cplx t, const Sweeps& sweeps, + const Args& args = Args::global()); + +template +Real TDVPWorker(MPS& psi, LocalOpT& PH, Cplx t, const Sweeps& sweeps, + DMRGObserver& obs, Args args = Args::global()); + +// +// Available TDVP methods: +// second order integrator: sweep left-to-right and right-to-left +// + +// +// TDVP with an MPO +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, const Sweeps& sweeps, + const Args& args = Args::global()) { + LocalMPO PH(H, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, args); + return energy; +} + +// +// TDVP with an MPO and custom DMRGObserver +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, const Sweeps& sweeps, + DMRGObserver& obs, const Args& args = Args::global()) { + LocalMPO PH(H, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +// +// TDVP with an MPO and boundary tensors LH, RH +// LH - H1 - H2 - ... - HN - RH +//(ok if one or both of LH, RH default constructed) +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, ITensor const& LH, + ITensor const& RH, const Sweeps& sweeps, + const Args& args = Args::global()) { + LocalMPO PH(H, LH, RH, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, args); + return energy; +} + +// +// TDVP with an MPO and boundary tensors LH, RH +// and a custom observer +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, ITensor const& LH, + ITensor const& RH, const Sweeps& sweeps, DMRGObserver& obs, + const Args& args = Args::global()) { + LocalMPO PH(H, LH, RH, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +// +// TDVP with a set of MPOs (lazily summed) +//(H vector is 0-indexed) +// +Real inline tdvp(MPS& psi, std::vector const& Hset, Cplx t, + const Sweeps& sweeps, const Args& args = Args::global()) { + LocalMPOSet PH(Hset, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, args); + return energy; +} + +// +// TDVP with a set of MPOs and a custom DMRGObserver +//(H vector is 0-indexed) +// +Real inline tdvp(MPS& psi, std::vector const& Hset, Cplx t, + const Sweeps& sweeps, DMRGObserver& obs, + const Args& args = Args::global()) { + LocalMPOSet PH(Hset, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +// +// TDVPWorker +// + +template +Real TDVPWorker(MPS& psi, LocalOpT& PH, Cplx t, Sweeps const& sweeps, + Args const& args) { + DMRGObserver obs(psi, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +vector reach_max_dim(const MPS& psi, int maxdim) { + int N = length(psi); + vector re(N); + for (int b = 1; b < N; b++) { + int m = dim(rightLinkIndex(psi, b)); + bool reach_max = (m >= maxdim ? true : false); + re.at(b) = reach_max; + } + return re; +} + +template +Real TDVPWorker(MPS& psi, LocalOpT& H, Cplx t, Sweeps const& sweeps, + DMRGObserver& obs, Args args) { + // Truncate blocks of degenerate singular values (or not) + args.add("RespectDegenerate", args.getBool("RespectDegenerate", true)); + + const bool silent = args.getBool("Silent", false); + if (silent) { + args.add("Quiet", true); + args.add("PrintEigs", false); + args.add("NoMeasure", true); + args.add("DebugLevel", -1); + } + const bool quiet = args.getBool("Quiet", false); + const int debug_level = args.getInt("DebugLevel", (quiet ? -1 : 0)); + const bool mixNumCenter = args.getBool("mixNumCenter", false); + + const int N = length(psi); + Real energy = NAN; + + auto halfSweep = args.getString("HalfSweep", ""); + if (halfSweep == "toLeft") + psi.position(N); + else + psi.position(1); + + args.add("DebugLevel", debug_level); + + for (int sw = 1; sw <= sweeps.nsweep(); ++sw) { + int numCenter = args.getInt("NumCenter", 2); + + args.add("Truncate", true); + + cpu_time sw_time; + args.add("Sweep", sw); + args.add("NSweep", sweeps.nsweep()); + args.add("Cutoff", sweeps.cutoff(sw)); + args.add("MinDim", sweeps.mindim(sw)); + args.add("MaxDim", sweeps.maxdim(sw)); + args.add("MaxIter", sweeps.niter(sw)); + + vector is_maxdim; + if (mixNumCenter) { + is_maxdim = reach_max_dim(psi, args.getInt("MaxDim")); + } + + if (!H.doWrite() && args.defined("WriteDim") && + sweeps.maxdim(sw) >= args.getInt("WriteDim")) { + if (!quiet) { + println("\nTurning on write to disk, write_dir = ", + args.getString("WriteDir", "./")); + } + + // psi.doWrite(true); + H.doWrite(true, args); + } + + // 0, 1 and 2-site wavefunctions + ITensor phi0, phi1; + Spectrum spec; + for (int b = 1, ha = 1; ha <= 2;) { + if (halfSweep == "toRight" && ha == 2) + continue; + else if (halfSweep == "toLeft" && ha == 1) + continue; + + if (!quiet) printfln("Sweep=%d, HS=%d, Bond=%d/%d", sw, ha, b, (N - 1)); + + // Forward propagation + H.numCenter(numCenter); + H.position(b, psi); + + if (numCenter == 2) + phi1 = psi(b) * psi(b + 1); + else if (numCenter == 1) + phi1 = psi(b); + + applyExp(H, phi1, -t / 2, args); + + if (args.getBool("DoNormalize", true)) phi1 /= norm(phi1); + + if (numCenter == 2) + spec = psi.svdBond(b, phi1, (ha == 1 ? Fromleft : Fromright), H, args); + else if (numCenter == 1) + psi.ref(b) = phi1; + + // Calculate energy + ITensor H_phi1; + H.product(phi1, H_phi1); + energy = real(eltC(dag(phi1) * H_phi1)); + + // mixed Nc + if (mixNumCenter) { + int maxdim = sweeps.maxdim(sw); + if (ha == 1) { + if (numCenter == 2 and b < N - 1 and !is_maxdim.at(b) and + is_maxdim.at(b + 1)) { + phi1 = psi(b + 1); + numCenter = 1; + b += 1; + } + } else if (ha == 2) { + if (numCenter == 2 and b > 1 and !is_maxdim.at(b) and + is_maxdim.at(b - 1)) { + phi1 = psi(b); + numCenter = 1; + } + } + } + + // Backward propagation + if ((ha == 1 && b + numCenter - 1 != N) || (ha == 2 && b != 1)) { + auto b1 = (ha == 1 ? b + 1 : b); + + if (numCenter == 2) { + phi0 = psi(b1); + } else if (numCenter == 1) { + Index l; + if (ha == 1) + l = commonIndex(psi(b), psi(b + 1)); + else + l = commonIndex(psi(b - 1), psi(b)); + ITensor U, S, V(l); + spec = svd(phi1, U, S, V, args); + psi.ref(b) = U; + phi0 = S * V; + } + + H.numCenter(numCenter - 1); + H.position(b1, psi); + + applyExp(H, phi0, +t / 2, args); + + if (args.getBool("DoNormalize", true)) phi0 /= norm(phi0); + + if (numCenter == 2) { + psi.ref(b1) = phi0; + } + if (numCenter == 1) { + if (ha == 1) { + psi.ref(b + 1) *= phi0; + psi.leftLim(b); + psi.rightLim(b + 2); + } else { + psi.ref(b - 1) *= phi0; + psi.leftLim(b - 2); + psi.rightLim(b); + } + } + + // Calculate energy + ITensor H_phi0; + H.product(phi0, H_phi0); + energy = real(eltC(dag(phi0) * H_phi0)); + } + + if (!quiet) { + printfln(" Truncated to Cutoff=%.1E, Min_dim=%d, Max_dim=%d", + sweeps.cutoff(sw), sweeps.mindim(sw), sweeps.maxdim(sw)); + printfln(" Trunc. err=%.1E, States kept: %s", spec.truncerr(), + showDim(linkIndex(psi, b))); + } + + obs.lastSpectrum(spec); + + args.add("AtBond", b); + args.add("HalfSweep", ha); + args.add("Energy", energy); + args.add("Truncerr", spec.truncerr()); + + obs.measure(args); + + // Next sweep + if (mixNumCenter) { + int maxdim = sweeps.maxdim(sw); + if (ha == 1) { + if (numCenter == 1 and b != N and is_maxdim.at(b) and + !is_maxdim.at(b + 1)) { + numCenter = 2; + } + } else if (ha == 2) { + if (numCenter == 1 and b > 2 and is_maxdim.at(b - 1) and + !is_maxdim.at(b - 2)) { + numCenter = 2; + b -= 1; + } + } + } + sweepnext(b, ha, N, {"NumCenter=", numCenter}); + } // for loop over b + + if (!silent) { + auto sm = sw_time.sincemark(); + printfln(" Sweep %d/%d CPU time = %s (Wall time = %s)", sw, + sweeps.nsweep(), showtime(sm.time), showtime(sm.wall)); + } + + if (obs.checkDone(args)) break; + + } // for loop over sw + + if (args.getBool("DoNormalize", true)) { + // if(numCenter==1) psi.position(1); + psi.normalize(); + } + + return energy; +} + +} // namespace itensor + +#endif diff --git a/hybridleads/dmrg/MyObserver.h b/hybridleads/dmrg/MyObserver.h new file mode 100644 index 0000000..b0bf5ff --- /dev/null +++ b/hybridleads/dmrg/MyObserver.h @@ -0,0 +1,89 @@ +#ifndef __MYOBSERVER_H_CMC__ +#define __MYOBSERVER_H_CMC__ +#include "itensor/all.h" +using namespace itensor; + +template +class MyObserver : public DMRGObserver { + public: + MyObserver(const SitesType& sites, const MPS& psi, + const Args& args = Args::global()) + : DMRGObserver(psi, args), + _sites(sites), + _ns(length(psi) + 1, 0.), + _Npar(0.) { + _write = args.getBool("Write", false); + _write_minm = args.getInt("out_minm", 0); + _out_dir = args.getString("out_dir", "."); + _ConserveNf = args.getBool("ConserveNf", true); + } + + void measure(const Args& args = Args::global()); + + Real Npar() const { return _Npar; } + + private: + bool _write; + string _out_dir; // empty string "" if not write + int _write_minm; + vector _iDel, _jDel; + vector _Delta; + bool _ConserveNf; + SitesType _sites; + + vector _ns; + Real _Npar; +}; + +Real Onsite_mea(const ITensor& A, const ITensor& op) { + ITensor re = A * op; + re.noPrime("Site"); + re *= dag(A); + return re.real(); +} + +template +void MyObserver::measure(const Args& args) { + DMRGObserver::measure(args); + + // Define your measurements below + // Call psi() to access the MPS + // + auto N = length(psi()); + auto b = args.getInt("AtBond", 1); + auto sw = args.getInt("Sweep", 0); + auto ha = args.getInt("HalfSweep", 0); + auto energy = args.getReal("Energy", 0); + + // On-site measure + int oc = orthoCenter(psi()); + /* if (oc == N || ha == 2) // measure during the second half of sweep + { + ITensor nup_op = _sites.op("Nup",oc), + ndn_op = _sites.op("Ndn",oc); + Real n_up = Onsite_mea (psi().A(oc), nup_op), + n_dn = Onsite_mea (psi().A(oc), ndn_op); + + Real ni = n_up + n_dn, + sz = 0.5*(n_up-n_dn); + + _Npar += ni - _ns.at(oc); + _ns.at(oc) = ni; + + cout << " Measure on site " << oc << endl; + cout << " n_up = " << n_up << endl; + cout << " n_dn = " << n_dn << endl; + cout << " N_tot = " << ni << endl; + cout << " sz = " << sz << endl; + }*/ + + if (oc == 1 && ha == 2) { + int m = args.getInt("MaxDim"); + // Write MPS + if (_write && m >= _write_minm) { + writeToFile(_out_dir + "/psi" + "_m" + to_string(m) + ".mps", psi()); + } + } +} + +#endif diff --git a/dmrg/input b/hybridleads/dmrg/input similarity index 100% rename from dmrg/input rename to hybridleads/dmrg/input diff --git a/hybridleads/dmrg/run.cc b/hybridleads/dmrg/run.cc new file mode 100644 index 0000000..0cb6d9e --- /dev/null +++ b/hybridleads/dmrg/run.cc @@ -0,0 +1,180 @@ +#include "../itdvp/GlobalIndices.h" +#include "GenMPO.h" +#include "IUtility.h" +#include "MyObserver.h" +#include "ReadInput.h" +#include "itensor/all.h" +using namespace itensor; +using namespace std; + +template +MPO current_correlation(const SitesType& sites, int i) { + int N = length(sites); + AutoMPO ampo(sites); + if constexpr (is_same_v) { + ampo += -1_i, "Cdag", i, "C", i + 1; + ampo += 1_i, "Cdag", i + 1, "C", i; + ampo += 1_i, "Cdag", N - i, "C", N - i + 1; + ampo += -1_i, "Cdag", N - i + 1, "C", N - i; + } + + return toMPO(ampo); +} + +Cplx myinner(const MPO& mpo, const MPS& psi) { + auto L = ITensor(1.); + int N = length(psi); + for (int i = 1; i <= N; i++) { + L *= psi(i) * mpo(i); + auto dagA = dag(psi(i)); + dagA.prime("Site"); + if (i == 1) { + auto ii = commonIndex(psi(i), psi(i + 1), "Link"); + dagA.prime(ii); + } else if (i == N) { + auto ii = commonIndex(psi(i), psi(i - 1), "Link"); + dagA.prime(ii); + } else { + dagA.prime("Link"); + } + L *= dagA; + } + return eltC(L); +} + +MPS toMPS(const SiteSet& sites, const ITensor& AL, const Index& iALl, + const Index& iALr, const Index& iALs, const ITensor& AR, + const Index& iARl, const Index& iARr, const Index& iARs, + const ITensor& AC, const Index& iACl, const Index& iACr, + const Index& iACs, int oc = 1) { + MPS psi(sites); + int N = length(psi); + + // Set tensors + vector ils(N + 1), irs(N + 1), iss(N + 1); + for (int i = 1; i <= N; i++) { + Index il, ir; + if (i < oc) { + psi.ref(i) = AL; + ils.at(i) = iALl; + irs.at(i) = iALr; + iss.at(i) = iALs; + } else if (i == oc) { + psi.ref(i) = AC; + ils.at(i) = iACl; + irs.at(i) = iACr; + iss.at(i) = iACs; + } else { + psi.ref(i) = AR; + ils.at(i) = iARl; + irs.at(i) = iARr; + iss.at(i) = iARs; + } + assert(hasIndex(psi(i), ils.at(i))); + assert(hasIndex(psi(i), irs.at(i))); + } + + // Replace indices + auto inew = noPrime(sim(irs.at(1))); + psi.ref(1).replaceInds({iss.at(1), irs.at(1)}, {sites(1), inew}); + irs.at(1) = inew; + for (int i = 2; i < N; i++) { + inew = noPrime(sim(irs.at(i))); + psi.ref(i).replaceInds({iss.at(i), ils.at(i), irs.at(i)}, + {sites(i), irs.at(i - 1), inew}); + ils.at(i) = irs.at(i - 1); + irs.at(i) = inew; + } + psi.ref(N).replaceInds({iss.at(N), ils.at(N)}, {sites(N), irs.at(N - 1)}); + ils.at(N) = irs.at(N - 1); + + // Check + for (int i = 1; i <= N; i++) { + assert(hasIndex(psi(i), sites(i))); + if (i != N) assert(commonIndex(psi(i), psi(i + 1))); + } + + return psi; +} + +int main(int argc, char* argv[]) { + string infile = argv[1]; + InputGroup input(infile, "basic"); + + auto L = input.getInt("L"); + auto imp_site = input.getInt("imp_site"); + auto t = input.getReal("t"); + auto t_impL = input.getReal("t_impL"); + auto t_impR = input.getReal("t_impR"); + auto muL = input.getReal("muL"); + auto muR = input.getReal("muR"); + auto mu_imp = input.getReal("mu_imp"); + auto V = input.getReal("V"); + auto V_impL = input.getReal("V_impL"); + auto V_impR = input.getReal("V_impR"); + auto read_dir_L = input.getString("read_dir_L"); + auto read_dir_R = input.getString("read_dir_R"); + + auto WriteDim = input.getInt("WriteDim", -1); + auto do_write = input.getYesNo("write_to_file"); + auto out_dir = input.getString("outdir", "."); + auto out_minm = input.getInt("out_minm", 0); + auto H_file = input.getString("H_outfile", "H.mpo"); + auto ConserveQNs = input.getYesNo("ConserveQNs", false); + auto sweeps = Read_sweeps(infile); + + // Read the tensors + ITensor AL_L, AR_L, AC_L, LW_L, RW_L; + ITensor AL_R, AR_R, AC_R, LW_R, RW_R; + readFromFile(read_dir_L + "/AL.itensor", AL_L); + readFromFile(read_dir_L + "/AR.itensor", AR_L); + readFromFile(read_dir_L + "/AC.itensor", AC_L); + readFromFile(read_dir_L + "/LW.itensor", LW_L); + readFromFile(read_dir_L + "/RW.itensor", RW_L); + readFromFile(read_dir_R + "/AL.itensor", AL_R); + readFromFile(read_dir_R + "/AR.itensor", AR_R); + readFromFile(read_dir_R + "/AC.itensor", AC_R); + readFromFile(read_dir_R + "/LW.itensor", LW_R); + readFromFile(read_dir_R + "/RW.itensor", RW_R); + GlobalIndices ISL, ISR; + ISL.read(read_dir_L + "/global.inds"); + ISR.read(read_dir_R + "/global.inds"); + + // Site set + using SitesType = Fermion; + auto sites = SitesType(L, {"ConserveQNs", ConserveQNs}); + + // Initialze MPS + MPS psi = toMPS(sites, AL_L, ISL.il(), prime(ISL.il(), 2), ISL.is(), AR_R, + prime(ISR.ir(), 2), ISR.ir(), ISR.is(), AC_L, ISL.il(), + ISL.ir(), ISL.is()); + psi.position(1); + assert(commonIndex(LW_L, psi(1))); + assert(commonIndex(RW_R, psi(L))); + + // Make MPO + MPO H = single_empurity_mpo(sites, imp_site, t, t_impL, t_impR, muL, muR, + mu_imp, V, V_impL, V_impR); + to_inf_mpo(H, ISL.iwl(), ISR.iwr()); + assert(commonIndex(LW_L, H(1))); + assert(commonIndex(RW_R, H(L))); + + // Write to files + writeToFile(out_dir + "/" + H_file, H); + + // DMRG + MyObserver myobs( + sites, psi, + {"Write", do_write, "out_dir", out_dir, "out_minm", out_minm}); + dmrg(psi, H, LW_L, RW_R, sweeps, myobs, {"WriteDim", WriteDim}); + /* + int N = length(sites); + for(int i = 1; i <= N/2; i++) + { + auto J = current_correlation (sites, i); + auto j = myinner (J, psi); + cout << " " << i << " = " << j << endl; + } + */ + return 0; +} diff --git a/hybridleads/itdvp/FixedPointTensor.h b/hybridleads/itdvp/FixedPointTensor.h new file mode 100644 index 0000000..900e2e7 --- /dev/null +++ b/hybridleads/itdvp/FixedPointTensor.h @@ -0,0 +1,188 @@ +#ifndef __FIXEDPOINTTENSOR_H_CMC__ +#define __FIXEDPOINTTENSOR_H_CMC__ + +#include + +#include "IUtility.h" +#include "itensor/all.h" +#include "uGauge.h" + +/** + * @brief For computing (x|[1 - E + |R)(1|] + * + */ +class SpecialTransferMatrix { + public: + SpecialTransferMatrix(const ITensor& AL, const ITensor& R) : _AL(AL), _R(R) { + assert(order(R) == 2); + } + + int size() const { return _R.inds()(1).dim() * _R.inds()(2).dim(); } + void product(const ITensor& x, ITensor& re) const { + assert(order(x) == 2); + // (x|1 + re = x; + + // (x|E + ITensor xE = applyTransfer(x, _AL, _AL); + re -= xE; + + // (x|R)(1| + auto xR = x * _R; + ITensor xRI; + if (isReal(xR)) + xRI = iut::Identity(re.inds(), elt(xR)); + else + xRI = iut::Identity(re.inds(), eltC(xR)); + re += xRI; + + assert(order(x) == order(re)); + assert(hasIndex(re, x.inds()(1))); + assert(hasIndex(re, x.inds()(2))); + } + + private: + ITensor _AL, _R; +}; + +/** + * @brief + * @details + * W should have indices: 1. up site index, 2. down site index, 3. left link + * index, 4. right link index AL should have indices: 1. site index, 2. left + * link index, 3. right link index + * + * --------AL---- + * | | + * L--(b)--W--(a) + * | | + * --------AL---- + * + * (2)--AL--(3)--C-- + * | + * (1) + * + * (1) + * | + * (3)--W--(4) + * | + * (2) + * + * @tparam dir + * @param AL + * @param W + * @param R + * @param La0 + * @param args + * @returns tuple + * + * @see PHYSICAL REVIEW B 97, 045145 (2018), Algorithm 6. + */ +template +tuple get_LRW(const ITensor& AL, const ITensor& W, + const ITensor& R, ITensor& La0, const Args& args) { + // Check W is of Schur form: + // Wba != 0 only for b >= a (lower triangled) + global::IS.check("W", W); + + Index ia, iWa, iWb; + if constexpr (dir == LEFT) { + global::IS.check("AL", AL); + global::IS.check("R", R); + ia = global::IS.il(); + iWa = global::IS.iwr(); + iWb = global::IS.iwl(); + } else { + global::IS.check("AR", AL); + global::IS.check("L", R); + ia = global::IS.ir(); + iWa = global::IS.iwl(); + iWb = global::IS.iwr(); + } + + // For LW: solve from a=dW to 1 + // For RW: solve from a=1 to dW + vector solve_order; + solve_order.push_back(2); + for (int i = iWa.dim(); i >= 1; i--) { + if (i != 2) solve_order.push_back(i); + } + if constexpr (dir == RIGHT) reverse(solve_order.begin(), solve_order.end()); + + auto M = SpecialTransferMatrix(AL, R); + + auto LRW = ITensor(ia, iWb, prime(ia)); + // 1) b == a == (dW or 1), Waa = I --> La = I + for (int i = 1; i <= ia.dim(); i++) LRW.set(i, solve_order.front(), i, 1.); + + // 2) + ITensor Ya; + for (int i = 1; i < solve_order.size(); i++) { + int a = solve_order.at(i); + auto Wa = W * setElt(dag(iWa) = a); + auto Waa = Wa * setElt(dag(iWb) = a); + assert(order(Waa) == 2); + + Ya = applyTransfer(LRW, AL, AL, Wa); + + // Waa == 0 + if (i != solve_order.size() - 1) { + assert(norm(Waa) == 0.); + auto Y = Ya * setElt(dag(iWb) = a); + + assert(order(Y) == 3); + LRW += Y; + } else + // solve (L1|[1 - E + |R)(1|] = (Ya| - (Ya|R)(1| + { + auto YR = Ya * R; // (Ya|R) + ITensor YRI; // (Ya|R)(1| + if (isReal(YR)) + YRI = iut::Identity(Ya.inds(), elt(YR)); + else + YRI = iut::Identity(Ya.inds(), eltC(YR)); + auto Ya2 = Ya - YRI; // (Ya| - (Ya|R)(1| + gmres(M, Ya2, La0, args); + + LRW += La0 * setElt(dag(iWb) = a); + } + +#ifdef DEBUG + // Check fixed point + auto crit = args.getReal("debug_err_crit", 1e-8); + auto LTW = applyTransfer(LRW, AL, AL, Wa); + auto LTWa = LRW * setElt(dag(iWb) = a); + auto d = LTW - LTWa; + if (a != solve_order.back()) { + if (norm(d) > crit) { + cout << "d = " << norm(d) << endl; + throw; + } + } else { + auto e = Ya * R; + ITensor Ie; + if (isReal(e)) + Ie = iut::Identity(d.inds(), elt(e)); + else + Ie = iut::Identity(d.inds(), eltC(e)); + auto dI = d - Ie; + auto d2 = norm(dI); + cout << "d = " << d2 << endl; + /*if (d2 > 1e-4) + { + cout << "d = " << d2 << endl; + //throw; + }*/ + } +#endif + } + auto en = iut::toReal(Ya * R); // energy density + if constexpr (dir == LEFT) + global::IS.check("LW", LRW); + else + global::IS.check("RW", LRW); + + return {LRW, en}; +} + +#endif diff --git a/hybridleads/itdvp/GenMPO.h b/hybridleads/itdvp/GenMPO.h new file mode 100644 index 0000000..773da9c --- /dev/null +++ b/hybridleads/itdvp/GenMPO.h @@ -0,0 +1,309 @@ +#ifndef __GENMPO_H_CMC__ +#define __GENMPO_H_CMC__ + +#include "IUtility.h" +#include "itensor/all.h" + +using namespace itensor; +using namespace std; + +/** + * @brief + * + * @param W + * @param is + * @param irow + * @param icol + * @returns bool - + * @throws + */ +bool check_Schur(const ITensor& W, const Index& is, const Index& irow, + const Index& icol) { + assert(irow.dim() == icol.dim()); + int dW = irow.dim(); + + for (int a = dW; a >= 1; a--) { + auto Wa = W * setElt(dag(icol) = a); + auto Waa = Wa * setElt(dag(irow) = a); + if (a == 2 || a == 1) { + auto I = iut::Identity(Waa.inds()); + if (norm(I - Waa) > 1e-12) throw; + } else { + if (norm(Waa) > 1e-12) throw; + } + + for (int b = 1; b <= dW; b++) { + int a2 = a, b2 = b; + if (a == 2) + a2 = dW; + else if (a == dW) + a2 = 2; + if (b == 2) + b2 = dW; + else if (b == dW) + b2 = 2; + + if (a2 > b2) { + auto Wab = Wa * setElt(dag(irow) = b); + if (norm(Wab) > 1e-12) throw; + } + } + } + return true; +} + +inline ITensor to_Schur(const ITensor& W1, const Index& is, const Index& irow, + const Index& icol) { + ITensor W; + int D = irow.dim(); + assert(irow.dim() == icol.dim()); + + auto new_ind = [&D](int i1) { + if (i1 == 1) + return i1; + else if (i1 == 2) + return D; + else + return i1 - 1; + }; + + for (int i1 = 1; i1 <= D; i1++) + for (int j1 = 1; j1 <= D; j1++) { + int i = new_ind(i1); + int j = new_ind(j1); + auto Wab1 = W1 * setElt(dag(irow) = i1) * setElt(dag(icol) = j1); + auto Wab = Wab1 * setElt(irow = i) * setElt(icol = j); + W += Wab; + } + check_Schur(W, is, irow, icol); + return W; +} + +inline tuple to_Schur(const MPO& H) { + auto W = H(2); + auto irow = commonIndex(H(2), H(1)); + auto icol = commonIndex(H(2), H(3)); + auto is = findIndex(W, "Site,0"); + auto W_re = to_Schur(W, is, irow, icol); + return {W_re, is, irow, icol}; +} + +inline tuple get_MPO_Tensor_TFI(Real gx) { + auto sites = SpinHalf(3, {"ConserveSz", false}); + AutoMPO ampo(sites); + for (int j = 1; j < 3; ++j) { + ampo += -4, "Sz", j, "Sz", j + 1; + ampo += 2 * gx, "Sx", j; + } + ampo += 2 * gx, "Sx", 3; + auto H = toMPO(ampo); + + auto W = H(2); + auto iwl = commonIndex(H(2), H(1)); + auto iwr = commonIndex(H(2), H(3)); + auto is = findIndex(W, "Site,0"); + return {W, is, iwl, iwr}; +} + +/** + * @brief Retrive the uniform MPO tensor from MPO object. + * + * @param H The MPO object. + * @returns tuple - {W, is, iwl, iwr}. + * @retval W - The uniform MPO tensor. + * @retval is - Physical index of MPO tensor W. + * @retval iwl - Left index of MPO tensor W. + * @retval iwr - Right index of MPO tensor W. + * @throws + */ +inline tuple get_W(const MPO& H) { + auto W = H(2); + auto iwl = commonIndex(H(2), H(1)); + auto iwr = commonIndex(H(2), H(3)); + auto is = findIndex(W, "Site,0"); + check_Schur(W, is, iwl, iwr); + return {W, is, iwl, iwr}; +} + +tuple get_MPO_Tensor_Hubbard_spinless( + Real t, Real mu, const Args& args = Args::global()) { + auto sites = Spinless(3, args); + AutoMPO ampo(sites); + for (int i = 1; i <= 2; i++) { + ampo += -t, "Cdag", i, "C", i + 1; + ampo += -t, "Cdag", i + 1, "C", i; + ampo += -mu, "N", 2; + } + ampo += -mu, "N", 3; + auto H = toMPO(ampo); + return get_W(H); +} + +tuple get_MPO_Tensor_XXZ_spin1( + Real Delta, const Args& args = Args::global()) { + auto sites = SpinOne(3, args); + AutoMPO ampo(sites); + for (int i = 1; i <= 2; i++) { + ampo += 0.5, "S+", i, "S-", i + 1; + ampo += 0.5, "S+", i + 1, "S-", i; + ampo += Delta, "Sz", i, "Sz", i + 1; + } + auto H = toMPO(ampo); + return get_W(H); +} + +void to_inf_mpo(MPO& mpo, Index iL = Index(), Index iR = Index()) { + int N = length(mpo); + if (N < 4) { + cout << "Error: " << __FUNCTION__ << ": MPO length must >= 4" << endl; + throw; + } + + auto is1 = findIndex(mpo(1), "Site,0"); + auto is2 = findIndex(mpo(2), "Site,0"); + auto i1r = commonIndex(mpo(1), mpo(2)); + auto i2r = commonIndex(mpo(2), mpo(3)); + auto i2l = dag(i1r); + if (!iL) iL = sim(i2l); + + mpo.ref(1) = replaceInds(mpo(2), {is2, prime(is2), i2l, i2r}, + {is1, prime(is1), iL, i1r}); + assert(commonIndex(mpo(1), mpo(2))); + + auto isN = findIndex(mpo(N), "Site,0"); + auto isN2 = findIndex(mpo(N - 1), "Site,0"); + auto iNl = commonIndex(mpo(N), mpo(N - 1)); + auto iN2l = commonIndex(mpo(N - 1), mpo(N - 2)); + auto iN2r = dag(iNl); + if (!iR) iR = sim(iN2r); + + mpo.ref(N) = replaceInds(mpo(N - 1), {isN2, prime(isN2), iN2l, iN2r}, + {isN, prime(isN), iNl, iR}); + assert(commonIndex(mpo(N - 1), mpo(N))); +} + +MPO single_empurity_mpo(const SiteSet& sites, int imp_site, Real t, Real t_impL, + Real t_impR, Real muL, Real muR, Real mu_imp, Real V, + Real V_impL, Real V_impR) { + auto N = length(sites); + AutoMPO ampo(sites); + for (int i = 1; i <= N; ++i) { + Real mui, ti, Vi; + // Set mu + if (i < imp_site) + mui = muL; + else if (i > imp_site) + mui = muR; + else + mui = mu_imp; + // Set t + if (i == imp_site - 1) + ti = t_impL; + else if (i == imp_site) + ti = t_impR; + else + ti = t; + // Set V + if (i == imp_site - 1) + Vi = V_impL; + else if (i == imp_site) + Vi = V_impR; + else + Vi = V; + + if (i != N) { + ampo += -ti, "Cdag", i, "C", i + 1; + ampo += -ti, "Cdag", i + 1, "C", i; + } + if (mui != 0.) ampo += -mui, "N", i; + if (Vi != 0. && i != N) ampo += Vi, "N", i, "N", i + 1; + } + auto H = toMPO(ampo); + + return H; +} + +tuple t_mu_V_ampo(const SiteSet& sites, int L_device, + Real t_leadL, Real t_leadR, Real t_device, + Real t_contactL, Real t_contactR, + Real mu_leadL, Real mu_leadR, + Real mu_device, Real V_leadL, Real V_leadR, + Real V_device, Real V_contactL, + Real V_contactR) { + auto N = length(sites); + int first_site_device = (N - L_device) / 2 + 1; + int last_site_device = first_site_device + L_device - 1; + AutoMPO ampo(sites); + for (int i = 1; i <= N; ++i) { + Real mui, ti, Vi; + // Set mu + if (i < first_site_device) + mui = mu_leadL; + else if (i > last_site_device) + mui = mu_leadR; + else + mui = mu_device; + // Set t and V + if (i == first_site_device - 1) { + ti = t_contactL; + Vi = V_contactL; + } else if (i == last_site_device) { + ti = t_contactR; + Vi = V_contactR; + } else if (i < first_site_device - 1) { + ti = t_leadL; + Vi = V_leadL; + } else if (i > last_site_device) { + ti = t_leadR; + Vi = V_leadR; + } else { + ti = t_device; + Vi = V_device; + } + + if (i != N) { + ampo += -ti, "Cdag", i, "C", i + 1; + ampo += -ti, "Cdag", i + 1, "C", i; + cout << "t: " << i << " " << i + 1 << " " << ti << endl; + } + if (mui != 0.) { + ampo += -mui, "N", i; + cout << "mu: " << i << " " << mui << endl; + } + if (Vi != 0. && i != N) { + ampo += Vi, "N", i, "N", i + 1; + cout << "V: " << i << " " << i + 1 << " " << Vi << endl; + } + } + return {ampo, first_site_device, last_site_device}; +} + +void ampo_add_mu(AutoMPO& ampo, const vector>& site_mu, + bool verbose = false) { + if (verbose) cout << "i mu" << endl; + for (auto [site, mu] : site_mu) { + if (mu != 0.) { + ampo += -mu, "N", site; + if (verbose) cout << site << " " << mu << endl; + } + } +} + +void ampo_add_tV(AutoMPO& ampo, + const vector>& link_t_V, + bool verbose = false) { + if (verbose) cout << "i j t V" << endl; + for (auto [i, j, t, V] : link_t_V) { + if (t != 0.) { + ampo += -t, "Cdag", i, "C", j; + ampo += -t, "Cdag", j, "C", i; + } + if (V != 0.) { + ampo += V, "N", i, "N", j; + ampo += V, "N", j, "N", i; + } + if (verbose) cout << i << " " << j << " " << t << " " << V << endl; + } +} + +#endif diff --git a/hybridleads/itdvp/GlobalIndices.h b/hybridleads/itdvp/GlobalIndices.h new file mode 100644 index 0000000..ca629f8 --- /dev/null +++ b/hybridleads/itdvp/GlobalIndices.h @@ -0,0 +1,105 @@ +#ifndef __GLOBALINDCIES_H_CMC__ +#define __GLOBALINDCIES_H_CMC__ +#include "itensor/all.h" +using namespace itensor; + +enum enumLR { LEFT, RIGHT }; + +struct GlobalIndices { + const Index& il() const { return _il; } + const Index& ir() const { return _ir; } + const Index& is() const { return _is; } + const Index& iwl() const { return _iwl; } + const Index& iwr() const { return _iwr; } + + void write(const string& filename) const { + ofstream ofs(filename); + write(ofs); + } + void write(ostream& ofs) const { + itensor::write(ofs, _is); + itensor::write(ofs, _il); + itensor::write(ofs, _ir); + itensor::write(ofs, _iwl); + itensor::write(ofs, _iwr); + } + void read(const string& filename) { + ifstream ifs(filename); + if (!ifs) { + cout << __FUNCTION__ << ": Cannot open file: " << filename << endl; + throw; + } + read(ifs); + } + void read(istream& ifs) { + itensor::read(ifs, _is); + itensor::read(ifs, _il); + itensor::read(ifs, _ir); + itensor::read(ifs, _iwl); + itensor::read(ifs, _iwr); + } + + vector LW_inds() const { return {_il, prime(_il), _iwl}; } + vector RW_inds() const { return {_ir, prime(_ir), _iwr}; } + + bool check(string name, const ITensor& T) const { + // assert (isReal (T)); + if (name == "A") { + assert(order(T) == 3); + assert(hasIndex(T, _is)); + assert(hasIndex(T, _il)); + assert(hasIndex(T, prime(_il, 2))); + } else if (name == "W") { + assert(order(T) == 4); + assert(hasIndex(T, _is)); + assert(hasIndex(T, prime(_is))); + assert(hasIndex(T, _iwl)); + assert(hasIndex(T, _iwr)); + } else if (name == "AL") { + assert(order(T) == 3); + assert(hasIndex(T, _is)); + assert(hasIndex(T, _il)); + assert(hasIndex(T, prime(_il, 2))); + } else if (name == "AR") { + assert(order(T) == 3); + assert(hasIndex(T, _is)); + assert(hasIndex(T, _ir)); + assert(hasIndex(T, prime(_ir, 2))); + } else if (name == "AC") { + assert(order(T) == 3); + assert(hasIndex(T, _is)); + assert(hasIndex(T, _il)); + assert(hasIndex(T, _ir)); + } else if (name == "C") { + assert(order(T) == 2); + assert(hasIndex(T, prime(_il, 2))); + assert(hasIndex(T, prime(_ir, 2))); + } else if (name == "R") { + assert(order(T) == 2); + assert(hasIndex(T, _il)); + assert(hasIndex(T, prime(_il))); + } else if (name == "L") { + assert(order(T) == 2); + assert(hasIndex(T, _ir)); + assert(hasIndex(T, prime(_ir))); + } else if (name == "LW") { + assert(order(T) == 3); + assert(hasIndex(T, _il)); + assert(hasIndex(T, prime(_il))); + assert(hasIndex(T, _iwl)); + } else if (name == "RW") { + assert(order(T) == 3); + assert(hasIndex(T, _ir)); + assert(hasIndex(T, prime(_ir))); + assert(hasIndex(T, _iwr)); + } + return true; + } + + Index _il, _ir, _is, _iwl, _iwr; +}; + +namespace global { +auto IS = GlobalIndices(); +} +#endif diff --git a/itdvp/Makefile b/hybridleads/itdvp/Makefile similarity index 97% rename from itdvp/Makefile rename to hybridleads/itdvp/Makefile index 2de089d..6512215 100644 --- a/itdvp/Makefile +++ b/hybridleads/itdvp/Makefile @@ -1,4 +1,4 @@ -# 1. Put this file in the same folder as your 'driver' code +# 1. Put this file in the same folder as your 'driver' code # (the code containing the 'main' function). # 2. Edit LIBRARY_DIR to point at the location of your ITensor Library @@ -63,4 +63,3 @@ clean: mkdebugdir: mkdir -p .debug_objs - diff --git a/hybridleads/itdvp/Solver.h b/hybridleads/itdvp/Solver.h new file mode 100644 index 0000000..a1915e6 --- /dev/null +++ b/hybridleads/itdvp/Solver.h @@ -0,0 +1,102 @@ +#ifndef __SOLVER_H_CMC__ +#define __SOLVER_H_CMC__ + +#include "GlobalIndices.h" +#include "itensor/all.h" + +using namespace itensor; +using namespace std; + +void apply_Heff(const ITensor& LW, const ITensor& RW, const ITensor& W, + ITensor& AC, ITensor& C) { + global::IS.check("LW", LW); + global::IS.check("RW", RW); + global::IS.check("W", W); + global::IS.check("AC", AC); + global::IS.check("C", C); + + auto iwl = global::IS.iwl(); + auto iwr = global::IS.iwr(); + + // Update AC + AC *= LW; + AC *= W; + AC *= RW; + AC.noPrime(); + + // Update C + auto RW0 = replaceInds(RW, {iwr}, {iwl}); + C.noPrime(); + C *= LW; + C *= RW0; + C.prime(); + + C /= norm(C); + AC /= norm(AC); + + global::IS.check("AC", AC); + global::IS.check("C", C); +} + +void solve_gs(const ITensor& LW, const ITensor& RW, const ITensor& W, + ITensor& AC, ITensor& C, const Args& args = Args::global()) +// args: ErrGoal, MaxIter +{ + global::IS.check("LW", LW); + global::IS.check("RW", RW); + global::IS.check("W", W); + global::IS.check("AC", AC); + global::IS.check("C", C); + + auto iwl = global::IS.iwl(); + auto iwr = global::IS.iwr(); + + // Update AC + LocalOp Heff1(W, LW, RW, {"numCenter=", 1}); + davidson(Heff1, AC, args); + + // Update C + auto RW0 = replaceInds(RW, {iwr}, {iwl}); + LocalOp Heff0(LW, RW0, {"NumCenter=", 0}); + auto C0 = noPrime(C); + davidson(Heff0, C0, args); + C = prime(C0, 2); + + C /= norm(C); + AC /= norm(AC); + + global::IS.check("AC", AC); + global::IS.check("C", C); +} + +template +void time_evolve(const ITensor& LW, const ITensor& RW, const ITensor& W, + ITensor& AC, ITensor& C, TimeType dt, + const Args& args = Args::global()) +// args: ErrGoal, MaxIter +{ + global::IS.check("LW", LW); + global::IS.check("RW", RW); + global::IS.check("W", W); + global::IS.check("AC", AC); + global::IS.check("C", C); + + auto iwl = global::IS.iwl(); + auto iwr = global::IS.iwr(); + + // Update AC + LocalOp Heff1(W, LW, RW, {"numCenter=", 1}); + applyExp(Heff1, AC, -dt, args); + + // Update C + auto RW0 = replaceInds(RW, {iwr}, {iwl}); + LocalOp Heff0(LW, RW0, {"NumCenter=", 0}); + auto C0 = noPrime(C); + applyExp(Heff0, C0, -dt, args); + C = prime(C0, 2); + + global::IS.check("AC", AC); + global::IS.check("C", C); +} + +#endif diff --git a/hybridleads/itdvp/iTDVP.h b/hybridleads/itdvp/iTDVP.h new file mode 100644 index 0000000..d3d7664 --- /dev/null +++ b/hybridleads/itdvp/iTDVP.h @@ -0,0 +1,152 @@ +#ifndef __ITDVP_H_CMC__ +#define __ITDVP_H_CMC__ + +#include +#include + +#include "FixedPointTensor.h" +#include "RandomUtility.h" +#include "Solver.h" +#include "itensor/all.h" +#include "uGauge.h" + +using namespace itensor; +using namespace std; + +/** + * @brief Randomly initialize iMPS in the mixed canonical form. + * + * @param W The uniform MPO tensor. + * @param is Physical index of MPO tensor `W`. + * @param iwl Left index of MPO tensor `W`. + * @param iwr Right index of MPO tensor `W`. + * @param A The initial MPS tensor. If an empty tensor (as an ill-defined + * ITensor) is provided, random elements will be filled in in accordance with + * `is` and `D`. + * @param D The maximum bond dimension. + * @param ErrGoal The tolerance for orthonormal procedure. + * @param MaxIter Maximum number of iteration for orthonormal procedure. + * @param seed Random seed used when `A` is empty. + * @returns tuple - {AL, + * AR, AC, C, La0, Ra0}. + * @retval AL - Left-normalized iMPS tensor in the mixed canonical form. + * @retval AR - Right-normalized iMPS tensor in the mixed canonical form. + * @retval AC - The center site tensor, `AL` @f$\times@f$ `C`. + * @retval C - The center matrix of iMPS in the mixed canonical form. + * @retval La0 - Left fixed-point tensor of `A`. + * @retval Ra0 - Right fixed-point tensor of `A`. + * + * @see https://scipost.org/SciPostPhysLectNotes.7 + */ +tuple itdvp_initial( + const ITensor& W, const Index& is, const Index& iwl, const Index& iwr, + ITensor& A, int D, Real ErrGoal, int MaxIter, RandGen::SeedType seed = 0) { + Index il; + if (!A) { + il = Index(D, "Link"); + A = ITensor(is, il, prime(il, 2)); + auto rand = RandGen(seed); + auto gen = [&rand]() { return rand.real(); }; + A.generate(gen); + // A.randomize(); + // A.set(1,1,1,1.); + } else { + il = findIndex(A, "Link,0"); + if (!hasIndex(A, prime(il, 2))) { + cout << "Error: " << __FUNCTION__ << ": A has wrong index structure" + << endl; + throw; + } + } + auto ir = sim(il); + global::IS._iwl = iwl; + global::IS._iwr = iwr; + global::IS._is = is; + global::IS._il = il; + global::IS._ir = ir; + + auto [AL, AR, C] = MixedCanonical(A, ErrGoal, MaxIter); + auto AC = get_AC(AL, C); + auto La0 = randomITensor(il, prime(il)); + auto Ra0 = randomITensor(ir, prime(ir)); + return {AL, AR, AC, C, La0, Ra0}; +} + +/** + * @brief + * + * @param AL + * @param C + * @param AC + * @return Real + */ +inline Real diff_ALC_AC(const ITensor& AL, const ITensor& C, + const ITensor& AC) { + auto ALC = get_AC(AL, C); + auto d = norm(ALC - AC); + return d; +} + +/** + * @brief Variational uniform MPS (VUMPS) algorithm. + * + * @tparam TimeType + * @param W The uniform MPO tensor. + * @param AL Left-normalized iMPS tensor in the mixed canonical form. + * @param AR Right-normalized iMPS tensor in the mixed canonical form. + * @param AC The center site tensor, `AL` @f$\times@f$ `C`. + * @param C The center matrix of iMPS in the mixed canonical form. + * @param La0 Left fixed-point tensor of initial iMPS `A`. + * @param Ra0 Right fixed-point tensor of initial iMPS `A`. + * @param dt Length of time step. If `dt` is real, imaginary time evolution will + * be performed; otherwise if `dt` is imaginary, real time evolution will be + * performed. + * @param args ErrGoal, MaxIter, used in applyExp and arnoldi. + * @returns tuple {en, err, LW, RW}. + * @retval en - The variational ground state energy. + * @retval err - Error measure. + * @retval LW - Left fixed-point tensor of `W`. + * @retval RW - Right fixed-point tensor of `W`. + * + * @note SciPost Phys. Lect. Notes 7 (2019), Algorithm 4 and Sec 5.3. + * @see https://scipost.org/SciPostPhysLectNotes.7 + */ +template +tuple itdvp(const ITensor& W, ITensor& AL, + ITensor& AR, ITensor& AC, ITensor& C, + ITensor& La0, ITensor& Ra0, + TimeType dt, + Args& args = Args::global()) { + // C --> L, R + auto L = get_LR(C); + auto R = get_LR(C); + + // AL, AR, L, R --> LW, RW, enL, enR + auto [LW, enL] = get_LRW(AL, W, R, La0, args); + auto [RW, enR] = get_LRW(AR, W, L, Ra0, args); + auto en = 0.5 * (enL + enR); + + // LW, RW, W, AC, C --> AC, C + if constexpr (is_same_v) { + if (isinf(dt)) + solve_gs(LW, RW, W, AC, C, args); + else + time_evolve(LW, RW, W, AC, C, dt, args); + } else { + time_evolve(LW, RW, W, AC, C, dt, args); + } + // AC, C --> AL, AR + AL = get_AL(AC, C); + AR = get_AR(AC, C); + // AL = get_ALR_2 (AC, C); + // AR = get_ALR_2 (AC, C); + + C /= norm(C); + AC /= norm(AC); + auto errL = diff_ALC_AC(AL, C, AC); + auto errR = diff_ALC_AC(AR, C, AC); + auto err = (errL > errR ? errL : errR); + return {en, err, LW, RW}; +} + +#endif diff --git a/itdvp/input b/hybridleads/itdvp/input similarity index 93% rename from itdvp/input rename to hybridleads/itdvp/input index fafb2b3..1a2d360 100644 --- a/itdvp/input +++ b/hybridleads/itdvp/input @@ -13,6 +13,6 @@ basic MaxIterInit = 20 SeedInit = 123456789 auto_err = yes - write = yes + write = no out_dir = . } diff --git a/hybridleads/itdvp/itdvp.cc b/hybridleads/itdvp/itdvp.cc new file mode 100644 index 0000000..9f457b6 --- /dev/null +++ b/hybridleads/itdvp/itdvp.cc @@ -0,0 +1,88 @@ +#include "iTDVP.h" + +#include +#include + +#include "FixedPointTensor.h" +#include "GenMPO.h" +#include "IUtility.h" +#include "Solver.h" +#include "itensor/all.h" +#include "uGauge.h" +using namespace itensor; +using namespace std; + +int main(int argc, char* argv[]) { + string infile = argv[1]; + InputGroup input(infile, "basic"); + + auto t = input.getReal("t"); + auto mu = input.getReal("mu"); + auto V = input.getReal("V"); + + auto time_steps = input.getInt("time_steps"); + auto dt_str = input.getString("dt"); + auto dt = + ((dt_str == "inf" || dt_str == "Inf" || dt_str == "INF") ? INFINITY + : stod(dt_str)); + auto D = input.getInt("max_dim"); + auto ErrGoal = input.getReal("ErrGoal"); + auto MaxIter = input.getInt("MaxIter"); + auto ErrGoalInit = input.getReal("ErrGoalInit", 1e-12); + auto MaxIterInit = input.getInt("MaxIterInit", 100); + auto SeedInit = input.getInt("SeedInit", 0); + auto write = input.getYesNo("write"); + auto out_dir = input.getString("out_dir", "."); + + //------------------------------------------ + cout << setprecision(18) << endl; + // Make MPO + auto sites = Fermion(3, {"ConserveQNs", false}); + // ************************************************************************ + // Change the Hamiltonian to whatever you want + auto H = single_empurity_mpo(sites, 2, t, t, t, mu, mu, mu, V, V, V); + // ************************************************************************ + auto [W, is, iwl, iwr] = get_W(H); + // W: MPO tensor + // is: physical index + // iwl: left index + // iwr: right index + + // Initialize MPS + auto A = ITensor(); // ill-defined tensor + auto [AL, AR, AC, C, La0, Ra0] = + itdvp_initial(W, is, iwl, iwr, A, D, ErrGoalInit, MaxIterInit, SeedInit); + // If A is ill-defined, A will be random generated in itdvp_initial + // This is just for tensors to be used in iTDVP + + // iTDVP + Args args = {"ErrGoal=", 1e-4, "MaxIter", MaxIter}; + ITensor LW, RW; + Real en, err; + for (int i = 1; i <= time_steps; i++) { + cout << "time step " << i << endl; + // Run iTDVP + // If dt is real, do imaginary time evolution + // imaginary, real + tie(en, err, LW, RW) = itdvp(W, AL, AR, AC, C, La0, Ra0, dt, args); + cout << "energy, error = " << en << " " << err << endl; + + // Decrease the ErrGoal dynamically + if (args.getReal("ErrGoal") > ErrGoal) args.add("ErrGoal=", err * 0.1); + } + //------------------------------------------ + + if (write) { + writeToFile(out_dir + "/AL.itensor", AL); + writeToFile(out_dir + "/AR.itensor", AR); + writeToFile(out_dir + "/AC.itensor", AC); + writeToFile(out_dir + "/C.itensor", C); + writeToFile(out_dir + "/LW.itensor", LW); + writeToFile(out_dir + "/RW.itensor", RW); + writeToFile(out_dir + "/W.itensor", W); + writeToFile(out_dir + "/La.itensor", La0); + writeToFile(out_dir + "/Ra.itensor", Ra0); + global::IS.write(out_dir + "/global.inds"); + } + return 0; +} diff --git a/hybridleads/itdvp/uGauge.h b/hybridleads/itdvp/uGauge.h new file mode 100644 index 0000000..7855076 --- /dev/null +++ b/hybridleads/itdvp/uGauge.h @@ -0,0 +1,378 @@ +#ifndef __uGauge_H_CMC__ +#define __uGauge_H_CMC__ + +#include "GlobalIndices.h" +#include "IUtility.h" +#include "itensor/all.h" + +using namespace itensor; +using namespace std; + +inline ITensor applyTransfer(const ITensor& x, const ITensor& A1, + const ITensor& A2, int pLV = 2) { + auto re = x * A1; + re *= prime(dag(A2), "Link"); + re.prime(-pLV, "Link"); + return re; +} + +inline ITensor applyTransfer(const ITensor& x, const ITensor& A1, + const ITensor& A2, const ITensor& W, int pLV = 2) { + auto re = x * A1; + re *= W; + re *= prime(dag(A2)); + re.prime(-pLV, "Link"); + return re; +} + +class TransferMatrix { + public: + TransferMatrix(const ITensor& A1, const ITensor& A2, int pLV = 2) + : _A1(A1), _A2(A2), _pLV(pLV) { + auto i1s = findInds(A1, "Link"); + auto i2s = findInds(A2, "Link"); + _dim = i1s(1).dim() * i2s(1).dim(); + } + + int size() const { return _dim; } + void product(const ITensor& x, ITensor& xE) const { + xE = applyTransfer(x, _A1, _A2, _pLV); + assert(order(x) == order(xE)); + assert(hasIndex(xE, x.inds()(1))); + assert(hasIndex(xE, x.inds()(2))); + } + + private: + ITensor _A1, _A2; + int _dim, _pLV; +}; + +inline Real applyT_diff(const ITensor& A1, const ITensor& A2, + const ITensor& X) { + auto reX = prime(X, 2) * A1; + reX *= prime(dag(A2), "Link"); + auto dd = reX - X; + auto d = norm(dd); + return d; +} + +inline bool check_leading_eigen(const ITensor& A1, const ITensor& A2, + const ITensor& X, Real crit = 1e-12) { + auto d = applyT_diff(A1, A2, X); + if (d > crit) { + cout << __FUNCTION__ << ": err = " << d << endl; + return false; + } + return true; +} + +/** + * @brief Gauge transform a uniform MPS A into left-orthonormal form. + * @details Solve + * + * --L--A-- = --AL--L-- + * | | + * + * where + * + * --- L------ ---A---- + * | | | + * l = | is the left leading eigenvector of | + * | | | + * --- dagL--- --dagA-- + * + * and + * + * ----AL--- --- + * | | = | = iut::Identity + * --dagAL-- --- + * + * Input: + * + * --A-- + * | + * + * Output: + * + * --AL-- + * | + * + * --L-- + * + * @param A The uniform MPS tensor. + * @param errGoal The tolerance. + * @param maxIter Maximum number iteration. + * @return tuple - {AL, L}. + * @retval AL - + * @retval L - + * + * @note SciPost Phys. Lect. Notes 7 (2019), Algorithm 1. + * @see https://scipost.org/SciPostPhysLectNotes.7 + */ +tuple Orthogonalize(const ITensor& A, Real errGoal = 1e-15, + int maxIter = 10000) { + global::IS.check("A", A); + auto is = global::IS.is(); + auto il = global::IS.il(); + auto il1 = prime(il); + auto il2 = prime(il, 2); + + // A = AL * L + auto args = Args({"MinDim", il.dim(), "MaxDim", il.dim()}); + auto [AL, L] = denmatDecomp(A, {is, il}, Fromleft, args); + L /= norm(L); + // Set indices + auto ic = commonIndex(AL, L); + AL.replaceInds({ic}, {il2}); + L.replaceInds({ic, il2}, {il1, il}); + + // First error + auto L_old = iut::Identity(L.inds()); + auto err = norm(L - L_old); + + // Iterate L*A = AL*L + auto drop = ITensor(); + for (int i = 0; i < maxIter; i++) { + // Arnoldi + auto E = TransferMatrix(A, AL); + arnoldi(E, L, {"ErrGoal=", 0.1 * err}); + L.takeReal(); + // + tie(drop, L) = polar(L, {il1}, args); + ic = commonIndex(L, drop); + L.replaceInds({ic}, {il1}); + L /= norm(L); + // QR + L_old = L; + tie(AL, L) = polar(L * A, {is, il1}, args); + L /= norm(L); + // Replace indices + ic = commonIndex(AL, L); + AL.replaceInds({il1, ic}, {il, il2}); + L.replaceInds({ic, il2}, {il1, il}); + err = norm(L - L_old); + cout << "err = " << err << endl; + if (err < errGoal) break; + } + return {AL, L}; +} + +inline void Rotate_basis(ITensor& C, ITensor& AL, ITensor& AR) { + global::IS.check("C", C); + global::IS.check("AL", AL); + global::IS.check("AR", AR); + auto is = global::IS.is(); + auto il = global::IS.il(); + auto il2 = prime(il, 2); + auto ir = global::IS.ir(); + auto ir2 = prime(ir, 2); + + assert(abs(1. - norm(C)) < 1e-12); + + ITensor U(il2), S, V; + svd(C, U, S, V); + auto iU = commonIndex(U, S); + auto iV = commonIndex(V, S); + + auto apply_rot = [&is, &C](ITensor& U, ITensor& A, const Index& iU) { + auto iU2 = prime(iU, 2); + U.setPrime(2, iU); + A *= U; + A *= noPrime(dag(U)); + }; + apply_rot(U, AL, iU); + apply_rot(V, AR, iV); + AL.replaceInds({iU, prime(iU, 2)}, {il, il2}); + AR.replaceInds({iV, prime(iV, 2)}, {ir, ir2}); + + C = S; + C = iut::hard_copy(C); + C.replaceInds({iU, iV}, {il2, ir2}); + + global::IS.check("C", C); + global::IS.check("AL", AL); + global::IS.check("AR", AR); +} + +/** + * @brief Gauge transform a given MPS tensor into the mixed canonical form. + * + * @param A The MPS tensor. + * @param errGoal The tolerance for orthonormal procedure. + * @param maxIter Maximum number of iteration for orthonormal procedure. + * @returns tuple {AL, AR, C}. + * @retval AL - + * @retval AR - + * @retval AC - + * + * @note SciPost Phys. Lect. Notes 7 (2019), Algorithm 2. + * @see https://scipost.org/SciPostPhysLectNotes.7 + */ +inline tuple MixedCanonical(const ITensor& A, + Real errGoal = 1e-12, + int maxIter = 10000) { + global::IS.check("A", A); + auto il = global::IS.il(); + auto ir = global::IS.ir(); + auto il2 = prime(il, 2); + auto ir2 = prime(ir, 2); + + Args args = {"Cutoff", 1e-14}; + + auto [AL, L] = Orthogonalize(A, errGoal, maxIter); + auto ALtmp = swapInds(AL, {il}, {il2}); + auto [AR, C] = Orthogonalize(ALtmp, errGoal, maxIter); + AR.replaceInds({il, il2}, {ir, ir2}); + C.replaceInds({il, prime(il)}, {il2, ir2}); + + global::IS.check("AL", AL); + global::IS.check("AR", AR); + global::IS.check("C", C); + + Rotate_basis(C, AL, AR); + + assert(check_ortho(AL, il2)); + assert(check_ortho(AR, ir2)); + return {AL, AR, C}; +} + +//---------------------------------------------------- + +inline ITensor get_AC(const ITensor& ALR, ITensor C) { + global::IS.check("C", C); + + auto AC = ALR * C; + AC.noPrime(); + + global::IS.check("AC", AC); + return AC; +} + +inline ITensor get_polarU(const ITensor& T, const IndexSet& iUs) { + ITensor U(iUs), S, V; + svd(T, U, S, V); + + V *= delta(S.inds()); + auto pU = U * V; + return pU; +} + +template +inline ITensor get_ALR_2(const ITensor& AC, const ITensor& C) { + assert(abs(norm(AC) - 1) < 1e-12); + assert(abs(norm(C) - 1) < 1e-12); + global::IS.check("AC", AC); + global::IS.check("C", C); + + auto is = global::IS.is(); + auto i1 = global::IS.il(); + auto i2 = global::IS.ir(); + if constexpr (dir == RIGHT) swap(i1, i2); + + auto UA = get_polarU(AC, {is, i1}); + auto UC = get_polarU(C, {prime(i1, 2)}); + auto UCdag = dag(UC); + UCdag.noPrime(prime(i2, 2)); + auto ALR = UA * UCdag; + + assert(check_ortho(ALR, prime(i1, 2))); + if constexpr (dir == LEFT) + global::IS.check("AL", ALR); + else + global::IS.check("AR", ALR); + return ALR; +} + +inline ITensor get_AL(const ITensor& AC, const ITensor& C) { + global::IS.check("AC", AC); + global::IS.check("C", C); + + auto is = global::IS.is(); + auto il = global::IS.il(); + auto ir = global::IS.ir(); + + auto Cdag = dag(C); + Cdag.noPrime(prime(ir, 2)); + auto AC_Cdag = AC * Cdag; + + auto AL = get_polarU(AC_Cdag, {is, il}); + + global::IS.check("AL", AL); + assert(check_ortho(AL, prime(il, 2))); + return AL; +} + +inline ITensor get_AR(const ITensor& AC, const ITensor& C) { + global::IS.check("AC", AC); + global::IS.check("C", C); + + auto is = global::IS.is(); + auto il = global::IS.il(); + auto ir = global::IS.ir(); + + auto Cdag = dag(C); + Cdag.noPrime(prime(il, 2)); + auto Cdag_AC = AC * Cdag; + + auto AR = get_polarU(Cdag_AC, {prime(ir, 2)}); + + global::IS.check("AR", AR); + assert(check_ortho(AR, prime(ir, 2))); + return AR; +} + +template +inline ITensor get_LR(const ITensor& C) { + global::IS.check("C", C); + + auto ic = global::IS.il(); + auto iu = global::IS.ir(); + if constexpr (dir == RIGHT) swap(ic, iu); + auto ic2 = prime(ic, 2); + auto iu2 = prime(iu, 2); + + auto Cdag = dag(C); + Cdag.prime(iu2); + auto LorR = C * Cdag; + LorR.prime(-2); + // LorR.replaceInds ({iu,prime(iu)}, {ic,prime(ic)}); + auto trace = LorR * dag(delta(LorR.inds())); + LorR /= iut::toReal(trace); + + if constexpr (dir == RIGHT) + global::IS.check("R", LorR); + else + global::IS.check("L", LorR); + return LorR; +} + +template +ITensor get_LR_2(const ITensor& AL, ITensor R, Real crit = 1e-15) { + if constexpr (dir == RIGHT) { + global::IS.check("AL", AL); + global::IS.check("R", R); + } else { + global::IS.check("AR", AL); + global::IS.check("L", R); + } + + R.prime(2); + auto E = TransferMatrix(AL, AL, -2); + arnoldi(E, R, {"ErrGoal=", crit}); + R.prime(-2); + + R.takeReal(); + + auto traceT = R * dag(delta(R.inds())); + R /= iut::toReal(traceT); + + // assert (check_leading_eigen (AL, AL, R)); + + if constexpr (dir == RIGHT) + global::IS.check("R", R); + else + global::IS.check("L", R); + return R; +} + +#endif diff --git a/hybridleads/kbasis/BdGBasis.h b/hybridleads/kbasis/BdGBasis.h new file mode 100644 index 0000000..1b46024 --- /dev/null +++ b/hybridleads/kbasis/BdGBasis.h @@ -0,0 +1,337 @@ +#ifndef __BDGBASIS_H_CMC__ +#define __BDGBASIS_H_CMC__ +#include "ReadWriteFile.h" +#include "itensor/all.h" +using namespace itensor; +using namespace std; + +Matrix BdG_Hamilt(int L, Real t, Real mu, Real Delta) { + int N = 2 * L; + Matrix H(N, N); + for (int ip = 0; ip < N; ip++) + for (int jp = 0; jp < N; jp++) { + int i = ip + 1, j = jp + 1; + bool dagi = true, dagj = false; + if (i > L) { + i -= L; + dagi = false; + } + if (j > L) { + j -= L; + dagj = true; + } + + // mu + if (i == j) { + if (dagi != dagj) { + if (dagi) + H(ip, jp) = -mu; + else + H(ip, jp) = mu; + } + } + if (abs(i - j) == 1) { + // t + if (dagi != dagj) { + if (dagi) + H(ip, jp) = -t; + else + H(ip, jp) = t; + } + // Delta + else { + if (dagi == (i > j)) + H(ip, jp) = Delta; + else + H(ip, jp) = -Delta; + } + } + } + return 0.5 * H; +} + +// Use only the positive-energy states, ordering from lowest to highest energy +class BdGBasis { + public: + BdGBasis() {} + BdGBasis(const string& name, int L, Real t, Real mu, Real Delta); + + tuple, vector, vector> C(int i); + + // Functions that every basis class must have + const string& name() const { return _name; } + vector> C_op(int i, bool dag) const; + vector> gamma_op(int k, bool dag) const; + // en(i) is the energy in H_BdG = sum_i^N { en(i) gamma_i^dag gamma_i } - + // sum_i^N { (1/2) en(i) } + Real en(int k) const { return _ens(k - 1); } + Real mu(int k) const { + mycheck(k <= this->size(), "Out of range"); + return -2. * _H(k - 1, k - 1); + } + int size() const { return _ens.size(); } + auto const& H() const { return _H; } + auto const& H(int i, int j) const { return _H(i, j); } + + void write(ostream& s) const { + itensor::write(s, _name); + itensor::write(s, _u); + itensor::write(s, _v); + itensor::write(s, _ens); + itensor::write(s, _H); + } + void read(istream& s) { + itensor::read(s, _name); + itensor::read(s, _u); + itensor::read(s, _v); + itensor::read(s, _ens); + itensor::read(s, _H); + } + + private: + string _name; + Vector _ens; + Matrix _u, _v, _H; +}; + +Vector particle_hole_transform(const Vector& v) { + int N = v.size() / 2; + Vector w(2 * N); + subVector(w, 0, N) &= subVector(v, N, 2 * N); + subVector(w, N, 2 * N) &= subVector(v, 0, N); + return w; +} + +// For each BdG basis state, suppose |phi_i> has energy E_i, +// S|phi_i> will have energy -E_i, where S is the particle-hole transformation. +// Suppose |phi_i> has dimension 2N. +// S|phi_i> swaps the first N subvector to the last N subvector of |phi_i>. +// +// The particle-hole correspondence for the E_i and -E_i basis states is +// autoomatically satisfied for the non-zero-energy modes. However for +// zero-energy (Majorana) modes, since |phi_i> and S|phi_i> are degenerate, +// numerically we will get in geenral arbitrary superpositions of them, +// so one needs to symmetrice them explicitly. +Matrix symmetrice_zero_energy_modes(const Vector& ens, Matrix U, + Real zero_crit = 1e-8) { + mycheck(ens.size() == nrows(U), "Size not match"); + int N = ens.size() / 2; + + // Target the zero-energy modes + vector is; + vector states; + for (int i = 0; i < 2 * N; i++) { + if (abs(ens(i)) < zero_crit) { + is.push_back(i); + states.emplace_back(column(U, i)); + } + } + if (states.size() == 0) return U; + mycheck(states.size() == 2, "Allow only upto 2 zero-energy modes"); + + // Define particle-hole transformation matrix S in zero-energy modes subspace + int N0 = states.size(); + Matrix S(N0, N0); + for (int i = 0; i < N0; i++) { + auto phi = states.at(i); + for (int j = 0; j < N0; j++) { + auto const& phip = states.at(j); + auto phit = particle_hole_transform(phi); + auto si = phip * phit; + S(i, j) = si; + } + } + + // Diagonalize S + Matrix W; + Vector eigvals; + diagHermitian(S, W, eigvals); + // Get the eigenstates of S + auto e_pos = eigvals(0); + auto e_neg = eigvals(1); + auto w_pos = Vector(column(W, 0)); + auto w_neg = Vector(column(W, 1)); + if (e_pos < 0.) { + swap(e_pos, e_neg); + swap(w_pos, w_neg); + } + Vector v_pos(2 * N), v_neg(2 * N); + for (int i = 0; i < N0; i++) { + v_pos += w_pos(i) * states.at(i); + v_neg += w_neg(i) * states.at(i); + } + mycheck(abs(e_pos - 1) < 1e-14 and abs(e_neg + 1) < 1e-14, + "particle-hole eigenvalues error"); + + // Suppose S has eigenvectors |u> and |v> with eigenvalues +1 and -1 + // The (unnormalized) symmetric zero-energy modes are |u>+|v> and |u>-|v> + auto phi1 = v_pos + v_neg; + phi1 /= norm(phi1); + auto phi2 = particle_hole_transform(phi1); + + // Update the zero-energy states in U + column(U, is.at(0)) &= phi1; + column(U, is.at(1)) &= phi2; + + return U; +} + +void check_orthogonal_to_particle_hole_transform(const Vector& v) { + Vector w = particle_hole_transform(v); + auto o = w * v; + if (!(abs(o) < 1e-8)) { + cout << "Not orthogonal to its particle-hole transformed state" << endl; + cout << abs(o) << endl; + throw; + } +} + +BdGBasis ::BdGBasis(const string& name, int L, Real t, Real mu, Real Delta) + : _name(name) { + _H = BdG_Hamilt(L, t, mu, Delta); + Matrix U; + Vector ens; + diagHermitian(_H, U, ens); + // ens(q) for q=0,1,...,2N-1 is the energy in descending order (highest to + // lowest). The energy for q=0,...N-1 is positive + // q=N,...2N-1 is negative + + U = symmetrice_zero_energy_modes(ens, U); + + auto tmp = U(0, 0); + if constexpr (!is_same_v) { + cout << "The current version is only for unitary matrix of Real type" + << endl; + cout << "Type found is: " << typeid(tmp).name() << endl; + throw; + } + + // _ens are for the positive energies in ascending order (lowest to highest) + // U = [ _u _v* ] is the unitrary matrix to diagonalize H + // [ _v _u* ] + // Take only the positive-energy states, i.e. the first N columns, to define + // _u and _v. + int N = ens.size() / 2; + _ens = Vector(N); + _u = Matrix(N, N); + _v = Matrix(N, N); + int j = 0; + for (int i = N - 1; i >= 0; i--) { + // Check the state is orthogonal to its particle-hole transformed state + auto phi = Vector(column(U, i)); + // check_orthogonal_to_particle_hole_transform (phi); + + _ens(j) = 2. * ens(i); + column(_u, j) &= subVector(phi, 0, N); + column(_v, j) &= subVector(phi, N, 2 * N); + j++; + } + + // Check the unitrary matrices U = [ u v* ] + // [ v u* ] + Matrix Uc(2 * N, 2 * N); + subMatrix(Uc, 0, N, 0, N) &= _u; + subMatrix(Uc, N, 2 * N, 0, N) &= _v; + subMatrix(Uc, 0, N, N, 2 * N) &= conj(_v); + subMatrix(Uc, N, 2 * N, N, 2 * N) &= conj(_u); + + auto Hd = transpose(Uc) * _H * Uc; + for (int i = 0; i < N; i++) { + Hd(i, i) -= 0.5 * _ens(i); + Hd(i + N, i + N) += 0.5 * _ens(i); + } + mycheck(abs(norm(Hd)) < 1e-10, "Construct unitray matrix failed"); +} + +// i is the site index in real space +// Return k, coef, dagger +// +// [ C ] = [ u v* ] [ gamma ] +// [ Cdag ] [ v u* ] [ gamma^dag ] +// +// C_i = sum_k^N { u(i,k) gamma + v*(i,k) gamma^dag } +vector> BdGBasis ::C_op(int i, bool dag) const { + int N = _ens.size(); + mycheck(i >= 1 and i <= N, "out of range"); // i is from 1 to N + + auto tmp = _u(0, 0); + vector> k_coef_dag; + // For C + for (int k = 0; k < N; k++) { + auto uk = _u(i - 1, k); + auto vk = _v(i - 1, k); + if (abs(uk) > 1e-14) k_coef_dag.emplace_back(k + 1, uk, false); + if (abs(vk) > 1e-14) k_coef_dag.emplace_back(k + 1, iut::conj(vk), true); + } + // If Cdag + if (dag) { + for (auto& [k, coef, dagk] : k_coef_dag) { + coef = iut::conj(coef); + dagk = !dagk; + } + } + + return k_coef_dag; +} + +// [ u v* ] +// [ gamma^dag gamma ] = [ C^dag C] [ v u* ] +// +// gamma^dag_k = sum_i^N { u(i,k) C_i^dag + v(i,k) C_i } +vector> BdGBasis ::gamma_op(int k, bool dag) const { + int N = _ens.size(); + mycheck(k >= 1 and k <= N, "out of range"); // i is from 1 to N + + auto tmp = _u(0, 0); + vector> i_coef_dag; + // For gamma^dag + for (int i = 0; i < N; i++) { + auto uk = _u(i, k - 1); + auto vk = _v(i, k - 1); + if (abs(uk) > 1e-14) i_coef_dag.emplace_back(i + 1, uk, true); + if (abs(vk) > 1e-14) i_coef_dag.emplace_back(i + 1, vk, false); + } + // If gamma + if (!dag) { + for (auto& [i, coef, dagk] : i_coef_dag) { + coef = iut::conj(coef); + dagk = !dagk; + } + } + return i_coef_dag; +} + +auto write(ostream& s, const BdGBasis& t) { t.write(s); } +auto read(istream& s, BdGBasis& t) { t.read(s); } + +// Original Hamiltonian in the BdG basis. +// Do not confuse with the BdG Hamiltonian. +// H_BdG = \sum_i^N epsilon_i gamma^dag_i gamma_i - \sum_i^N (1/2) epsilon_i +// H = H_BdG - (1/2) * sum_i^N mu_i +template +AutoMPO H_AMPO_BdG_basis(const SiteType& sites, const BdGBasis& bdg) { + AutoMPO ampo(sites); + int N = length(sites); + for (int i = 1; i <= N; i++) { + ampo += bdg.en(i), "N", i; + ampo += -0.5 * (bdg.en(i) + bdg.mu(i)), "I", i; + } + return ampo; +} + +// Ground state energy for the orignal Hamiltonian (not the BdG Hamiltonian) +Real ground_state_energy(const BdGBasis& b) { + Real en = 0.; + for (int i = 0; i < b.size(); i++) { + en -= 0.5 * b.en(i + 1) - b.H(i, i); + } + return en; +} + +void print_ops(const vector>& ops) { + cout << "site, coef, dag" << endl; + for (auto& [k, coef, dagk] : ops) { + cout << k << " " << coef << " " << dagk << endl; + } +} +#endif diff --git a/kbasis/Hamiltonian.h b/hybridleads/kbasis/Hamiltonian.h similarity index 94% rename from kbasis/Hamiltonian.h rename to hybridleads/kbasis/Hamiltonian.h index 4bdb074..0dd9cdd 100644 --- a/kbasis/Hamiltonian.h +++ b/hybridleads/kbasis/Hamiltonian.h @@ -10,9 +10,11 @@ using namespace itensor; using namespace std; /** - * @brief Collecting operator information in the new basis. - * C(i1,dag1) * C(i2,dag2) = \sum_k1 coef_i1,k1 C(k1,dag'1) * \sum_k2 coef_i2,k2 - * C(k2,dag'2) + * @brief Helper function for collecting operator information in the new basis. + * @f{eqnarray*} + * C(i1,dag1) * C(i2,dag2) = \sum_k1 coef_i1,k1 C(k1,dag'1) * \sum_k2 + * coef_i2,k2 C(k2,dag'2) + * @f} * * @tparam Basis1 * @tparam Basis2 @@ -23,8 +25,8 @@ using namespace std; * @param dag1 * @param dag2 * @param cutoff - * @return vector > vector of (coef, k1, dag'1, - * k2, dag'2), where \f$coef = coef_{i1,k1} * coef_{i2,k2}\f$. + * @returns vector > - vector of (coef, k1, + * dag'1, k2, dag'2), where \f$coef = coef_{i1,k1} * coef_{i2,k2}\f$. */ template vector> quadratic_operator_new( @@ -185,7 +187,7 @@ void add_SC(AutoMPO& ampo, const Basis1& basis1, const Basis2& basis2, int i1, * @param sites * @param para * @param to_glob - * @return AutoMPO + * @returns AutoMPO */ template @@ -240,7 +242,7 @@ AutoMPO get_ampo_Kitaev_chain(const BasisL& leadL, const BasisR& leadR, } /** - * @brief Get AutoMPO object for tight-binding model + * @brief Get AutoMPO object for tight-binding model. * * @tparam BasisL * @tparam BasisR @@ -253,7 +255,7 @@ AutoMPO get_ampo_Kitaev_chain(const BasisL& leadL, const BasisR& leadR, * @param sites * @param para * @param to_glob - * @return AutoMPO + * @returns AutoMPO */ template @@ -289,4 +291,5 @@ AutoMPO get_ampo_tight_binding(const BasisL& leadL, const BasisR& leadR, } return ampo; } + #endif diff --git a/hybridleads/kbasis/InitState.h b/hybridleads/kbasis/InitState.h new file mode 100644 index 0000000..459199e --- /dev/null +++ b/hybridleads/kbasis/InitState.h @@ -0,0 +1,317 @@ +#ifndef __INITSTATE_H_CMC__ +#define __INITSTATE_H_CMC__ +#include "SortBasis.h" + +// The charging energy is Ec * (N - Ng)^2 +// Find out N that lowest the charging energy in even and odd number of particle +// sectors +tuple en_charging_energy(int maxOcc, Real Ec, Real Ng) { + Real en_even = std::numeric_limits::max(), + en_odd = std::numeric_limits::max(); + vector ns(1, 0); + for (int n = 1; n <= maxOcc; n++) { + ns.push_back(n); + ns.push_back(-n); + } + int n_even, n_odd; + for (int n : ns) { + // Compute charging energy + Real nn = n - Ng; + Real enC = Ec * nn * nn; + // + if (n % 2 == 0 and enC < en_even) { + en_even = enC; + n_even = n; + } + if (n % 2 == 1 and enC < en_odd) { + en_odd = enC; + n_odd = n; + } + } + return {en_even, en_odd, n_even, n_odd}; +} + +template +MPS get_ground_state_BdG_scatter(const BasisL& leadL, const BasisR& leadR, + const BasisS& scatterer, const SiteType& sites, + Real muL, Real muR, const Para& para, + int maxOcc, const ToGlobDict& to_glob) { + int N = to_glob.size(); + mycheck(length(sites) == N, "size not match"); + + // Get the ground state for SC (even particle number) + vector state(N + 1); + + // Leads + auto occ_negative_en_states = [&to_glob, &state](const auto& basis, Real mu) { + string p = basis.name(); + for (int k = 1; k <= basis.size(); k++) { + int i = to_glob.at({p, k}); + auto en = basis.en(k); + if (en < mu) + state.at(i) = "Occ"; + else + state.at(i) = "Emp"; + } + }; + occ_negative_en_states(leadL, muL); + occ_negative_en_states(leadR, muR); + + // Scatterer + string sname = scatterer.name(); + for (int k = 1; k <= scatterer.size(); k++) { + int i = to_glob.at({sname, k}); + state.at(i) = "Emp"; + } + + // Superconducting gap + Real SC_gap = scatterer.en(1); + cout << "SC gap = " << SC_gap << endl; + + // Capacity site + // Find out the charge numbers, which are integers, that lowest the charging + // energy, for even and odd parities + auto [enC0, enC1, n_even, n_odd] = + en_charging_energy(maxOcc, para.Ec, para.Ng); + // Combine the scatter energies to decide to choose even or odd sector + Real en0 = enC0, en1 = enC1 + SC_gap; + cout << "n (even,odd) = " << n_even << ", " << n_odd << endl; + cout << "E (even,odd) = " << en0 << ", " << en1 << endl; + if (en1 < en0) // First excited state in superconductor + { + int is1 = to_glob.at({"S", 1}); + state.at(is1) = "Occ"; + cout << "Ground state has odd parity" << endl; + } else { + cout << "Ground state has even parity" << endl; + } + int n = (en0 <= en1 ? n_even : n_odd); + cout << "Initial charge = " << n << endl; + // Set the charge site + int ic = to_glob.at({"C", 1}); + state.at(ic) = str(n); + // Set state + InitState init(sites); + for (int i = 1; i <= N; i++) init.set(i, state.at(i)); + + auto psi = MPS(init); + /* + // If Majorana zero mode exists, use the equal-weight superposition of + occupied and unoccupied state auto const& en = visit (basis::en(1), + sys.parts().at("S")); if (abs(en) < 1e-14) + { + int iglob = sys.to_glob ("S",1); + auto A = psi(iglob); + auto links = findInds (A, "Link"); + auto il1 = links(1); + auto il2 = links(2); + auto is = findIndex (A, "Site"); + mycheck (dim(il1) == 1 and dim(il2) == 1, "Link indices dimension + error"); Real ele = 1./sqrt(2); A.set (il1=1, il2=1, is=1, ele); A.set (il1=1, + il2=1, is=2, ele); psi.ref(iglob) = A; PrintData(psi(iglob)); + } + exit(0);*/ + return psi; +} + +template +MPS get_non_inter_ground_state(const BasisL& leadL, const BasisR& leadR, + const BasisS& scatterer, const SiteType& sites, + Real muL, Real muS, Real muR, + const ToGlobDict& to_glob) { + int N = to_glob.size(); + mycheck(length(sites) == N, "size not match"); + + int Ns = 0, Np = 0; + Real E = 0.; + vector state(N + 1, "Emp"); + + // Leads and scatterer + auto occ_negative_en_states = [&to_glob, &state, &E, &Np, &Ns]( + const auto& basis, Real mu) { + string p = basis.name(); + for (int k = 1; k <= basis.size(); k++) { + int i = to_glob.at({p, k}); + auto en = basis.en(k); + if (en < mu) { + state.at(i) = "Occ"; + E += en; + Np++; + if (p == "S") Ns++; + } else { + state.at(i) = "Emp"; + } + } + }; + occ_negative_en_states(leadL, muL); + occ_negative_en_states(leadR, muR); + occ_negative_en_states(scatterer, muS); + + InitState init(sites); + for (int i = 1; i <= N; i++) init.set(i, state.at(i)); + + // Print information + cout << "initial energy = " << E << endl; + cout << "initial particle number = " << Np << endl; + return MPS(init); +} + +template +tuple get_scatter_ground_state_SC( + const BasisS& scatterer, Real mu, Real Delta, const Sweeps& sweeps, + const ToGlobDict& to_glob, const Args& args) { + int Ns = scatterer.size(); + SpecialFermion sites(Ns, {args, "in_scatter", true}); + + // Find the first site of the scatter + string sname = scatterer.name(); + int imin = std::numeric_limits::max(); + for (int i = 1; i <= Ns; i++) { + int j = to_glob.at({sname, i}); + if (imin > j) imin = j; + } + + int L_offset = imin - 1; + AutoMPO ampo(sites); + // Diagonal terms + for (int i = 1; i <= Ns; i++) { + int j = to_glob.at({sname, i}) - L_offset; + auto en = scatterer.en(i); + ampo += en - mu, "N", j; + } + // Superconducting + for (int i = 1; i < Ns; i++) { + auto terms = + quadratic_operator(scatterer, scatterer, i, i + 1, false, false); + for (auto [c12, k1, dag1, k2, dag2] : terms) { + int j1 = to_glob.at({sname, k1}) - L_offset; + int j2 = to_glob.at({sname, k2}) - L_offset; + if (j1 != j2) { + auto c = Delta * c12; + auto cc = iut::conj(c); + string op1 = (dag1 ? "Cdag" : "C"); + string op2 = (dag2 ? "Cdag" : "C"); + string op1dag = (dag1 ? "C" : "Cdag"); + string op2dag = (dag2 ? "C" : "Cdag"); + ampo += -c, op1, j1, op2, j2; + ampo += -cc, op1dag, j2, op2dag, j1; + } + } + } + auto H0 = toMPO(ampo); + + // Solve the ground states in even and odd parities by DMRG + InitState init(sites); + auto psi0 = MPS(init); + init.set(1, "Occ"); + auto psi1 = MPS(init); + + auto en0 = dmrg(psi0, H0, sweeps, {"Quiet", true}); + auto en1 = dmrg(psi1, H0, sweeps, {"Quiet", true}); + + cout << setprecision(14); + AutoMPO Nampo(sites); + for (int i = 1; i <= length(sites); i++) Nampo += 1.0, "N", i; + auto Nmpo = toMPO(Nampo); + Real Np0 = inner(psi0, Nmpo, psi0), Np1 = inner(psi1, Nmpo, psi1); + + return {psi0, psi1, en0, en1, Np0, Np1, L_offset}; +} + +template +MPS get_ground_state_SC(const BasisL& leadL, const BasisR& leadR, + const BasisS& scatterer, const BasisC& charge, + const SiteType& sites, Real muL, Real muS, Real muR, + const Para& para, const Sweeps& sweeps, + const ToGlobDict& to_glob, const Args& args) { + int N = to_glob.size(); + mycheck(length(sites) == N, "size not match"); + + Real E_lead = 0.; + int Np_lead = 0; + vector state(N + 1, "Emp"); + + // Leads + auto occ_neg_en_levels = [&E_lead, &Np_lead, &state, &to_glob]( + const auto& basis, Real mu) { + string p = basis.name(); + for (int i = 1; i <= basis.size(); i++) { + auto en = basis.en(i); + int j = to_glob.at({p, i}); + if (en < mu) { + state.at(j) = "Occ"; + E_lead += en - mu; + Np_lead++; + } else { + state.at(j) = "Emp"; + } + } + }; + occ_neg_en_levels(leadL, muL); + occ_neg_en_levels(leadR, muR); + cout << "lead E = " << E_lead << endl; + cout << "lead Np = " << Np_lead << endl; + + // Get ground state of the scatterer + auto [psi0, psi1, enSC0, enSC1, Np0, Np1, L_offset] = + get_scatter_ground_state_SC(scatterer, muS, para.Delta, sweeps, to_glob, + args); + + // Capacity site + // Find out the charge numbers, which are integers, that lowest the charging + // energy, for even and odd parities + int maxOcc = args.getInt("MaxOcc"); + auto [enC0, enC1, n_even, n_odd] = + en_charging_energy(maxOcc, para.Ec, para.Ng); + // Combine the scatter energies to decide to choose even or odd sector + Real en0 = enC0 + enSC0, en1 = enC1 + enSC1; + Real en = (en0 < en1 ? en0 : en1); + int n = (en0 < en1 ? n_even : n_odd); + Real Np = (en0 < en1 ? Np0 : Np1); + auto psiS = (en0 < en1 ? psi0 : psi1); + // Set state + int ic = to_glob.at({"C", 1}); + state.at(ic) = str(n); + // Print + cout << "Init scatter Np (even,odd) = (" << Np0 << "," << Np1 << ") -> " << Np + << endl; + cout << "Init scatter E (even,odd) = (" << en0 << "," << en1 << ") -> " << en + << endl + << "\tE_C = (" << enC0 << "," << enC1 << ")" << endl + << "\tE_SC, gap = (" << enSC0 << "," << enSC1 << "), " + << abs(enSC0 - enSC1) << endl; + cout << "Init scatter total charge = (" << n_even << "," << n_odd << ") -> " + << n << endl; + + // Initialize the leads and the charge site + InitState init(sites); + for (int i = 1; i <= N; i++) init.set(i, state.at(i)); + auto psi = MPS(init); + + // Replace the tensors in the scatter + for (int i = 1; i <= length(psiS); i++) { + int i0 = i + L_offset; + auto iis = findIndex(psi(i0), "Site"); + auto iis2 = findIndex(psiS(i), "Site"); + Index iil; + if (i == 1) + iil = leftLinkIndex(psi, i0); + else if (i == length(psiS)) + iil = rightLinkIndex(psi, i0); + psiS.ref(i).replaceInds({iis2}, {iis}); + psi.ref(i0) = psiS(i); + if (iil) { + mycheck(dim(iil) == 1, "not dummy index"); + psi.ref(i0) *= setElt(iil = 1); + } + state.at(i0) = "*"; + } + psi.position(1); + psi.normalize(); + + return psi; +} + +#endif diff --git a/kbasis/Makefile b/hybridleads/kbasis/Makefile similarity index 97% rename from kbasis/Makefile rename to hybridleads/kbasis/Makefile index d0aac1d..e1b0715 100644 --- a/kbasis/Makefile +++ b/hybridleads/kbasis/Makefile @@ -1,4 +1,4 @@ -# 1. Put this file in the same folder as your 'driver' code +# 1. Put this file in the same folder as your 'driver' code # (the code containing the 'main' function). # 2. Edit LIBRARY_DIR to point at the location of your ITensor Library @@ -64,4 +64,3 @@ clean: mkdebugdir: mkdir -p .debug_objs - diff --git a/hybridleads/kbasis/MixedBasis.h b/hybridleads/kbasis/MixedBasis.h new file mode 100644 index 0000000..82e29cc --- /dev/null +++ b/hybridleads/kbasis/MixedBasis.h @@ -0,0 +1,63 @@ +#ifndef __SpecialMixedSiteSet_H_CMC__ +#define __SpecialMixedSiteSet_H_CMC__ +#include "ContainerUtility.h" +#include "SpecialBoson.h" +#include "SpecialFermion.h" +#include "itensor/all.h" +using namespace itensor; + +class MixedBasis : public SiteSet { + int _maxOcc; + + public: + int maxOcc() const { return _maxOcc; } + + MixedBasis() {} + + MixedBasis(int N, int iL, int iR, int iC, Args const& args = Args::global()) { + _maxOcc = args.getInt("MaxOcc"); + auto sites = SiteStore(N); + for (int j = 1; j <= N; ++j) { + bool in_scatter = (j >= iL and j <= iR); + if (j == iC) + sites.set(j, SpecialBosonSite({args, "SiteNumber=", j})); + else + sites.set(j, SpecialFermionSite( + {args, "SiteNumber=", j, "in_scatter", in_scatter})); + } + SiteSet::init(std::move(sites)); + } + + MixedBasis(int N, const vector& scatter_sites, int iC, + Args const& args = Args::global()) { + _maxOcc = args.getInt("MaxOcc"); + auto sites = SiteStore(N); + for (int j = 1; j <= N; ++j) { + bool in_scatter = iut::in_vector(scatter_sites, j); + if (j == iC) + sites.set(j, SpecialBosonSite({args, "SiteNumber=", j})); + else + sites.set(j, SpecialFermionSite( + {args, "SiteNumber=", j, "in_scatter", in_scatter})); + } + SiteSet::init(std::move(sites)); + } + + MixedBasis(IndexSet const& is, Args const& args = Args::global()) { + int N = is.length(); + auto sites = SiteStore(N); + for (auto j : range1(N)) { + auto ii = is(j); + mycheck(hasTags(ii, "Boson") or hasTags(ii, "Fermion"), + "unknown site index"); + if (hasTags(ii, "Boson")) { + sites.set(j, SpecialBosonSite(ii, args)); + int d = dim(ii); + _maxOcc = (d - 1) / 2; + } else + sites.set(j, SpecialFermionSite(ii)); + } + SiteSet::init(std::move(sites)); + } +}; +#endif diff --git a/hybridleads/kbasis/MyObserver.h b/hybridleads/kbasis/MyObserver.h new file mode 100644 index 0000000..d7b6976 --- /dev/null +++ b/hybridleads/kbasis/MyObserver.h @@ -0,0 +1,82 @@ +#ifndef __MYOBSERVER_H_CMC__ +#define __MYOBSERVER_H_CMC__ +#include "Entanglement.h" +#include "itensor/all.h" +using namespace itensor; + +template +class MyObserver : public DMRGObserver { + public: + MyObserver(const SitesType& sites, const MPS& psi, + const Args& args = Args::global()) + : DMRGObserver(psi, args), + _sites(sites), + _ns(length(psi), 0.), + _Npar(0.) { + _write = args.getBool("Write", false); + _write_minm = args.getInt("out_minm", 0); + _out_dir = args.getString("out_dir", "."); + } + + void measure(const Args& args = Args::global()); + + Real Npar() const { return _Npar; } + auto const& ns() const { return _ns; } + + private: + bool _write; + string _out_dir; // empty string "" if not write + int _write_minm; + vector _iDel, _jDel; + vector _Delta; + SitesType _sites; + + vector _ns; + Real _Npar; +}; + +Real Onsite_mea(const ITensor& A, const ITensor& op) { + ITensor re = A * op; + re.noPrime("Site"); + re *= dag(A); + return re.real(); +} + +template +void MyObserver::measure(const Args& args) { + DMRGObserver::measure(args); + + // Define your measurements below + // Call psi() to access the MPS + // + auto N = length(psi()); + auto b = args.getInt("AtBond", 1); + auto sw = args.getInt("Sweep", 0); + auto ha = args.getInt("HalfSweep", 0); + auto energy = args.getReal("Energy", 0); + + // On-site measure + int oc = orthoCenter(psi()); + if (oc == N || ha == 2) // measure during the second half of sweep + { + // Density + ITensor n_op = _sites.op("N", oc); + Real ni = Onsite_mea(psi().A(oc), n_op); + cout << "\tn " << oc << " = " << ni << endl; + _ns.at(oc - 1) = ni; + + // Entanglement entropy + Real S = EntangEntropy(spectrum()); + cout << "\tentang entropy " << oc << " = " << S << endl; + } + + if (oc == 1 && ha == 2) { + int m = args.getInt("MaxDim"); + // Write MPS + if (_write && m >= _write_minm) { + writeToFile(_out_dir + "/psi" + "_m" + to_string(m) + ".mps", psi()); + } + } +} + +#endif diff --git a/kbasis/OneParticleBasis.h b/hybridleads/kbasis/OneParticleBasis.h similarity index 93% rename from kbasis/OneParticleBasis.h rename to hybridleads/kbasis/OneParticleBasis.h index f1fb27b..25d3cd5 100644 --- a/kbasis/OneParticleBasis.h +++ b/hybridleads/kbasis/OneParticleBasis.h @@ -17,7 +17,7 @@ using namespace std; * effect. * @param damp_from_right * @param verbose Print out the matrix elements. - * @return Matrix The Hamiltonian matrix, which is tridiagonal. + * @returns Matrix - The Hamiltonian matrix, which is tridiagonal. */ Matrix tight_binding_Hamilt(int L, Real t, Real mu, Real damp_fac = 1., bool damp_from_right = true, bool verbose = false) { @@ -86,12 +86,14 @@ class OneParticleBasis { /** * @brief Get the operator information in this basis for the operator Cdag_i, - * C_i = \sum_k U_ik C_k - * Cdag_i = \sum_k U_ik^* Cdag_k + * @f{eqnarray*} + * C_i = \sum_k U_ik C_k \\ + * C_i^\dagger = \sum_k U_ik^* C_k^\dagger + * @f} * * @param i The real-space site index, 1-index. * @param dag Whether the operator has a dagger or not. - * @return vector> The basis index (k, 0-index) in + * @returns vector> - The basis index (k, 0-index) in * ascending order, coefficient U_ik, and whether the operator has a dagger or * not. */ @@ -114,4 +116,5 @@ vector> OneParticleBasis ::C_op(int i, bool dag) const { auto write(ostream& s, const OneParticleBasis& t) { t.write(s); } auto read(istream& s, OneParticleBasis& t) { t.read(s); } + #endif diff --git a/kbasis/SortBasis.h b/hybridleads/kbasis/SortBasis.h similarity index 82% rename from kbasis/SortBasis.h rename to hybridleads/kbasis/SortBasis.h index f87e826..9edd834 100644 --- a/kbasis/SortBasis.h +++ b/hybridleads/kbasis/SortBasis.h @@ -6,6 +6,10 @@ using namespace itensor; using namespace std; +/** + * @brief + * + */ using ToGlobDict = map, int>; // {partition, ki} -> ortical index using ToLocDict = @@ -18,7 +22,7 @@ using SortInfo = tuple; // basis name, orbital index, energ * A Global index is just a number. * * @param orbs - * @return tuple to_glob[name,ki] = i, to_local[i] = + * @returns tuple - to_glob[name,ki] = i, to_local[i] = * {name,ki}, (Both ki and i are 1-index) */ tuple make_orb_dicts(const vector& orbs) { @@ -57,7 +61,7 @@ vector get_sort_info(const BasisT& basis, const Bases&... bases) { * * @tparam Bases * @param bases Arbitrary number of bases - * @return vector + * @returns vector */ template vector sort_by_energy(const Bases&... bases) { @@ -70,8 +74,16 @@ vector sort_by_energy(const Bases&... bases) { return orbs; } -// Sort all the basis states by energy; however put the states from in -// zero energy of the other states +/** + * @brief Sort all the basis states by energy; however put the states from + * in zero energy of the other states + * + * @tparam SysBasis + * @tparam LeadBasis + * @param chainS + * @param other_chains + * @returns vector + */ template vector sort_by_energy_S_middle( const SysBasis& chainS, std::initializer_list other_chains) { @@ -86,7 +98,17 @@ vector sort_by_energy_S_middle( return orbs; } -// chainS at the middle; chainC at the left of chainS +/** + * @brief chainS at the middle; chainC at the left of chainS. + * + * @tparam SysBasis + * @tparam LeadBasis + * @tparam ChargeBasis + * @param chainS + * @param chainC + * @param other_chains + * @returns vector + */ template vector sort_by_energy_S_middle_charging( const SysBasis& chainS, const ChargeBasis& chainC, @@ -102,7 +124,15 @@ vector sort_by_energy_S_middle_charging( return orbs; } -// Put the charging site at zero energy +/** + * @brief Put the charging site at zero energy. + * + * @tparam BasisC + * @tparam Bases + * @param chainC + * @param bases + * @returns vector + */ template vector sort_by_energy_charging(const BasisC& chainC, const Bases&... bases) { @@ -116,4 +146,5 @@ vector sort_by_energy_charging(const BasisC& chainC, orbs.insert(it, orb_C.begin(), orb_C.end()); return orbs; } + #endif diff --git a/hybridleads/kbasis/SpecialBoson.h b/hybridleads/kbasis/SpecialBoson.h new file mode 100644 index 0000000..d2ac433 --- /dev/null +++ b/hybridleads/kbasis/SpecialBoson.h @@ -0,0 +1,104 @@ +#ifndef __SPECIALBOSON_H_CMC__ +#define __SPECIALBOSON_H_CMC__ +#include "itensor/mps/siteset.h" + +class SpecialBosonSite { + Index s; + + vector _ns; + + public: + int n(int i) const { return _ns.at(i - 1); } + + SpecialBosonSite(Index I, Args const& args = Args::global()) : s(I) { + auto maxOcc = args.getInt("MaxOcc"); + for (int n = -maxOcc; n <= maxOcc; n++) _ns.push_back(n); + } + + SpecialBosonSite(Args const& args = Args::global()) { + auto systype = args.getString("SystemType"); + + auto tags = TagSet("Site,Boson"); + if (args.defined("SiteNumber")) { + auto n = args.getInt("SiteNumber"); + tags.addTags("n=" + str(n)); + } + + auto maxOcc = args.getInt("MaxOcc"); + for (int n = -maxOcc; n <= maxOcc; n++) _ns.push_back(n); + + auto qints = Index::qnstorage(_ns.size()); + for (int i = 0; i < _ns.size(); i++) { + int n = _ns.at(i); + if (systype == "SC_scatter") { + int p = n % 2; + qints[i] = QNInt(QN({"Nf", n, -1}, {"Ps", p, -2}), 1); + } else if (systype == "Normal") { + qints[i] = QNInt(QN({"Nf", 0, -1}, {"Ns", -n, -1}), 1); + } else if (systype == "SC_Josephson_scatter") { + int p = n % 2; + qints[i] = QNInt(QN({"Pf", p, -2}, {"Ps", p, -2}), 1); + } else { + cout << "Unknown system type: " << systype << endl; + throw; + } + } + s = Index(std::move(qints), Out, tags); + } + + Index index() const { return s; } + + IndexVal state(std::string state) { + if (state == "Emp") state = "0"; + for (int i = 0; i < _ns.size(); i++) { + if (state == str(_ns.at(i))) return s(i + 1); + } + throw ITError("State " + state + " not recognized"); + return IndexVal{}; + } + + ITensor op(std::string const& opname, Args const& args) const { + auto sP = prime(s); + + auto Op = ITensor(dag(s), sP); + + if (opname == "N" || opname == "n") { + for (int i = 0; i < _ns.size(); i++) { + int j = i + 1; + int n = _ns.at(i); + Op.set(s = j, sP = j, n); + } + } else if (opname == "NSqr" || opname == "nSqr") { + for (int i = 0; i < _ns.size(); i++) { + int j = i + 1; + int n = _ns.at(i); + Op.set(s = j, sP = j, n * n); + } + } else if (opname == "A" or opname == "C") { + for (int i = 1; i < _ns.size(); i++) { + Op.set(s = 1 + i, sP = i, 1); + } + } else if (opname == "Adag" or opname == "Cdag") { + for (int i = 1; i < _ns.size(); i++) { + Op.set(s = i, sP = 1 + i, 1); + } + } else if (opname == "A2") { + for (int i = 3; i <= _ns.size(); i++) { + Op.set(s = i, sP = i - 2, 1); + } + } else if (opname == "A2dag") { + for (int i = 3; i <= _ns.size(); i++) { + Op.set(s = i - 2, sP = i, 1); + } + } else if (opname == "I") { + for (int i = 1; i <= _ns.size(); i++) { + Op.set(s = i, sP = i, 1); + } + } else { + throw ITError("Operator \"" + opname + "\" name not recognized"); + } + + return Op; + } +}; +#endif diff --git a/hybridleads/kbasis/SpecialFermion.h b/hybridleads/kbasis/SpecialFermion.h new file mode 100644 index 0000000..d5118a1 --- /dev/null +++ b/hybridleads/kbasis/SpecialFermion.h @@ -0,0 +1,104 @@ +#ifndef __SPECIALFERMION_H_CMC__ +#define __SPECIALFERMION_H_CMC__ +#include "itensor/mps/siteset.h" +#include "itensor/util/str.h" +using namespace itensor; + +class SpecialFermionSite { + Index s; + + public: + SpecialFermionSite(Index I) : s(I) {} + + SpecialFermionSite(Args const& args = Args::global()) { + auto systype = args.getString("SystemType"); + auto ts = TagSet("Site,Fermion"); + auto n = 1; + if (args.defined("SiteNumber")) { + n = args.getInt("SiteNumber"); + ts.addTags("n=" + str(n)); + } + auto in_scatter = args.getBool("in_scatter"); + if (systype == "SC_scatter") { + if (in_scatter) { + s = Index(QN({"Nf", 0, -1}, {"Ps", 0, -2}), 1, + QN({"Nf", 0, -1}, {"Ps", 1, -2}), 1, Out, ts); + } else { + s = Index(QN({"Nf", 0, -1}, {"Ps", 0, -2}), 1, + QN({"Nf", 1, -1}, {"Ps", 0, -2}), 1, Out, ts); + } + } else if (systype == "Normal") { + if (in_scatter) { + s = Index(QN({"Nf", 0, -1}, {"Ns", 0, -1}), 1, + QN({"Nf", 1, -1}, {"Ns", 1, -1}), 1, Out, ts); + } else { + s = Index(QN({"Nf", 0, -1}, {"Ns", 0, -1}), 1, + QN({"Nf", 1, -1}, {"Ns", 0, -1}), 1, Out, ts); + } + } else if (systype == "SC_Josephson_scatter") { + if (in_scatter) { + s = Index(QN({"Pf", 0, -2}, {"Ps", 0, -2}), 1, + QN({"Pf", 0, -2}, {"Ps", 1, -2}), 1, Out, ts); + } else { + s = Index(QN({"Pf", 0, -2}, {"Ps", 0, -2}), 1, + QN({"Pf", 1, -2}, {"Ps", 0, -2}), 1, Out, ts); + } + } else { + cout << "Unknown system type: " << systype << endl; + throw; + } + } + + Index index() const { return s; } + + IndexVal state(std::string const& state) { + if (state == "Emp" || state == "0") { + return s(1); + } else if (state == "Occ" || state == "1") { + return s(2); + } else { + throw ITError("State " + state + " not recognized"); + } + return IndexVal{}; + } + + ITensor op(std::string const& opname, Args const& args) const { + auto sP = prime(s); + + auto Emp = s(1); + auto EmpP = sP(1); + auto Occ = s(2); + auto OccP = sP(2); + + auto Op = ITensor(dag(s), sP); + + if (opname == "N" || opname == "n") { + Op.set(Occ, OccP, 1); + } else if (opname == "C") { + Op.set(Occ, EmpP, 1); + } else if (opname == "Cdag") { + Op.set(Emp, OccP, 1); + } else if (opname == "A") { + Op.set(Occ, EmpP, 1); + } else if (opname == "Adag") { + Op.set(Emp, OccP, 1); + } else if (opname == "F" || opname == "FermiPhase") { + Op.set(Emp, EmpP, 1); + Op.set(Occ, OccP, -1); + } else if (opname == "projEmp") { + Op.set(Emp, EmpP, 1); + } else if (opname == "projOcc") { + Op.set(Occ, OccP, 1); + } else if (opname == "I") { + Op.set(Occ, OccP, 1); + Op.set(Emp, EmpP, 1); + } else { + throw ITError("Operator \"" + opname + "\" name not recognized"); + } + + return Op; + } +}; + +using SpecialFermion = BasicSiteSet; +#endif diff --git a/hybridleads/kbasis/TDVPObserver.h b/hybridleads/kbasis/TDVPObserver.h new file mode 100644 index 0000000..365bae2 --- /dev/null +++ b/hybridleads/kbasis/TDVPObserver.h @@ -0,0 +1,100 @@ +#ifndef __TDVPOBSERVER_H_CMC__ +#define __TDVPOBSERVER_H_CMC__ +#include +#include + +#include "ContainerUtility.h" +#include "Entanglement.h" +#include "itensor/all.h" +using namespace iut; +using namespace iutility; + +template +class TDVPObserver : public DMRGObserver { + public: + TDVPObserver(const SitesType& sites, const MPS& psi, + const Args& args = Args::global()) + : DMRGObserver(psi, args), + _sites(sites), + _ns(length(psi), 0.), + _Npar(0.), + _specs(length(psi)) { + _write = args.getBool("Write", false); + _out_dir = args.getString("out_dir", "."); + _charge_site = args.getInt("charge_site", -1); + } + + void measure(const Args& args); + + Real Npar() const { return _Npar; } + auto const& ns() const { return _ns; } + const Spectrum& spec(int i) const { return _specs.at(i); } + + private: + bool _write; + string _out_dir; // empty string "" if not write + SitesType _sites; + int _charge_site = -1; + + // Observables + vector _ns; + Real _Npar; + vector _specs; +}; + +template +void TDVPObserver::measure(const Args& args) { + DMRGObserver::measure(args); + + cout << scientific << setprecision(14); + // Define your measurements below + // Call psi() to access the MPS + // + auto N = length(psi()); + auto b = args.getInt("AtBond"); + auto sw = args.getInt("Sweep"); + auto ha = args.getInt("HalfSweep"); + auto energy = args.getReal("Energy", 0); + + if (b != N) _specs.at(b) = spectrum(); + + int oc = orthoCenter(psi()); + int nc = args.getInt("NumCenter"); + // measure during the second half of sweep + if (oc == N || ha == 2) { + // Density + ITensor n_op = noPrime(psi().A(oc) * _sites.op("N", oc), "Site"); + + n_op *= dag(psi().A(oc)); + Real ni = real(eltC(n_op)); + cout << "\t*den " << oc << " " << ni << endl; + _ns.at(oc - 1) = ni; + + // Entanglement entropy + Real S = EntangEntropy(spectrum()); + cout << "\t*entS " << oc << " " << S << endl; + + /*if (oc == _charge_site) + { + auto denmat = psi()(oc) * dag(prime(psi()(oc),"Site")); + denmat.takeReal(); + auto [Q,D] = diagHermitian (denmat); + auto ii = denmat.inds()(1); + int maxOcc = _sites.maxOcc(); + for(int i = 1; i <= dim(ii); i++) + cout << "\t*nC " << i-maxOcc-1 << " " << elt (denmat,i,i) << endl; + }*/ + } + + // At the end of a sweep + if (oc == 1 && ha == 2 && b == 1) { + for (int i = 1; i < N; i++) + cout << "\t*m " << i << " " << dim(rightLinkIndex(psi(), i)) << endl; + + if (_write) { + cout << "write MPS" << endl; + writeToFile(_out_dir + "/psi.mps", psi()); + } + } +} +#endif diff --git a/kbasis/analysis.py b/hybridleads/kbasis/analysis.py similarity index 100% rename from kbasis/analysis.py rename to hybridleads/kbasis/analysis.py diff --git a/hybridleads/kbasis/basisextension.h b/hybridleads/kbasis/basisextension.h new file mode 100644 index 0000000..7ffa070 --- /dev/null +++ b/hybridleads/kbasis/basisextension.h @@ -0,0 +1,238 @@ +#include "itensor/decomp.h" +#include "itensor/mps/localop.h" +#include "itensor/mps/mpo.h" +#include "itensor/mps/mps.h" +#include "itensor/mps/mpsalgs.cc" +#include "itensor/tensor/slicemat.h" +#include "itensor/util/cputime.h" +#include "itensor/util/print_macro.h" + +namespace itensor { + +void denmatSumDecomp(std::vector const& psis, MPS& res, + std::vector& Bs, int b, Direction dir, + Args args = Args::global()) { + // NumCenter can only be 1 if not want to treat res exactly + const int numCenter = args.getInt("NumCenter", 1); + const bool quiet = args.getBool("Quiet", false); + + // SVD site tensor of res without truncaion + auto [V1, S1, U1] = svd(Bs.front(), dir == Fromleft ? rightLinkIndex(res, b) + : leftLinkIndex(res, b)); + + // Find the indices to be left to the density matrix + auto& to_orth = Bs.front(); + auto& newoc = (dir == Fromleft ? res(b + 1) : res(b - 1)); + auto& activeInds = to_orth.inds(); + auto cinds = stdx::reserve_vector(activeInds.r()); + for (auto& I : activeInds) { + if (!hasIndex(newoc, I)) cinds.push_back(I); + } + + auto [cmb, mid] = combiner(std::move(cinds)); + + if (dim(mid) <= dim(commonIndex(U1, S1))) { + res.ref(b) = U1; + if (!quiet) + printfln("warning: at bond %d, already reach maximum bond dimension.", b); + } else { + // Density matrix summation to be truncated + ITensor rho2; + if (numCenter == 1) { + auto psi = psis.begin(); + for (auto B = Bs.begin() + 1; B != Bs.end(); ++B, ++psi) { + rho2 += prime(*B) * + dag(prime(*B, dir == Fromleft ? rightLinkIndex(*psi, b) + : leftLinkIndex(*psi, b))); + } + rho2.swapPrime(0, 1); + } else { + Error("numCenter can only be one"); + } + + // Form the density matrix with only 2 indices + U1 *= cmb; + rho2 *= cmb; + cmb.prime(); + cmb.dag(); + rho2 *= cmb; + cmb.noPrime(); + + // Project rho2c to the orthogonal complement of U1 + auto proj2 = + toDense(delta(dag(mid), prime(mid))) - dag(U1) * prime(U1, mid); + auto normrho2 = norm(rho2); + rho2 *= mapPrime(proj2, 0, 2); + rho2 *= proj2; + rho2.mapPrime(2, 0); + rho2.swapPrime(0, 1); + auto normPrho2P = norm(rho2); + if (normPrho2P / normrho2 < + 1E-14) // TODO: changed to calculate the trace will have less + // complexity and have the same effect! + { + res.ref(b) = cmb * U1; + if (!quiet) printfln("warning: at bond %d, not adding any new basis.", b); + } else { + // Diagonalize rho2c to obtain U2 + ITensor U2, D2; + args.add("Truncate", true); + diag_hermitian(rho2, U2, D2, args); // T==prime(U)*D*dag(U) + U2.dag(); + + // Direct sum of U1 and U2 + auto i1 = commonIndex(U1, S1); + auto i2 = commonIndex(U2, D2); + auto sumind = Index(dim(i1) + dim(i2), "Link"); + sumind.setDir(i1.dir()); + ITensor expand1, expand2; + plussers(i1, i2, sumind, expand1, expand2); + auto U = U1 * expand1 + U2 * expand2; + + res.ref(b) = cmb * U; + } + } + + // Obtain the new Bs for the operation of the next site + Bs.front() *= dag(res(b)); + Bs.front() *= (dir == Fromleft ? res(b + 1) : res(b - 1)); + auto psi = psis.begin(); + for (auto B = Bs.begin() + 1; B != Bs.end(); ++B, ++psi) { + (*B) *= dag(res(b)); + (*B) *= (dir == Fromleft ? (*psi).A(b + 1) : (*psi).A(b - 1)); + } +} + +void addBasisWorker(std::vector const& psis, MPS& res, Direction dir, + const Args& args = Args::global()) { + int N = length(res); + int nt = psis.size() + 1; + + if (dir == Fromleft) { + if (orthoCenter(res) != 1) Error("OC need set to be 1"); + for (auto& psi : psis) { + if (orthoCenter(psi) != 1) Error("OC need set to be 1"); + } + + auto Bs = std::vector(nt); + Bs.front() = res(1); + auto psi = psis.begin(); + for (auto B = Bs.begin() + 1; B != Bs.end(); ++B, ++psi) { + (*B) = (*psi).A(1); + } + + for (int b = 1; b < N; ++b) { + denmatSumDecomp(psis, res, Bs, b, Fromleft, args); + } + + res.Aref(N) = Bs.front(); + } else { + if (orthoCenter(res) != N) Error("OC need set to be N"); + for (auto& psi : psis) { + if (orthoCenter(psi) != N) Error("OC need set to be N"); + } + + auto Bs = std::vector(nt); + Bs.front() = res(N); + auto psi = psis.begin(); + for (auto B = Bs.begin() + 1; B != Bs.end(); ++B, ++psi) { + (*B) = (*psi).A(N); + } + + for (int b = N; b > 1; --b) { + denmatSumDecomp(psis, res, Bs, b, Fromright, args); + } + + res.Aref(1) = Bs.front(); + } +} + +void addBasis(MPS& phi, const MPO& H, Real truncK, int maxDim, + const Args& args0 = Args::global()) { + auto quiet = args0.getBool("Quiet", false); + auto dk = args0.getInt("KrylovOrd", 2); + auto method = args0.getString("Method", "DensityMatrix"); + auto nsw = args0.getInt("Nsweep", 2); + auto donormalize = + args0.getBool("DoNormalize", + false); // TODO: add a function to construct general 1-tauH + + auto psis = std::vector(dk - 1); + + cpu_time expand_time; + for (int i = 0; i < dk - 1; ++i) { + auto args1 = Args("Method=", method, "Cutoff=", truncK, "MaxDim=", maxDim, + "Nsweep=", nsw); + + if (i == 0) + psis.at(i) = applyMPO(H, phi, args1); + else + psis.at(i) = applyMPO(H, psis.at(i - 1), args1); + + psis.at(i).noPrime(); + if (donormalize) psis.at(i).normalize(); + + if (!quiet) { + printfln("norm(psi%d)=%.20f", i + 1, norm(psis.at(i))); + printfln("maxLinkDim(psi%d) = %d", i + 1, maxLinkDim(psis.at(i))); + } + } + + int N = length(phi); + for (int i = 0; i < dk - 1; ++i) { + psis.at(i).position(N); + } + phi.position(N); + + // TODO: adjustable weight for each psi + addBasisWorker(psis, phi, Fromright, args0); + + auto sm = expand_time.sincemark(); + printfln("\nmaxLinkDim after global subspace expansion = %d", + maxLinkDim(phi)); + printfln("Global subspace expansion: cputime = %s, walltime = %s", + showtime(sm.time), showtime(sm.wall)); +} + +void addBasis(MPS& phi, const MPO& H, std::vector const& maxdimK, + const Args& args0 = Args::global()) { + auto dk = args0.getInt("KrylovOrd", 2); + auto method = args0.getString("Method", "DensityMatrix"); + auto nsw = args0.getInt("Nsweep", 2); + auto donormalize = args0.getBool("DoNormalize", false); + + auto psis = std::vector(dk - 1); + + cpu_time expand_time; + for (int i = 0; i < dk - 1; ++i) { + auto args1 = + Args("Method=", method, "MaxDim=", maxdimK.at(i), "Nsweep=", nsw); + + if (i == 0) + psis.at(i) = applyMPO(H, phi, args1); + else + psis.at(i) = applyMPO(H, psis.at(i - 1), args1); + + psis.at(i).noPrime(); + if (donormalize) psis.at(i).normalize(); + + printfln("norm(psi%d)=%.20f", i + 1, norm(psis.at(i))); + printfln("maxLinkDim(psi%d) = %d", i + 1, maxLinkDim(psis.at(i))); + } + + int N = length(phi); + for (int i = 0; i < dk - 1; ++i) { + psis.at(i).position(N); + } + phi.position(N); + + addBasisWorker(psis, phi, Fromright, args0); + + auto sm = expand_time.sincemark(); + printfln("\nmaxLinkDim after global subspace expansion = %d", + maxLinkDim(phi)); + printfln("Global subspace expansion: cputime = %s, walltime = %s", + showtime(sm.time), showtime(sm.wall)); +} + +} // namespace itensor diff --git a/kbasis/input b/hybridleads/kbasis/input similarity index 100% rename from kbasis/input rename to hybridleads/kbasis/input diff --git a/hybridleads/kbasis/quench.cc b/hybridleads/kbasis/quench.cc new file mode 100644 index 0000000..903fcd3 --- /dev/null +++ b/hybridleads/kbasis/quench.cc @@ -0,0 +1,274 @@ +#include + +#include "Timer.h" +#include "itensor/all.h" +Timers timer; +#include "BdGBasis.h" +#include "ContainerUtility.h" +#include "Hamiltonian.h" +#include "IUtility.h" +#include "InitState.h" +#include "MPSUtility.h" +#include "MixedBasis.h" +#include "MyObserver.h" +#include "OneParticleBasis.h" +#include "ReadInput.h" +#include "ReadWriteFile.h" +#include "TDVPObserver.h" +#include "basisextension.h" +#include "tdvp.h" +using namespace itensor; +using namespace std; + +// Define some parameters +struct Para { + Real tcL = 0., tcR = 0.; + + void write(ostream& s) const { + iut::write(s, tcL); + iut::write(s, tcR); + } + + void read(istream& s) { + iut::read(s, tcL); + iut::read(s, tcR); + } +}; + +void writeAll(const string& filename, const MPS& psi, const MPO& H, + const Para& para, const Args& args_basis, int step, + const ToGlobDict& to_glob, const ToLocDict& to_loc) { + ofstream ofs(filename); + itensor::write(ofs, psi); + itensor::write(ofs, H); + itensor::write(ofs, args_basis); + itensor::write(ofs, step); + para.write(ofs); + iut::write(ofs, to_glob); + iut::write(ofs, to_loc); +} + +void readAll(const string& filename, MPS& psi, MPO& H, Para& para, + Args& args_basis, int& step, ToGlobDict& to_glob, + ToLocDict& to_loc) { + ifstream ifs = open_file(filename); + itensor::read(ifs, psi); + itensor::read(ifs, H); + itensor::read(ifs, args_basis); + itensor::read(ifs, step); + para.read(ifs); + iut::read(ifs, to_glob); + iut::read(ifs, to_loc); +} + +void print_orbs(const vector& orbs) { + cout << "Orbitals: name, ki, energy" << endl; + for (int i = 1; i <= orbs.size(); i++) { + auto [name, ki, en] = orbs.at(i - 1); + cout << i << ": " << name << " " << ki << " " << en << endl; + } +} + +// Make MPO for the current operator +template +MPO get_current_mpo(const SiteType& sites, const Basis1& basis1, + const Basis2& basis2, int i1, int i2, + const ToGlobDict& to_glob) { + AutoMPO ampo(sites); + add_CdagC(ampo, basis1, basis2, i1, i2, 1., to_glob); + auto mpo = toMPO(ampo); + return mpo; +} + +inline Real get_current(const MPO& JMPO, const MPS& psi) { + auto J = innerC(psi, JMPO, psi); + return -2. * imag(J); +} + +int main(int argc, char* argv[]) { + string infile = argv[1]; + InputGroup input(infile, "basic"); + + auto L_lead = input.getInt("L_lead"); + auto L_device = input.getInt("L_device"); + auto t_lead = input.getReal("t_lead"); + auto t_device = input.getReal("t_device"); + auto t_contactL = input.getReal("t_contactL"); + auto t_contactR = input.getReal("t_contactR"); + auto mu_leadL = input.getReal("mu_leadL"); + auto mu_leadR = input.getReal("mu_leadR"); + auto mu_device = input.getReal("mu_device"); + auto mu_biasL = input.getReal("mu_biasL"); + auto mu_biasS = input.getReal("mu_biasS"); + auto mu_biasR = input.getReal("mu_biasR"); + auto damp_decay_length = input.getInt("damp_decay_length", 0); + + auto dt = input.getReal("dt"); + auto time_steps = input.getInt("time_steps"); + auto NumCenter = input.getInt("NumCenter"); + auto Truncate = input.getYesNo("Truncate"); + auto mixNumCenter = input.getYesNo("mixNumCenter", false); + auto globExpanNStr = input.getString("globExpanN", "inf"); + int globExpanN; + if (globExpanNStr == "inf" or globExpanNStr == "Inf" or + globExpanNStr == "INF") + globExpanN = std::numeric_limits::max(); + else + globExpanN = std::stoi(globExpanNStr); + auto globExpanItv = input.getInt("globExpanItv", 1); + auto globExpanCutoff = input.getReal("globExpanCutoff", 1e-8); + auto globExpanKrylovDim = input.getInt("globExpanKrylovDim", 3); + auto globExpanHpsiCutoff = input.getReal("globExpanHpsiCutoff", 1e-8); + auto globExpanHpsiMaxDim = input.getInt("globExpanHpsiMaxDim", 300); + auto globExpanMethod = input.getString("globExpanMethod", "DensityMatrix"); + + auto UseSVD = input.getYesNo("UseSVD", true); + auto SVDmethod = + input.getString("SVDMethod", "gesdd"); // can be also "ITensor" + auto WriteDim = input.getInt("WriteDim"); + + auto write = input.getYesNo("write", false); + auto write_dir = input.getString("write_dir", "."); + auto write_file = input.getString("write_file", ""); + auto read = input.getYesNo("read", false); + auto read_dir = input.getString("read_dir", "."); + auto read_file = input.getString("read_file", ""); + + auto sweeps = iut::Read_sweeps(infile, "sweeps"); + + cout << setprecision(14) << endl; + + MPS psi; + MPO H; + // Define + int step = 1; + auto sites = Fermion(); + Para para; + Args args_basis; + + ToGlobDict to_glob; + ToLocDict to_loc; + OneParticleBasis leadL, leadR, charge; + OneParticleBasis scatterer; + + // -- Initialization -- + if (!read) { + // Factor for exponentially decaying hoppings + Real damp_fac = + (damp_decay_length == 0 ? 1. : exp(-1. / damp_decay_length)); + // Create bases for the leads + cout << "H left lead" << endl; + leadL = + OneParticleBasis("L", L_lead, t_lead, mu_leadL, damp_fac, true, true); + cout << "H right lead" << endl; + leadR = + OneParticleBasis("R", L_lead, t_lead, mu_leadR, damp_fac, false, true); + // Create basis for scatterer + cout << "H dev" << endl; + scatterer = OneParticleBasis("S", L_device, t_device, mu_device); + + // Combine and sort all the basis states + auto info = sort_by_energy(leadL, leadR, scatterer); + tie(to_glob, to_loc) = make_orb_dicts(info); + print_orbs(info); + + // SiteSet + int N = to_glob.size(); + sites = Fermion(N); + + // Make Hamiltonian MPO for time evolution + para.tcL = t_contactL; + para.tcR = t_contactR; + auto ampo = + get_ampo_tight_binding(leadL, leadR, scatterer, sites, para, to_glob); + H = toMPO(ampo); + cout << "MPO dim = " << maxLinkDim(H) << endl; + + // Initialize MPS + // ********************************* + { + /*auto para0 = para; + para0.tcL = 0.; + para0.tcR = 0.; + auto leadL0 = OneParticleBasis ("L", L_lead, t_lead, mu_leadL+mu_biasL, + damp_fac, true, true); auto leadR0 = OneParticleBasis ("R", L_lead, + t_lead, mu_leadR+mu_biasR, damp_fac, false, true);*/ + psi = get_non_inter_ground_state( + leadL, leadR, scatterer, sites, mu_leadL + mu_biasL, + mu_device + mu_biasS, mu_leadR + mu_biasR, to_glob); + /*auto ampo0 = get_ampo_tight_binding (leadL0, leadR0, scatterer, sites, + para, to_glob); auto H0 = toMPO (ampo0); auto sweeps0 = iut::Read_sweeps + (infile, "DMRG_sweeps"); dmrg (psi, H0, sweeps0, {"WriteDim",WriteDim});*/ + } + // ********************************* + psi.position(1); + + // Check initial energy + cout << "Initial energy = " << inner(psi, H, psi) << endl; + } else { + readAll(read_dir + "/" + read_file, psi, H, para, args_basis, step, to_glob, + to_loc); + sites = Fermion(siteInds(psi)); + } + // -- End of initialization -- + + // -- Observer -- + auto obs = TDVPObserver(sites, psi); + // Current MPO + auto jmpoL = get_current_mpo(sites, leadL, leadL, -2, -1, to_glob); + auto jmpoR = get_current_mpo(sites, leadR, leadR, 1, 2, to_glob); + + // -- Time evolution -- + cout << "Start time evolution" << endl; + cout << sweeps << endl; + psi.position(1); + Real en, err; + + Args args_tdvp_expansion = {"Cutoff", globExpanCutoff, + "Method", "DensityMatrix", + "KrylovOrd", globExpanKrylovDim, + "DoNormalize", true, + "Quiet", true}; + Args args_tdvp = {"Quiet", true, "NumCenter", NumCenter, + "DoNormalize", true, "Truncate", Truncate, + "UseSVD", UseSVD, "SVDmethod", SVDmethod, + "WriteDim", WriteDim, "mixNumCenter", mixNumCenter}; + + LocalMPO PH(H, args_tdvp); + while (step <= time_steps) { + cout << "step = " << step << endl; + + // Subspace expansion + if (maxLinkDim(psi) < sweeps.mindim(1) or + (step < globExpanN and (step - 1) % globExpanItv == 0)) { + timer["glob expan"].start(); + addBasis(psi, H, globExpanHpsiCutoff, globExpanHpsiMaxDim, + args_tdvp_expansion); + PH.reset(); + timer["glob expan"].stop(); + } + + // Time evolution + timer["tdvp"].start(); + TDVPWorker(psi, PH, 1_i * dt, sweeps, obs, args_tdvp); + timer["tdvp"].stop(); + auto d1 = maxLinkDim(psi); + + // Measure currents by MPO + timer["current mps"].start(); + auto jL = get_current(jmpoL, psi); + auto jR = get_current(jmpoR, psi); + cout << "\tI L/R = " << jL << " " << jR << endl; + timer["current mps"].stop(); + + step++; + if (write) { + timer["write"].start(); + writeAll(write_dir + "/" + write_file, psi, H, para, args_basis, step, + to_glob, to_loc); + timer["write"].stop(); + } + } + timer.print(); + return 0; +} diff --git a/hybridleads/kbasis/tdvp.h b/hybridleads/kbasis/tdvp.h new file mode 100644 index 0000000..a9e6521 --- /dev/null +++ b/hybridleads/kbasis/tdvp.h @@ -0,0 +1,330 @@ +#ifndef __ITENSOR_TDVP_H +#define __ITENSOR_TDVP_H + +#include "itensor/iterativesolvers.h" +#include "itensor/mps/DMRGObserver.h" +#include "itensor/mps/localmposet.h" +#include "itensor/mps/sweeps.h" +#include "itensor/util/cputime.h" + +namespace itensor { + +template +Real TDVPWorker(MPS& psi, LocalOpT& PH, Cplx t, const Sweeps& sweeps, + const Args& args = Args::global()); + +template +Real TDVPWorker(MPS& psi, LocalOpT& PH, Cplx t, const Sweeps& sweeps, + DMRGObserver& obs, Args args = Args::global()); + +// +// Available TDVP methods: +// second order integrator: sweep left-to-right and right-to-left +// + +// +// TDVP with an MPO +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, const Sweeps& sweeps, + const Args& args = Args::global()) { + LocalMPO PH(H, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, args); + return energy; +} + +// +// TDVP with an MPO and custom DMRGObserver +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, const Sweeps& sweeps, + DMRGObserver& obs, const Args& args = Args::global()) { + LocalMPO PH(H, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +// +// TDVP with an MPO and boundary tensors LH, RH +// LH - H1 - H2 - ... - HN - RH +//(ok if one or both of LH, RH default constructed) +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, ITensor const& LH, + ITensor const& RH, const Sweeps& sweeps, + const Args& args = Args::global()) { + LocalMPO PH(H, LH, RH, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, args); + return energy; +} + +// +// TDVP with an MPO and boundary tensors LH, RH +// and a custom observer +// +Real inline tdvp(MPS& psi, MPO const& H, Cplx t, ITensor const& LH, + ITensor const& RH, const Sweeps& sweeps, DMRGObserver& obs, + const Args& args = Args::global()) { + LocalMPO PH(H, LH, RH, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +// +// TDVP with a set of MPOs (lazily summed) +//(H vector is 0-indexed) +// +Real inline tdvp(MPS& psi, std::vector const& Hset, Cplx t, + const Sweeps& sweeps, const Args& args = Args::global()) { + LocalMPOSet PH(Hset, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, args); + return energy; +} + +// +// TDVP with a set of MPOs and a custom DMRGObserver +//(H vector is 0-indexed) +// +Real inline tdvp(MPS& psi, std::vector const& Hset, Cplx t, + const Sweeps& sweeps, DMRGObserver& obs, + const Args& args = Args::global()) { + LocalMPOSet PH(Hset, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +// +// TDVPWorker +// + +template +Real TDVPWorker(MPS& psi, LocalOpT& PH, Cplx t, Sweeps const& sweeps, + Args const& args) { + DMRGObserver obs(psi, args); + Real energy = TDVPWorker(psi, PH, t, sweeps, obs, args); + return energy; +} + +vector reach_max_dim(const MPS& psi, int maxdim) { + int N = length(psi); + vector re(N); + for (int b = 1; b < N; b++) { + int m = dim(rightLinkIndex(psi, b)); + bool reach_max = (m >= maxdim ? true : false); + re.at(b) = reach_max; + } + return re; +} + +template +Real TDVPWorker(MPS& psi, LocalOpT& H, Cplx t, Sweeps const& sweeps, + DMRGObserver& obs, Args args) { + // Truncate blocks of degenerate singular values (or not) + args.add("RespectDegenerate", args.getBool("RespectDegenerate", true)); + + const bool silent = args.getBool("Silent", false); + if (silent) { + args.add("Quiet", true); + args.add("PrintEigs", false); + args.add("NoMeasure", true); + args.add("DebugLevel", -1); + } + const bool quiet = args.getBool("Quiet", false); + const int debug_level = args.getInt("DebugLevel", (quiet ? -1 : 0)); + const bool mixNumCenter = args.getBool("mixNumCenter", false); + + const int N = length(psi); + Real energy = NAN; + + auto halfSweep = args.getString("HalfSweep", ""); + if (halfSweep == "toLeft") + psi.position(N); + else + psi.position(1); + + args.add("DebugLevel", debug_level); + + bool write_to_disk = false; + for (int sw = 1; sw <= sweeps.nsweep(); ++sw) { + int numCenter = args.getInt("NumCenter", 2); + + args.add("Truncate", true); + + cpu_time sw_time; + args.add("Sweep", sw); + args.add("NSweep", sweeps.nsweep()); + args.add("Cutoff", sweeps.cutoff(sw)); + args.add("MinDim", sweeps.mindim(sw)); + args.add("MaxDim", sweeps.maxdim(sw)); + args.add("MaxIter", sweeps.niter(sw)); + + vector is_maxdim; + if (mixNumCenter) { + is_maxdim = reach_max_dim(psi, args.getInt("MaxDim")); + } + + if (!write_to_disk and maxLinkDim(psi) >= args.getInt("WriteDim")) + write_to_disk = true; + if (!H.doWrite() && args.defined("WriteDim") && + sweeps.maxdim(sw) >= args.getInt("WriteDim") && write_to_disk) { + if (!quiet) { + println("\nTurning on write to disk, write_dir = ", + args.getString("WriteDir", "./")); + } + + // psi.doWrite(true); + H.doWrite(true, args); + } + + // 0, 1 and 2-site wavefunctions + ITensor phi0, phi1; + Spectrum spec; + for (int b = 1, ha = 1; ha <= 2;) { + if (halfSweep == "toRight" && ha == 2) + continue; + else if (halfSweep == "toLeft" && ha == 1) + continue; + + if (!quiet) printfln("Sweep=%d, HS=%d, Bond=%d/%d", sw, ha, b, (N - 1)); + + // Forward propagation + H.numCenter(numCenter); + H.position(b, psi); + + if (numCenter == 2) + phi1 = psi(b) * psi(b + 1); + else if (numCenter == 1) + phi1 = psi(b); + + applyExp(H, phi1, -t / 2, args); + + if (args.getBool("DoNormalize", true)) phi1 /= norm(phi1); + + if (numCenter == 2) + spec = psi.svdBond(b, phi1, (ha == 1 ? Fromleft : Fromright), H, args); + else if (numCenter == 1) + psi.ref(b) = phi1; + + // Calculate energy + ITensor H_phi1; + H.product(phi1, H_phi1); + energy = real(eltC(dag(phi1) * H_phi1)); + + // mixed Nc + if (mixNumCenter) { + int maxdim = sweeps.maxdim(sw); + if (ha == 1) { + if (numCenter == 2 and b < N - 1 and !is_maxdim.at(b) and + is_maxdim.at(b + 1)) { + phi1 = psi(b + 1); + numCenter = 1; + b += 1; + } + } else if (ha == 2) { + if (numCenter == 2 and b > 1 and !is_maxdim.at(b) and + is_maxdim.at(b - 1)) { + phi1 = psi(b); + numCenter = 1; + } + } + } + + // Backward propagation + if ((ha == 1 && b + numCenter - 1 != N) || (ha == 2 && b != 1)) { + auto b1 = (ha == 1 ? b + 1 : b); + + if (numCenter == 2) { + phi0 = psi(b1); + } else if (numCenter == 1) { + Index l; + if (ha == 1) + l = commonIndex(psi(b), psi(b + 1)); + else + l = commonIndex(psi(b - 1), psi(b)); + ITensor U, S, V(l); + spec = svd(phi1, U, S, V, args); + psi.ref(b) = U; + phi0 = S * V; + } + + H.numCenter(numCenter - 1); + H.position(b1, psi); + + applyExp(H, phi0, +t / 2, args); + + if (args.getBool("DoNormalize", true)) phi0 /= norm(phi0); + + if (numCenter == 2) { + psi.ref(b1) = phi0; + } + if (numCenter == 1) { + if (ha == 1) { + psi.ref(b + 1) *= phi0; + psi.leftLim(b); + psi.rightLim(b + 2); + } else { + psi.ref(b - 1) *= phi0; + psi.leftLim(b - 2); + psi.rightLim(b); + } + } + + // Calculate energy + ITensor H_phi0; + H.product(phi0, H_phi0); + energy = real(eltC(dag(phi0) * H_phi0)); + } + + if (!quiet) { + printfln(" Truncated to Cutoff=%.1E, Min_dim=%d, Max_dim=%d", + sweeps.cutoff(sw), sweeps.mindim(sw), sweeps.maxdim(sw)); + printfln(" Trunc. err=%.1E, States kept: %s", spec.truncerr(), + showDim(linkIndex(psi, b))); + } + + obs.lastSpectrum(spec); + + args.add("AtBond", b); + args.add("HalfSweep", ha); + args.add("Energy", energy); + args.add("Truncerr", spec.truncerr()); + + obs.measure(args); + + // Next sweep + if (mixNumCenter) { + int maxdim = sweeps.maxdim(sw); + if (ha == 1) { + if (numCenter == 1 and b != N and is_maxdim.at(b) and + !is_maxdim.at(b + 1)) { + numCenter = 2; + } + } else if (ha == 2) { + if (numCenter == 1 and b > 2 and is_maxdim.at(b - 1) and + !is_maxdim.at(b - 2)) { + numCenter = 2; + b -= 1; + } + } + } + sweepnext(b, ha, N, {"NumCenter=", numCenter}); + } // for loop over b + + if (!silent) { + auto sm = sw_time.sincemark(); + printfln(" Sweep %d/%d CPU time = %s (Wall time = %s)", sw, + sweeps.nsweep(), showtime(sm.time), showtime(sm.wall)); + } + + if (obs.checkDone(args)) break; + + } // for loop over sw + + if (args.getBool("DoNormalize", true)) { + // if(numCenter==1) psi.position(1); + psi.normalize(); + } + + return energy; +} + +} // namespace itensor + +#endif diff --git a/tdvp/Makefile b/hybridleads/tdvp/Makefile similarity index 97% rename from tdvp/Makefile rename to hybridleads/tdvp/Makefile index e90a6b0..a55ee6f 100644 --- a/tdvp/Makefile +++ b/hybridleads/tdvp/Makefile @@ -1,4 +1,4 @@ -# 1. Put this file in the same folder as your 'driver' code +# 1. Put this file in the same folder as your 'driver' code # (the code containing the 'main' function). # 2. Edit LIBRARY_DIR to point at the location of your ITensor Library @@ -64,4 +64,3 @@ clean: mkdebugdir: mkdir -p .debug_objs - diff --git a/hybridleads/tdvp/MyLocalmpo.h b/hybridleads/tdvp/MyLocalmpo.h new file mode 100644 index 0000000..0fb0d6a --- /dev/null +++ b/hybridleads/tdvp/MyLocalmpo.h @@ -0,0 +1,543 @@ +// +// Copyright 2018 The Simons Foundation, Inc. - All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef __ITENSOR_MyLocalMPO_CMC__ +#define __ITENSOR_MyLocalMPO_CMC__ +#include "itensor/mps/localop.h" +#include "itensor/mps/mpo.h" +// #include "itensor/util/print_macro.h" + +namespace itensor { + +// +// The MyLocalMPO class projects an MPO +// into the reduced Hilbert space of +// some number of sites of an MPS. +// (The default is 2 sites.) +// +// .----...--- ----...--. +// | | | | | | | +// W1-W2-..Wj-1 - Wj - Wj+1 -- Wj+2..-WN +// | | | | | | | +// '----...--- ----...--' +// +// +// Here the W's are the site tensors +// of the MPO "Op" and the method position(j,psi) +// has been called using the MPS 'psi' as a basis +// for the projection. +// +// This results in an unprojected region of +// num_center sites starting at site j. +// + +class MyLocalMPO { + public: + // + // Constructors + // + + MyLocalMPO(); + + // + // Regular case where H is an MPO for a finite system + // + MyLocalMPO(MPO const& H, Args const& args = Args::global()); + + // + // Use an MPS instead of an MPO. Equivalent to using an MPO + // of the outer product |Psi>length() + 1; + } + + ITensor const& L() const { return PH_[LHlim_]; } + // Replace left edge tensor at current bond + void L(ITensor const& nL) { PH_[LHlim_] = nL; } + // Replace left edge tensor bordering site j + // (so that nL includes sites < j) + void L(int j, ITensor const& nL); + + ITensor const& R() const { return PH_[RHlim_]; } + // Replace right edge tensor at current bond + void R(ITensor const& nR) { PH_[RHlim_] = nR; } + // Replace right edge tensor bordering site j + // (so that nR includes sites > j) + void R(int j, ITensor const& nR); + + ITensor const& L(int i) const { return PH_[i - 1]; } + ITensor const& R(int i) const { return PH_[i + 1]; } + void setL(int i, const ITensor& L) { PH_[i - 1] = L; } + void setR(int i, const ITensor& R) { PH_[i + 1] = R; } + + const MPO& H() const { + if (Op_ == 0) Error("MyLocalMPO is null or contains an MPS"); + return *Op_; + } + + int numCenter() const { return nc_; } + void numCenter(int val) { + if (val < 0 || val > 2) Error("numCenter must be set 0 or 1 or 2"); + nc_ = val; + lop_.numCenter(val); + } + + size_t size() const { return lop_.size(); } + + explicit operator bool() const { return Op_ != 0 || Psi_ != 0; } + + bool doWrite() const { return do_write_; } + void doWrite(bool val, Args const& args = Args::global()) { + if (Psi_ != 0) + Error( + "Write to disk not yet supported for MyLocalMPO initialized with an " + "MPS"); + if (!do_write_ && (val == true)) { + initWrite(args); + } + do_write_ = val; + } + + std::string const& writeDir() const { return writedir_; } + + int leftLim() const { return LHlim_; } + + int rightLim() const { return RHlim_; } + + void setLHlim(int val); + + void setRHlim(int val); + + const ITensor& operator()(int i) const { return PH_.at(i); } + + private: + ///////////////// + // + // Data Members + // + + const MPO* Op_; + std::vector PH_; + int LHlim_, RHlim_; // LHlim_ will be nc-1; RHlim_ will be nc+Nuc + int nc_; + + LocalOp lop_; + + bool do_write_ = false; + std::string writedir_ = "./"; + + const MPS* Psi_; + + // + ///////////////// + + void makeL(const MPS& psi, int k); + + void makeR(const MPS& psi, int k); + + void initWrite(Args const& args); + + std::string PHFName(int j) const { + return format("%s/PH_%03d", writedir_, j); + } +}; + +inline MyLocalMPO::MyLocalMPO() + : Op_(0), LHlim_(-1), RHlim_(-1), nc_(2), Psi_(0) {} + +inline MyLocalMPO::MyLocalMPO(const MPO& H, const Args& args) + : Op_(&H), + PH_(H.length() + 2), + LHlim_(0), + RHlim_(H.length() + 1), + nc_(2), + Psi_(0) { + if (args.defined("NumCenter")) numCenter(args.getInt("NumCenter")); +} + +inline MyLocalMPO::MyLocalMPO(const MPS& Psi, const Args& args) + : Op_(0), + PH_(Psi.length() + 2), + LHlim_(0), + RHlim_(Psi.length() + 1), + nc_(2), + Psi_(&Psi) { + if (args.defined("NumCenter")) numCenter(args.getInt("NumCenter")); +} + +inline MyLocalMPO::MyLocalMPO(const ITensor& LH, const ITensor& RH, + const Args& args) + : Op_(0), PH_(2), LHlim_(0), RHlim_(1), nc_(0), Psi_(0) { + PH_[0] = LH; + PH_[1] = RH; + lop_.update(L(), R()); // nc_ must be set to 0 in this case +} + +inline MyLocalMPO::MyLocalMPO(const MPO& H, const ITensor& LH, + const ITensor& RH, const Args& args) + : Op_(&H), + PH_(H.length() + 2), + LHlim_(0), + RHlim_(H.length() + 1), + nc_(2), + Psi_(0) { + PH_[0] = LH; + PH_[H.length() + 1] = RH; + if (H.length() == 1) + lop_.update(Op_->A(1), L(), R()); + else if (H.length() == 2) + lop_.update(Op_->A(1), Op_->A(2), L(), R()); + if (args.defined("NumCenter")) numCenter(args.getInt("NumCenter")); +} + +inline MyLocalMPO::MyLocalMPO(MPS const& Psi, ITensor const& LP, + ITensor const& RP, Args const& args) + : Op_(0), + PH_(Psi.length() + 2), + LHlim_(0), + RHlim_(Psi.length() + 1), + nc_(2), + Psi_(&Psi) { + PH_[0] = LP; + PH_[Psi.length() + 1] = RP; + if (args.defined("NumCenter")) numCenter(args.getInt("NumCenter")); +} + +inline MyLocalMPO::MyLocalMPO(MPO const& H, ITensor const& LH, int LHlim, + ITensor const& RH, int RHlim, Args const& args) + : Op_(&H), + PH_(H.length() + 2), + LHlim_(LHlim), + RHlim_(RHlim), + nc_(2), + Psi_(0) { + PH_.at(LHlim) = LH; + PH_.at(RHlim) = RH; + if (H.length() == 1) lop_.update(Op_->A(1), L(), R()); + if (H.length() == 2) lop_.update(Op_->A(1), Op_->A(2), L(), R()); + if (args.defined("NumCenter")) numCenter(args.getInt("NumCenter")); +} + +void inline MyLocalMPO::product(ITensor const& phi, ITensor& phip) const { + if (Op_ != 0) { + lop_.product(phi, phip); + } else if (Psi_ != 0) { + int b = position(); + + ITensor othr; + if (nc_ == 2) { + othr = (!L() ? dag(prime(Psi_->A(b), "Link")) + : L() * dag(prime(Psi_->A(b), "Link"))); + othr *= (!R() ? dag(prime(Psi_->A(b + 1), "Link")) + : R() * dag(prime(Psi_->A(b + 1), "Link"))); + } else if (nc_ == 1) { + othr = (!L() ? dag(prime(Psi_->A(b), "Link")) + : L() * dag(prime(Psi_->A(b), "Link"))); + if (R()) othr *= R(); + } else if (nc_ == 0) { + if (!L()) { + if (!R()) + Error("MyLocalMPO: Empty L() and R() in function product"); + else + othr = R(); + } else { + othr = L(); + if (R()) othr *= R(); + } + } + + auto z = (othr * phi).eltC(); + + phip = dag(othr); + phip *= z; + } else { + Error("MyLocalMPO is null"); + } +} + +void inline MyLocalMPO::L(int j, ITensor const& nL) { + if (LHlim_ > j - 1) setLHlim(j - 1); + PH_[LHlim_] = nL; +} + +void inline MyLocalMPO::R(int j, ITensor const& nR) { + if (RHlim_ < j + 1) setRHlim(j + 1); + PH_[RHlim_] = nR; +} + +inline void MyLocalMPO::position(int b, MPS const& psi) { + if (!(*this)) Error("MyLocalMPO is null"); + + makeL(psi, b - 1); + makeR(psi, b + nc_); + + setLHlim(b - 1); // not redundant since LHlim_ could be > b-1 + setRHlim(b + nc_); // not redundant since RHlim_ could be < b+nc_ + +#ifdef DEBUG + if (nc_ != 2 && nc_ != 1 && nc_ != 0) { + Error("LocalOp only supports 0 and 1 and 2 center sites currently"); + } +#endif + + if (Op_ != 0) // normal MPO case + { + if (nc_ == 2) + lop_.update(Op_->A(b), Op_->A(b + 1), L(), R()); + else if (nc_ == 1) + lop_.update(Op_->A(b), L(), R()); + else if (nc_ == 0) + lop_.update(L(), R()); + } +} + +int inline MyLocalMPO::position() const { + if (RHlim_ - LHlim_ != (nc_ + 1)) { + throw ITError("MyLocalMPO position not set"); + } + return LHlim_ + 1; +} + +inline void MyLocalMPO::shift(int j, Direction dir, ITensor const& A) { + if (!(*this)) Error("MyLocalMPO is null"); + +#ifdef DEBUG + if (nc_ != 2 && nc_ != 1 && nc_ != 0) { + Error("LocalOp only supports 0 and 1 and 2 center sites currently"); + } +#endif + + if (dir == Fromleft) { + if ((j - 1) != LHlim_) { + std::cout << "j-1 = " << (j - 1) << ", LHlim = " << LHlim_ << std::endl; + Error("Can only shift at LHlim"); + } + auto& E = PH_.at(LHlim_); + auto& nE = PH_.at(j); + nE = E * A; + nE *= Op_->A(j); + nE *= dag(prime(A)); + setLHlim(j); + setRHlim(j + nc_ + 1); + + if (nc_ == 2) + lop_.update(Op_->A(j + 1), Op_->A(j + 2), L(), R()); + else if (nc_ == 1) + lop_.update(Op_->A(j + 1), L(), R()); + else if (nc_ == 0) + lop_.update(L(), R()); + } else // dir == Fromright + { + if ((j + 1) != LHlim_) { + std::cout << "j+1 = " << (j + 1) << ", RHlim_ = " << RHlim_ << std::endl; + Error("Can only shift at RHlim_"); + } + auto& E = PH_.at(RHlim_); + auto& nE = PH_.at(j); + nE = E * A; + nE *= Op_->A(j); + nE *= dag(prime(A)); + setLHlim(j - nc_ - 1); + setRHlim(j); + + if (nc_ == 2) + lop_.update(Op_->A(j - 2), Op_->A(j - 1), L(), R()); + else if (nc_ == 1) + lop_.update(Op_->A(j - 1), L(), R()); + else if (nc_ == 0) + lop_.update(L(), R()); + } +} + +inline void MyLocalMPO::makeL(MPS const& psi, int k) { + if (!PH_.empty()) { + if (Op_ == 0) // Op is actually an MPS + { + while (LHlim_ < k) { + auto ll = LHlim_; + PH_.at(ll + 1) = (!PH_.at(ll) ? psi(ll + 1) : PH_[ll] * psi(ll + 1)); + PH_[ll + 1] *= dag(prime(Psi_->A(ll + 1), "Link")); + setLHlim(ll + 1); + } + } else // normal MPO case + { + while (LHlim_ < k) { + auto ll = LHlim_; + if (PH_.at(ll)) { + PH_.at(ll + 1) = PH_.at(ll) * psi(ll + 1); + } else { + PH_.at(ll + 1) = psi(ll + 1); + } + PH_.at(ll + 1) *= Op_->A(ll + 1); + PH_.at(ll + 1) *= dag(prime(psi(ll + 1))); + setLHlim(ll + 1); + assert(order(PH_.at(ll + 1)) == 3); + } + } + } +} + +inline void MyLocalMPO::makeR(MPS const& psi, int k) { + if (!PH_.empty()) { + if (Op_ == 0) // Op is actually an MPS + { + while (RHlim_ > k) { + const int rl = RHlim_; + PH_.at(rl - 1) = (!PH_.at(rl) ? psi(rl - 1) : PH_[rl] * psi(rl - 1)); + PH_[rl - 1] *= dag(prime(Psi_->A(rl - 1), "Link")); + setRHlim(rl - 1); + } + } else // normal MPO case + { + while (RHlim_ > k) { + auto rl = RHlim_; + // printfln(" Making environment with rl=%d (using H[%d])",rl,rl-1); + // Print(PH_.at(rl)); + // Print(Op_->A(rl-1)); + // Print(psi(rl-1)); + if (PH_.at(rl)) { + PH_.at(rl - 1) = PH_.at(rl) * psi(rl - 1); + } else { + PH_.at(rl - 1) = psi(rl - 1); + } + PH_.at(rl - 1) *= Op_->A(rl - 1); + PH_.at(rl - 1) *= dag(prime(psi(rl - 1))); + // printfln("PH[%d] = \n%s",rl-1,PH_.at(rl-1)); + // PAUSE + setRHlim(rl - 1); + } + } + } +} + +void inline MyLocalMPO::setLHlim(int val) { + if (!do_write_) { + LHlim_ = val; + return; + } + + if (LHlim_ != val && PH_.at(LHlim_)) { + writeToFile(PHFName(LHlim_), PH_.at(LHlim_)); + PH_.at(LHlim_) = ITensor(); + } + LHlim_ = val; + if (LHlim_ < 1) { + // Set to null tensor and return + PH_.at(LHlim_) = ITensor(); + return; + } + if (!PH_.at(LHlim_)) { + std::string fname = PHFName(LHlim_); + readFromFile(fname, PH_.at(LHlim_)); + } +} + +void inline MyLocalMPO::setRHlim(int val) { + if (!do_write_) { + RHlim_ = val; + return; + } + + if (RHlim_ != val && PH_.at(RHlim_)) { + writeToFile(PHFName(RHlim_), PH_.at(RHlim_)); + PH_.at(RHlim_) = ITensor(); + } + RHlim_ = val; + if (RHlim_ > Op_->length()) { + // Set to null tensor and return + PH_.at(RHlim_) = ITensor(); + return; + } + if (!PH_.at(RHlim_)) { + std::string fname = PHFName(RHlim_); + readFromFile(fname, PH_.at(RHlim_)); + } +} + +void inline MyLocalMPO::initWrite(Args const& args) { + auto basedir = args.getString("WriteDir", "./"); + writedir_ = mkTempDir("PH", basedir); +} + +} // namespace itensor + +#endif diff --git a/hybridleads/tdvp/MyObserver.h b/hybridleads/tdvp/MyObserver.h new file mode 100644 index 0000000..46f5dc9 --- /dev/null +++ b/hybridleads/tdvp/MyObserver.h @@ -0,0 +1,106 @@ +#ifndef __MYOBSERVER_H_CMC__ +#define __MYOBSERVER_H_CMC__ +#include + +#include "Entanglement.h" +#include "itensor/all.h" + +class MyObserver : public DMRGObserver { + public: + MyObserver(const Fermion& sites, const MPS& psi, + const Args& args = Args::global()) + : DMRGObserver(psi, args), + _sites(sites), + _ns(length(psi) + 1, 0.), + _Npar(0.), + _sites_obs(2, args), + _specs(length(psi)) { + _write = args.getBool("Write", false); + _out_dir = args.getString("out_dir", "."); + + // Current operator + AutoMPO ampo(_sites_obs); + ampo += -2_i, "Cdag", 1, "C", 2; + auto mpo = toMPO(ampo); + _current_op = mpo(1) * mpo(2); + } + + void measure(const Args& args); + + Real Npar() const { return _Npar; } + const Spectrum& spec(int i) const { return _specs.at(i); } + + private: + bool _write; + string _out_dir; // empty string "" if not write + Fermion _sites; + + vector _ns; + Real _Npar; + + // Observables + Fermion _sites_obs; + ITensor _current_op; + vector _specs; +}; + +inline Real Onsite_mea(const ITensor& A, const ITensor& op) { + ITensor re = A * op; + re.noPrime("Site"); + re *= dag(A); + return iut::toReal(re); +} + +void MyObserver ::measure(const Args& args) { + DMRGObserver::measure(args); + + cout << scientific << setprecision(14); + // Define your measurements below + // Call psi() to access the MPS + // + auto N = length(psi()); + auto b = args.getInt("AtBond"); + auto sw = args.getInt("Sweep"); + auto ha = args.getInt("HalfSweep"); + auto energy = args.getReal("Energy", 0); + + if (b != N) _specs.at(b) = spectrum(); + + int oc = orthoCenter(psi()); + int nc = args.getInt("NumCenter"); + // measure during the second half of sweep + if ((nc == 2 && oc == N) || ha == 2) { + // Density + ITensor n_op = _sites.op("N", oc); + Real ni = Onsite_mea(psi().A(oc), n_op); + cout << "\tn " << oc << " = " << ni << endl; + + // Current + if (oc != N) { + auto const& i1 = _sites_obs(1); + auto const& i2 = _sites_obs(2); + auto const& j1 = _sites(oc); + auto const& j2 = _sites(oc + 1); + auto Jop = replaceInds(_current_op, {i1, prime(i1), i2, prime(i2)}, + {j1, prime(j1), j2, prime(j2)}); + + auto phi = psi()(oc) * psi()(oc + 1); + auto phiJ = noPrime(phi * Jop, "Site"); + Cplx j = eltC(phiJ * dag(phi)); + cout << "\tcurrent " << oc << " " << oc + 1 << " = " << j << endl; + } + + // Entanglement entropy + Real S = iut::EntangEntropy(spectrum()); + cout << "EE " << oc << " = " << S << endl; + } + + if (oc == 1 && ha == 2) { + if (_write) { + cout << "write MPS" << endl; + writeToFile(_out_dir + "/psi.mps", psi()); + } + } +} + +#endif diff --git a/hybridleads/tdvp/TDVPWorker.h b/hybridleads/tdvp/TDVPWorker.h new file mode 100644 index 0000000..b52d44e --- /dev/null +++ b/hybridleads/tdvp/TDVPWorker.h @@ -0,0 +1,168 @@ +#ifndef __ITENSOR_TDVP_H +#define __ITENSOR_TDVP_H + +#include "MyLocalmpo.h" +#include "itensor/iterativesolvers.h" +#include "itensor/mps/DMRGObserver.h" +#include "itensor/mps/localmposet.h" +#include "itensor/mps/sweeps.h" +#include "itensor/util/cputime.h" + +template +void back_propagate(int ha, int b, int numCenter, TimeType t, MPS& psi, + const ITensor& phi1, MyLocalMPO& H, Spectrum& spec, + const Args& args) { + auto b1 = (ha == 1 ? b + 1 : b); + + ITensor phi0; + if (numCenter == 2) { + phi0 = psi(b1); + } else if (numCenter == 1) { + Index l; + if (ha == 1) + l = commonIndex(psi(b), psi(b + 1)); + else + l = commonIndex(psi(b - 1), psi(b)); + ITensor U, S, V(l); + spec = svd(phi1, U, S, V, args); + psi.ref(b) = U; + phi0 = S * V; + } + + H.numCenter(numCenter - 1); + H.position(b1, psi); + + applyExp(H, phi0, t / 2, args); + + if (args.getBool("DoNormalize", true)) phi0 /= norm(phi0); + + if (numCenter == 2) { + psi.ref(b1) = phi0; + psi.leftLim(b1 - 1); + psi.rightLim(b1 + 1); + } + if (numCenter == 1) { + if (ha == 1) { + psi.ref(b + 1) *= phi0; + psi.leftLim(b); + psi.rightLim(b + 2); + } else { + psi.ref(b - 1) *= phi0; + psi.leftLim(b - 2); + psi.rightLim(b); + } + } +} + +template +void TDVPWorker(MPS& psi, MyLocalMPO& H, Cplx t, Sweeps const& sweeps, + DMRGObserver& obs, Args args) { + // Truncate blocks of degenerate singular values (or not) + args.add("RespectDegenerate", args.getBool("RespectDegenerate", true)); + + const bool silent = args.getBool("Silent", false); + if (silent) { + args.add("Quiet", true); + args.add("PrintEigs", false); + args.add("NoMeasure", true); + args.add("DebugLevel", -1); + } + const bool quiet = args.getBool("Quiet", false); + const int debug_level = args.getInt("DebugLevel", (quiet ? -1 : 0)); + const int numCenter = args.getInt("NumCenter", 2); + if (numCenter != 1) + args.add("Truncate", args.getBool("Truncate", true)); + else + args.add("Truncate", args.getBool("Truncate", false)); + + const int N = length(psi); + + int oc = orthoCenter(psi); + + args.add("DebugLevel", debug_level); + + // The bonds to sweep + vector bs; + if (dir == Fromleft) { + for (int i = oc; i < N; i++) bs.push_back(i); + if (numCenter == 1) bs.push_back(N); + } else { + for (int i = oc - numCenter + 1; i >= 1; i--) bs.push_back(i); + } + + for (int sw = 1; sw <= sweeps.nsweep(); ++sw) { + cpu_time sw_time; + args.add("Sweep", sw); + args.add("NSweep", sweeps.nsweep()); + args.add("Cutoff", sweeps.cutoff(sw)); + args.add("MinDim", sweeps.mindim(sw)); + args.add("MaxDim", sweeps.maxdim(sw)); + args.add("MaxIter", sweeps.niter(sw)); + + if (!H.doWrite() && args.defined("WriteDim") && + sweeps.maxdim(sw) >= args.getInt("WriteDim")) { + if (!quiet) { + println("\nTurning on write to disk, write_dir = ", + args.getString("WriteDir", "./")); + } + + // psi.doWrite(true); + H.doWrite(true, args); + } + + // 0, 1 and 2-site wavefunctions + ITensor phi0, phi1; + Spectrum spec; + const int ha = (dir == Fromleft ? 1 : 2); + for (int b : bs) { + if (!quiet) printfln("Sweep=%d, HS=%d, Bond=%d/%d", sw, ha, b, (N - 1)); + + H.numCenter(numCenter); + H.position(b, psi); + + if (numCenter == 2) + phi1 = psi(b) * psi(b + 1); + else if (numCenter == 1) + phi1 = psi(b); + + applyExp(H, phi1, -t / 2, args); + + if (args.getBool("DoNormalize", true)) phi1 /= norm(phi1); + + if (numCenter == 2) + spec = psi.svdBond(b, phi1, (ha == 1 ? Fromleft : Fromright), H, args); + else if (numCenter == 1) + psi.ref(b) = phi1; + + if ((ha == 1 && b + numCenter - 1 != N) || (ha == 2 && b != 1)) { + back_propagate(ha, b, numCenter, t, psi, phi1, H, spec, args); + } + + if (!quiet) { + printfln(" Truncated to Cutoff=%.1E, Min_dim=%d, Max_dim=%d", + sweeps.cutoff(sw), sweeps.mindim(sw), sweeps.maxdim(sw)); + printfln(" Trunc. err=%.1E, States kept: %s", spec.truncerr(), + showDim(linkIndex(psi, b))); + } + + obs.lastSpectrum(spec); + args.add("AtBond", b); + args.add("HalfSweep", ha); + args.add("Truncerr", spec.truncerr()); + obs.measure(args); + + } // for loop over b + + if (!silent) { + auto sm = sw_time.sincemark(); + printfln(" Sweep %d/%d CPU time = %s (Wall time = %s)", sw, + sweeps.nsweep(), showtime(sm.time), showtime(sm.wall)); + } + } // for loop over sw + + if (args.getBool("DoNormalize", true)) { + psi.normalize(); + } +} + +#endif diff --git a/tdvp/analysis.py b/hybridleads/tdvp/analysis.py similarity index 100% rename from tdvp/analysis.py rename to hybridleads/tdvp/analysis.py diff --git a/tdvp/input b/hybridleads/tdvp/input similarity index 100% rename from tdvp/input rename to hybridleads/tdvp/input diff --git a/hybridleads/tdvp/tdvp.cc b/hybridleads/tdvp/tdvp.cc new file mode 100644 index 0000000..3017c7b --- /dev/null +++ b/hybridleads/tdvp/tdvp.cc @@ -0,0 +1,543 @@ +#include "Entanglement.h" +#include "FixedPointTensor.h" +#include "GenMPO.h" +#include "GeneralUtility.h" +#include "GlobalIndices.h" +#include "IUtility.h" +#include "MPSUtility.h" +#include "MyLocalmpo.h" +#include "MyObserver.h" +#include "ReadInput.h" +#include "TDVPWorker.h" +#include "iTDVP.h" +#include "itensor/all.h" +#include "uGauge.h" +using namespace itensor; +using namespace std; + +vector get_site_inds(const MPS& psi) { + int N = length(psi); + vector sites(N); + for (int i = 1; i <= N; i++) { + auto ii = findIndex(psi(i), "Site"); + sites.at(i - 1) = ii; + } + return sites; +} + +Fermion get_SiteSet(const MPS& mps) { + auto inds = get_site_inds(mps); + return Fermion(inds); +} + +ITensor get_W(const MPO& H, int i) { + auto W = H(i); + auto iWl = leftLinkIndex(H, i); + auto iWr = rightLinkIndex(H, i); + auto is = findIndex(W, "Site,0"); + + auto is2 = global::IS.is(); + auto iwl2 = global::IS.iwl(); + auto iwr2 = global::IS.iwr(); + auto is2pr = prime(is2); + W.replaceInds({is, prime(is), iWl, iWr}, {is2, is2pr, iwl2, iwr2}); + return W; +} + +inline ITensor to_itdvp_AR(const ITensor& AR, const Index& iAl, + const Index& iAr) { + auto ir = global::IS.ir(); + auto is = global::IS.is(); + auto iAs = findIndex(AR, "Site"); + auto ARre = replaceInds(AR, {iAl, iAr, iAs}, {prime(ir, 2), ir, is}); + global::IS.check("AR", ARre); + return ARre; +} + +inline ITensor to_itdvp_AL(const ITensor& AL, const Index& iAl, + const Index& iAr) { + auto il = global::IS.il(); + auto is = global::IS.is(); + auto iAs = findIndex(AL, "Site"); + auto ALre = replaceInds(AL, {iAl, iAr, iAs}, {il, prime(il, 2), is}); + global::IS.check("AL", ALre); + return ALre; +} + +ITensor get_L(const ITensor& AR, const Index& iAl, const Index& iAr, + Real crit = 1e-15) { + auto il = global::IS.il(); + auto ir = global::IS.ir(); + auto is = global::IS.is(); + auto iAs = findIndex(AR, "Site"); + auto ARt = replaceInds(AR, {iAl, iAr, iAs}, {il, prime(il, 2), is}); + + auto [AL, C] = Orthogonalize(ARt, crit); + C.replaceInds({il}, {ir}); + + auto Cdag = dag(C); + Cdag.prime(ir); + + auto L = C * Cdag; + global::IS.check("L", L); + assert(check_leading_eigen(AR, AR, L)); + return L; +} + +ITensor get_R(const ITensor& AL, const Index& iAl, const Index& iAr, + Real crit = 1e-15) { + auto il = global::IS.il(); + auto is = global::IS.is(); + auto iAs = findIndex(AL, "Site"); + auto ALt = replaceInds(AL, {iAl, iAr, iAs}, {prime(il, 2), il, is}); + + auto [AR, C] = Orthogonalize(ALt, crit); + C.mapPrime(1, 2); + + auto Cdag = dag(C); + Cdag.mapPrime(0, 2); + + auto R = C * Cdag; + global::IS.check("R", R); + assert(check_leading_eigen(AL, AL, R)); + return R; +} + +void expandL(MPS& psi, MPO& H, unique_ptr& PH, int n, int NumCenter, + const ITensor& AL, const Index& iALl, const Index& iALr) { + int oc = orthoCenter(psi); + PH->position(oc, psi); + + int N = length(psi); + int N2 = N + n; + + auto WL = H(1); + + auto iALs = findIndex(AL, "Site"); + auto iWLl = iut::leftIndex(H, 1); + auto iWLr = iut::rightIndex(H, 1); + auto iWLs = findIndex(H(1), "Site,0"); + + // Set MPS and MPO + MPS psi2(N2); + MPO H2(N2); + // Insert original tensors + for (int i = 1; i <= N; i++) { + psi2.ref(n + i) = psi(i); + H2.ref(n + i) = H(i); + } + // Insert new tensor to the left + auto il0 = iut::leftIndex(psi, 1); + auto iHl0 = iut::leftIndex(H, 1); + auto il = sim(il0); + auto iHl = sim(iHl0); + psi2.ref(n + 1).replaceInds({il0}, {il}); + H2.ref(n + 1).replaceInds({iHl0}, {iHl}); + for (int i = n; i >= 1; i--) { + auto is2 = sim(iALs); + // Replace the site and the right indices + psi2.ref(i) = replaceInds(AL, {iALs, iALr}, {is2, dag(il)}); + H2.ref(i) = + replaceInds(WL, {iWLs, prime(iWLs), iWLr}, {is2, prime(is2), dag(iHl)}); + il = sim(iALl); + iHl = sim(iWLl); + if (i != 1) { + psi2.ref(i).replaceInds({iALl}, {il}); + H2.ref(i).replaceInds({iWLl}, {iHl}); + } + } + psi2.rightLim(n + 2); + psi2.position(n + 1); + psi = psi2; + H = H2; + + // Set PH + Args args = {"NumCenter", NumCenter}; + auto PH2 = make_unique(H, PH->L(1), PH->R(N), args); + for (int i = N - 1; i >= 1; i--) PH2->setR(i + n, PH->R(i)); + PH2->setRHlim(n + 1 + NumCenter); + PH2->position(n + 1, psi); + PH = move(PH2); +} + +void expandR(MPS& psi, MPO& H, unique_ptr& PH, int n, int NumCenter, + const ITensor& AR, const Index& iARl, const Index& iARr) { + int oc = orthoCenter(psi); + PH->position(oc, psi); + + int N = length(psi); + int N2 = N + n; + + auto WR = H(N); + + auto iARs = findIndex(AR, "Site"); + auto iWRl = iut::leftIndex(H, N); + auto iWRr = iut::rightIndex(H, N); + auto iWRs = findIndex(H(N), "Site,0"); + + // Set MPS and MPO + MPS psi2(N2); + MPO H2(N2); + // Insert original tensors + for (int i = 1; i <= N; i++) { + psi2.ref(i) = psi(i); + H2.ref(i) = H(i); + } + // Insert new tensors to the right + auto ir0 = iut::rightIndex(psi, N); + auto iHr0 = iut::rightIndex(H, N); + auto ir = sim(ir0); + auto iHr = sim(iHr0); + psi2.ref(N).replaceInds({ir0}, {ir}); + H2.ref(N).replaceInds({iHr0}, {iHr}); + for (int i = N + 1; i <= N2; i++) { + auto is2 = sim(iARs); + // Replace the site and the left indices + psi2.ref(i) = replaceInds(AR, {iARs, iARl}, {is2, dag(ir)}); + H2.ref(i) = + replaceInds(WR, {iWRs, prime(iWRs), iWRl}, {is2, prime(is2), dag(iHr)}); + ir = sim(iARr); + iHr = sim(iWRr); + if (i != N2) { + psi2.ref(i).replaceInds({iARr}, {ir}); + H2.ref(i).replaceInds({iWRr}, {iHr}); + } + } + psi2.leftLim(N - 1); + psi2.position(N); + psi = psi2; + H = H2; + + // Set PH + Args args = {"NumCenter", NumCenter}; + auto PH2 = make_unique(H, PH->L(1), PH->R(N), args); + for (int i = 2; i <= N; i++) PH2->setL(i, PH->L(i)); + PH2->setLHlim(N - NumCenter); + PH2->position(N, psi2); + PH = move(PH2); +} + +void get_init(const string& infile, MPS& psi, MPO& H, Fermion& sites, Index& il, + Index& ir, ITensor& WL, ITensor& WR, ITensor& AL_left, + ITensor& AR_left, ITensor& AC_left, ITensor& C_left, + ITensor& La_left, ITensor& Ra_left, ITensor& LW_left, + ITensor& RW_left, ITensor& AL_right, ITensor& AR_right, + ITensor& AC_right, ITensor& C_right, ITensor& La_right, + ITensor& Ra_right, ITensor& LW_right, ITensor& RW_right, + int& idev_first, int& idev_last) { + InputGroup input(infile, "basic"); + + auto L_window = input.getInt("L_window"); + auto L_device = input.getInt("L_device"); + auto t_lead = input.getReal("t_lead"); + auto t_device = input.getReal("t_device"); + auto t_contact = input.getReal("t_contact"); + auto mu_leadL = input.getReal("mu_leadL"); + auto mu_leadR = input.getReal("mu_leadR"); + auto mu_device = input.getReal("mu_device"); + auto V_lead = input.getReal("V_lead"); + auto V_device = input.getReal("V_device"); + auto V_contact = input.getReal("V_contact"); + + auto psi_dir = input.getString("psi_dir"); + auto psi_file = input.getString("psi_file"); + auto itdvp_dir = input.getString("itdvp_dir"); + auto ErrGoal = input.getReal("ErrGoal"); + auto MaxIter_LRW = input.getInt("MaxIter_LRW"); + + // Read the iTDVP tensors + ITensor AL, AR, AC, C, LW, RW, La, Ra; + readFromFile(itdvp_dir + "/AL.itensor", AL); + readFromFile(itdvp_dir + "/AR.itensor", AR); + readFromFile(itdvp_dir + "/AC.itensor", AC); + readFromFile(itdvp_dir + "/C.itensor", C); + readFromFile(itdvp_dir + "/LW.itensor", LW); + readFromFile(itdvp_dir + "/RW.itensor", RW); + readFromFile(itdvp_dir + "/La.itensor", La); + readFromFile(itdvp_dir + "/Ra.itensor", Ra); + global::IS.read(itdvp_dir + "/global.inds"); + il = global::IS.il(); + ir = global::IS.ir(); + int dim = ir.dim(); + // Read MPS in the window + readFromFile(psi_dir + "/" + psi_file, psi); + int N = length(psi); + // Site indices + auto site_inds = get_site_inds(psi); + sites = Fermion(site_inds); + // Hamiltonian + AutoMPO ampo; + tie(ampo, idev_first, idev_last) = t_mu_V_ampo( + sites, L_device, t_lead, t_lead, t_device, t_contact, t_contact, mu_leadL, + mu_leadR, mu_device, V_lead, V_lead, V_device, V_contact, V_contact); + auto locamu = read_bracket_values(infile, "local_mu", 1); + auto localtV = + read_bracket_values(infile, "local_tV", 1); + ampo_add_mu(ampo, locamu, true); + ampo_add_tV(ampo, localtV, true); + H = toMPO(ampo); + to_inf_mpo(H, global::IS.iwl(), global::IS.iwr()); + // Boundary tensors + cout << "Compute boundary tensors" << endl; + WL = get_W(H, 2); + WR = get_W(H, N - 1); + Args args_itdvp = {"ErrGoal=", ErrGoal, "MaxIter", MaxIter_LRW}; + auto L = get_LR(C); + auto R = get_LR(C); + Real enL, enR; + tie(LW, enL) = get_LRW(AL, WL, R, La, args_itdvp); + tie(RW, enR) = get_LRW(AR, WR, L, Ra, args_itdvp); + // Check + mycheck(commonIndex(LW, H(1)), "LW and H(1) has no common Index"); + mycheck(commonIndex(RW, H(N)), "RW and H(N) has no common Index"); + mycheck(commonIndex(LW, psi(1)), "LW and psi(1) has no common Index"); + mycheck(commonIndex(RW, psi(N)), "RW and psi(N) has no common Index"); + // Left boundaries + AL_left = AL, AR_left = AR, AC_left = AC, C_left = C, La_left = La, + Ra_left = Ra, LW_left = LW, RW_left = RW; + // Right boundaries + AL_right = AL, AR_right = AR, AC_right = AC, C_right = C, La_right = La, + Ra_right = Ra, LW_right = LW, RW_right = RW; +} + +void writeAll(const string& filename, const MPS& psi, const MPO& H, + const Index& il, const Index& ir, const ITensor& WL, + const ITensor& WR, const ITensor& AL_left, const ITensor& AR_left, + const ITensor& AC_left, const ITensor& C_left, + const ITensor& La_left, const ITensor& Ra_left, + const ITensor& LW_left, const ITensor& RW_left, + const ITensor& AL_right, const ITensor& AR_right, + const ITensor& AC_right, const ITensor& C_right, + const ITensor& La_right, const ITensor& Ra_right, + const ITensor& LW_right, const ITensor& RW_right, int step, + int idev_first, int idev_last, bool expand, bool expand_next) { + ofstream ofs(filename); + write(ofs, psi); + write(ofs, H); + write(ofs, il); + write(ofs, ir); + write(ofs, WL); + write(ofs, WR); + write(ofs, AL_left); + write(ofs, AR_left); + write(ofs, AC_left); + write(ofs, C_left); + write(ofs, La_left); + write(ofs, Ra_left); + write(ofs, LW_left); + write(ofs, RW_left); + write(ofs, AL_right); + write(ofs, AR_right); + write(ofs, AC_right); + write(ofs, C_right); + write(ofs, La_right); + write(ofs, Ra_right); + write(ofs, LW_right); + write(ofs, RW_right); + write(ofs, step); + write(ofs, idev_first); + write(ofs, idev_last); + write(ofs, expand); + write(ofs, expand_next); + global::IS.write(ofs); +} + +void readAll(const string& filename, MPS& psi, MPO& H, Index& il, Index& ir, + ITensor& WL, ITensor& WR, ITensor& AL_left, ITensor& AR_left, + ITensor& AC_left, ITensor& C_left, ITensor& La_left, + ITensor& Ra_left, ITensor& LW_left, ITensor& RW_left, + ITensor& AL_right, ITensor& AR_right, ITensor& AC_right, + ITensor& C_right, ITensor& La_right, ITensor& Ra_right, + ITensor& LW_right, ITensor& RW_right, int& step, int& idev_first, + int& idev_last, bool& expand, bool& expand_next) { + ifstream ifs = open_file(filename); + read(ifs, psi); + read(ifs, H); + read(ifs, il); + read(ifs, ir); + read(ifs, WL); + read(ifs, WR); + read(ifs, AL_left); + read(ifs, AR_left); + read(ifs, AC_left); + read(ifs, C_left); + read(ifs, La_left); + read(ifs, Ra_left); + read(ifs, LW_left); + read(ifs, RW_left); + read(ifs, AL_right); + read(ifs, AR_right); + read(ifs, AC_right); + read(ifs, C_right); + read(ifs, La_right); + read(ifs, Ra_right); + read(ifs, LW_right); + read(ifs, RW_right); + read(ifs, step); + read(ifs, idev_first); + read(ifs, idev_last); + read(ifs, expand); + read(ifs, expand_next); + global::IS.read(ifs); +} + +int main(int argc, char* argv[]) { + string infile = argv[1]; + InputGroup input(infile, "basic"); + + auto dt = input.getReal("dt"); + auto time_steps = input.getInt("time_steps"); + auto NumCenter = input.getInt("NumCenter"); + auto ConserveQNs = input.getYesNo("ConserveQNs", false); + auto expandN = input.getInt("expandN", 0); + auto expand_checkN = input.getInt("expand_checkN", 5); + auto expandS_crit = input.getReal("expandS_crit", 1e10); + auto max_window = input.getInt("max_window", 10000); + auto sweeps = iut::Read_sweeps(infile); + + auto ErrGoal = input.getReal("ErrGoal"); + auto MaxIter_LRW = input.getInt("MaxIter_LRW"); + auto UseSVD = input.getYesNo("UseSVD", true); + auto SVDmethod = + input.getString("SVDMethod", "gesdd"); // can be also "ITensor" + auto WriteDim = input.getInt("WriteDim"); + + auto write = input.getYesNo("write", false); + auto write_dir = input.getString("write_dir", "."); + auto write_file = input.getString("write_file", ""); + auto read = input.getYesNo("read", false); + auto read_dir = input.getString("read_dir", "."); + auto read_file = input.getString("read_file", ""); + + auto out_dir = input.getString("outdir", "."); + if (write_dir == "." && out_dir != ".") write_dir = out_dir; + + // Declare variables + MPS psi; + MPO H; + Fermion sites; + Index il, ir; + ITensor WL, WR, AL_left, AR_left, AC_left, C_left, La_left, Ra_left, LW_left, + RW_left, AL_right, AR_right, AC_right, C_right, La_right, Ra_right, + LW_right, RW_right; + bool expand = false, expand_next = false; + int step = 1; + int idev_first, idev_last; + + // Initialize variables + if (!read) { + get_init(infile, psi, H, sites, il, ir, WL, WR, AL_left, AR_left, AC_left, + C_left, La_left, Ra_left, LW_left, RW_left, AL_right, AR_right, + AC_right, C_right, La_right, Ra_right, LW_right, RW_right, + idev_first, idev_last); + } + // Read variables + else { + readAll(read_dir + "/" + read_file, psi, H, il, ir, WL, WR, AL_left, + AR_left, AC_left, C_left, La_left, Ra_left, LW_left, RW_left, + AL_right, AR_right, AC_right, C_right, La_right, Ra_right, LW_right, + RW_right, step, idev_first, idev_last, expand, expand_next); + auto site_inds = get_site_inds(psi); + sites = Fermion(site_inds); + } + + // Args parameters + Args args_itdvp = {"ErrGoal=", ErrGoal, "MaxIter", MaxIter_LRW}; + Args args_obs = {"ConserveQNs", ConserveQNs}; + Args args_tdvp = {"Quiet", true, "NumCenter", NumCenter, + "DoNormalize", true, "UseSVD", UseSVD, + "SVDmethod", SVDmethod, "WriteDim", WriteDim}; + + // Observer + auto obs = make_unique(sites, psi, args_obs); + + // Effective Hamiltonian + auto PH = make_unique(H, LW_left, RW_right, args_tdvp); + + // Time evolution + cout << "Start time evolution" << endl; + cout << sweeps << endl; + psi.position(1); + Real en, err; + int N = length(psi); + + for (int i = 0; i < time_steps; i++) { + cout << "step = " << step++ << endl; + + // Extend left edge + if (expand) { + // Expand + expandL(psi, H, PH, expandN, NumCenter, AL_left, il, prime(il, 2)); + idev_first += expandN; + idev_last += expandN; + cout << "expand left " << expandN << endl; + // Observer + sites = get_SiteSet(psi); + obs = make_unique(sites, psi, args_obs); + N = length(psi); + psi.position(1); + } + cout << "device site = " << idev_first << " " << idev_last << endl; + + // Evolve left edge + tie(en, err, LW_left, RW_left) = + itdvp(WL, AL_left, AR_left, AC_left, C_left, La_left, Ra_left, 1_i * dt, + args_itdvp); + PH->L(1, LW_left); + + // From left to right + TDVPWorker(psi, *PH, 1_i * dt, sweeps, *obs, args_tdvp); + + if (expandN != 0) { + auto const& specR = obs->spec(N - expand_checkN); + auto SR_sys = iut::EntangEntropy(specR); + auto SR = iut::EntangEntropy_singular(C_right); + expand_next = (abs(SR_sys - SR) > expandS_crit); + } + + // Extend right boundary + if (expand) { + // Expand + expandR(psi, H, PH, expandN, NumCenter, AR_right, prime(ir, 2), ir); + // Observer + sites = get_SiteSet(psi); + obs = make_unique(sites, psi, args_obs); + N = length(psi); + psi.position(N); + } + + // Evolve right boundary + tie(en, err, LW_right, RW_right) = + itdvp(WR, AL_right, AR_right, AC_right, C_right, La_right, Ra_right, + 1_i * dt, args_itdvp); + PH->R(N, RW_right); + + // From right to left + TDVPWorker(psi, *PH, 1_i * dt, sweeps, *obs, args_tdvp); + + if (expandN != 0) { + auto const& specL = obs->spec(expand_checkN); + auto SL_sys = iut::EntangEntropy(specL); + auto SL = iut::EntangEntropy_singular(C_left); + if (abs(SL_sys - SL) > expandS_crit) expand_next = true; + } + + // if (maxLinkDim(psi) >= sweeps.maxdim(1)) + // args_tdvp.add ("NumCenter",1); + expand = expand_next; + if (N >= max_window) { + expand = false; + expandN = 0; + } + + if (write) { + writeAll(write_dir + "/" + write_file, psi, H, il, ir, WL, WR, AL_left, + AR_left, AC_left, C_left, La_left, Ra_left, LW_left, RW_left, + AL_right, AR_right, AC_right, C_right, La_right, Ra_right, + LW_right, RW_right, step, idev_first, idev_last, expand, + expand_next); + } + } + + return 0; +} diff --git a/itdvp/FixedPointTensor.h b/itdvp/FixedPointTensor.h deleted file mode 100644 index 5b4004f..0000000 --- a/itdvp/FixedPointTensor.h +++ /dev/null @@ -1,189 +0,0 @@ -#ifndef __FIXEDPOINTTENSOR_H_CMC__ -#define __FIXEDPOINTTENSOR_H_CMC__ -#include -#include "itensor/all.h" -#include "IUtility.h" -#include "uGauge.h" - -class SpecialTransferMatrix -// For computing (x|[1 - E + |R)(1|] -{ - public: - SpecialTransferMatrix (const ITensor& AL, const ITensor& R) - : _AL (AL) - , _R (R) - { - assert (order(R) == 2); - } - - int size () const { return _R.inds()(1).dim() * _R.inds()(2).dim(); } - void product (const ITensor& x, ITensor& re) const - { - assert (order(x) == 2); - // (x|1 - re = x; - - // (x|E - ITensor xE = applyTransfer (x, _AL, _AL); - re -= xE; - - // (x|R)(1| - auto xR = x * _R; - ITensor xRI; - if (isReal (xR)) - xRI = iut::Identity (re.inds(), elt(xR)); - else - xRI = iut::Identity (re.inds(), eltC(xR)); - re += xRI; - - assert (order(x) == order(re)); - assert (hasIndex (re, x.inds()(1))); - assert (hasIndex (re, x.inds()(2))); - } - - private: - ITensor _AL, _R; -}; - -template -tuple get_LRW (const ITensor& AL, const ITensor& W, const ITensor& R, ITensor& La0, const Args& args) -// PHYSICAL REVIEW B 97, 045145 (2018), Algorithm 6 -// W should have indices: 1. up site index, 2. down site index, 3. left link index, 4. right link index -// AL should have indices: 1. site index, 2. left link index, 3. right link index -// -// --------AL---- -// | | -// L--(b)--W--(a) -// | | -// --------AL---- -// -// (2)--AL--(3)--C-- -// | -// (1) -// -// (1) -// | -// (3)--W--(4) -// | -// (2) -// -{ - // Check W is of Schur form: - // Wba != 0 only for b >= a (lower triangled) - global::IS.check("W",W); - - Index ia, iWa, iWb; - if constexpr (dir == LEFT) - { - global::IS.check("AL",AL); - global::IS.check("R",R); - ia = global::IS.il(); - iWa = global::IS.iwr(); - iWb = global::IS.iwl(); - } - else - { - global::IS.check("AR",AL); - global::IS.check("L",R); - ia = global::IS.ir(); - iWa = global::IS.iwl(); - iWb = global::IS.iwr(); - } - - // For LW: solve from a=dW to 1 - // For RW: solve from a=1 to dW - vector solve_order; - solve_order.push_back(2); - for(int i = iWa.dim(); i >= 1; i--) - { - if (i != 2) - solve_order.push_back (i); - } - if constexpr (dir == RIGHT) - reverse (solve_order.begin(), solve_order.end()); - - auto M = SpecialTransferMatrix (AL, R); - - auto LRW = ITensor (ia, iWb, prime(ia)); - // 1) b == a == (dW or 1), Waa = I --> La = I - for(int i = 1; i <= ia.dim(); i++) - LRW.set (i, solve_order.front(), i, 1.); - - // 2) - ITensor Ya; - for(int i = 1; i < solve_order.size(); i++) - { - int a = solve_order.at(i); - auto Wa = W * setElt (dag(iWa)=a); - auto Waa = Wa * setElt (dag(iWb)=a); - assert (order(Waa) == 2); - - Ya = applyTransfer (LRW, AL, AL, Wa); - - // Waa == 0 - if (i != solve_order.size()-1) - { - assert (norm(Waa) == 0.); - auto Y = Ya * setElt (dag(iWb)=a); - - assert (order(Y) == 3); - LRW += Y; - } - else - //solve (L1|[1 - E + |R)(1|] = (Ya| - (Ya|R)(1| - { - auto YR = Ya * R; // (Ya|R) - ITensor YRI; // (Ya|R)(1| - if (isReal (YR)) - YRI = iut::Identity (Ya.inds(), elt(YR)); - else - YRI = iut::Identity (Ya.inds(), eltC(YR)); - auto Ya2 = Ya - YRI; // (Ya| - (Ya|R)(1| - gmres (M, Ya2, La0, args); - - LRW += La0 * setElt (dag(iWb)=a); - } - -#ifdef DEBUG - // Check fixed point - auto crit = args.getReal("debug_err_crit",1e-8); - auto LTW = applyTransfer (LRW, AL, AL, Wa); - auto LTWa = LRW * setElt(dag(iWb)=a); - auto d = LTW - LTWa; - if (a != solve_order.back()) - { - if (norm(d) > crit) - { - cout << "d = " << norm(d) << endl; - throw; - } - } - else - { - auto e = Ya * R; - ITensor Ie; - if (isReal(e)) - Ie = iut::Identity (d.inds(), elt(e)); - else - Ie = iut::Identity (d.inds(), eltC(e)); - auto dI = d - Ie; - auto d2 = norm(dI); - cout << "d = " << d2 << endl; - /*if (d2 > 1e-4) - { - cout << "d = " << d2 << endl; - //throw; - }*/ - } -#endif - } - auto en = iut::toReal (Ya * R); // energy density - if constexpr (dir == LEFT) - global::IS.check("LW",LRW); - else - global::IS.check("RW",LRW); - - return {LRW, en}; -} - -#endif diff --git a/itdvp/GenMPO.h b/itdvp/GenMPO.h deleted file mode 100644 index 9386392..0000000 --- a/itdvp/GenMPO.h +++ /dev/null @@ -1,311 +0,0 @@ -#ifndef __GENMPO_H_CMC__ -#define __GENMPO_H_CMC__ -#include "itensor/mps/mpo.h" -using namespace itensor; -using namespace std; - -bool check_Schur (const ITensor& W, const Index& is, const Index& irow, const Index& icol) -{ - assert (irow.dim() == icol.dim()); - int dW = irow.dim(); - - - for(int a = dW; a >= 1; a--) - { - auto Wa = W * setElt (dag(icol)=a); - auto Waa = Wa * setElt (dag(irow)=a); - if (a == 2 || a == 1) - { - auto I = iut::Identity (Waa.inds()); - if (norm(I-Waa) > 1e-12) - throw; - } - else - { - if (norm(Waa) > 1e-12) - throw; - } - - for(int b = 1; b <= dW; b++) - { - int a2=a, b2=b; - if (a == 2) a2 = dW; - else if (a == dW) a2 = 2; - if (b == 2) b2 = dW; - else if (b == dW) b2 = 2; - - if (a2 > b2) - { - auto Wab = Wa * setElt (dag(irow)=b); - if (norm(Wab) > 1e-12) - throw; - } - } - } - return true; -} - -inline ITensor to_Schur (const ITensor& W1, const Index& is, const Index& irow, const Index& icol) -{ - ITensor W; - int D = irow.dim(); - assert (irow.dim() == icol.dim()); - - auto new_ind = [&D] (int i1) - { - if (i1 == 1) - return i1; - else if (i1 == 2) - return D; - else - return i1-1; - }; - - for(int i1 = 1; i1 <= D; i1++) - for(int j1 = 1; j1 <= D; j1++) - { - int i = new_ind (i1); - int j = new_ind (j1); - auto Wab1 = W1 * setElt (dag(irow)=i1) * setElt (dag(icol)=j1); - auto Wab = Wab1 * setElt (irow=i) * setElt (icol=j); - W += Wab; - } - check_Schur (W, is, irow, icol); - return W; -} - -inline tuple to_Schur (const MPO& H) -{ - auto W = H(2); - auto irow = commonIndex (H(2),H(1)); - auto icol = commonIndex (H(2),H(3)); - auto is = findIndex (W, "Site,0"); - auto W_re = to_Schur (W, is, irow, icol); - return {W_re, is, irow, icol}; -} - -inline tuple get_MPO_Tensor_TFI (Real gx) -{ - auto sites = SpinHalf (3,{"ConserveSz",false}); - AutoMPO ampo (sites); - for(int j = 1; j < 3; ++j) - { - ampo += -4,"Sz",j,"Sz",j+1; - ampo += 2*gx,"Sx",j; - } - ampo += 2*gx,"Sx",3; - auto H = toMPO (ampo); - - auto W = H(2); - auto iwl = commonIndex (H(2),H(1)); - auto iwr = commonIndex (H(2),H(3)); - auto is = findIndex (W, "Site,0"); - return {W, is, iwl, iwr}; -} - -inline tuple get_W (const MPO& H) -{ - auto W = H(2); - auto iwl = commonIndex (H(2),H(1)); - auto iwr = commonIndex (H(2),H(3)); - auto is = findIndex (W, "Site,0"); - check_Schur (W, is, iwl, iwr); - return {W, is, iwl, iwr}; -} - -tuple get_MPO_Tensor_Hubbard_spinless (Real t, Real mu, const Args& args=Args::global()) -{ - auto sites = Spinless (3, args); - AutoMPO ampo (sites); - for(int i = 1; i <= 2; i++) - { - ampo += -t,"Cdag",i,"C",i+1; - ampo += -t,"Cdag",i+1,"C",i; - ampo += -mu,"N",2; - } - ampo += -mu,"N",3; - auto H = toMPO (ampo); - return get_W (H); -} - -tuple get_MPO_Tensor_XXZ_spin1 (Real Delta, const Args& args=Args::global()) -{ - auto sites = SpinOne (3, args) ; - AutoMPO ampo (sites); - for(int i = 1; i <= 2; i++) - { - ampo += 0.5,"S+",i,"S-",i+1; - ampo += 0.5,"S+",i+1,"S-",i; - ampo += Delta,"Sz",i,"Sz",i+1; - } - auto H = toMPO (ampo); - return get_W (H); -} - -void to_inf_mpo (MPO& mpo, Index iL=Index(), Index iR=Index()) -{ - int N = length (mpo); - if (N < 4) - { - cout << "Error: " << __FUNCTION__ << ": MPO length must >= 4" << endl; - throw; - } - - auto is1 = findIndex (mpo(1), "Site,0"); - auto is2 = findIndex (mpo(2), "Site,0"); - auto i1r = commonIndex (mpo(1), mpo(2)); - auto i2r = commonIndex (mpo(2), mpo(3)); - auto i2l = dag(i1r); - if (!iL) iL = sim(i2l); - - mpo.ref(1) = replaceInds (mpo(2), {is2, prime(is2), i2l, i2r}, {is1, prime(is1), iL, i1r}); - assert (commonIndex (mpo(1), mpo(2))); - - auto isN = findIndex (mpo(N), "Site,0"); - auto isN2 = findIndex (mpo(N-1), "Site,0"); - auto iNl = commonIndex (mpo(N), mpo(N-1)); - auto iN2l = commonIndex (mpo(N-1), mpo(N-2)); - auto iN2r = dag(iNl); - if (!iR) iR = sim(iN2r); - - mpo.ref(N) = replaceInds (mpo(N-1), {isN2, prime(isN2), iN2l, iN2r}, {isN, prime(isN), iNl, iR}); - assert (commonIndex (mpo(N-1), mpo(N))); -} - -MPO single_empurity_mpo (const SiteSet& sites, int imp_site, Real t, Real t_impL, Real t_impR, Real muL, Real muR, Real mu_imp, - Real V, Real V_impL, Real V_impR) -{ - auto N = length (sites); - AutoMPO ampo (sites); - for(int i = 1; i <= N; ++i) - { - Real mui, ti, Vi; - // Set mu - if (i < imp_site) mui = muL; - else if (i > imp_site) mui = muR; - else mui = mu_imp; - // Set t - if (i == imp_site-1) ti = t_impL; - else if (i == imp_site) ti = t_impR; - else ti = t; - // Set V - if (i == imp_site-1) Vi = V_impL; - else if (i == imp_site) Vi = V_impR; - else Vi = V; - - if (i != N) - { - ampo += -ti,"Cdag",i,"C",i+1; - ampo += -ti,"Cdag",i+1,"C",i; - } - if (mui != 0.) - ampo += -mui,"N",i; - if (Vi != 0. && i != N) - ampo += Vi,"N",i,"N",i+1; - } - auto H = toMPO (ampo); - - return H; -} - -tuple -t_mu_V_ampo -(const SiteSet& sites, int L_device, - Real t_leadL, Real t_leadR, Real t_device, Real t_contactL, Real t_contactR, - Real mu_leadL, Real mu_leadR, Real mu_device, - Real V_leadL, Real V_leadR, Real V_device, Real V_contactL, Real V_contactR) -{ - auto N = length (sites); - int first_site_device = (N - L_device) / 2 + 1; - int last_site_device = first_site_device + L_device - 1; - AutoMPO ampo (sites); - for(int i = 1; i <= N; ++i) - { - Real mui, ti, Vi; - // Set mu - if (i < first_site_device) mui = mu_leadL; - else if (i > last_site_device) mui = mu_leadR; - else mui = mu_device; - // Set t and V - if (i == first_site_device-1) - { - ti = t_contactL; - Vi = V_contactL; - } - else if (i == last_site_device) - { - ti = t_contactR; - Vi = V_contactR; - } - else if (i < first_site_device-1) - { - ti = t_leadL; - Vi = V_leadL; - } - else if (i > last_site_device) - { - ti = t_leadR; - Vi = V_leadR; - } - else - { - ti = t_device; - Vi = V_device; - } - - if (i != N) - { - ampo += -ti,"Cdag",i,"C",i+1; - ampo += -ti,"Cdag",i+1,"C",i; -cout << "t: " << i << " " << i+1 << " " << ti << endl; - } - if (mui != 0.) - { - ampo += -mui,"N",i; -cout << "mu: " << i << " " << mui << endl; - } - if (Vi != 0. && i != N) - { - ampo += Vi,"N",i,"N",i+1; -cout << "V: " << i << " " << i+1 << " " << Vi << endl; - } - } - return {ampo, first_site_device, last_site_device}; -} - -void ampo_add_mu (AutoMPO& ampo, const vector>& site_mu, bool verbose=false) -{ - if (verbose) - cout << "i mu" << endl; - for(auto [site, mu] : site_mu) - { - if (mu != 0.) - { - ampo += -mu,"N",site; - if (verbose) - cout << site << " " << mu << endl; - } - } -} - -void ampo_add_tV (AutoMPO& ampo, const vector>& link_t_V, bool verbose=false) -{ - if (verbose) - cout << "i j t V" << endl; - for(auto [i, j, t, V] : link_t_V) - { - if (t != 0.) - { - ampo += -t,"Cdag",i,"C",j; - ampo += -t,"Cdag",j,"C",i; - } - if (V != 0.) - { - ampo += V,"N",i,"N",j; - ampo += V,"N",j,"N",i; - } - if (verbose) - cout << i << " " << j << " " << t << " " << V << endl; - } -} -#endif diff --git a/itdvp/GlobalIndices.h b/itdvp/GlobalIndices.h deleted file mode 100644 index 714ab13..0000000 --- a/itdvp/GlobalIndices.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef __GLOBALINDCIES_H_CMC__ -#define __GLOBALINDCIES_H_CMC__ -#include "itensor/all.h" -using namespace itensor; - -enum enumLR { LEFT, RIGHT }; - -struct GlobalIndices -{ - const Index& il () const { return _il; } - const Index& ir () const { return _ir; } - const Index& is () const { return _is; } - const Index& iwl () const { return _iwl; } - const Index& iwr () const { return _iwr; } - - void write (const string& filename) const - { - ofstream ofs (filename); - write (ofs); - } - void write (ostream& ofs) const - { - itensor::write (ofs, _is); - itensor::write (ofs, _il); - itensor::write (ofs, _ir); - itensor::write (ofs, _iwl); - itensor::write (ofs, _iwr); - } - void read (const string& filename) - { - ifstream ifs (filename); - if (!ifs) - { - cout << __FUNCTION__ << ": Cannot open file: " << filename << endl; - throw; - } - read (ifs); - } - void read (istream& ifs) - { - itensor::read (ifs, _is); - itensor::read (ifs, _il); - itensor::read (ifs, _ir); - itensor::read (ifs, _iwl); - itensor::read (ifs, _iwr); - } - - vector LW_inds () const { return {_il, prime(_il), _iwl}; } - vector RW_inds () const { return {_ir, prime(_ir), _iwr}; } - - bool check (string name, const ITensor& T) const - { - //assert (isReal (T)); - if (name == "A") - { - assert (order(T) == 3); - assert (hasIndex (T, _is)); - assert (hasIndex (T, _il)); - assert (hasIndex (T, prime(_il,2))); - } - else if (name == "W") - { - assert (order(T) == 4); - assert (hasIndex (T, _is)); - assert (hasIndex (T, prime(_is))); - assert (hasIndex (T, _iwl)); - assert (hasIndex (T, _iwr)); - } - else if (name == "AL") - { - assert (order(T) == 3); - assert (hasIndex (T, _is)); - assert (hasIndex (T, _il)); - assert (hasIndex (T, prime(_il,2))); - } - else if (name == "AR") - { - assert (order(T) == 3); - assert (hasIndex (T, _is)); - assert (hasIndex (T, _ir)); - assert (hasIndex (T, prime(_ir,2))); - } - else if (name == "AC") - { - assert (order(T) == 3); - assert (hasIndex (T, _is)); - assert (hasIndex (T, _il)); - assert (hasIndex (T, _ir)); - } - else if (name == "C") - { - assert (order(T) == 2); - assert (hasIndex (T, prime(_il,2))); - assert (hasIndex (T, prime(_ir,2))); - } - else if (name == "R") - { - assert (order(T) == 2); - assert (hasIndex (T, _il)); - assert (hasIndex (T, prime(_il))); - } - else if (name == "L") - { - assert (order(T) == 2); - assert (hasIndex (T, _ir)); - assert (hasIndex (T, prime(_ir))); - } - else if (name == "LW") - { - assert (order(T) == 3); - assert (hasIndex (T, _il)); - assert (hasIndex (T, prime(_il))); - assert (hasIndex (T, _iwl)); - } - else if (name == "RW") - { - assert (order(T) == 3); - assert (hasIndex (T, _ir)); - assert (hasIndex (T, prime(_ir))); - assert (hasIndex (T, _iwr)); - } - return true; - } - - Index _il, _ir, _is, _iwl, _iwr; -}; - -namespace global { auto IS = GlobalIndices (); } -#endif diff --git a/itdvp/Solver.h b/itdvp/Solver.h deleted file mode 100644 index e731bf7..0000000 --- a/itdvp/Solver.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef __SOLVER_H_CMC__ -#define __SOLVER_H_CMC__ -#include "itensor/all.h" -#include "GlobalIndices.h" -using namespace itensor; -using namespace std; - -void apply_Heff (const ITensor& LW, const ITensor& RW, const ITensor& W, ITensor& AC, ITensor& C) -{ - global::IS.check("LW",LW); - global::IS.check("RW",RW); - global::IS.check("W",W); - global::IS.check("AC",AC); - global::IS.check("C",C); - - auto iwl = global::IS.iwl(); - auto iwr = global::IS.iwr(); - - // Update AC - AC *= LW; - AC *= W; - AC *= RW; - AC.noPrime(); - - // Update C - auto RW0 = replaceInds (RW, {iwr}, {iwl}); - C.noPrime(); - C *= LW; - C *= RW0; - C.prime(); - - C /= norm(C); - AC /= norm(AC); - - global::IS.check("AC",AC); - global::IS.check("C",C); -} - -void solve_gs (const ITensor& LW, const ITensor& RW, const ITensor& W, ITensor& AC, ITensor& C, const Args& args=Args::global()) -// args: ErrGoal, MaxIter -{ - global::IS.check("LW",LW); - global::IS.check("RW",RW); - global::IS.check("W",W); - global::IS.check("AC",AC); - global::IS.check("C",C); - - auto iwl = global::IS.iwl(); - auto iwr = global::IS.iwr(); - - // Update AC - LocalOp Heff1 (W, LW, RW, {"numCenter=",1}); - davidson (Heff1, AC, args); - - // Update C - auto RW0 = replaceInds (RW, {iwr}, {iwl}); - LocalOp Heff0 (LW, RW0, {"NumCenter=",0}); - auto C0 = noPrime(C); - davidson (Heff0, C0, args); - C = prime (C0, 2); - - C /= norm(C); - AC /= norm(AC); - - global::IS.check("AC",AC); - global::IS.check("C",C); -} - -template -void time_evolve (const ITensor& LW, const ITensor& RW, const ITensor& W, ITensor& AC, ITensor& C, TimeType dt, - const Args& args=Args::global()) -// args: ErrGoal, MaxIter -{ - global::IS.check("LW",LW); - global::IS.check("RW",RW); - global::IS.check("W",W); - global::IS.check("AC",AC); - global::IS.check("C",C); - - auto iwl = global::IS.iwl(); - auto iwr = global::IS.iwr(); - - // Update AC - LocalOp Heff1 (W, LW, RW, {"numCenter=",1}); - applyExp (Heff1, AC, -dt, args); - - // Update C - auto RW0 = replaceInds (RW, {iwr}, {iwl}); - LocalOp Heff0 (LW, RW0, {"NumCenter=",0}); - auto C0 = noPrime(C); - applyExp (Heff0, C0, -dt, args); - C = prime (C0, 2); - - global::IS.check("AC",AC); - global::IS.check("C",C); -} -#endif diff --git a/itdvp/iTDVP.h b/itdvp/iTDVP.h deleted file mode 100644 index 5e77727..0000000 --- a/itdvp/iTDVP.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __ITDVP_H_CMC__ -#define __ITDVP_H_CMC__ -#include -#include -#include "itensor/all.h" -#include "Solver.h" -#include "RandomUtility.h" -using namespace itensor; -using namespace std; - -tuple -itdvp_initial (const ITensor& W, const Index& is, const Index& iwl, const Index& iwr, ITensor& A, int D, Real ErrGoal, int MaxIter, - RandGen::SeedType seed=0) -{ - Index il; - if (!A) - { - il = Index(D,"Link"); - A = ITensor (is, il, prime(il,2)); - auto rand = RandGen (seed); - auto gen = [&rand]() { return rand.real(); }; - A.generate (gen); - //A.randomize(); - //A.set(1,1,1,1.); - } - else - { - il = findIndex (A, "Link,0"); - if (!hasIndex (A, prime(il,2))) - { - cout << "Error: " << __FUNCTION__ << ": A has wrong index structure" << endl; - throw; - } - } - auto ir = sim(il); - - global::IS._iwl = iwl; - global::IS._iwr = iwr; - global::IS._is = is; - global::IS._il = il; - global::IS._ir = ir; - - auto [AL, AR, C] = MixedCanonical (A, ErrGoal, MaxIter); - - auto AC = get_AC (AL, C); - - auto La0 = randomITensor (il, prime(il)); - auto Ra0 = randomITensor (ir, prime(ir)); - - return {AL, AR, AC, C, La0, Ra0}; -} - -inline Real diff_ALC_AC (const ITensor& AL, const ITensor& C, const ITensor& AC) -{ - auto ALC = get_AC (AL, C); - auto d = norm (ALC - AC); - return d; -} - -template -tuple -itdvp -(const ITensor& W, ITensor& AL, ITensor& AR, ITensor& AC, ITensor& C, ITensor& La0, ITensor& Ra0, TimeType dt, - Args& args=Args::global()) -// args: ErrGoal, MaxIter, used in applyExp and arnoldi -{ - // C --> L, R - auto L = get_LR (C); - auto R = get_LR (C); - - // AL, AR, L, R --> LW, RW, enL, enR - auto [LW, enL] = get_LRW (AL, W, R, La0, args); - auto [RW, enR] = get_LRW (AR, W, L, Ra0, args); - - auto en = 0.5*(enL + enR); - - // LW, RW, W, AC, C --> AC, C - if constexpr(is_same_v) - { - if (isinf (dt)) - solve_gs (LW, RW, W, AC, C, args); - else - time_evolve (LW, RW, W, AC, C, dt, args); - } - else - { - time_evolve (LW, RW, W, AC, C, dt, args); - } - // AC, C --> AL, AR - AL = get_AL (AC, C); - AR = get_AR (AC, C); - //AL = get_ALR_2 (AC, C); - //AR = get_ALR_2 (AC, C); - - C /= norm(C); - AC /= norm(AC); - - auto errL = diff_ALC_AC (AL, C, AC); - auto errR = diff_ALC_AC (AR, C, AC); - auto err = (errL > errR ? errL : errR); - - return {en, err, LW, RW}; -} -#endif diff --git a/itdvp/itdvp.cc b/itdvp/itdvp.cc deleted file mode 100644 index 6fed781..0000000 --- a/itdvp/itdvp.cc +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include -#include "itensor/all.h" -#include "IUtility.h" -#include "uGauge.h" -#include "GenMPO.h" -#include "FixedPointTensor.h" -#include "Solver.h" -#include "iTDVP.h" -using namespace itensor; -using namespace std; - - - -int main(int argc, char* argv[]) -{ - string infile = argv[1]; - InputGroup input (infile,"basic"); - - auto t = input.getReal("t"); - auto mu = input.getReal("mu"); - auto V = input.getReal("V"); - - auto time_steps = input.getInt("time_steps"); - auto dt_str = input.getString("dt"); - auto dt = ((dt_str == "inf" || dt_str == "Inf" || dt_str == "INF") ? INFINITY : stod(dt_str)); - auto D = input.getInt("max_dim"); - auto ErrGoal = input.getReal("ErrGoal"); - auto MaxIter = input.getInt("MaxIter"); - auto ErrGoalInit = input.getReal("ErrGoalInit",1e-12); - auto MaxIterInit = input.getInt("MaxIterInit",100); - auto SeedInit = input.getInt("SeedInit",0); - auto write = input.getYesNo("write"); - auto out_dir = input.getString("out_dir","."); - - //------------------------------------------ - cout << setprecision(18) << endl; - // Make MPO - auto sites = Fermion (3, {"ConserveQNs",false}); - // ************************************************************************ - // Change the Hamiltonian to whatever you want - auto H = single_empurity_mpo (sites, 2, t, t, t, mu, mu, mu, V, V, V); - // ************************************************************************ - auto [W, is, iwl, iwr] = get_W (H); - // W: MPO tensor - // is: physical index - // iwl: left index - // iwr: right index - - // Initialize MPS - auto A = ITensor(); // ill-defined tensor - auto [AL, AR, AC, C, La0, Ra0] = itdvp_initial (W, is, iwl, iwr, A, D, ErrGoalInit, MaxIterInit, SeedInit); - // If A is ill-defined, A will be random generated in itdvp_initial - // This is just for tensors to be used in iTDVP - - // iTDVP - Args args = {"ErrGoal=",1e-4,"MaxIter",MaxIter}; - ITensor LW, RW; - Real en, err; - for(int i = 1; i <= time_steps; i++) - { - cout << "time step " << i << endl; - // Run iTDVP - // If dt is real, do imaginary time evolution - // imaginary, real - tie (en, err, LW, RW) = itdvp (W, AL, AR, AC, C, La0, Ra0, dt, args); - cout << "energy, error = " << en << " " << err << endl; - - // Decrease the ErrGoal dynamically - if (args.getReal("ErrGoal") > ErrGoal) - args.add("ErrGoal=",err*0.1); - } - //------------------------------------------ - - if (write) - { - writeToFile (out_dir+"/AL.itensor",AL); - writeToFile (out_dir+"/AR.itensor",AR); - writeToFile (out_dir+"/AC.itensor",AC); - writeToFile (out_dir+"/C.itensor",C); - writeToFile (out_dir+"/LW.itensor",LW); - writeToFile (out_dir+"/RW.itensor",RW); - writeToFile (out_dir+"/W.itensor",W); - writeToFile (out_dir+"/La.itensor",La0); - writeToFile (out_dir+"/Ra.itensor",Ra0); - global::IS.write (out_dir+"/global.inds"); - } - return 0; -} diff --git a/itdvp/uGauge.h b/itdvp/uGauge.h deleted file mode 100644 index 4e4afd7..0000000 --- a/itdvp/uGauge.h +++ /dev/null @@ -1,370 +0,0 @@ -#ifndef __uGauge_H_CMC__ -#define __uGauge_H_CMC__ -#include "itensor/all.h" -#include "IUtility.h" -#include "GlobalIndices.h" -using namespace itensor; -using namespace std; - -inline ITensor applyTransfer (const ITensor& x, const ITensor& A1, const ITensor& A2, int pLV=2) -{ - auto re = x * A1; - re *= prime(dag(A2),"Link"); - re.prime(-pLV,"Link"); - return re; -} - -inline ITensor applyTransfer (const ITensor& x, const ITensor& A1, const ITensor& A2, const ITensor& W, int pLV=2) -{ - auto re = x * A1; - re *= W; - re *= prime(dag(A2)); - re.prime(-pLV,"Link"); - return re; -} - -class TransferMatrix -{ - public: - TransferMatrix (const ITensor& A1, const ITensor& A2, int pLV=2) - : _A1 (A1) - , _A2 (A2) - , _pLV (pLV) - { - auto i1s = findInds (A1, "Link"); - auto i2s = findInds (A2, "Link"); - _dim = i1s(1).dim() * i2s(1).dim(); - } - - int size () const { return _dim; } - void product (const ITensor& x, ITensor& xE) const - { - xE = applyTransfer (x, _A1, _A2, _pLV); - assert (order(x) == order(xE)); - assert (hasIndex (xE, x.inds()(1))); - assert (hasIndex (xE, x.inds()(2))); - } - - private: - ITensor _A1, _A2; - int _dim, _pLV; -}; - -inline Real applyT_diff (const ITensor& A1, const ITensor& A2, const ITensor& X) -{ - auto reX = prime(X,2) * A1; - reX *= prime (dag(A2), "Link"); - auto dd = reX - X; - auto d = norm(dd); - return d; -} - -inline bool check_leading_eigen (const ITensor& A1, const ITensor& A2, const ITensor& X, Real crit=1e-12) -{ - auto d = applyT_diff (A1, A2, X); - if (d > crit) - { - cout << __FUNCTION__ << ": err = " << d << endl; - return false; - } - return true; -} -// SciPost Phys. Lect. Notes 7 (2019), Algorithm 1 -// -// Solve -// -// --L--A-- = --AL--L-- -// | | -// -// where -// -// --- L------ ---A---- -// | | | -// l = | is the left leading eigenvector of | -// | | | -// --- dagL--- --dagA-- -// -// and -// -// ----AL--- --- -// | | = | = iut::Identity -// --dagAL-- --- -// -// Input: -// -// --A-- -// | -// -// Output: -// -// --AL-- -// | -// -// --L-- -// -tuple Orthogonalize (const ITensor& A, Real errGoal=1e-15, int maxIter=10000) -{ - global::IS.check("A",A); - auto is = global::IS.is(); - auto il = global::IS.il(); - auto il1 = prime(il); - auto il2 = prime(il,2); - - // A = AL * L - auto args = Args ({"MinDim",il.dim(),"MaxDim",il.dim()}); - auto [AL, L] = denmatDecomp (A, {is, il}, Fromleft, args); - L /= norm(L); - // Set indices - auto ic = commonIndex (AL, L); - AL.replaceInds ({ic}, {il2}); - L.replaceInds ({ic,il2}, {il1,il}); - - // First error - auto L_old = iut::Identity (L.inds()); - auto err = norm (L - L_old); - - // Iterate L*A = AL*L - auto drop = ITensor(); - for(int i = 0; i < maxIter; i++) - { - // Arnoldi - auto E = TransferMatrix (A, AL); - arnoldi (E, L, {"ErrGoal=",0.1*err}); - L.takeReal(); - // - tie (drop, L) = polar (L, {il1}, args); - ic = commonIndex (L, drop); - L.replaceInds ({ic}, {il1}); - L /= norm(L); - // QR - L_old = L; - tie (AL, L) = polar (L*A, {is,il1}, args); - L /= norm(L); - // Replace indices - ic = commonIndex (AL, L); - AL.replaceInds ({il1,ic}, {il,il2}); - L.replaceInds ({ic,il2}, {il1,il}); - err = norm (L - L_old); - cout << "err = " << err << endl; - if (err < errGoal) break; - } - return {AL, L}; -} - -inline void Rotate_basis (ITensor& C, ITensor& AL, ITensor& AR) -{ - global::IS.check("C",C); - global::IS.check("AL",AL); - global::IS.check("AR",AR); - auto is = global::IS.is(); - auto il = global::IS.il(); - auto il2 = prime(il,2); - auto ir = global::IS.ir(); - auto ir2 = prime(ir,2); - - assert (abs(1.-norm(C)) < 1e-12); - - ITensor U(il2), S, V; - svd (C, U, S, V); - auto iU = commonIndex (U,S); - auto iV = commonIndex (V,S); - - auto apply_rot = [&is, &C] (ITensor& U, ITensor& A, const Index& iU) - { - auto iU2 = prime(iU,2); - U.setPrime (2, iU); - A *= U; - A *= noPrime(dag(U)); - }; - apply_rot (U, AL, iU); - apply_rot (V, AR, iV); - AL.replaceInds ({iU, prime(iU,2)}, {il, il2}); - AR.replaceInds ({iV, prime(iV,2)}, {ir, ir2}); - - C = S; - C = iut::hard_copy (C); - C.replaceInds ({iU,iV}, {il2,ir2}); - - global::IS.check("C",C); - global::IS.check("AL",AL); - global::IS.check("AR",AR); -} - -inline tuple MixedCanonical (const ITensor& A, Real errGoal=1e-12, int maxIter=10000) -{ - global::IS.check("A",A); - auto il = global::IS.il(); - auto ir = global::IS.ir(); - auto il2 = prime(il,2); - auto ir2 = prime(ir,2); - - Args args = {"Cutoff",1e-14}; - - auto [AL, L] = Orthogonalize (A, errGoal, maxIter); - auto ALtmp = swapInds (AL, {il}, {il2}); - auto [AR, C] = Orthogonalize (ALtmp, errGoal, maxIter); - AR.replaceInds ({il,il2}, {ir,ir2}); - C.replaceInds ({il,prime(il)}, {il2,ir2}); - - global::IS.check("AL",AL); - global::IS.check("AR",AR); - global::IS.check("C",C); - - Rotate_basis (C, AL, AR); - - assert (check_ortho (AL, il2)); - assert (check_ortho (AR, ir2)); - return {AL, AR, C}; -} - -//---------------------------------------------------- - -inline ITensor get_AC (const ITensor& ALR, ITensor C) -{ - global::IS.check("C",C); - - auto AC = ALR * C; - AC.noPrime(); - - global::IS.check("AC",AC); - return AC; -} - -inline ITensor get_polarU (const ITensor& T, const IndexSet& iUs) -{ - ITensor U (iUs), S, V; - svd (T, U, S, V); - - V *= delta (S.inds()); - auto pU = U * V; - return pU; -} - -template -inline ITensor get_ALR_2 (const ITensor& AC, const ITensor& C) -{ - assert (abs(norm(AC)-1) < 1e-12); - assert (abs(norm(C)-1) < 1e-12); - global::IS.check("AC",AC); - global::IS.check("C",C); - - auto is = global::IS.is(); - auto i1 = global::IS.il(); - auto i2 = global::IS.ir(); - if constexpr (dir == RIGHT) - swap (i1,i2); - - auto UA = get_polarU (AC, {is,i1}); - auto UC = get_polarU (C, {prime(i1,2)}); - auto UCdag = dag(UC); - UCdag.noPrime (prime(i2,2)); - auto ALR = UA * UCdag; - - assert (check_ortho (ALR, prime(i1,2))); - if constexpr (dir == LEFT) - global::IS.check("AL",ALR); - else - global::IS.check("AR",ALR); - return ALR; -} - -inline ITensor get_AL (const ITensor& AC, const ITensor& C) -{ - global::IS.check("AC",AC); - global::IS.check("C",C); - - auto is = global::IS.is(); - auto il = global::IS.il(); - auto ir = global::IS.ir(); - - auto Cdag = dag(C); - Cdag.noPrime (prime(ir,2)); - auto AC_Cdag = AC * Cdag; - - auto AL = get_polarU (AC_Cdag, {is,il}); - - global::IS.check("AL",AL); - assert (check_ortho (AL, prime(il,2))); - return AL; -} - -inline ITensor get_AR (const ITensor& AC, const ITensor& C) -{ - global::IS.check("AC",AC); - global::IS.check("C",C); - - auto is = global::IS.is(); - auto il = global::IS.il(); - auto ir = global::IS.ir(); - - auto Cdag = dag(C); - Cdag.noPrime (prime(il,2)); - auto Cdag_AC = AC * Cdag; - - auto AR = get_polarU (Cdag_AC, {prime(ir,2)}); - - global::IS.check("AR",AR); - assert (check_ortho (AR, prime(ir,2))); - return AR; -} - -template -inline ITensor get_LR (const ITensor& C) -{ - global::IS.check("C",C); - - auto ic = global::IS.il(); - auto iu = global::IS.ir(); - if constexpr (dir == RIGHT) - swap (ic, iu); - auto ic2 = prime(ic,2); - auto iu2 = prime(iu,2); - - auto Cdag = dag(C); - Cdag.prime (iu2); - auto LorR = C * Cdag; - LorR.prime(-2); - //LorR.replaceInds ({iu,prime(iu)}, {ic,prime(ic)}); - auto trace = LorR * dag(delta(LorR.inds())); - LorR /= iut::toReal (trace); - - if constexpr (dir == RIGHT) - global::IS.check("R",LorR); - else - global::IS.check("L",LorR); - return LorR; -} - -template -ITensor get_LR_2 (const ITensor& AL, ITensor R, Real crit=1e-15) -{ - if constexpr (dir == RIGHT) - { - global::IS.check("AL",AL); - global::IS.check("R",R); - } - else - { - global::IS.check("AR",AL); - global::IS.check("L",R); - } - - R.prime(2); - auto E = TransferMatrix (AL, AL, -2); - arnoldi (E, R, {"ErrGoal=",crit}); - R.prime(-2); - - R.takeReal(); - - auto traceT = R * dag(delta(R.inds())); - R /= iut::toReal (traceT); - -// assert (check_leading_eigen (AL, AL, R)); - - if constexpr (dir == RIGHT) - global::IS.check("R",R); - else - global::IS.check("L",R); - return R; -} -#endif diff --git a/kbasis/BdGBasis.h b/kbasis/BdGBasis.h deleted file mode 100644 index b7ac90f..0000000 --- a/kbasis/BdGBasis.h +++ /dev/null @@ -1,382 +0,0 @@ -#ifndef __BDGBASIS_H_CMC__ -#define __BDGBASIS_H_CMC__ -#include "itensor/all.h" -#include "ReadWriteFile.h" -using namespace itensor; -using namespace std; - -Matrix BdG_Hamilt (int L, Real t, Real mu, Real Delta) -{ - int N = 2*L; - Matrix H (N,N); - for(int ip = 0; ip < N; ip++) - for(int jp = 0; jp < N; jp++) - { - int i = ip+1, - j = jp+1; - bool dagi = true, - dagj = false; - if (i > L) - { - i -= L; - dagi = false; - } - if (j > L) - { - j -= L; - dagj = true; - } - - // mu - if (i == j) - { - if (dagi != dagj) - { - if (dagi) - H(ip,jp) = -mu; - else - H(ip,jp) = mu; - } - } - if (abs(i-j) == 1) - { - // t - if (dagi != dagj) - { - if (dagi) - H(ip,jp) = -t; - else - H(ip,jp) = t; - } - // Delta - else - { - if (dagi == (i > j)) - H(ip,jp) = Delta; - else - H(ip,jp) = -Delta; - } - } - } - return 0.5*H; -} - -// Use only the positive-energy states, ordering from lowest to highest energy -class BdGBasis -{ - public: - BdGBasis () {} - BdGBasis (const string& name, int L, Real t, Real mu, Real Delta); - - tuple,vector,vector> C (int i); - - // Functions that every basis class must have - const string& name () const { return _name; } - vector> C_op (int i, bool dag) const; - vector> gamma_op (int k, bool dag) const; - // en(i) is the energy in H_BdG = sum_i^N { en(i) gamma_i^dag gamma_i } - sum_i^N { (1/2) en(i) } - Real en (int k) const { return _ens(k-1); } - Real mu (int k) const { mycheck (k <= this->size(), "Out of range"); return -2. * _H(k-1,k-1); } - int size () const { return _ens.size(); } - auto const& H () const { return _H; } - auto const& H (int i, int j) const { return _H(i,j); } - - void write (ostream& s) const - { - itensor::write(s,_name); - itensor::write(s,_u); - itensor::write(s,_v); - itensor::write(s,_ens); - itensor::write(s,_H); - } - void read (istream& s) - { - itensor::read(s,_name); - itensor::read(s,_u); - itensor::read(s,_v); - itensor::read(s,_ens); - itensor::read(s,_H); - } - - private: - string _name; - Vector _ens; - Matrix _u, _v, _H; -}; - -Vector particle_hole_transform (const Vector& v) -{ - int N = v.size()/2; - Vector w (2*N); - subVector (w,0,N) &= subVector (v,N,2*N); - subVector (w,N,2*N) &= subVector (v,0,N); - return w; -} - -// For each BdG basis state, suppose |phi_i> has energy E_i, -// S|phi_i> will have energy -E_i, where S is the particle-hole transformation. -// Suppose |phi_i> has dimension 2N. -// S|phi_i> swaps the first N subvector to the last N subvector of |phi_i>. -// -// The particle-hole correspondence for the E_i and -E_i basis states is autoomatically satisfied for the non-zero-energy modes. -// However for zero-energy (Majorana) modes, since |phi_i> and S|phi_i> are degenerate, -// numerically we will get in geenral arbitrary superpositions of them, -// so one needs to symmetrice them explicitly. -Matrix symmetrice_zero_energy_modes (const Vector& ens, Matrix U, Real zero_crit=1e-8) -{ - mycheck (ens.size() == nrows(U), "Size not match"); - int N = ens.size()/2; - - // Target the zero-energy modes - vector is; - vector states; - for(int i = 0; i < 2*N; i++) - { - if (abs(ens(i)) < zero_crit) - { - is.push_back (i); - states.emplace_back (column (U, i)); - } - } - if (states.size() == 0) - return U; - mycheck (states.size() == 2, "Allow only upto 2 zero-energy modes"); - - // Define particle-hole transformation matrix S in zero-energy modes subspace - int N0 = states.size(); - Matrix S (N0, N0); - for(int i = 0; i < N0; i++) - { - auto phi = states.at(i); - for(int j = 0; j < N0; j++) - { - auto const& phip = states.at(j); - auto phit = particle_hole_transform (phi); - auto si = phip * phit; - S(i,j) = si; - } - } - - // Diagonalize S - Matrix W; - Vector eigvals; - diagHermitian (S, W, eigvals); - // Get the eigenstates of S - auto e_pos = eigvals(0); - auto e_neg = eigvals(1); - auto w_pos = Vector (column (W, 0)); - auto w_neg = Vector (column (W, 1)); - if (e_pos < 0.) - { - swap (e_pos, e_neg); - swap (w_pos, w_neg); - } - Vector v_pos (2*N), - v_neg (2*N); - for(int i = 0; i < N0; i++) - { - v_pos += w_pos (i) * states.at(i); - v_neg += w_neg (i) * states.at(i); - } - mycheck (abs(e_pos-1) < 1e-14 and abs(e_neg+1) < 1e-14, "particle-hole eigenvalues error"); - - // Suppose S has eigenvectors |u> and |v> with eigenvalues +1 and -1 - // The (unnormalized) symmetric zero-energy modes are |u>+|v> and |u>-|v> - auto phi1 = v_pos + v_neg; - phi1 /= norm(phi1); - auto phi2 = particle_hole_transform (phi1); - - // Update the zero-energy states in U - column (U,is.at(0)) &= phi1; - column (U,is.at(1)) &= phi2; - - return U; -} - -void check_orthogonal_to_particle_hole_transform (const Vector& v) -{ - Vector w = particle_hole_transform (v); - auto o = w*v; - if (!(abs(o) < 1e-8)) - { - cout << "Not orthogonal to its particle-hole transformed state" << endl; - cout << abs(o) << endl; - throw; - } -} - -BdGBasis :: BdGBasis (const string& name, int L, Real t, Real mu, Real Delta) -: _name (name) -{ - _H = BdG_Hamilt (L,t,mu,Delta); - Matrix U; - Vector ens; - diagHermitian (_H, U, ens); - // ens(q) for q=0,1,...,2N-1 is the energy in descending order (highest to lowest). - // The energy for q=0,...N-1 is positive - // q=N,...2N-1 is negative - - U = symmetrice_zero_energy_modes (ens, U); - - auto tmp = U(0,0); - if constexpr (!is_same_v ) - { - cout << "The current version is only for unitary matrix of Real type" << endl; - cout << "Type found is: " << typeid(tmp).name() << endl; - throw; - } - - // _ens are for the positive energies in ascending order (lowest to highest) - // U = [ _u _v* ] is the unitrary matrix to diagonalize H - // [ _v _u* ] - // Take only the positive-energy states, i.e. the first N columns, to define _u and _v. - int N = ens.size()/2; - _ens = Vector (N); - _u = Matrix (N,N); - _v = Matrix (N,N); - int j = 0; - for(int i = N-1; i >= 0; i--) - { - // Check the state is orthogonal to its particle-hole transformed state - auto phi = Vector (column (U, i)); - //check_orthogonal_to_particle_hole_transform (phi); - - _ens(j) = 2.*ens(i); - column (_u,j) &= subVector (phi,0,N); - column (_v,j) &= subVector (phi,N,2*N); - j++; - } - - // Check the unitrary matrices U = [ u v* ] - // [ v u* ] - Matrix Uc (2*N, 2*N); - subMatrix(Uc,0,N,0,N) &= _u; - subMatrix(Uc,N,2*N,0,N) &= _v; - subMatrix(Uc,0,N,N,2*N) &= conj (_v); - subMatrix(Uc,N,2*N,N,2*N) &= conj (_u); - - auto Hd = transpose(Uc) * _H * Uc; - for(int i = 0; i < N; i++) - { - Hd(i,i) -= 0.5*_ens(i); - Hd(i+N,i+N) += 0.5*_ens(i); - } - mycheck (abs(norm(Hd)) < 1e-10, "Construct unitray matrix failed"); -} - -// i is the site index in real space -// Return k, coef, dagger -// -// [ C ] = [ u v* ] [ gamma ] -// [ Cdag ] [ v u* ] [ gamma^dag ] -// -// C_i = sum_k^N { u(i,k) gamma + v*(i,k) gamma^dag } -vector> BdGBasis :: C_op (int i, bool dag) const -{ - int N = _ens.size(); - mycheck (i >= 1 and i <= N, "out of range"); // i is from 1 to N - - auto tmp = _u(0,0); - vector> k_coef_dag; - // For C - for(int k = 0; k < N; k++) - { - auto uk = _u(i-1,k); - auto vk = _v(i-1,k); - if (abs(uk) > 1e-14) - k_coef_dag.emplace_back (k+1, uk, false); - if (abs(vk) > 1e-14) - k_coef_dag.emplace_back (k+1, iut::conj(vk), true); - } - // If Cdag - if (dag) - { - for(auto& [k, coef, dagk] : k_coef_dag) - { - coef = iut::conj (coef); - dagk = !dagk; - } - } - - return k_coef_dag; -} - -// [ u v* ] -// [ gamma^dag gamma ] = [ C^dag C] [ v u* ] -// -// gamma^dag_k = sum_i^N { u(i,k) C_i^dag + v(i,k) C_i } -vector> BdGBasis :: gamma_op (int k, bool dag) const -{ - int N = _ens.size(); - mycheck (k >= 1 and k <= N, "out of range"); // i is from 1 to N - - auto tmp = _u(0,0); - vector> i_coef_dag; - // For gamma^dag - for(int i = 0; i < N; i++) - { - auto uk = _u(i,k-1); - auto vk = _v(i,k-1); - if (abs(uk) > 1e-14) - i_coef_dag.emplace_back (i+1, uk, true); - if (abs(vk) > 1e-14) - i_coef_dag.emplace_back (i+1, vk, false); - } - // If gamma - if (!dag) - { - for(auto& [i, coef, dagk] : i_coef_dag) - { - coef = iut::conj (coef); - dagk = !dagk; - } - } - return i_coef_dag; -} - -auto write (ostream& s, const BdGBasis& t) -{ - t.write (s); -} -auto read (istream& s, BdGBasis& t) -{ - t.read (s); -} - -// Original Hamiltonian in the BdG basis. -// Do not confuse with the BdG Hamiltonian. -// H_BdG = \sum_i^N epsilon_i gamma^dag_i gamma_i - \sum_i^N (1/2) epsilon_i -// H = H_BdG - (1/2) * sum_i^N mu_i -template -AutoMPO H_AMPO_BdG_basis (const SiteType& sites, const BdGBasis& bdg) -{ - AutoMPO ampo (sites); - int N = length (sites); - for(int i = 1; i <= N; i++) - { - ampo += bdg.en(i),"N",i; - ampo += -0.5 * (bdg.en(i) + bdg.mu(i)), "I", i; - } - return ampo; -} - -// Ground state energy for the orignal Hamiltonian (not the BdG Hamiltonian) -Real ground_state_energy (const BdGBasis& b) -{ - Real en = 0.; - for(int i = 0; i < b.size(); i++) - { - en -= 0.5*b.en(i+1) - b.H(i,i); - } - return en; -} - -void print_ops (const vector>& ops) -{ - cout << "site, coef, dag" << endl; - for(auto& [k, coef, dagk] : ops) - { - cout << k << " " << coef << " " << dagk << endl; - } -} -#endif diff --git a/kbasis/InitState.h b/kbasis/InitState.h deleted file mode 100644 index 72711d4..0000000 --- a/kbasis/InitState.h +++ /dev/null @@ -1,340 +0,0 @@ -#ifndef __INITSTATE_H_CMC__ -#define __INITSTATE_H_CMC__ -#include "SortBasis.h" - -// The charging energy is Ec * (N - Ng)^2 -// Find out N that lowest the charging energy in even and odd number of particle sectors -tuple en_charging_energy (int maxOcc, Real Ec, Real Ng) -{ - Real en_even = std::numeric_limits::max(), - en_odd = std::numeric_limits::max(); - vector ns (1,0); - for(int n = 1; n <= maxOcc; n++) - { - ns.push_back (n); - ns.push_back (-n); - } - int n_even, n_odd; - for(int n : ns) - { - // Compute charging energy - Real nn = n - Ng; - Real enC = Ec * nn*nn; - // - if (n % 2 == 0 and enC < en_even) - { - en_even = enC; - n_even = n; - } - if (n % 2 == 1 and enC < en_odd) - { - en_odd = enC; - n_odd = n; - } - } - return {en_even, en_odd, n_even, n_odd}; -} - -template -MPS get_ground_state_BdG_scatter (const BasisL& leadL, const BasisR& leadR, const BasisS& scatterer, - const SiteType& sites, Real muL, Real muR, const Para& para, int maxOcc, const ToGlobDict& to_glob) -{ - int N = to_glob.size(); - mycheck (length(sites) == N, "size not match"); - - // Get the ground state for SC (even particle number) - vector state (N+1); - - // Leads - auto occ_negative_en_states = [&to_glob, &state] (const auto& basis, Real mu) - { - string p = basis.name(); - for(int k = 1; k <= basis.size(); k++) - { - int i = to_glob.at({p,k}); - auto en = basis.en(k); - if (en < mu) - state.at(i) = "Occ"; - else - state.at(i) = "Emp"; - } - }; - occ_negative_en_states (leadL, muL); - occ_negative_en_states (leadR, muR); - - // Scatterer - string sname = scatterer.name(); - for(int k = 1; k <= scatterer.size(); k++) - { - int i = to_glob.at({sname,k}); - state.at(i) = "Emp"; - } - - // Superconducting gap - Real SC_gap = scatterer.en(1); - cout << "SC gap = " << SC_gap << endl; - - - // Capacity site - // Find out the charge numbers, which are integers, that lowest the charging energy, for even and odd parities - auto [enC0, enC1, n_even, n_odd] = en_charging_energy (maxOcc, para.Ec, para.Ng); - // Combine the scatter energies to decide to choose even or odd sector - Real en0 = enC0, - en1 = enC1 + SC_gap; - cout << "n (even,odd) = " << n_even << ", " << n_odd << endl; - cout << "E (even,odd) = " << en0 << ", " << en1 << endl; - if (en1 < en0) // First excited state in superconductor - { - int is1 = to_glob.at({"S",1}); - state.at(is1) = "Occ"; - cout << "Ground state has odd parity" << endl; - } - else - { - cout << "Ground state has even parity" << endl; - } - int n = (en0 <= en1 ? n_even : n_odd); - cout << "Initial charge = " << n << endl; - // Set the charge site - int ic = to_glob.at({"C",1}); - state.at(ic) = str(n); - // Set state - InitState init (sites); - for(int i = 1; i <= N; i++) - init.set (i, state.at(i)); - - auto psi = MPS (init); -/* - // If Majorana zero mode exists, use the equal-weight superposition of occupied and unoccupied state - auto const& en = visit (basis::en(1), sys.parts().at("S")); - if (abs(en) < 1e-14) - { - int iglob = sys.to_glob ("S",1); - auto A = psi(iglob); - auto links = findInds (A, "Link"); - auto il1 = links(1); - auto il2 = links(2); - auto is = findIndex (A, "Site"); - mycheck (dim(il1) == 1 and dim(il2) == 1, "Link indices dimension error"); - Real ele = 1./sqrt(2); - A.set (il1=1, il2=1, is=1, ele); - A.set (il1=1, il2=1, is=2, ele); - psi.ref(iglob) = A; - PrintData(psi(iglob)); - } -exit(0);*/ - return psi; -} - -template -MPS get_non_inter_ground_state (const BasisL& leadL, const BasisR& leadR, const BasisS& scatterer, - const SiteType& sites, Real muL, Real muS, Real muR, const ToGlobDict& to_glob) -{ - int N = to_glob.size(); - mycheck (length(sites) == N, "size not match"); - - int Ns=0, Np=0; - Real E = 0.; - vector state (N+1, "Emp"); - - // Leads and scatterer - auto occ_negative_en_states = [&to_glob, &state, &E, &Np, &Ns] (const auto& basis, Real mu) - { - string p = basis.name(); - for(int k = 1; k <= basis.size(); k++) - { - int i = to_glob.at({p,k}); - auto en = basis.en(k); - if (en < mu) - { - state.at(i) = "Occ"; - E += en; - Np++; - if (p == "S") - Ns++; - } - else - { - state.at(i) = "Emp"; - } - } - }; - occ_negative_en_states (leadL, muL); - occ_negative_en_states (leadR, muR); - occ_negative_en_states (scatterer, muS); - - InitState init (sites); - for(int i = 1; i <= N; i++) - init.set (i, state.at(i)); - - // Print information - cout << "initial energy = " << E << endl; - cout << "initial particle number = " << Np << endl; - return MPS (init); -} - -template -tuple -get_scatter_ground_state_SC -(const BasisS& scatterer, Real mu, Real Delta, const Sweeps& sweeps, const ToGlobDict& to_glob, const Args& args) -{ - int Ns = scatterer.size(); - SpecialFermion sites (Ns, {args,"in_scatter",true}); - - // Find the first site of the scatter - string sname = scatterer.name(); - int imin = std::numeric_limits::max(); - for(int i = 1; i <= Ns; i++) - { - int j = to_glob.at({sname,i}); - if (imin > j) - imin = j; - } - - int L_offset = imin-1; - AutoMPO ampo (sites); - // Diagonal terms - for(int i = 1; i <= Ns; i++) - { - int j = to_glob.at({sname,i}) - L_offset; - auto en = scatterer.en(i); - ampo += en-mu, "N", j; - } - // Superconducting - for(int i = 1; i < Ns; i++) - { - auto terms = quadratic_operator (scatterer, scatterer, i, i+1, false, false); - for(auto [c12, k1, dag1, k2, dag2] : terms) - { - int j1 = to_glob.at({sname,k1}) - L_offset; - int j2 = to_glob.at({sname,k2}) - L_offset; - if (j1 != j2) - { - auto c = Delta * c12; - auto cc = iut::conj (c); - string op1 = (dag1 ? "Cdag" : "C"); - string op2 = (dag2 ? "Cdag" : "C"); - string op1dag = (dag1 ? "C" : "Cdag"); - string op2dag = (dag2 ? "C" : "Cdag"); - ampo += -c, op1, j1, op2, j2; - ampo += -cc, op1dag, j2, op2dag, j1; - } - } - } - auto H0 = toMPO (ampo); - - // Solve the ground states in even and odd parities by DMRG - InitState init (sites); - auto psi0 = MPS (init); - init.set(1,"Occ"); - auto psi1 = MPS (init); - - auto en0 = dmrg (psi0, H0, sweeps, {"Quiet",true}); - auto en1 = dmrg (psi1, H0, sweeps, {"Quiet",true}); - - cout << setprecision(14); - AutoMPO Nampo (sites); - for(int i = 1; i <= length(sites); i++) - Nampo += 1.0,"N",i; - auto Nmpo = toMPO (Nampo); - Real Np0 = inner (psi0,Nmpo,psi0), - Np1 = inner (psi1,Nmpo,psi1); - - return {psi0, psi1, en0, en1, Np0, Np1, L_offset}; -} - -template -MPS get_ground_state_SC (const BasisL& leadL, const BasisR& leadR, const BasisS& scatterer, const BasisC& charge, - const SiteType& sites, - Real muL, Real muS, Real muR, const Para& para, - const Sweeps& sweeps, const ToGlobDict& to_glob, const Args& args) -{ - int N = to_glob.size(); - mycheck (length(sites) == N, "size not match"); - - Real E_lead = 0.; - int Np_lead = 0; - vector state (N+1, "Emp"); - - // Leads - auto occ_neg_en_levels = [&E_lead, &Np_lead, &state, &to_glob] (const auto& basis, Real mu) - { - string p = basis.name(); - for(int i = 1; i <= basis.size(); i++) - { - auto en = basis.en(i); - int j = to_glob.at({p,i}); - if (en < mu) - { - state.at(j) = "Occ"; - E_lead += en-mu; - Np_lead++; - } - else - { - state.at(j) = "Emp"; - } - } - }; - occ_neg_en_levels (leadL, muL); - occ_neg_en_levels (leadR, muR); - cout << "lead E = " << E_lead << endl; - cout << "lead Np = " << Np_lead << endl; - - // Get ground state of the scatterer - auto [psi0, psi1, enSC0, enSC1, Np0, Np1, L_offset] = get_scatter_ground_state_SC (scatterer, muS, para.Delta, sweeps, to_glob, args); - - // Capacity site - // Find out the charge numbers, which are integers, that lowest the charging energy, for even and odd parities - int maxOcc = args.getInt("MaxOcc"); - auto [enC0, enC1, n_even, n_odd] = en_charging_energy (maxOcc, para.Ec, para.Ng); - // Combine the scatter energies to decide to choose even or odd sector - Real en0 = enC0 + enSC0, - en1 = enC1 + enSC1; - Real en = (en0 < en1 ? en0 : en1); - int n = (en0 < en1 ? n_even : n_odd); - Real Np = (en0 < en1 ? Np0 : Np1); - auto psiS = (en0 < en1 ? psi0 : psi1); - // Set state - int ic = to_glob.at({"C",1}); - state.at(ic) = str(n); - // Print - cout << "Init scatter Np (even,odd) = (" << Np0 << "," << Np1 << ") -> " << Np << endl; - cout << "Init scatter E (even,odd) = (" << en0 << "," << en1 << ") -> " << en << endl - << "\tE_C = (" << enC0 << "," << enC1 << ")" << endl - << "\tE_SC, gap = (" << enSC0 << "," << enSC1 << "), " << abs(enSC0-enSC1) << endl; - cout << "Init scatter total charge = (" << n_even << "," << n_odd << ") -> " << n << endl; - - // Initialize the leads and the charge site - InitState init (sites); - for(int i = 1; i <= N; i++) - init.set (i, state.at(i)); - auto psi = MPS (init); - - // Replace the tensors in the scatter - for(int i = 1; i <= length(psiS); i++) - { - int i0 = i+L_offset; - auto iis = findIndex (psi(i0), "Site"); - auto iis2 = findIndex (psiS(i), "Site"); - Index iil; - if (i == 1) - iil = leftLinkIndex (psi, i0); - else if (i == length(psiS)) - iil = rightLinkIndex (psi, i0); - psiS.ref(i).replaceInds ({iis2}, {iis}); - psi.ref(i0) = psiS(i); - if (iil) - { - mycheck (dim(iil) == 1, "not dummy index"); - psi.ref(i0) *= setElt(iil=1); - } - state.at(i0) = "*"; - } - psi.position(1); - psi.normalize(); - - return psi; -} - -#endif diff --git a/kbasis/MixedBasis.h b/kbasis/MixedBasis.h deleted file mode 100644 index 274429c..0000000 --- a/kbasis/MixedBasis.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef __SpecialMixedSiteSet_H_CMC__ -#define __SpecialMixedSiteSet_H_CMC__ -#include "itensor/all.h" -#include "SpecialFermion.h" -#include "SpecialBoson.h" -#include "ContainerUtility.h" -using namespace itensor; - -class MixedBasis : public SiteSet -{ - int _maxOcc; - - public: - - int maxOcc () const { return _maxOcc; } - - MixedBasis() {} - - MixedBasis (int N, int iL, int iR, int iC, Args const& args=Args::global()) - { - _maxOcc = args.getInt("MaxOcc"); - auto sites = SiteStore(N); - for(int j = 1; j <= N; ++j) - { - bool in_scatter = (j >= iL and j <= iR); - if(j == iC) sites.set (j,SpecialBosonSite ({args,"SiteNumber=",j})); - else sites.set (j,SpecialFermionSite ({args,"SiteNumber=",j,"in_scatter",in_scatter})); - } - SiteSet::init(std::move(sites)); - } - - MixedBasis (int N, const vector& scatter_sites, int iC, Args const& args=Args::global()) - { - _maxOcc = args.getInt("MaxOcc"); - auto sites = SiteStore(N); - for(int j = 1; j <= N; ++j) - { - bool in_scatter = iut::in_vector (scatter_sites, j); - if(j == iC) sites.set (j,SpecialBosonSite ({args,"SiteNumber=",j})); - else sites.set (j,SpecialFermionSite ({args,"SiteNumber=",j,"in_scatter",in_scatter})); - } - SiteSet::init(std::move(sites)); - } - - MixedBasis (IndexSet const& is, Args const& args=Args::global()) - { - int N = is.length(); - auto sites = SiteStore(N); - for(auto j : range1(N)) - { - auto ii = is(j); - mycheck (hasTags(ii,"Boson") or hasTags(ii,"Fermion"), "unknown site index"); - if (hasTags(ii,"Boson")) - { - sites.set(j,SpecialBosonSite(ii,args)); - int d = dim(ii); - _maxOcc = (d-1)/2; - } - else - sites.set(j,SpecialFermionSite(ii)); - } - SiteSet::init(std::move(sites)); - } -}; -#endif diff --git a/kbasis/MyObserver.h b/kbasis/MyObserver.h deleted file mode 100644 index 56e6246..0000000 --- a/kbasis/MyObserver.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __MYOBSERVER_H_CMC__ -#define __MYOBSERVER_H_CMC__ -#include "itensor/all.h" -#include "Entanglement.h" -using namespace itensor; - -template -class MyObserver : public DMRGObserver -{ - public: - MyObserver (const SitesType& sites, const MPS& psi, const Args& args = Args::global()) - : DMRGObserver (psi, args) - , _sites (sites) - , _ns (length(psi),0.) - , _Npar (0.) - { - _write = args.getBool ("Write",false); - _write_minm = args.getInt ("out_minm",0); - _out_dir = args.getString("out_dir","."); - } - - void measure (const Args& args = Args::global()); - - Real Npar () const { return _Npar; } - auto const& ns () const { return _ns; } - - private: - bool _write; - string _out_dir; // empty string "" if not write - int _write_minm; - vector _iDel, _jDel; - vector _Delta; - SitesType _sites; - - vector _ns; - Real _Npar; -}; - -Real Onsite_mea (const ITensor& A, const ITensor& op) -{ - ITensor re = A * op; - re.noPrime ("Site"); - re *= dag(A); - return re.real(); -} - -template -void MyObserver :: measure (const Args& args) -{ - DMRGObserver::measure (args); - - // Define your measurements below - // Call psi() to access the MPS - // - auto N = length(psi()); - auto b = args.getInt("AtBond",1); - auto sw = args.getInt("Sweep",0); - auto ha = args.getInt("HalfSweep",0); - auto energy = args.getReal("Energy",0); - - // On-site measure - int oc = orthoCenter(psi()); - if (oc == N || ha == 2) // measure during the second half of sweep - { - // Density - ITensor n_op = _sites.op("N",oc); - Real ni = Onsite_mea (psi().A(oc), n_op); - cout << "\tn " << oc << " = " << ni << endl; - _ns.at(oc-1) = ni; - - // Entanglement entropy - Real S = EntangEntropy (spectrum()); - cout << "\tentang entropy " << oc << " = " << S << endl; - } - - if (oc == 1 && ha == 2) - { - int m = args.getInt("MaxDim"); - // Write MPS - if (_write && m >= _write_minm) - { - writeToFile (_out_dir+"/psi"+"_m"+to_string(m)+".mps", psi()); - } - } -} - -#endif diff --git a/kbasis/SpecialBoson.h b/kbasis/SpecialBoson.h deleted file mode 100644 index aa71695..0000000 --- a/kbasis/SpecialBoson.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef __SPECIALBOSON_H_CMC__ -#define __SPECIALBOSON_H_CMC__ -#include "itensor/mps/siteset.h" - -class SpecialBosonSite -{ - Index s; - - vector _ns; - - public: - - int n (int i) const { return _ns.at(i-1); } - - SpecialBosonSite(Index I, Args const& args = Args::global()) : s(I) - { - auto maxOcc = args.getInt("MaxOcc"); - for(int n = -maxOcc; n <= maxOcc; n++) - _ns.push_back (n); - } - - SpecialBosonSite(Args const& args = Args::global()) - { - auto systype = args.getString("SystemType"); - - auto tags = TagSet("Site,Boson"); - if (args.defined("SiteNumber")) - { - auto n = args.getInt("SiteNumber"); - tags.addTags("n="+str(n)); - } - - auto maxOcc = args.getInt("MaxOcc"); - for(int n = -maxOcc; n <= maxOcc; n++) - _ns.push_back (n); - - auto qints = Index::qnstorage(_ns.size()); - for(int i = 0; i < _ns.size(); i++) - { - int n = _ns.at(i); - if(systype == "SC_scatter") - { - int p = n % 2; - qints[i] = QNInt(QN({"Nf",n,-1},{"Ps",p,-2}),1); - } - else if (systype == "Normal") - { - qints[i] = QNInt(QN({"Nf",0,-1},{"Ns",-n,-1}),1); - } - else if (systype == "SC_Josephson_scatter") - { - int p = n % 2; - qints[i] = QNInt(QN({"Pf",p,-2},{"Ps",p,-2}),1); - } - else - { - cout << "Unknown system type: " << systype << endl; - throw; - } - } - s = Index(std::move(qints),Out,tags); - } - - Index - index() const { return s; } - - IndexVal - state(std::string state) - { - if(state == "Emp") - state = "0"; - for(int i = 0; i < _ns.size(); i++) - { - if(state == str(_ns.at(i))) return s(i+1); - } - throw ITError("State " + state + " not recognized"); - return IndexVal{}; - } - - ITensor op (std::string const& opname, Args const& args) const - { - auto sP = prime(s); - - auto Op = ITensor(dag(s),sP); - - if(opname == "N" || opname == "n") - { - for(int i = 0; i < _ns.size(); i++) - { - int j = i+1; - int n = _ns.at(i); - Op.set(s=j,sP=j,n); - } - } - else if(opname == "NSqr" || opname == "nSqr") - { - for(int i = 0; i < _ns.size(); i++) - { - int j = i+1; - int n = _ns.at(i); - Op.set(s=j,sP=j,n*n); - } - } - else if(opname == "A" or opname == "C") - { - for(int i = 1; i < _ns.size(); i++) - { - Op.set(s=1+i,sP=i,1); - } - } - else if(opname == "Adag" or opname == "Cdag") - { - for(int i = 1; i < _ns.size(); i++) - { - Op.set(s=i,sP=1+i,1); - } - } - else if(opname == "A2") - { - for(int i = 3; i <= _ns.size(); i++) - { - Op.set(s=i,sP=i-2,1); - } - } - else if(opname == "A2dag") - { - for(int i = 3; i <= _ns.size(); i++) - { - Op.set(s=i-2,sP=i,1); - } - } - else if(opname == "I") - { - for(int i = 1; i <= _ns.size(); i++) - { - Op.set(s=i,sP=i,1); - } - } - else - { - throw ITError("Operator \"" + opname + "\" name not recognized"); - } - - return Op; - } -}; -#endif diff --git a/kbasis/SpecialFermion.h b/kbasis/SpecialFermion.h deleted file mode 100644 index adbbf33..0000000 --- a/kbasis/SpecialFermion.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef __SPECIALFERMION_H_CMC__ -#define __SPECIALFERMION_H_CMC__ -#include "itensor/mps/siteset.h" -#include "itensor/util/str.h" -using namespace itensor; - -class SpecialFermionSite -{ - Index s; - public: - - SpecialFermionSite(Index I) : s(I) { } - - SpecialFermionSite(Args const& args = Args::global()) - { - auto systype = args.getString("SystemType"); - auto ts = TagSet("Site,Fermion"); - auto n = 1; - if(args.defined("SiteNumber")) - { - n = args.getInt("SiteNumber"); - ts.addTags("n="+str(n)); - } - auto in_scatter = args.getBool("in_scatter"); - if (systype == "SC_scatter") - { - if (in_scatter) - { - s = Index(QN({"Nf",0,-1},{"Ps",0,-2}),1, - QN({"Nf",0,-1},{"Ps",1,-2}),1, - Out,ts); - } - else - { - s = Index(QN({"Nf",0,-1},{"Ps",0,-2}),1, - QN({"Nf",1,-1},{"Ps",0,-2}),1, - Out,ts); - } - } - else if (systype == "Normal") - { - if (in_scatter) - { - s = Index(QN({"Nf",0,-1},{"Ns",0,-1}),1, - QN({"Nf",1,-1},{"Ns",1,-1}),1, - Out,ts); - } - else - { - s = Index(QN({"Nf",0,-1},{"Ns",0,-1}),1, - QN({"Nf",1,-1},{"Ns",0,-1}),1, - Out,ts); - } - } - else if (systype == "SC_Josephson_scatter") - { - if (in_scatter) - { - s = Index(QN({"Pf",0,-2},{"Ps",0,-2}),1, - QN({"Pf",0,-2},{"Ps",1,-2}),1, - Out,ts); - } - else - { - s = Index(QN({"Pf",0,-2},{"Ps",0,-2}),1, - QN({"Pf",1,-2},{"Ps",0,-2}),1, - Out,ts); - } - } - else - { - cout << "Unknown system type: " << systype << endl; - throw; - } - } - - Index - index() const { return s; } - - IndexVal - state(std::string const& state) - { - if(state == "Emp" || state == "0") - { - return s(1); - } - else - if(state == "Occ" || state == "1") - { - return s(2); - } - else - { - throw ITError("State " + state + " not recognized"); - } - return IndexVal{}; - } - - ITensor - op(std::string const& opname, - Args const& args) const - { - auto sP = prime(s); - - auto Emp = s(1); - auto EmpP = sP(1); - auto Occ = s(2); - auto OccP = sP(2); - - auto Op = ITensor(dag(s),sP); - - if(opname == "N" || opname == "n") - { - Op.set(Occ,OccP,1); - } - else - if(opname == "C") - { - Op.set(Occ,EmpP,1); - } - else - if(opname == "Cdag") - { - Op.set(Emp,OccP,1); - } - else - if(opname == "A") - { - Op.set(Occ,EmpP,1); - } - else - if(opname == "Adag") - { - Op.set(Emp,OccP,1); - } - else - if(opname == "F" || opname == "FermiPhase") - { - Op.set(Emp,EmpP,1); - Op.set(Occ,OccP,-1); - } - else - if(opname == "projEmp") - { - Op.set(Emp,EmpP,1); - } - else - if(opname == "projOcc") - { - Op.set(Occ,OccP,1); - } - else - if(opname == "I") - { - Op.set(Occ,OccP,1); - Op.set(Emp,EmpP,1); - } - else - { - throw ITError("Operator \"" + opname + "\" name not recognized"); - } - - return Op; - } -}; - -using SpecialFermion = BasicSiteSet; -#endif diff --git a/kbasis/TDVPObserver.h b/kbasis/TDVPObserver.h deleted file mode 100644 index d41b4e3..0000000 --- a/kbasis/TDVPObserver.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef __TDVPOBSERVER_H_CMC__ -#define __TDVPOBSERVER_H_CMC__ -#include -#include -#include "itensor/all.h" -#include "Entanglement.h" -#include "ContainerUtility.h" -using namespace iut; -using namespace iutility; - -template -class TDVPObserver : public DMRGObserver -{ - public: - TDVPObserver (const SitesType& sites, const MPS& psi, const Args& args=Args::global()) - : DMRGObserver (psi, args) - , _sites (sites) - , _ns (length(psi),0.) - , _Npar (0.) - , _specs (length(psi)) - { - _write = args.getBool ("Write",false); - _out_dir = args.getString("out_dir","."); - _charge_site = args.getInt("charge_site",-1); - } - - void measure (const Args& args); - - Real Npar () const { return _Npar; } - auto const& ns () const { return _ns; } - const Spectrum& spec (int i) const { return _specs.at(i); } - - private: - bool _write; - string _out_dir; // empty string "" if not write - SitesType _sites; - int _charge_site=-1; - - // Observables - vector _ns; - Real _Npar; - vector _specs; -}; - -template -void TDVPObserver :: measure (const Args& args) -{ - DMRGObserver::measure (args); - - cout << scientific << setprecision(14); - // Define your measurements below - // Call psi() to access the MPS - // - auto N = length(psi()); - auto b = args.getInt("AtBond"); - auto sw = args.getInt("Sweep"); - auto ha = args.getInt("HalfSweep"); - auto energy = args.getReal("Energy",0); - - if (b != N) - _specs.at(b) = spectrum(); - - int oc = orthoCenter(psi()); - int nc = args.getInt("NumCenter"); - // measure during the second half of sweep - if (oc == N || ha == 2) - { - // Density - ITensor n_op = noPrime (psi().A(oc) * _sites.op("N",oc), "Site"); - - n_op *= dag(psi().A(oc)); - Real ni = real(eltC(n_op)); - cout << "\t*den " << oc << " " << ni << endl; - _ns.at(oc-1) = ni; - - // Entanglement entropy - Real S = EntangEntropy (spectrum()); - cout << "\t*entS " << oc << " " << S << endl; - - /*if (oc == _charge_site) - { - auto denmat = psi()(oc) * dag(prime(psi()(oc),"Site")); - denmat.takeReal(); - auto [Q,D] = diagHermitian (denmat); - auto ii = denmat.inds()(1); - int maxOcc = _sites.maxOcc(); - for(int i = 1; i <= dim(ii); i++) - cout << "\t*nC " << i-maxOcc-1 << " " << elt (denmat,i,i) << endl; - }*/ - } - - // At the end of a sweep - if (oc == 1 && ha == 2 && b == 1) - { - for(int i = 1; i < N; i++) - cout << "\t*m " << i << " " << dim(rightLinkIndex (psi(), i)) << endl; - - if (_write) - { - cout << "write MPS" << endl; - writeToFile (_out_dir+"/psi.mps", psi()); - } - } -} -#endif diff --git a/kbasis/basisextension.h b/kbasis/basisextension.h deleted file mode 100644 index 8695dff..0000000 --- a/kbasis/basisextension.h +++ /dev/null @@ -1,274 +0,0 @@ -#include "itensor/decomp.h" -#include "itensor/mps/mps.h" -#include "itensor/mps/mpo.h" -#include "itensor/mps/mpsalgs.cc" -#include "itensor/mps/localop.h" -#include "itensor/util/print_macro.h" -#include "itensor/util/cputime.h" -#include "itensor/tensor/slicemat.h" - -namespace itensor{ - -void -denmatSumDecomp(std::vector const& psis, - MPS & res, - std::vector & Bs, - int b, - Direction dir, - Args args = Args::global()) - { - // NumCenter can only be 1 if not want to treat res exactly - const int numCenter = args.getInt("NumCenter",1); - const bool quiet = args.getBool("Quiet",false); - - // SVD site tensor of res without truncaion - auto [V1,S1,U1] = svd(Bs.front(), dir == Fromleft? rightLinkIndex(res,b): leftLinkIndex(res,b)); - - // Find the indices to be left to the density matrix - auto& to_orth = Bs.front(); - auto& newoc = (dir==Fromleft? res(b+1) : res(b-1)); - auto& activeInds = to_orth.inds(); - auto cinds = stdx::reserve_vector(activeInds.r()); - for(auto& I : activeInds) - { - if(!hasIndex(newoc,I)) cinds.push_back(I); - } - - auto [cmb,mid] = combiner(std::move(cinds)); - - if(dim(mid) <= dim(commonIndex(U1,S1))) - { - res.ref(b) = U1; - if(!quiet) - printfln("warning: at bond %d, already reach maximum bond dimension.", b); - } - else - { - // Density matrix summation to be truncated - ITensor rho2; - if(numCenter == 1) - { - auto psi = psis.begin(); - for(auto B = Bs.begin()+1; B != Bs.end(); ++B, ++psi) - { - rho2 += prime(*B)*dag(prime(*B,dir == Fromleft? rightLinkIndex(*psi,b): leftLinkIndex(*psi,b))); - } - rho2.swapPrime(0,1); - } - else - { - Error("numCenter can only be one"); - } - - // Form the density matrix with only 2 indices - U1 *= cmb; - rho2 *= cmb; - cmb.prime(); - cmb.dag(); - rho2 *= cmb; - cmb.noPrime(); - - // Project rho2c to the orthogonal complement of U1 - auto proj2 = toDense(delta(dag(mid),prime(mid)))-dag(U1)*prime(U1,mid); - auto normrho2 = norm(rho2); - rho2 *= mapPrime(proj2,0,2); - rho2 *= proj2; - rho2.mapPrime(2,0); - rho2.swapPrime(0,1); - auto normPrho2P = norm(rho2); - if(normPrho2P/normrho2 < 1E-14)// TODO: changed to calculate the trace will have less complexity and have the same effect! - { - res.ref(b) = cmb * U1; - if(!quiet) - printfln("warning: at bond %d, not adding any new basis.", b); - } - else - { - // Diagonalize rho2c to obtain U2 - ITensor U2, D2; - args.add("Truncate",true); - diag_hermitian(rho2,U2,D2,args);// T==prime(U)*D*dag(U) - U2.dag(); - - // Direct sum of U1 and U2 - auto i1 = commonIndex(U1,S1); - auto i2 = commonIndex(U2,D2); - auto sumind = Index(dim(i1)+dim(i2),"Link"); - sumind.setDir(i1.dir()); - ITensor expand1,expand2; - plussers(i1,i2,sumind,expand1,expand2); - auto U = U1*expand1 + U2*expand2; - - res.ref(b) = cmb * U; - } - - } - - // Obtain the new Bs for the operation of the next site - Bs.front() *= dag(res(b)); - Bs.front() *= (dir == Fromleft? res(b+1): res(b-1)); - auto psi = psis.begin(); - for(auto B = Bs.begin()+1; B != Bs.end(); ++B, ++psi) - { - (*B) *= dag(res(b)); - (*B) *= (dir == Fromleft? (*psi).A(b+1): (*psi).A(b-1)); - } - - } - -void -addBasisWorker(std::vector const& psis, - MPS & res, - Direction dir, - const Args & args = Args::global()) - { - int N = length(res); - int nt = psis.size()+1; - - if(dir == Fromleft) - { - if(orthoCenter(res) != 1) - Error("OC need set to be 1"); - for(auto& psi : psis) - { - if(orthoCenter(psi) != 1) - Error("OC need set to be 1"); - } - - auto Bs = std::vector(nt); - Bs.front() = res(1); - auto psi = psis.begin(); - for(auto B = Bs.begin()+1; B != Bs.end(); ++B, ++psi) - { - (*B) = (*psi).A(1); - } - - for(int b = 1; b < N ; ++b) - { - denmatSumDecomp(psis,res,Bs,b,Fromleft,args); - } - - res.Aref(N) = Bs.front(); - } - else - { - if(orthoCenter(res) != N) - Error("OC need set to be N"); - for(auto& psi : psis) - { - if(orthoCenter(psi) != N) - Error("OC need set to be N"); - } - - auto Bs = std::vector(nt); - Bs.front() = res(N); - auto psi = psis.begin(); - for(auto B = Bs.begin()+1; B != Bs.end(); ++B, ++psi) - { - (*B) = (*psi).A(N); - } - - for(int b = N; b > 1 ; --b) - { - denmatSumDecomp(psis,res,Bs,b,Fromright,args); - } - - res.Aref(1) = Bs.front(); - } - } - -void addBasis(MPS& phi, - const MPO& H, - Real truncK, - int maxDim, - const Args& args0 = Args::global()) - { - auto quiet = args0.getBool("Quiet",false); - auto dk = args0.getInt("KrylovOrd",2); - auto method = args0.getString("Method","DensityMatrix"); - auto nsw = args0.getInt("Nsweep",2); - auto donormalize = args0.getBool("DoNormalize",false);//TODO: add a function to construct general 1-tauH - - auto psis = std::vector(dk-1); - - cpu_time expand_time; - for(int i = 0; i < dk-1; ++i) - { - auto args1 = Args("Method=",method,"Cutoff=",truncK,"MaxDim=",maxDim,"Nsweep=",nsw); - - if(i==0) - psis.at(i) = applyMPO(H,phi,args1); - else - psis.at(i) = applyMPO(H,psis.at(i-1),args1); - - psis.at(i).noPrime(); - if(donormalize) - psis.at(i).normalize(); - - if(!quiet) - { - printfln("norm(psi%d)=%.20f",i+1,norm(psis.at(i))); - printfln("maxLinkDim(psi%d) = %d",i+1,maxLinkDim(psis.at(i))); - } - } - - int N = length(phi); - for(int i = 0; i < dk-1; ++i) - { - psis.at(i).position(N); - } - phi.position(N); - - //TODO: adjustable weight for each psi - addBasisWorker(psis,phi,Fromright,args0); - - auto sm = expand_time.sincemark(); - printfln("\nmaxLinkDim after global subspace expansion = %d",maxLinkDim(phi)); - printfln("Global subspace expansion: cputime = %s, walltime = %s",showtime(sm.time),showtime(sm.wall)); - } - -void addBasis(MPS& phi, - const MPO& H, - std::vector const& maxdimK, - const Args& args0 = Args::global()) - { - auto dk = args0.getInt("KrylovOrd",2); - auto method = args0.getString("Method","DensityMatrix"); - auto nsw = args0.getInt("Nsweep",2); - auto donormalize = args0.getBool("DoNormalize",false); - - auto psis = std::vector(dk-1); - - cpu_time expand_time; - for(int i = 0; i < dk-1; ++i) - { - auto args1 = Args("Method=",method,"MaxDim=",maxdimK.at(i),"Nsweep=",nsw); - - if(i==0) - psis.at(i) = applyMPO(H,phi,args1); - else - psis.at(i) = applyMPO(H,psis.at(i-1),args1); - - psis.at(i).noPrime(); - if(donormalize) - psis.at(i).normalize(); - - printfln("norm(psi%d)=%.20f",i+1,norm(psis.at(i))); - printfln("maxLinkDim(psi%d) = %d",i+1,maxLinkDim(psis.at(i))); - } - - int N = length(phi); - for(int i = 0; i < dk-1; ++i) - { - psis.at(i).position(N); - } - phi.position(N); - - addBasisWorker(psis,phi,Fromright,args0); - - auto sm = expand_time.sincemark(); - printfln("\nmaxLinkDim after global subspace expansion = %d",maxLinkDim(phi)); - printfln("Global subspace expansion: cputime = %s, walltime = %s",showtime(sm.time),showtime(sm.wall)); - } - -}// namespace itensor diff --git a/kbasis/quench.cc b/kbasis/quench.cc deleted file mode 100644 index 058aa7c..0000000 --- a/kbasis/quench.cc +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include "itensor/all.h" -#include "Timer.h" -Timers timer; -#include "ReadInput.h" -#include "IUtility.h" -#include "MyObserver.h" -#include "MPSUtility.h" -#include "MixedBasis.h" -#include "ContainerUtility.h" -#include "TDVPObserver.h" -#include "tdvp.h" -#include "basisextension.h" -#include "InitState.h" -#include "Hamiltonian.h" -#include "ReadWriteFile.h" -#include "OneParticleBasis.h" -#include "BdGBasis.h" -using namespace itensor; -using namespace std; - -// Define some parameters -struct Para -{ - Real tcL=0., tcR=0.; - - void write (ostream& s) const - { - iut::write(s,tcL); - iut::write(s,tcR); - } - - void read (istream& s) - { - iut::read(s,tcL); - iut::read(s,tcR); - } -}; - -void writeAll (const string& filename, - const MPS& psi, const MPO& H, - const Para& para, - const Args& args_basis, - int step, - const ToGlobDict& to_glob, - const ToLocDict& to_loc) -{ - ofstream ofs (filename); - itensor::write (ofs, psi); - itensor::write (ofs, H); - itensor::write (ofs, args_basis); - itensor::write (ofs, step); - para.write (ofs); - iut::write (ofs, to_glob); - iut::write (ofs, to_loc); -} - -void readAll (const string& filename, - MPS& psi, MPO& H, - Para& para, - Args& args_basis, - int& step, - ToGlobDict& to_glob, - ToLocDict& to_loc) -{ - ifstream ifs = open_file (filename); - itensor::read (ifs, psi); - itensor::read (ifs, H); - itensor::read (ifs, args_basis); - itensor::read (ifs, step); - para.read (ifs); - iut::read (ifs, to_glob); - iut::read (ifs, to_loc); -} - -void print_orbs (const vector& orbs) -{ - cout << "Orbitals: name, ki, energy" << endl; - for(int i = 1; i <= orbs.size(); i++) - { - auto [name, ki, en] = orbs.at(i-1); - cout << i << ": " << name << " " << ki << " " << en << endl; - } -} - -// Make MPO for the current operator -template -MPO get_current_mpo (const SiteType& sites, const Basis1& basis1, const Basis2& basis2, int i1, int i2, const ToGlobDict& to_glob) -{ - AutoMPO ampo (sites); - add_CdagC (ampo, basis1, basis2, i1, i2, 1., to_glob); - auto mpo = toMPO (ampo); - return mpo; -} - -inline Real get_current (const MPO& JMPO, const MPS& psi) -{ - auto J = innerC (psi, JMPO, psi); - return -2. * imag(J); -} - -int main(int argc, char* argv[]) -{ - string infile = argv[1]; - InputGroup input (infile,"basic"); - - auto L_lead = input.getInt("L_lead"); - auto L_device = input.getInt("L_device"); - auto t_lead = input.getReal("t_lead"); - auto t_device = input.getReal("t_device"); - auto t_contactL = input.getReal("t_contactL"); - auto t_contactR = input.getReal("t_contactR"); - auto mu_leadL = input.getReal("mu_leadL"); - auto mu_leadR = input.getReal("mu_leadR"); - auto mu_device = input.getReal("mu_device"); - auto mu_biasL = input.getReal("mu_biasL"); - auto mu_biasS = input.getReal("mu_biasS"); - auto mu_biasR = input.getReal("mu_biasR"); - auto damp_decay_length = input.getInt("damp_decay_length",0); - - auto dt = input.getReal("dt"); - auto time_steps = input.getInt("time_steps"); - auto NumCenter = input.getInt("NumCenter"); - auto Truncate = input.getYesNo("Truncate"); - auto mixNumCenter = input.getYesNo("mixNumCenter",false); - auto globExpanNStr = input.getString("globExpanN","inf"); - int globExpanN; - if (globExpanNStr == "inf" or globExpanNStr == "Inf" or globExpanNStr == "INF") - globExpanN = std::numeric_limits::max(); - else - globExpanN = std::stoi (globExpanNStr); - auto globExpanItv = input.getInt("globExpanItv",1); - auto globExpanCutoff = input.getReal("globExpanCutoff",1e-8); - auto globExpanKrylovDim = input.getInt("globExpanKrylovDim",3); - auto globExpanHpsiCutoff = input.getReal("globExpanHpsiCutoff",1e-8); - auto globExpanHpsiMaxDim = input.getInt("globExpanHpsiMaxDim",300); - auto globExpanMethod = input.getString("globExpanMethod","DensityMatrix"); - - auto UseSVD = input.getYesNo("UseSVD",true); - auto SVDmethod = input.getString("SVDMethod","gesdd"); // can be also "ITensor" - auto WriteDim = input.getInt("WriteDim"); - - auto write = input.getYesNo("write",false); - auto write_dir = input.getString("write_dir","."); - auto write_file = input.getString("write_file",""); - auto read = input.getYesNo("read",false); - auto read_dir = input.getString("read_dir","."); - auto read_file = input.getString("read_file",""); - - auto sweeps = iut::Read_sweeps (infile, "sweeps"); - - cout << setprecision(14) << endl; - - MPS psi; - MPO H; - // Define - int step = 1; - auto sites = Fermion(); - Para para; - Args args_basis; - - ToGlobDict to_glob; - ToLocDict to_loc; - OneParticleBasis leadL, leadR, charge; - OneParticleBasis scatterer; - - // -- Initialization -- - if (!read) - { - // Factor for exponentially decaying hoppings - Real damp_fac = (damp_decay_length == 0 ? 1. : exp(-1./damp_decay_length)); - // Create bases for the leads - cout << "H left lead" << endl; - leadL = OneParticleBasis ("L", L_lead, t_lead, mu_leadL, damp_fac, true, true); - cout << "H right lead" << endl; - leadR = OneParticleBasis ("R", L_lead, t_lead, mu_leadR, damp_fac, false, true); - // Create basis for scatterer - cout << "H dev" << endl; - scatterer = OneParticleBasis ("S", L_device, t_device, mu_device); - - // Combine and sort all the basis states - auto info = sort_by_energy (leadL, leadR, scatterer); - tie(to_glob, to_loc) = make_orb_dicts (info); - print_orbs(info); - - // SiteSet - int N = to_glob.size(); - sites = Fermion (N); - - // Make Hamiltonian MPO for time evolution - para.tcL = t_contactL; para.tcR = t_contactR; - auto ampo = get_ampo_tight_binding (leadL, leadR, scatterer, sites, para, to_glob); - H = toMPO (ampo); - cout << "MPO dim = " << maxLinkDim(H) << endl; - - // Initialize MPS - // ********************************* - { - /*auto para0 = para; - para0.tcL = 0.; - para0.tcR = 0.; - auto leadL0 = OneParticleBasis ("L", L_lead, t_lead, mu_leadL+mu_biasL, damp_fac, true, true); - auto leadR0 = OneParticleBasis ("R", L_lead, t_lead, mu_leadR+mu_biasR, damp_fac, false, true);*/ - psi = get_non_inter_ground_state (leadL, leadR, scatterer, sites, mu_leadL+mu_biasL, mu_device+mu_biasS, mu_leadR+mu_biasR, to_glob); - /*auto ampo0 = get_ampo_tight_binding (leadL0, leadR0, scatterer, sites, para, to_glob); - auto H0 = toMPO (ampo0); - auto sweeps0 = iut::Read_sweeps (infile, "DMRG_sweeps"); - dmrg (psi, H0, sweeps0, {"WriteDim",WriteDim});*/ - } - // ********************************* - psi.position(1); - - // Check initial energy - cout << "Initial energy = " << inner (psi,H,psi) << endl; - } - else - { - readAll (read_dir+"/"+read_file, psi, H, para, args_basis, step, to_glob, to_loc); - sites = Fermion (siteInds(psi)); - } - // -- End of initialization -- - - - // -- Observer -- - auto obs = TDVPObserver (sites, psi); - // Current MPO - auto jmpoL = get_current_mpo (sites, leadL, leadL, -2, -1, to_glob); - auto jmpoR = get_current_mpo (sites, leadR, leadR, 1, 2, to_glob); - - // -- Time evolution -- - cout << "Start time evolution" << endl; - cout << sweeps << endl; - psi.position(1); - Real en, err; - - Args args_tdvp_expansion = {"Cutoff",globExpanCutoff, "Method","DensityMatrix", - "KrylovOrd",globExpanKrylovDim, "DoNormalize",true, "Quiet",true}; - Args args_tdvp = {"Quiet",true,"NumCenter",NumCenter,"DoNormalize",true,"Truncate",Truncate, - "UseSVD",UseSVD,"SVDmethod",SVDmethod,"WriteDim",WriteDim,"mixNumCenter",mixNumCenter}; - - - LocalMPO PH (H, args_tdvp); - while (step <= time_steps) - { - cout << "step = " << step << endl; - - // Subspace expansion - if (maxLinkDim(psi) < sweeps.mindim(1) or (step < globExpanN and (step-1) % globExpanItv == 0)) - { - timer["glob expan"].start(); - addBasis (psi, H, globExpanHpsiCutoff, globExpanHpsiMaxDim, args_tdvp_expansion); - PH.reset(); - timer["glob expan"].stop(); - } - - // Time evolution - timer["tdvp"].start(); - TDVPWorker (psi, PH, 1_i*dt, sweeps, obs, args_tdvp); - timer["tdvp"].stop(); - auto d1 = maxLinkDim(psi); - - // Measure currents by MPO - timer["current mps"].start(); - auto jL = get_current (jmpoL, psi); - auto jR = get_current (jmpoR, psi); - cout << "\tI L/R = " << jL << " " << jR << endl; - timer["current mps"].stop(); - - step++; - if (write) - { - timer["write"].start(); - writeAll (write_dir+"/"+write_file, psi, H, para, args_basis, step, to_glob, to_loc); - timer["write"].stop(); - } - } - timer.print(); - return 0; -} diff --git a/kbasis/tdvp.h b/kbasis/tdvp.h deleted file mode 100644 index ad9ab35..0000000 --- a/kbasis/tdvp.h +++ /dev/null @@ -1,420 +0,0 @@ -#ifndef __ITENSOR_TDVP_H -#define __ITENSOR_TDVP_H - -#include "itensor/iterativesolvers.h" -#include "itensor/mps/localmposet.h" -#include "itensor/mps/sweeps.h" -#include "itensor/mps/DMRGObserver.h" -#include "itensor/util/cputime.h" - -namespace itensor { - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& PH, - Cplx t, - const Sweeps& sweeps, - const Args& args = Args::global()); - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& PH, - Cplx t, - const Sweeps& sweeps, - DMRGObserver & obs, - Args args = Args::global()); - -// -// Available TDVP methods: -// second order integrator: sweep left-to-right and right-to-left -// - -// -//TDVP with an MPO -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - const Sweeps& sweeps, - const Args& args = Args::global()) - { - LocalMPO PH(H,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,args); - return energy; - } - -// -//TDVP with an MPO and custom DMRGObserver -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - const Sweeps& sweeps, - DMRGObserver & obs, - const Args& args = Args::global()) - { - LocalMPO PH(H,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - -// -//TDVP with an MPO and boundary tensors LH, RH -// LH - H1 - H2 - ... - HN - RH -//(ok if one or both of LH, RH default constructed) -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - ITensor const& LH, - ITensor const& RH, - const Sweeps& sweeps, - const Args& args = Args::global()) - { - LocalMPO PH(H,LH,RH,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,args); - return energy; - } - -// -//TDVP with an MPO and boundary tensors LH, RH -//and a custom observer -// -Real inline -tdvp(MPS & psi, - MPO const& H, - Cplx t, - ITensor const& LH, - ITensor const& RH, - const Sweeps& sweeps, - DMRGObserver& obs, - const Args& args = Args::global()) - { - LocalMPO PH(H,LH,RH,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - -// -//TDVP with a set of MPOs (lazily summed) -//(H vector is 0-indexed) -// -Real inline -tdvp(MPS& psi, - std::vector const& Hset, - Cplx t, - const Sweeps& sweeps, - const Args& args = Args::global()) - { - LocalMPOSet PH(Hset,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,args); - return energy; - } - -// -//TDVP with a set of MPOs and a custom DMRGObserver -//(H vector is 0-indexed) -// -Real inline -tdvp(MPS & psi, - std::vector const& Hset, - Cplx t, - const Sweeps& sweeps, - DMRGObserver& obs, - const Args& args = Args::global()) - { - LocalMPOSet PH(Hset,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - - -// -// TDVPWorker -// - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& PH, - Cplx t, - Sweeps const& sweeps, - Args const& args) - { - DMRGObserver obs(psi,args); - Real energy = TDVPWorker(psi,PH,t,sweeps,obs,args); - return energy; - } - -vector reach_max_dim (const MPS& psi, int maxdim) -{ - int N = length(psi); - vector re (N); - for(int b = 1; b < N; b++) - { - int m = dim (rightLinkIndex (psi, b)); - bool reach_max = (m >= maxdim ? true : false); - re.at(b) = reach_max; - } - return re; -} - -template -Real -TDVPWorker(MPS & psi, - LocalOpT& H, - Cplx t, - Sweeps const& sweeps, - DMRGObserver& obs, - Args args) -{ - // Truncate blocks of degenerate singular values (or not) - args.add("RespectDegenerate",args.getBool("RespectDegenerate",true)); - - const bool silent = args.getBool("Silent",false); - if(silent) - { - args.add("Quiet",true); - args.add("PrintEigs",false); - args.add("NoMeasure",true); - args.add("DebugLevel",-1); - } - const bool quiet = args.getBool("Quiet",false); - const int debug_level = args.getInt("DebugLevel",(quiet ? -1 : 0)); - const bool mixNumCenter = args.getBool("mixNumCenter",false); - - const int N = length(psi); - Real energy = NAN; - - auto halfSweep = args.getString("HalfSweep",""); - if (halfSweep == "toLeft") - psi.position(N); - else - psi.position(1); - - args.add("DebugLevel",debug_level); - - bool write_to_disk = false; - for(int sw = 1; sw <= sweeps.nsweep(); ++sw) - { - int numCenter = args.getInt("NumCenter",2); - - args.add("Truncate",true); - - cpu_time sw_time; - args.add("Sweep",sw); - args.add("NSweep",sweeps.nsweep()); - args.add("Cutoff",sweeps.cutoff(sw)); - args.add("MinDim",sweeps.mindim(sw)); - args.add("MaxDim",sweeps.maxdim(sw)); - args.add("MaxIter",sweeps.niter(sw)); - - vector is_maxdim; - if (mixNumCenter) - { - is_maxdim = reach_max_dim (psi, args.getInt("MaxDim")); - } - - if (!write_to_disk and maxLinkDim (psi) >= args.getInt("WriteDim")) - write_to_disk = true; - if(!H.doWrite() - && args.defined("WriteDim") - && sweeps.maxdim(sw) >= args.getInt("WriteDim") - && write_to_disk) - { - if(!quiet) - { - println("\nTurning on write to disk, write_dir = ", - args.getString("WriteDir","./")); - } - - //psi.doWrite(true); - H.doWrite(true,args); - } - - // 0, 1 and 2-site wavefunctions - ITensor phi0,phi1; - Spectrum spec; - for(int b = 1, ha = 1; ha <= 2; ) - { - if (halfSweep == "toRight" && ha == 2) - continue; - else if (halfSweep == "toLeft" && ha == 1) - continue; - - if(!quiet) - printfln("Sweep=%d, HS=%d, Bond=%d/%d",sw,ha,b,(N-1)); - - // Forward propagation - H.numCenter(numCenter); - H.position(b,psi); - - if(numCenter == 2) - phi1 = psi(b)*psi(b+1); - else if(numCenter == 1) - phi1 = psi(b); - - applyExp(H,phi1,-t/2,args); - - if(args.getBool("DoNormalize",true)) - phi1 /= norm(phi1); - - if(numCenter == 2) - spec = psi.svdBond(b,phi1,(ha==1 ? Fromleft : Fromright),H,args); - else if(numCenter == 1) - psi.ref(b) = phi1; - - // Calculate energy - ITensor H_phi1; - H.product(phi1,H_phi1); - energy = real(eltC(dag(phi1)*H_phi1)); - - - // mixed Nc - if (mixNumCenter) - { - int maxdim = sweeps.maxdim(sw); - if (ha == 1) - { - if (numCenter == 2 and b < N-1 and !is_maxdim.at(b) and is_maxdim.at(b+1)) - { - phi1 = psi(b+1); - numCenter = 1; - b += 1; - } - } - else if (ha == 2) - { - if (numCenter == 2 and b > 1 and !is_maxdim.at(b) and is_maxdim.at(b-1)) - { - phi1 = psi(b); - numCenter = 1; - } - } - } - - // Backward propagation - if((ha == 1 && b+numCenter-1 != N) || (ha == 2 && b != 1)) - { - auto b1 = (ha == 1 ? b+1 : b); - - if(numCenter == 2) - { - phi0 = psi(b1); - } - else if(numCenter == 1) - { - Index l; - if(ha == 1) l = commonIndex(psi(b),psi(b+1)); - else l = commonIndex(psi(b-1),psi(b)); - ITensor U,S,V(l); - spec = svd(phi1,U,S,V,args); - psi.ref(b) = U; - phi0 = S*V; - } - - H.numCenter(numCenter-1); - H.position(b1,psi); - - applyExp(H,phi0,+t/2,args); - - if(args.getBool("DoNormalize",true)) - phi0 /= norm(phi0); - - if(numCenter == 2) - { - psi.ref(b1) = phi0; - } - if(numCenter == 1) - { - if(ha == 1) - { - psi.ref(b+1) *= phi0; - psi.leftLim(b); - psi.rightLim(b+2); - } - else - { - psi.ref(b-1) *= phi0; - psi.leftLim(b-2); - psi.rightLim(b); - } - } - - // Calculate energy - ITensor H_phi0; - H.product(phi0,H_phi0); - energy = real(eltC(dag(phi0)*H_phi0)); - } - - if(!quiet) - { - printfln(" Truncated to Cutoff=%.1E, Min_dim=%d, Max_dim=%d", - sweeps.cutoff(sw), - sweeps.mindim(sw), - sweeps.maxdim(sw) ); - printfln(" Trunc. err=%.1E, States kept: %s", - spec.truncerr(), - showDim(linkIndex(psi,b)) ); - } - - obs.lastSpectrum(spec); - - args.add("AtBond",b); - args.add("HalfSweep",ha); - args.add("Energy",energy); - args.add("Truncerr",spec.truncerr()); - - obs.measure(args); - - // Next sweep - if (mixNumCenter) - { - int maxdim = sweeps.maxdim(sw); - if (ha == 1) - { - if (numCenter == 1 and b != N and is_maxdim.at(b) and !is_maxdim.at(b+1)) - { - numCenter = 2; - } - } - else if (ha == 2) - { - if (numCenter == 1 and b > 2 and is_maxdim.at(b-1) and !is_maxdim.at(b-2)) - { - numCenter = 2; - b -= 1; - } - } - } - sweepnext(b,ha,N,{"NumCenter=",numCenter}); - } //for loop over b - - if(!silent) - { - auto sm = sw_time.sincemark(); - printfln(" Sweep %d/%d CPU time = %s (Wall time = %s)", - sw,sweeps.nsweep(),showtime(sm.time),showtime(sm.wall)); - } - - if(obs.checkDone(args)) break; - - } //for loop over sw - - if(args.getBool("DoNormalize",true)) - { - //if(numCenter==1) psi.position(1); - psi.normalize(); - } - - return energy; -} - -} //namespace itensor - -#endif diff --git a/tdvp/MyLocalmpo.h b/tdvp/MyLocalmpo.h deleted file mode 100644 index fc1a9d7..0000000 --- a/tdvp/MyLocalmpo.h +++ /dev/null @@ -1,696 +0,0 @@ -// -// Copyright 2018 The Simons Foundation, Inc. - All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef __ITENSOR_MyLocalMPO_CMC__ -#define __ITENSOR_MyLocalMPO_CMC__ -#include "itensor/mps/mpo.h" -#include "itensor/mps/localop.h" -//#include "itensor/util/print_macro.h" - -namespace itensor { - -// -// The MyLocalMPO class projects an MPO -// into the reduced Hilbert space of -// some number of sites of an MPS. -// (The default is 2 sites.) -// -// .----...--- ----...--. -// | | | | | | | -// W1-W2-..Wj-1 - Wj - Wj+1 -- Wj+2..-WN -// | | | | | | | -// '----...--- ----...--' -// -// -// Here the W's are the site tensors -// of the MPO "Op" and the method position(j,psi) -// has been called using the MPS 'psi' as a basis -// for the projection. -// -// This results in an unprojected region of -// num_center sites starting at site j. -// - -class MyLocalMPO - { - public: - - // - // Constructors - // - - MyLocalMPO(); - - // - //Regular case where H is an MPO for a finite system - // - MyLocalMPO(MPO const& H, - Args const& args = Args::global()); - - // - //Use an MPS instead of an MPO. Equivalent to using an MPO - //of the outer product |Psi>length()+1; - } - - ITensor const& - L() const { return PH_[LHlim_]; } - // Replace left edge tensor at current bond - void - L(ITensor const& nL) { PH_[LHlim_] = nL; } - // Replace left edge tensor bordering site j - // (so that nL includes sites < j) - void - L(int j, ITensor const& nL); - - - ITensor const& - R() const { return PH_[RHlim_]; } - // Replace right edge tensor at current bond - void - R(ITensor const& nR) { PH_[RHlim_] = nR; } - // Replace right edge tensor bordering site j - // (so that nR includes sites > j) - void - R(int j, ITensor const& nR); - - - ITensor const& L (int i) const { return PH_[i-1]; } - ITensor const& R (int i) const { return PH_[i+1]; } - void setL (int i, const ITensor& L) { PH_[i-1] = L; } - void setR (int i, const ITensor& R) { PH_[i+1] = R; } - - const MPO& - H() const - { - if(Op_ == 0) - Error("MyLocalMPO is null or contains an MPS"); - return *Op_; - } - - int - numCenter() const { return nc_; } - void - numCenter(int val) - { - if(val < 0 || val > 2) Error("numCenter must be set 0 or 1 or 2"); - nc_ = val; - lop_.numCenter(val); - } - - size_t - size() const { return lop_.size(); } - - explicit operator bool() const { return Op_ != 0 || Psi_ != 0; } - - bool - doWrite() const { return do_write_; } - void - doWrite(bool val, - Args const& args = Args::global()) - { - if(Psi_ != 0) Error("Write to disk not yet supported for MyLocalMPO initialized with an MPS"); - if(!do_write_ && (val == true)) - { - initWrite(args); - } - do_write_ = val; - } - - std::string const& - writeDir() const { return writedir_; } - - int - leftLim() const { return LHlim_; } - - int - rightLim() const { return RHlim_; } - - void - setLHlim(int val); - - void - setRHlim(int val); - - - const ITensor& operator() (int i) const { return PH_.at(i); } - - private: - - ///////////////// - // - // Data Members - // - - const MPO* Op_; - std::vector PH_; - int LHlim_,RHlim_; // LHlim_ will be nc-1; RHlim_ will be nc+Nuc - int nc_; - - LocalOp lop_; - - bool do_write_ = false; - std::string writedir_ = "./"; - - const MPS* Psi_; - - // - ///////////////// - - void - makeL(const MPS& psi, int k); - - void - makeR(const MPS& psi, int k); - - void - initWrite(Args const& args); - - std::string - PHFName(int j) const - { - return format("%s/PH_%03d",writedir_,j); - } - - }; - -inline MyLocalMPO:: -MyLocalMPO() - : Op_(0), - LHlim_(-1), - RHlim_(-1), - nc_(2), - Psi_(0) - { } - -inline MyLocalMPO:: -MyLocalMPO(const MPO& H, - const Args& args) - : Op_(&H), - PH_(H.length()+2), - LHlim_(0), - RHlim_(H.length()+1), - nc_(2), - Psi_(0) - { - if(args.defined("NumCenter")) - numCenter(args.getInt("NumCenter")); - } - -inline MyLocalMPO:: -MyLocalMPO(const MPS& Psi, - const Args& args) - : Op_(0), - PH_(Psi.length()+2), - LHlim_(0), - RHlim_(Psi.length()+1), - nc_(2), - Psi_(&Psi) - { - if(args.defined("NumCenter")) - numCenter(args.getInt("NumCenter")); - } - -inline MyLocalMPO:: -MyLocalMPO(const ITensor& LH, const ITensor& RH, - const Args& args) - : Op_(0), - PH_(2), - LHlim_(0), - RHlim_(1), - nc_(0), - Psi_(0) - { - PH_[0] = LH; - PH_[1] = RH; - lop_.update(L(), R());//nc_ must be set to 0 in this case - } - -inline MyLocalMPO:: -MyLocalMPO(const MPO& H, - const ITensor& LH, const ITensor& RH, - const Args& args) - : Op_(&H), - PH_(H.length()+2), - LHlim_(0), - RHlim_(H.length()+1), - nc_(2), - Psi_(0) - { - PH_[0] = LH; - PH_[H.length()+1] = RH; - if(H.length() == 1) - lop_.update(Op_->A(1), L(), R()); - else if(H.length() == 2) - lop_.update(Op_->A(1), Op_->A(2), L(), R()); - if(args.defined("NumCenter")) - numCenter(args.getInt("NumCenter")); - } - -inline MyLocalMPO:: -MyLocalMPO(MPS const& Psi, - ITensor const& LP, - ITensor const& RP, - Args const& args) - : Op_(0), - PH_(Psi.length()+2), - LHlim_(0), - RHlim_(Psi.length()+1), - nc_(2), - Psi_(&Psi) - { - PH_[0] = LP; - PH_[Psi.length()+1] = RP; - if(args.defined("NumCenter")) - numCenter(args.getInt("NumCenter")); - } - -inline MyLocalMPO:: -MyLocalMPO(MPO const& H, - ITensor const& LH, - int LHlim, - ITensor const& RH, - int RHlim, - Args const& args) - : Op_(&H), - PH_(H.length()+2), - LHlim_(LHlim), - RHlim_(RHlim), - nc_(2), - Psi_(0) - { - PH_.at(LHlim) = LH; - PH_.at(RHlim) = RH; - if(H.length() == 1) - lop_.update(Op_->A(1), L(), R()); - if(H.length() == 2) - lop_.update(Op_->A(1), Op_->A(2), L(), R()); - if(args.defined("NumCenter")) numCenter(args.getInt("NumCenter")); - } - -void inline MyLocalMPO:: -product(ITensor const& phi, - ITensor& phip) const - { - if(Op_ != 0) - { - lop_.product(phi,phip); - } - else - if(Psi_ != 0) - { - int b = position(); - - ITensor othr; - if(nc_ == 2) - { - othr = (!L() ? dag(prime(Psi_->A(b),"Link")) : L()*dag(prime(Psi_->A(b),"Link"))); - othr *= (!R() ? dag(prime(Psi_->A(b+1),"Link")) : R()*dag(prime(Psi_->A(b+1),"Link"))); - } - else if(nc_ == 1) - { - othr = (!L() ? dag(prime(Psi_->A(b),"Link")) : L()*dag(prime(Psi_->A(b),"Link"))); - if(R()) othr *= R(); - } - else if(nc_ == 0) - { - if(!L()) - { - if(!R()) Error("MyLocalMPO: Empty L() and R() in function product"); - else othr = R(); - } - else - { - othr = L(); - if(R()) othr *= R(); - } - } - - auto z = (othr*phi).eltC(); - - phip = dag(othr); - phip *= z; - } - else - { - Error("MyLocalMPO is null"); - } - } - -void inline MyLocalMPO:: -L(int j, ITensor const& nL) - { - if(LHlim_ > j-1) setLHlim(j-1); - PH_[LHlim_] = nL; - } - -void inline MyLocalMPO:: -R(int j, ITensor const& nR) - { - if(RHlim_ < j+1) setRHlim(j+1); - PH_[RHlim_] = nR; - } - -inline void MyLocalMPO:: -position(int b, MPS const& psi) - { - if(!(*this)) Error("MyLocalMPO is null"); - - makeL(psi,b-1); - makeR(psi,b+nc_); - - setLHlim(b-1); //not redundant since LHlim_ could be > b-1 - setRHlim(b+nc_); //not redundant since RHlim_ could be < b+nc_ - -#ifdef DEBUG - if(nc_ != 2 && nc_ != 1 && nc_ != 0) - { - Error("LocalOp only supports 0 and 1 and 2 center sites currently"); - } -#endif - - if(Op_ != 0) //normal MPO case - { - if(nc_ == 2) - lop_.update(Op_->A(b), Op_->A(b+1), L(), R()); - else if(nc_ == 1) - lop_.update(Op_->A(b), L(), R()); - else if(nc_ == 0) - lop_.update(L(),R()); - } - } - -int inline MyLocalMPO:: -position() const - { - if(RHlim_-LHlim_ != (nc_+1)) - { - throw ITError("MyLocalMPO position not set"); - } - return LHlim_+1; - } - -inline void MyLocalMPO:: -shift(int j, - Direction dir, - ITensor const& A) - { - if(!(*this)) Error("MyLocalMPO is null"); - -#ifdef DEBUG - if(nc_ != 2 && nc_ != 1 && nc_ != 0) - { - Error("LocalOp only supports 0 and 1 and 2 center sites currently"); - } -#endif - - if(dir == Fromleft) - { - if((j-1) != LHlim_) - { - std::cout << "j-1 = " << (j-1) << ", LHlim = " << LHlim_ << std::endl; - Error("Can only shift at LHlim"); - } - auto& E = PH_.at(LHlim_); - auto& nE = PH_.at(j); - nE = E * A; - nE *= Op_->A(j); - nE *= dag(prime(A)); - setLHlim(j); - setRHlim(j+nc_+1); - - if(nc_ == 2) - lop_.update(Op_->A(j+1), Op_->A(j+2), L(), R()); - else if(nc_ == 1) - lop_.update(Op_->A(j+1), L(), R()); - else if(nc_ == 0) - lop_.update(L(), R()); - } - else //dir == Fromright - { - if((j+1) != LHlim_) - { - std::cout << "j+1 = " << (j+1) << ", RHlim_ = " << RHlim_ << std::endl; - Error("Can only shift at RHlim_"); - } - auto& E = PH_.at(RHlim_); - auto& nE = PH_.at(j); - nE = E * A; - nE *= Op_->A(j); - nE *= dag(prime(A)); - setLHlim(j-nc_-1); - setRHlim(j); - - if(nc_ == 2) - lop_.update(Op_->A(j-2), Op_->A(j-1), L(), R()); - else if(nc_ == 1) - lop_.update(Op_->A(j-1), L(), R()); - else if(nc_ == 0) - lop_.update(L(), R()); - } - } - -inline void MyLocalMPO:: -makeL(MPS const& psi, int k) - { - if(!PH_.empty()) - { - if(Op_ == 0) //Op is actually an MPS - { - while(LHlim_ < k) - { - auto ll = LHlim_; - PH_.at(ll+1) = (!PH_.at(ll) ? psi(ll+1) : PH_[ll]*psi(ll+1)); - PH_[ll+1] *= dag(prime(Psi_->A(ll+1),"Link")); - setLHlim(ll+1); - } - } - else //normal MPO case - { - while(LHlim_ < k) - { - auto ll = LHlim_; - if(PH_.at(ll)) - { - PH_.at(ll+1) = PH_.at(ll)*psi(ll+1); - } - else - { - PH_.at(ll+1) = psi(ll+1); - } - PH_.at(ll+1) *= Op_->A(ll+1); - PH_.at(ll+1) *= dag(prime(psi(ll+1))); - setLHlim(ll+1); -assert (order(PH_.at(ll+1)) == 3); - } - } - } - } - -inline void MyLocalMPO:: -makeR(MPS const& psi, int k) - { - if(!PH_.empty()) - { - if(Op_ == 0) //Op is actually an MPS - { - while(RHlim_ > k) - { - const int rl = RHlim_; - PH_.at(rl-1) = (!PH_.at(rl) ? psi(rl-1) : PH_[rl]*psi(rl-1)); - PH_[rl-1] *= dag(prime(Psi_->A(rl-1),"Link")); - setRHlim(rl-1); - } - } - else //normal MPO case - { - while(RHlim_ > k) - { - auto rl = RHlim_; - //printfln(" Making environment with rl=%d (using H[%d])",rl,rl-1); - //Print(PH_.at(rl)); - //Print(Op_->A(rl-1)); - //Print(psi(rl-1)); - if(PH_.at(rl)) - { - PH_.at(rl-1) = PH_.at(rl)*psi(rl-1); - } - else - { - PH_.at(rl-1) = psi(rl-1); - } - PH_.at(rl-1) *= Op_->A(rl-1); - PH_.at(rl-1) *= dag(prime(psi(rl-1))); - //printfln("PH[%d] = \n%s",rl-1,PH_.at(rl-1)); - //PAUSE - setRHlim(rl-1); - } - } - } - } - -void inline MyLocalMPO:: -setLHlim(int val) - { - if(!do_write_) - { - LHlim_ = val; - return; - } - - if(LHlim_ != val && PH_.at(LHlim_)) - { - writeToFile(PHFName(LHlim_),PH_.at(LHlim_)); - PH_.at(LHlim_) = ITensor(); - } - LHlim_ = val; - if(LHlim_ < 1) - { - //Set to null tensor and return - PH_.at(LHlim_) = ITensor(); - return; - } - if(!PH_.at(LHlim_)) - { - std::string fname = PHFName(LHlim_); - readFromFile(fname,PH_.at(LHlim_)); - } - } - -void inline MyLocalMPO:: -setRHlim(int val) - { - if(!do_write_) - { - RHlim_ = val; - return; - } - - if(RHlim_ != val && PH_.at(RHlim_)) - { - writeToFile(PHFName(RHlim_),PH_.at(RHlim_)); - PH_.at(RHlim_) = ITensor(); - } - RHlim_ = val; - if(RHlim_ > Op_->length()) - { - //Set to null tensor and return - PH_.at(RHlim_) = ITensor(); - return; - } - if(!PH_.at(RHlim_)) - { - std::string fname = PHFName(RHlim_); - readFromFile(fname,PH_.at(RHlim_)); - } - } - -void inline MyLocalMPO:: -initWrite(Args const& args) - { - auto basedir = args.getString("WriteDir","./"); - writedir_ = mkTempDir("PH",basedir); - } - -} //namespace itensor - - -#endif diff --git a/tdvp/MyObserver.h b/tdvp/MyObserver.h deleted file mode 100644 index 686a781..0000000 --- a/tdvp/MyObserver.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef __MYOBSERVER_H_CMC__ -#define __MYOBSERVER_H_CMC__ -#include -#include "itensor/all.h" -#include "Entanglement.h" - -class MyObserver : public DMRGObserver -{ - public: - MyObserver (const Fermion& sites, const MPS& psi, const Args& args = Args::global()) - : DMRGObserver (psi, args) - , _sites (sites) - , _ns (length(psi)+1,0.) - , _Npar (0.) - , _sites_obs (2, args) - , _specs (length(psi)) - { - _write = args.getBool ("Write",false); - _out_dir = args.getString("out_dir","."); - - // Current operator - AutoMPO ampo (_sites_obs); - ampo += -2_i,"Cdag",1,"C",2; - auto mpo = toMPO (ampo); - _current_op = mpo(1) * mpo(2); - } - - void measure (const Args& args); - - Real Npar () const { return _Npar; } - const Spectrum& spec (int i) const { return _specs.at(i); } - - private: - bool _write; - string _out_dir; // empty string "" if not write - Fermion _sites; - - vector _ns; - Real _Npar; - - // Observables - Fermion _sites_obs; - ITensor _current_op; - vector _specs; -}; - -inline Real Onsite_mea (const ITensor& A, const ITensor& op) -{ - ITensor re = A * op; - re.noPrime ("Site"); - re *= dag(A); - return iut::toReal (re); -} - -void MyObserver :: measure (const Args& args) -{ - DMRGObserver::measure (args); - - cout << scientific << setprecision(14); - // Define your measurements below - // Call psi() to access the MPS - // - auto N = length(psi()); - auto b = args.getInt("AtBond"); - auto sw = args.getInt("Sweep"); - auto ha = args.getInt("HalfSweep"); - auto energy = args.getReal("Energy",0); - - if (b != N) - _specs.at(b) = spectrum(); - - int oc = orthoCenter(psi()); - int nc = args.getInt("NumCenter"); - // measure during the second half of sweep - if ((nc == 2 && oc == N) || ha == 2) - { - // Density - ITensor n_op = _sites.op("N",oc); - Real ni = Onsite_mea (psi().A(oc), n_op); - cout << "\tn " << oc << " = " << ni << endl; - - // Current - if (oc != N) - { - auto const& i1 = _sites_obs (1); - auto const& i2 = _sites_obs (2); - auto const& j1 = _sites (oc); - auto const& j2 = _sites (oc+1); - auto Jop = replaceInds (_current_op, {i1, prime(i1), i2, prime(i2)}, {j1, prime(j1), j2, prime(j2)}); - - auto phi = psi()(oc) * psi()(oc+1); - auto phiJ = noPrime (phi * Jop, "Site"); - Cplx j = eltC (phiJ * dag(phi)); - cout << "\tcurrent " << oc << " " << oc+1 << " = " << j << endl; - } - - // Entanglement entropy - Real S = iut::EntangEntropy (spectrum()); - cout << "EE " << oc << " = " << S << endl; - } - - if (oc == 1 && ha == 2) - { - if (_write) - { - cout << "write MPS" << endl; - writeToFile (_out_dir+"/psi.mps", psi()); - } - } -} - -#endif diff --git a/tdvp/TDVPWorker.h b/tdvp/TDVPWorker.h deleted file mode 100644 index f3f930e..0000000 --- a/tdvp/TDVPWorker.h +++ /dev/null @@ -1,203 +0,0 @@ -#ifndef __ITENSOR_TDVP_H -#define __ITENSOR_TDVP_H - -#include "itensor/iterativesolvers.h" -#include "itensor/mps/localmposet.h" -#include "itensor/mps/sweeps.h" -#include "itensor/mps/DMRGObserver.h" -#include "itensor/util/cputime.h" -#include "MyLocalmpo.h" - -template -void back_propagate -(int ha, int b, int numCenter, TimeType t, MPS& psi, const ITensor& phi1, MyLocalMPO& H, Spectrum& spec, const Args& args) -{ - auto b1 = (ha == 1 ? b+1 : b); - - ITensor phi0; - if(numCenter == 2) - { - phi0 = psi(b1); - } - else if(numCenter == 1) - { - Index l; - if(ha == 1) l = commonIndex(psi(b),psi(b+1)); - else l = commonIndex(psi(b-1),psi(b)); - ITensor U,S,V(l); - spec = svd(phi1,U,S,V,args); - psi.ref(b) = U; - phi0 = S*V; - } - - H.numCenter(numCenter-1); - H.position(b1,psi); - - applyExp(H,phi0,t/2,args); - - if(args.getBool("DoNormalize",true)) - phi0 /= norm(phi0); - - if(numCenter == 2) - { - psi.ref(b1) = phi0; - psi.leftLim (b1-1); - psi.rightLim (b1+1); - } - if(numCenter == 1) - { - if(ha == 1) - { - psi.ref(b+1) *= phi0; - psi.leftLim(b); - psi.rightLim(b+2); - } - else - { - psi.ref(b-1) *= phi0; - psi.leftLim(b-2); - psi.rightLim(b); - } - } -} - - -template -void -TDVPWorker(MPS & psi, - MyLocalMPO& H, - Cplx t, - Sweeps const& sweeps, - DMRGObserver& obs, - Args args) -{ - // Truncate blocks of degenerate singular values (or not) - args.add("RespectDegenerate",args.getBool("RespectDegenerate",true)); - - const bool silent = args.getBool("Silent",false); - if(silent) - { - args.add("Quiet",true); - args.add("PrintEigs",false); - args.add("NoMeasure",true); - args.add("DebugLevel",-1); - } - const bool quiet = args.getBool("Quiet",false); - const int debug_level = args.getInt("DebugLevel",(quiet ? -1 : 0)); - const int numCenter = args.getInt("NumCenter",2); - if(numCenter != 1) - args.add("Truncate",args.getBool("Truncate",true)); - else - args.add("Truncate",args.getBool("Truncate",false)); - - const int N = length(psi); - - int oc = orthoCenter (psi); - - args.add("DebugLevel",debug_level); - - // The bonds to sweep - vector bs; - if (dir == Fromleft) - { - for(int i = oc; i < N; i++) - bs.push_back (i); - if (numCenter == 1) - bs.push_back (N); - } - else - { - for(int i = oc-numCenter+1; i >= 1; i--) - bs.push_back (i); - } - - for(int sw = 1; sw <= sweeps.nsweep(); ++sw) - { - cpu_time sw_time; - args.add("Sweep",sw); - args.add("NSweep",sweeps.nsweep()); - args.add("Cutoff",sweeps.cutoff(sw)); - args.add("MinDim",sweeps.mindim(sw)); - args.add("MaxDim",sweeps.maxdim(sw)); - args.add("MaxIter",sweeps.niter(sw)); - - if(!H.doWrite() - && args.defined("WriteDim") - && sweeps.maxdim(sw) >= args.getInt("WriteDim")) - { - if(!quiet) - { - println("\nTurning on write to disk, write_dir = ", - args.getString("WriteDir","./")); - } - - //psi.doWrite(true); - H.doWrite(true,args); - } - - // 0, 1 and 2-site wavefunctions - ITensor phi0,phi1; - Spectrum spec; - const int ha = (dir == Fromleft ? 1 : 2); - for(int b : bs) - { - if(!quiet) - printfln("Sweep=%d, HS=%d, Bond=%d/%d",sw,ha,b,(N-1)); - - H.numCenter(numCenter); - H.position(b,psi); - - if(numCenter == 2) - phi1 = psi(b)*psi(b+1); - else if(numCenter == 1) - phi1 = psi(b); - - applyExp(H,phi1,-t/2,args); - - if(args.getBool("DoNormalize",true)) - phi1 /= norm(phi1); - - if(numCenter == 2) - spec = psi.svdBond(b,phi1,(ha==1 ? Fromleft : Fromright),H,args); - else if(numCenter == 1) - psi.ref(b) = phi1; - - if((ha == 1 && b+numCenter-1 != N) || (ha == 2 && b != 1)) - { - back_propagate (ha, b, numCenter, t, psi, phi1, H, spec, args); - } - - if(!quiet) - { - printfln(" Truncated to Cutoff=%.1E, Min_dim=%d, Max_dim=%d", - sweeps.cutoff(sw), - sweeps.mindim(sw), - sweeps.maxdim(sw) ); - printfln(" Trunc. err=%.1E, States kept: %s", - spec.truncerr(), - showDim(linkIndex(psi,b)) ); - } - - obs.lastSpectrum(spec); - args.add("AtBond",b); - args.add("HalfSweep",ha); - args.add("Truncerr",spec.truncerr()); - obs.measure (args); - - } //for loop over b - - if(!silent) - { - auto sm = sw_time.sincemark(); - printfln(" Sweep %d/%d CPU time = %s (Wall time = %s)", - sw,sweeps.nsweep(),showtime(sm.time),showtime(sm.wall)); - } - } //for loop over sw - - if(args.getBool("DoNormalize",true)) - { - psi.normalize(); - } -} - -#endif diff --git a/tdvp/tdvp.cc b/tdvp/tdvp.cc deleted file mode 100644 index 0810a7c..0000000 --- a/tdvp/tdvp.cc +++ /dev/null @@ -1,583 +0,0 @@ -#include "itensor/all.h" -#include "ReadInput.h" -#include "IUtility.h" -#include "TDVPWorker.h" -#include "MyObserver.h" -#include "MyLocalmpo.h" -#include "GlobalIndices.h" -#include "GenMPO.h" -#include "FixedPointTensor.h" -#include "uGauge.h" -#include "iTDVP.h" -#include "MPSUtility.h" -#include "Entanglement.h" -#include "GeneralUtility.h" -using namespace itensor; -using namespace std; - -vector get_site_inds (const MPS& psi) -{ - int N = length(psi); - vector sites (N); - for(int i = 1; i <= N; i++) - { - auto ii = findIndex (psi(i), "Site"); - sites.at(i-1) = ii; - } - return sites; -} - -Fermion get_SiteSet (const MPS& mps) -{ - auto inds = get_site_inds (mps); - return Fermion (inds); -} - -ITensor get_W (const MPO& H, int i) -{ - auto W = H(i); - auto iWl = leftLinkIndex (H, i); - auto iWr = rightLinkIndex (H, i); - auto is = findIndex (W, "Site,0"); - - auto is2 = global::IS.is(); - auto iwl2 = global::IS.iwl(); - auto iwr2 = global::IS.iwr(); - auto is2pr = prime(is2); - W.replaceInds ({is, prime(is), iWl, iWr}, {is2, is2pr, iwl2, iwr2}); - return W; -} - -inline ITensor to_itdvp_AR (const ITensor& AR, const Index& iAl, const Index& iAr) -{ - auto ir = global::IS.ir(); - auto is = global::IS.is(); - auto iAs = findIndex (AR, "Site"); - auto ARre = replaceInds (AR, {iAl, iAr, iAs}, {prime(ir,2), ir, is}); - global::IS.check ("AR",ARre); - return ARre; -} - -inline ITensor to_itdvp_AL (const ITensor& AL, const Index& iAl, const Index& iAr) -{ - auto il = global::IS.il(); - auto is = global::IS.is(); - auto iAs = findIndex (AL, "Site"); - auto ALre = replaceInds (AL, {iAl, iAr, iAs}, {il, prime(il,2), is}); - global::IS.check ("AL",ALre); - return ALre; -} - -ITensor get_L (const ITensor& AR, const Index& iAl, const Index& iAr, Real crit=1e-15) -{ - auto il = global::IS.il(); - auto ir = global::IS.ir(); - auto is = global::IS.is(); - auto iAs = findIndex (AR, "Site"); - auto ARt = replaceInds (AR, {iAl, iAr, iAs}, {il, prime(il,2), is}); - - auto [AL, C] = Orthogonalize (ARt, crit); - C.replaceInds ({il}, {ir}); - - auto Cdag = dag(C); - Cdag.prime (ir); - - auto L = C * Cdag; - global::IS.check ("L",L); - assert (check_leading_eigen (AR, AR, L)); - return L; -} - -ITensor get_R (const ITensor& AL, const Index& iAl, const Index& iAr, Real crit=1e-15) -{ - auto il = global::IS.il(); - auto is = global::IS.is(); - auto iAs = findIndex (AL, "Site"); - auto ALt = replaceInds (AL, {iAl, iAr, iAs}, {prime(il,2), il, is}); - - auto [AR, C] = Orthogonalize (ALt, crit); - C.mapPrime(1,2); - - auto Cdag = dag(C); - Cdag.mapPrime(0,2); - - auto R = C * Cdag; - global::IS.check ("R",R); - assert (check_leading_eigen (AL, AL, R)); - return R; -} - -void -expandL -(MPS& psi, MPO& H, unique_ptr& PH, int n, int NumCenter, - const ITensor& AL, const Index& iALl, const Index& iALr) -{ - int oc = orthoCenter (psi); - PH->position (oc, psi); - - int N = length (psi); - int N2 = N + n; - - auto WL = H(1); - - auto iALs = findIndex (AL, "Site"); - auto iWLl = iut::leftIndex (H, 1); - auto iWLr = iut::rightIndex (H, 1); - auto iWLs = findIndex (H(1), "Site,0"); - - // Set MPS and MPO - MPS psi2 (N2); - MPO H2 (N2); - // Insert original tensors - for(int i = 1; i <= N; i++) - { - psi2.ref(n+i) = psi(i); - H2.ref(n+i) = H(i); - } - // Insert new tensor to the left - auto il0 = iut::leftIndex (psi, 1); - auto iHl0 = iut::leftIndex (H, 1); - auto il = sim (il0); - auto iHl = sim (iHl0); - psi2.ref(n+1).replaceInds ({il0}, {il}); - H2.ref(n+1).replaceInds ({iHl0}, {iHl}); - for(int i = n; i >= 1; i--) - { - auto is2 = sim(iALs); - // Replace the site and the right indices - psi2.ref(i) = replaceInds (AL, {iALs, iALr}, {is2, dag(il)}); - H2.ref(i) = replaceInds (WL, {iWLs, prime(iWLs), iWLr}, {is2, prime(is2), dag(iHl)}); - il = sim (iALl); - iHl = sim (iWLl); - if (i != 1) - { - psi2.ref(i).replaceInds ({iALl}, {il}); - H2.ref(i).replaceInds ({iWLl}, {iHl}); - } - } - psi2.rightLim (n+2); - psi2.position (n+1); - psi = psi2; - H = H2; - - // Set PH - Args args = {"NumCenter",NumCenter}; - auto PH2 = make_unique (H, PH->L(1), PH->R(N), args); - for(int i = N-1; i >= 1; i--) - PH2->setR (i+n, PH->R(i)); - PH2->setRHlim (n+1+NumCenter); - PH2->position (n+1, psi); - PH = move (PH2); -} - -void -expandR -(MPS& psi, MPO& H, unique_ptr& PH, int n, int NumCenter, - const ITensor& AR, const Index& iARl, const Index& iARr) -{ - int oc = orthoCenter (psi); - PH->position (oc, psi); - - int N = length (psi); - int N2 = N + n; - - auto WR = H(N); - - auto iARs = findIndex (AR, "Site"); - auto iWRl = iut::leftIndex (H, N); - auto iWRr = iut::rightIndex (H, N); - auto iWRs = findIndex (H(N), "Site,0"); - - // Set MPS and MPO - MPS psi2 (N2); - MPO H2 (N2); - // Insert original tensors - for(int i = 1; i <= N; i++) - { - psi2.ref(i) = psi(i); - H2.ref(i) = H(i); - } - // Insert new tensors to the right - auto ir0 = iut::rightIndex (psi, N); - auto iHr0 = iut::rightIndex (H, N); - auto ir = sim (ir0); - auto iHr = sim (iHr0); - psi2.ref(N).replaceInds ({ir0}, {ir}); - H2.ref(N).replaceInds ({iHr0}, {iHr}); - for(int i = N+1; i <= N2; i++) - { - auto is2 = sim(iARs); - // Replace the site and the left indices - psi2.ref(i) = replaceInds (AR, {iARs, iARl}, {is2, dag(ir)}); - H2.ref(i) = replaceInds (WR, {iWRs, prime(iWRs), iWRl}, {is2, prime(is2), dag(iHr)}); - ir = sim (iARr); - iHr = sim (iWRr); - if (i != N2) - { - psi2.ref(i).replaceInds ({iARr}, {ir}); - H2.ref(i).replaceInds ({iWRr}, {iHr}); - } - } - psi2.leftLim (N-1); - psi2.position(N); - psi = psi2; - H = H2; - - // Set PH - Args args = {"NumCenter",NumCenter}; - auto PH2 = make_unique (H, PH->L(1), PH->R(N), args); - for(int i = 2; i <= N; i++) - PH2->setL (i, PH->L(i)); - PH2->setLHlim (N-NumCenter); - PH2->position (N, psi2); - PH = move (PH2); -} - -void get_init (const string& infile, - MPS& psi, MPO& H, Fermion& sites, - Index& il, Index& ir, - ITensor& WL, ITensor& WR, - ITensor& AL_left, ITensor& AR_left, ITensor& AC_left, ITensor& C_left, - ITensor& La_left, ITensor& Ra_left, ITensor& LW_left, ITensor& RW_left, - ITensor& AL_right, ITensor& AR_right, ITensor& AC_right, ITensor& C_right, - ITensor& La_right, ITensor& Ra_right, ITensor& LW_right, ITensor& RW_right, - int& idev_first, int& idev_last) -{ - InputGroup input (infile,"basic"); - - auto L_window = input.getInt("L_window"); - auto L_device = input.getInt("L_device"); - auto t_lead = input.getReal("t_lead"); - auto t_device = input.getReal("t_device"); - auto t_contact = input.getReal("t_contact"); - auto mu_leadL = input.getReal("mu_leadL"); - auto mu_leadR = input.getReal("mu_leadR"); - auto mu_device = input.getReal("mu_device"); - auto V_lead = input.getReal("V_lead"); - auto V_device = input.getReal("V_device"); - auto V_contact = input.getReal("V_contact"); - - auto psi_dir = input.getString("psi_dir"); - auto psi_file = input.getString("psi_file"); - auto itdvp_dir = input.getString("itdvp_dir"); - auto ErrGoal = input.getReal("ErrGoal"); - auto MaxIter_LRW = input.getInt("MaxIter_LRW"); - - // Read the iTDVP tensors - ITensor AL, AR, AC, C, LW, RW, La, Ra; - readFromFile (itdvp_dir+"/AL.itensor", AL); - readFromFile (itdvp_dir+"/AR.itensor", AR); - readFromFile (itdvp_dir+"/AC.itensor", AC); - readFromFile (itdvp_dir+"/C.itensor", C); - readFromFile (itdvp_dir+"/LW.itensor", LW); - readFromFile (itdvp_dir+"/RW.itensor", RW); - readFromFile (itdvp_dir+"/La.itensor", La); - readFromFile (itdvp_dir+"/Ra.itensor", Ra); - global::IS.read (itdvp_dir+"/global.inds"); - il = global::IS.il(); - ir = global::IS.ir(); - int dim = ir.dim(); - // Read MPS in the window - readFromFile (psi_dir+"/"+psi_file, psi); - int N = length (psi); - // Site indices - auto site_inds = get_site_inds (psi); - sites = Fermion (site_inds); - // Hamiltonian - AutoMPO ampo; - tie (ampo, idev_first, idev_last) - = t_mu_V_ampo (sites, L_device, - t_lead, t_lead, t_device, t_contact, t_contact, - mu_leadL, mu_leadR, mu_device, - V_lead, V_lead, V_device, V_contact, V_contact); - auto locamu = read_bracket_values (infile, "local_mu", 1); - auto localtV = read_bracket_values (infile, "local_tV", 1); - ampo_add_mu (ampo, locamu, true); - ampo_add_tV (ampo, localtV, true); - H = toMPO (ampo); - to_inf_mpo (H, global::IS.iwl(), global::IS.iwr()); - // Boundary tensors - cout << "Compute boundary tensors" << endl; - WL = get_W (H, 2); - WR = get_W (H, N-1); - Args args_itdvp = {"ErrGoal=",ErrGoal,"MaxIter",MaxIter_LRW}; - auto L = get_LR (C); - auto R = get_LR (C); - Real enL, enR; - tie (LW, enL) = get_LRW (AL, WL, R, La, args_itdvp); - tie (RW, enR) = get_LRW (AR, WR, L, Ra, args_itdvp); - // Check - mycheck (commonIndex (LW, H(1)), "LW and H(1) has no common Index"); - mycheck (commonIndex (RW, H(N)), "RW and H(N) has no common Index"); - mycheck (commonIndex (LW, psi(1)), "LW and psi(1) has no common Index"); - mycheck (commonIndex (RW, psi(N)), "RW and psi(N) has no common Index"); - // Left boundaries - AL_left = AL, - AR_left = AR, - AC_left = AC, - C_left = C, - La_left = La, - Ra_left = Ra, - LW_left = LW, - RW_left = RW; - // Right boundaries - AL_right = AL, - AR_right = AR, - AC_right = AC, - C_right = C, - La_right = La, - Ra_right = Ra, - LW_right = LW, - RW_right = RW; -} - -void writeAll (const string& filename, - const MPS& psi, const MPO& H, - const Index& il, const Index& ir, - const ITensor& WL, const ITensor& WR, - const ITensor& AL_left, const ITensor& AR_left, const ITensor& AC_left, const ITensor& C_left, - const ITensor& La_left, const ITensor& Ra_left, const ITensor& LW_left, const ITensor& RW_left, - const ITensor& AL_right, const ITensor& AR_right, const ITensor& AC_right, const ITensor& C_right, - const ITensor& La_right, const ITensor& Ra_right, const ITensor& LW_right, const ITensor& RW_right, - int step, int idev_first, int idev_last, bool expand, bool expand_next) -{ - ofstream ofs (filename); - write (ofs, psi); - write (ofs, H); - write (ofs, il); - write (ofs, ir); - write (ofs, WL); - write (ofs, WR); - write (ofs, AL_left); - write (ofs, AR_left); - write (ofs, AC_left); - write (ofs, C_left); - write (ofs, La_left); - write (ofs, Ra_left); - write (ofs, LW_left); - write (ofs, RW_left); - write (ofs, AL_right); - write (ofs, AR_right); - write (ofs, AC_right); - write (ofs, C_right); - write (ofs, La_right); - write (ofs, Ra_right); - write (ofs, LW_right); - write (ofs, RW_right); - write (ofs, step); - write (ofs, idev_first); - write (ofs, idev_last); - write (ofs, expand); - write (ofs, expand_next); - global::IS.write (ofs); -} - -void readAll (const string& filename, - MPS& psi, MPO& H, - Index& il, Index& ir, - ITensor& WL, ITensor& WR, - ITensor& AL_left, ITensor& AR_left, ITensor& AC_left, ITensor& C_left, - ITensor& La_left, ITensor& Ra_left, ITensor& LW_left, ITensor& RW_left, - ITensor& AL_right, ITensor& AR_right, ITensor& AC_right, ITensor& C_right, - ITensor& La_right, ITensor& Ra_right, ITensor& LW_right, ITensor& RW_right, - int& step, int& idev_first, int& idev_last, bool& expand, bool& expand_next) -{ - ifstream ifs = open_file (filename); - read (ifs, psi); - read (ifs, H); - read (ifs, il); - read (ifs, ir); - read (ifs, WL); - read (ifs, WR); - read (ifs, AL_left); - read (ifs, AR_left); - read (ifs, AC_left); - read (ifs, C_left); - read (ifs, La_left); - read (ifs, Ra_left); - read (ifs, LW_left); - read (ifs, RW_left); - read (ifs, AL_right); - read (ifs, AR_right); - read (ifs, AC_right); - read (ifs, C_right); - read (ifs, La_right); - read (ifs, Ra_right); - read (ifs, LW_right); - read (ifs, RW_right); - read (ifs, step); - read (ifs, idev_first); - read (ifs, idev_last); - read (ifs, expand); - read (ifs, expand_next); - global::IS.read (ifs); -} - -int main(int argc, char* argv[]) -{ - string infile = argv[1]; - InputGroup input (infile,"basic"); - - auto dt = input.getReal("dt"); - auto time_steps = input.getInt("time_steps"); - auto NumCenter = input.getInt("NumCenter"); - auto ConserveQNs = input.getYesNo("ConserveQNs",false); - auto expandN = input.getInt("expandN",0); - auto expand_checkN = input.getInt("expand_checkN",5); - auto expandS_crit = input.getReal("expandS_crit",1e10); - auto max_window = input.getInt("max_window",10000); - auto sweeps = iut::Read_sweeps (infile); - - auto ErrGoal = input.getReal("ErrGoal"); - auto MaxIter_LRW = input.getInt("MaxIter_LRW"); - auto UseSVD = input.getYesNo("UseSVD",true); - auto SVDmethod = input.getString("SVDMethod","gesdd"); // can be also "ITensor" - auto WriteDim = input.getInt("WriteDim"); - - auto write = input.getYesNo("write",false); - auto write_dir = input.getString("write_dir","."); - auto write_file = input.getString("write_file",""); - auto read = input.getYesNo("read",false); - auto read_dir = input.getString("read_dir","."); - auto read_file = input.getString("read_file",""); - - auto out_dir = input.getString("outdir","."); - if (write_dir == "." && out_dir != ".") - write_dir = out_dir; - - // Declare variables - MPS psi; - MPO H; - Fermion sites; - Index il, ir; - ITensor WL, WR, - AL_left, AR_left, AC_left, C_left, La_left, Ra_left, LW_left, RW_left, - AL_right, AR_right, AC_right, C_right, La_right, Ra_right, LW_right, RW_right; - bool expand = false, - expand_next = false; - int step = 1; - int idev_first, idev_last; - - // Initialize variables - if (!read) - { - get_init (infile, - psi, H, sites, il, ir, WL, WR, - AL_left, AR_left, AC_left, C_left, La_left, Ra_left, LW_left, RW_left, - AL_right, AR_right, AC_right, C_right, La_right, Ra_right, LW_right, RW_right, - idev_first, idev_last); - } - // Read variables - else - { - readAll (read_dir+"/"+read_file, - psi, H, il, ir, WL, WR, - AL_left, AR_left, AC_left, C_left, La_left, Ra_left, LW_left, RW_left, - AL_right, AR_right, AC_right, C_right, La_right, Ra_right, LW_right, RW_right, - step, idev_first, idev_last, expand, expand_next); - auto site_inds = get_site_inds (psi); - sites = Fermion (site_inds); - } - - // Args parameters - Args args_itdvp = {"ErrGoal=",ErrGoal,"MaxIter",MaxIter_LRW}; - Args args_obs = {"ConserveQNs",ConserveQNs}; - Args args_tdvp = {"Quiet",true,"NumCenter",NumCenter,"DoNormalize",true,"UseSVD",UseSVD,"SVDmethod",SVDmethod,"WriteDim",WriteDim}; - - // Observer - auto obs = make_unique (sites, psi, args_obs); - - // Effective Hamiltonian - auto PH = make_unique (H, LW_left, RW_right, args_tdvp); - - // Time evolution - cout << "Start time evolution" << endl; - cout << sweeps << endl; - psi.position(1); - Real en, err; - int N = length (psi); - - for(int i = 0; i < time_steps; i++) - { - cout << "step = " << step++ << endl; - - // Extend left edge - if (expand) - { - // Expand - expandL (psi, H, PH, expandN, NumCenter, AL_left, il, prime(il,2)); - idev_first += expandN; - idev_last += expandN; - cout << "expand left " << expandN << endl; - // Observer - sites = get_SiteSet (psi); - obs = make_unique (sites, psi, args_obs); - N = length (psi); - psi.position(1); - } - cout << "device site = " << idev_first << " " << idev_last << endl; - - // Evolve left edge - tie (en, err, LW_left, RW_left) = itdvp (WL, AL_left, AR_left, AC_left, C_left, La_left, Ra_left, 1_i*dt, args_itdvp); - PH->L (1, LW_left); - - // From left to right - TDVPWorker (psi, *PH, 1_i*dt, sweeps, *obs, args_tdvp); - - if (expandN != 0) - { - auto const& specR = obs->spec (N - expand_checkN); - auto SR_sys = iut::EntangEntropy (specR); - auto SR = iut::EntangEntropy_singular (C_right); - expand_next = (abs(SR_sys-SR) > expandS_crit); - } - - // Extend right boundary - if (expand) - { - // Expand - expandR (psi, H, PH, expandN, NumCenter, AR_right, prime(ir,2), ir); - // Observer - sites = get_SiteSet (psi); - obs = make_unique (sites, psi, args_obs); - N = length (psi); - psi.position (N); - } - - // Evolve right boundary - tie (en, err, LW_right, RW_right) = itdvp (WR, AL_right, AR_right, AC_right, C_right, La_right, Ra_right, 1_i*dt, args_itdvp); - PH->R (N, RW_right); - - // From right to left - TDVPWorker (psi, *PH, 1_i*dt, sweeps, *obs, args_tdvp); - - if (expandN != 0) - { - auto const& specL = obs->spec (expand_checkN); - auto SL_sys = iut::EntangEntropy (specL); - auto SL = iut::EntangEntropy_singular (C_left); - if (abs(SL_sys-SL) > expandS_crit) - expand_next = true; - } - - //if (maxLinkDim(psi) >= sweeps.maxdim(1)) - //args_tdvp.add ("NumCenter",1); - expand = expand_next; - if (N >= max_window) - { - expand = false; - expandN = 0; - } - - if (write) - { - writeAll (write_dir+"/"+write_file, - psi, H, il, ir, WL, WR, - AL_left, AR_left, AC_left, C_left, La_left, Ra_left, LW_left, RW_left, - AL_right, AR_right, AC_right, C_right, La_right, Ra_right, LW_right, RW_right, - step, idev_first, idev_last, expand, expand_next); - } - } - - return 0; -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c646caa..c764c5a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,9 @@ project(Unittest LANGUAGES CXX) set(ITENSOR_DIR "/root/itensor") +# Include module headers +include_directories("/home/hybridleads/") + # Include 3rd party headers from itensor.utility include_directories("/root/itensor.utility/") @@ -21,10 +24,14 @@ include_directories(${ARMADILLO_INCLUDE_DIRS}) # Include Catch2 framework for unit test find_package(Catch2 3 REQUIRED) +# Include trompeloeil for mocking in unit test +find_package(trompeloeil REQUIRED) + # Name all your sources filename here without extension set(SOURCES_FILES test_one_particle_basis test_hamiltonian + test_itdvp ) foreach(name ${SOURCES_FILES}) # Define sources and output executable @@ -43,5 +50,6 @@ foreach(name ${SOURCES_FILES}) PRIVATE itensor "${ITENSOR_LIBFLAGS}" ${ARMADILLO_LIBRARIES} Catch2::Catch2WithMain + trompeloeil::trompeloeil ) endforeach() diff --git a/tests/test_hamiltonian.cpp b/tests/test_hamiltonian.cpp index aac6aea..0adfef8 100644 --- a/tests/test_hamiltonian.cpp +++ b/tests/test_hamiltonian.cpp @@ -2,10 +2,10 @@ #include #include -#include "../kbasis/Hamiltonian.h" -#include "../kbasis/OneParticleBasis.h" -#include "../kbasis/SortBasis.h" #include "itensor/all.h" +#include "kbasis/Hamiltonian.h" +#include "kbasis/OneParticleBasis.h" +#include "kbasis/SortBasis.h" using namespace itensor; using namespace Catch; diff --git a/tests/test_itdvp.cpp b/tests/test_itdvp.cpp new file mode 100644 index 0000000..d1c9c5b --- /dev/null +++ b/tests/test_itdvp.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +#include "itdvp/GenMPO.h" +#include "itdvp/iTDVP.h" +#include "itensor/all.h" +#include "kbasis/OneParticleBasis.h" + +using namespace itensor; +using namespace Catch; + +TEST_CASE("Check ", "[FixPointTensor]") {} diff --git a/tests/test_one_particle_basis.cpp b/tests/test_one_particle_basis.cpp index fa09a37..b03b25a 100644 --- a/tests/test_one_particle_basis.cpp +++ b/tests/test_one_particle_basis.cpp @@ -2,13 +2,39 @@ #include #include #include +#include -#include "../kbasis/OneParticleBasis.h" #include "itensor/all.h" +#include "kbasis/OneParticleBasis.h" using namespace itensor; using namespace Catch; +/** + * @brief Element-wise comparison of 2 given itensors. + * + * @param t1 The first tensor. + * @param t2 The second tensor. + * @param atol The absolute tolerance. Default to 1e-12. + * @return bool + */ +bool ALLCLOSE(ITensor t1, ITensor t2, double atol = 1e-12) { + REQUIRE(order(t1) == order(t2)); + auto check_close_zero = [&atol](Real r) { + if (abs(r) > atol) { + throw std::logic_error("Two tensors are not close."); + } + }; + try { + t2.replaceInds(inds(t2), inds(t1)); + auto diff_t = t1 - t2; + diff_t.visit(check_close_zero); // visit() only accepts lambda func + } catch (std::logic_error& e) { + return false; + } + return true; +} + TEST_CASE("Check matrix elements of tight-binding Hamiltonian", "[tight_binding_Hamilt]") { int mat_dim = 4; @@ -41,6 +67,11 @@ TEST_CASE("Check one particle basis", "[OneParticleBasis]") { } } +/** + * @brief Check AutoMPO in real space basis. + * @note The order of indices is not taken care by ITensor. + * @see /~https://github.com/chiamin/HybridLeads/issues/4 + */ TEST_CASE("Check AutoMPO in real space basis", "[RealSpaceBasis]") { int N = 3; auto t = 0.5; @@ -62,8 +93,7 @@ TEST_CASE("Check AutoMPO in real space basis", "[RealSpaceBasis]") { auto T = H(1) * H(2) * H(3); auto idxs = inds(T); - // Note that the order of indices is not taken care by ITensor - // See: /~https://github.com/chiamin/HybridLeads/issues/4 + // Warning: order of indices may not consist auto [Comb, c] = combiner(idxs[0], idxs[2], idxs[4]); auto [Combp, cp] = combiner(idxs[1], idxs[3], idxs[5]); @@ -102,7 +132,7 @@ TEST_CASE("Check AutoMPO in real space basis", "[RealSpaceBasis]") { CHECK(min_en == Approx(energy).epsilon(1e-8)); } -TEST_CASE("Check AutoMPO in hybrid basis", "[HybridBasis]") { +TEST_CASE("Check AutoMPO in hybrid basis by DMRG", "[HybridBasisDMRG]") { int N = GENERATE(8, 16, 20); auto t = 0.5; auto mu = GENERATE(0.0, 0.1); @@ -191,3 +221,165 @@ TEST_CASE("Check AutoMPO in hybrid basis", "[HybridBasis]") { dmrg(expected_H, psi0, sweeps, {"Silent", true}); CHECK(energy == Approx(expected_energy).epsilon(1e-8)); } + +/** + * @brief Check AutoMPO in hybrid basis element-wisely. + * @note Without the arg {"Exact=", true}, the approaximated MPO will have + * equal bond dim, otherwise the bond dim will be different on every bonds. + * @note Element-wise conparison only make sence when 2 MPO tensors have + * same bond dim. + * @see /~https://github.com/chiamin/HybridLeads/issues/9. + */ +TEST_CASE("Check AutoMPO in hybrid basis element-wisely", + "[HybridBasisMPOElem]") { + int N = GENERATE(8, 16, 20); + auto t = 0.5; + auto mu = GENERATE(0.0, 0.1); + auto sites = Fermion(N); + + // Construct AutoMPO in real-space basis + auto expected_ampo = AutoMPO(sites); + for (int i = 1; i <= N; ++i) { + expected_ampo += -mu, "N", i; + } + for (int i = 1; i < N; ++i) { + expected_ampo += -t, "Cdag", i, "C", i + 1; + expected_ampo += -t, "Cdag", i + 1, "C", i; + } + + // Construct AutoMPO in hybrid basis + auto ampo = AutoMPO(sites); + OneParticleBasis basis("sub_sys", N / 2, t, mu); + // 1. The k-space part + for (int k = 1; k <= N / 2; ++k) { + auto coef = basis.en(k); + ampo += coef, "N", k; + } + // 2. The mixing part + auto coef = basis.C_op(N / 2, false); + for (int k = 1; k <= N / 2; ++k) { + ampo += -t * get<1>(coef[k - 1]), "Cdag", k, "C", N / 2 + 1; + ampo += -t * get<1>(coef[k - 1]), "Cdag", N / 2 + 1, "C", k; + } + // 3. The real-space part + for (int i = N / 2 + 1; i <= N; ++i) { + ampo += -mu, "N", i; + } + for (int i = N / 2 + 1; i < N; ++i) { + ampo += -t, "Cdag", i, "C", i + 1; + ampo += -t, "Cdag", i + 1, "C", i; + } + + SECTION("toMPO without argument Exact") { + auto H = toMPO(ampo); + auto expected_H = toMPO(expected_ampo); + for (int i = 2; i <= N / 2 - 1; ++i) { + CHECK(ALLCLOSE(H(i), H(i + 1)) == false); // k-space part + } + CHECK(ALLCLOSE(H(N / 2), H(N / 2 + 1)) == false); // contact + CHECK(ALLCLOSE(H(N / 2 + 1), H(N / 2 + 2)) == false); // real-space + for (int i = N / 2 + 2; i <= N - 2; ++i) { + CHECK(ALLCLOSE(H(i), H(i + 1)) == true); // real-space part + } + for (int i = 1; i <= N / 2 + 1; ++i) { + // k-space part + 1st one in real-space part + CHECK(ALLCLOSE(H(i), expected_H(i)) == false); + } + for (int i = N / 2 + 2; i <= N; ++i) { + // rest of the real-space part + CHECK(ALLCLOSE(H(i), expected_H(i)) == true); + } + // Check that coef Uik is not equally-partitioned into H(N/2) and H(N/2 + 1) + auto idxs = inds(H(N / 2)); + CHECK(hasTags(idxs[0], "Link")); + CHECK(hasTags(idxs[1], "Link")); + CHECK(hasTags(idxs[2], "Site")); + CHECK(hasTags(idxs[3], "Site")); + CHECK_FALSE(elt(H(N / 2), 2, 3, 1, 2) == + Approx(elt(H(N / 2 + 1), 2, 3, 1, 2)).epsilon(1e-12)); + CHECK_FALSE(elt(H(N / 2), 2, 4, 2, 1) == + Approx(elt(H(N / 2 + 1), 2, 4, 2, 1)).epsilon(1e-12)); + } + + SECTION("toMPO with argument Exact") { + auto H = toMPO(ampo, {"Exact=", true}); + auto expected_H = toMPO(expected_ampo, {"Exact=", true}); + // TODO: the following 2 checks will fail, even bond dim mismatch. why? + // CHECK(ALLCLOSE(H(N / 2 + 1), expected_H(N / 2 + 1)) == true); + // CHECK(ALLCLOSE(H(N / 2 + 2), expected_H(N / 2 + 2)) == true); + for (int i = N / 2 + 3; i <= N; ++i) { + CHECK(ALLCLOSE(H(i), expected_H(i)) == true); // real-space part + } + } +} + +/** + * @brief The mocked class. + * @note Additional parentheses is required for nested return type. + * @see /~https://github.com/rollbear/trompeloeil/issues/164 + * @see /~https://github.com/rollbear/trompeloeil/blob/main/docs/ CookBook.md#mocking_return_template + */ +class MockOneParticleBasis : public OneParticleBasis { + public: + MockOneParticleBasis(const string& name, int L, Real t, Real mu) {} + MAKE_CONST_MOCK1(en, Real(int)); + MAKE_CONST_MOCK2(C_op, + (std::vector>)(int, bool)); +}; + +/** + * @brief Check AutoMPO in hybrid basis element-wisely by mocking coefficients. + * @note Even though we have mocked the one-particle basis eigenmodes, MPO + * tensors can still differ upon a Jordan-Wigner string. + * @see /~https://github.com/chiamin/HybridLeads/issues/9 + * @see https://www.itensor.org/docs.cgi?page=tutorials/fermions + */ +TEST_CASE( + "Check AutoMPO in hybrid basis element-wisely by mocking coefficients", + "[HybridBasisMPOMockElem]") { + int N = GENERATE(8, 16, 20); + auto t = 0.5; + auto mu = GENERATE(0.0, 0.1); + auto sites = Fermion(N); + auto ampo = AutoMPO(sites); + + // Mock one-particle eigen pairs to be one and one vector. + MockOneParticleBasis basis("sub_sys", N / 2, t, mu); + std::vector> mock_coef; + for (int i = 0; i < N; i++) { + mock_coef.emplace_back(i + 1, 1, false); + } + ALLOW_CALL(basis, en(trompeloeil::ge(1))).RETURN(1); + ALLOW_CALL(basis, C_op(trompeloeil::ge(1), ANY(bool))).RETURN(mock_coef); + + // 1. The k-space part + for (int k = 1; k <= N / 2; ++k) { + auto coef = basis.en(k); + ampo += coef, "N", k; + } + // 2. The mixing part + auto coef = basis.C_op(N / 2, false); + for (int k = 1; k <= N / 2; ++k) { + ampo += -t * get<1>(coef[k - 1]), "Cdag", k, "C", N / 2 + 1; + ampo += -t * get<1>(coef[k - 1]), "Cdag", N / 2 + 1, "C", k; + } + // 3. The real-space part + for (int i = N / 2 + 1; i <= N; ++i) { + ampo += -mu, "N", i; + } + for (int i = N / 2 + 1; i < N; ++i) { + ampo += -t, "Cdag", i, "C", i + 1; + ampo += -t, "Cdag", i + 1, "C", i; + } + + auto H = toMPO(ampo); + CHECK(ALLCLOSE(H(2), H(3)) == false); // k-space part + for (int i = 3; i <= N / 2 - 1; ++i) { + CHECK(ALLCLOSE(H(i), H(i + 1)) == true); // k-space part + } + CHECK(ALLCLOSE(H(N / 2 + 1), H(N / 2 + 2)) == false); // real-space part + for (int i = N / 2 + 2; i <= N - 2; ++i) { + CHECK(ALLCLOSE(H(i), H(i + 1)) == true); // real-space part + } +}