// // 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. // #include #include #include #include #include #include "itensor/util/stdx.h" #include "itensor/tensor/algs.h" #include "itensor/tensor/slicemat.h" #include "itensor/decomp.h" #include "itensor/util/print_macro.h" #include "itensor/itdata/qutil.h" namespace itensor { //const auto MAX_INT = std::numeric_limits::max(); const auto REAL_EPSILON = std::numeric_limits::epsilon(); using std::swap; using std::istream; using std::ostream; using std::vector; using std::find; using std::pair; using std::make_pair; using std::string; using std::sqrt; using std::move; using std::tie; /////////////// template struct ToMatRefc { using value_type = V; long nrows=0, ncols=0; bool transpose=false; ToMatRefc(long nr, long nc, bool trans=false) : nrows(nr), ncols(nc), transpose(trans) { } }; template MatRefc doTask(ToMatRefc const& T, Dense const& d) { auto res = makeMatRef(d.data(),d.size(),T.nrows,T.ncols); if(T.transpose) return transpose(res); return res; } template MatRefc toMatRefc(ITensor const& T, Index const& i1, Index const& i2) { if(i1 == T.inds().front()) { return doTask(ToMatRefc{dim(i1),dim(i2)},T.store()); } return doTask(ToMatRefc{dim(i2),dim(i1),true},T.store()); } template MatRefc toMatRefc(ITensor const& T, Index const& i1, Index const& i2); template MatRefc toMatRefc(ITensor const& T, Index const& i1, Index const& i2); ///////////// template vector> doTask(GetBlocks const& G, QDense const& d) { if(G.is.order() != 2) Error("doTask(GetBlocks,QDenseReal) only supports 2-index tensors"); auto res = vector>{d.offsets.size()}; size_t n = 0; for(auto const& dio : d.offsets) { auto& R = res[n++]; auto nrow = G.is[0].blocksize0(dio.block[0]); auto ncol = G.is[1].blocksize0(dio.block[1]); R.i1 = dio.block[0]; R.i2 = dio.block[1]; R.M = makeMatRef(d.data()+dio.offset,d.size()-dio.offset,nrow,ncol); } if(G.transpose) { for(auto& R : res) { R.M = transpose(R.M); std::swap(R.i1,R.i2); } } return res; } template vector> doTask(GetBlocks const& G, QDense const& d); template vector> doTask(GetBlocks const& G, QDense const& d); /////////////// Spectrum svd(ITensor const& AA, ITensor & U, ITensor & D, ITensor & V, Args args) { if( args.defined("Minm") ) { if( args.defined("MinDim") ) { Global::warnDeprecated("Args Minm and MinDim are both defined. Minm is deprecated in favor of MinDim, MinDim will be used."); } else { Global::warnDeprecated("Arg Minm is deprecated in favor of MinDim."); args.add("MinDim",args.getInt("Minm")); } } if( args.defined("Maxm") ) { if( args.defined("MaxDim") ) { Global::warnDeprecated("Args Maxm and MaxDim are both defined. Maxm is deprecated in favor of MaxDim, MaxDim will be used."); } else { Global::warnDeprecated("Arg Maxm is deprecated in favor of MaxDim."); args.add("MaxDim",args.getInt("Maxm")); } } if( args.defined("UseOrigM") ) { if( args.defined("UseOrigDim") ) { Global::warnDeprecated("Args UseOrigM and UseOrigDim are both defined. UseOrigM is deprecated in favor of UseOrigDim, UseOrigDim will be used."); } else { Global::warnDeprecated("Arg UseOrigM is deprecated in favor of UseOrigDim."); args.add("UseOrigDim",args.getBool("UseOrigM")); } } #ifdef DEBUG if(!U && !V) Error("U and V default-initialized in svd, must indicate at least one index on U or V"); #endif auto noise = args.getReal("Noise",0); auto useOrigDim = args.getBool("UseOrigDim",false); if(noise > 0) Error("Noise term not implemented for svd"); //if(isZero(AA,Args("Fast"))) // throw ResultIsZero("svd: AA is zero"); //Combiners which transform AA //into a order 2 tensor std::vector Uinds, Vinds; Uinds.reserve(AA.order()); Vinds.reserve(AA.order()); //Divide up indices based on U //If U is null, use V instead auto &L = (U ? U : V); auto &Linds = (U ? Uinds : Vinds), &Rinds = (U ? Vinds : Uinds); for(const auto& I : AA.inds()) { if(hasIndex(L,I)) Linds.push_back(I); else Rinds.push_back(I); } ITensor Ucomb, Vcomb; Index ui, vi; auto AAcomb = AA; if(!Uinds.empty()) { std::tie(Ucomb,ui) = combiner(std::move(Uinds)); AAcomb *= Ucomb; } if(!Vinds.empty()) { std::tie(Vcomb,vi) = combiner(std::move(Vinds)); AAcomb *= Vcomb; } if(useOrigDim) { //Try to determine current m, //then set mindim_ and maxdim_ to this. args.add("Cutoff",-1); long mindim = 1, maxdim = MAX_DIM; if(D.order() == 0) { //auto mid = commonIndex(U,V,Link); //TODO: check this does the same thing auto mid = commonIndex(U,V); if(mid) mindim = maxdim = dim(mid); else mindim = maxdim = 1; } else { mindim = maxdim = dim(D.inds().front()); } args.add("MinDim",mindim); args.add("MaxDim",maxdim); } //auto ui = commonIndex(AAcomb,Ucomb); //auto vi = commonIndex(AAcomb,Vcomb); auto spec = svdOrd2(AAcomb,ui,vi,U,D,V,args); U = dag(Ucomb) * U; V = V * dag(Vcomb); return spec; } //svd std::tuple svd(ITensor const& AA, IndexSet const& Uis, IndexSet const& Vis, Args args) { if( !hasSameInds(inds(AA),IndexSet(Uis,Vis)) ) Error("In svd, U indices and V indices must match the indices of the input ITensor"); return svd(AA,Uis,args); } std::tuple svd(ITensor const& AA, IndexSet const& Uis, Args args) { ITensor U(Uis),S,V; svd(AA,U,S,V,args); auto u = commonIndex(U,S); auto v = commonIndex(S,V); return std::tuple(U,S,V); } std::tuple polar(ITensor const& T, IndexSet const& Uis, Args const& args) { auto [U,S,V] = svd(T,Uis); auto u = commonIndex(S,U); auto v = commonIndex(S,V); auto Vis = uniqueInds(inds(V),{v}); auto Vp = prime(V,Vis)*delta(v,u); auto Q = U*Vp; auto P = dag(Vp)*S*V; auto qis = commonInds(Q,P); // Add tags to the internal indices auto ts = getTagSet(args,"AddTags",""); // Prime the internal indices. // Prime by one less than specified, // since they are already primed by one // above. auto plinc = args.getInt("Prime",1)-1; if(primeLevel(ts) >= 0) Error("In polar, specify a prime level increment with the Prime argument"); if(ts=="" && plinc==-1) Error("In polar, must either increment prime level or add tags to new indices"); Q.addTags(ts,qis); P.addTags(ts,qis); qis.addTags(ts); Q.prime(plinc,qis); P.prime(plinc,qis); return std::tuple(Q,P); } // output: truncerr,docut_lower,docut_upper,ndegen_below std::tuple truncate(Vector & P, long maxdim, long mindim, Real cutoff, bool absoluteCutoff, bool doRelCutoff, Args const& args) { auto respectDegenerate = args.getBool("RespectDegenerate",false); long origm = P.size(); long n = origm-1; Real docut_lower = 0; Real docut_upper = 0; //Special case if P's are zero if(P(0) == 0.0) { resize(P,1); auto degen_cutoff = REAL_EPSILON; auto docut_lower = -degen_cutoff; auto docut_upper = degen_cutoff; auto ndegen_below = 1; return std::make_tuple(0.,docut_lower,docut_upper,ndegen_below); } if(origm == 1) { auto degen_cutoff = 1E-3*P(0); auto docut_lower = P(0)-degen_cutoff; auto docut_upper = P(0)+degen_cutoff; auto ndegen_below = 1; return std::make_tuple(0.,docut_lower,docut_upper,ndegen_below); } // TODO: check this is correct // First normalize P by the largest value to scale things properly // We will undo this at the end auto P0 = P(0); P /= P0; // If we are not using a relative cutoff, // also normalize the cutoff value if(absoluteCutoff || !doRelCutoff) { cutoff /= P0; } //Zero out any negative weight for(auto zn = n; zn >= 0; --zn) { if(P(zn) >= 0) break; P(zn) = 0; } Real truncerr = 0; //Always truncate down to at least m==maxdim (m==n+1) while(n >= maxdim) { truncerr += P(n); --n; } if(absoluteCutoff) //absoluteCutoff is typically false { //Test if individual prob. weights fall below cutoff //rather than using *sum* of discarded weights for(; P(n) <= cutoff && n >= mindim; --n) { truncerr += P(n); } } else { Real scale = 1.0; //if doRelCutoff, use normalized P's when truncating if(doRelCutoff) { scale = sumels(P); if(scale == 0.0) scale = 1.0; } //Continue truncating until *sum* of discarded probability //weight reaches cutoff reached (or m==mindim) while(truncerr+P(n) <= cutoff*scale && n >= mindim) { truncerr += P(n); --n; } truncerr = (scale == 0 ? 0 : truncerr/scale); } if(n < 0) n = 0; //P is 0-indexed, so add 1 to n to //get correct state count m auto m = n+1; auto degen_cutoff = 1E-3*P(n); if(P(n) == 0.0) { degen_cutoff = REAL_EPSILON; } docut_lower = P(n)-degen_cutoff; docut_upper = P(n)+degen_cutoff; //Check for a degeneracy: auto ndegen_below = 1; if(n > 0) { while( (n-ndegen_below >= 0) && (std::abs(P(n-ndegen_below)-P(n)) < degen_cutoff) ) { ++ndegen_below; } } auto ndegen_above = 0; if(n+1 < origm) { while( (n+1+ndegen_above < origm) && (std::abs(P(n+1+ndegen_above)-P(n)) < degen_cutoff) ) { ++ndegen_above; } } // If the new dimension falls within a degenerate subspace and we // are respecting degeneracies (we are making sure to not truncate // within a degenerate subspace) if( respectDegenerate && (ndegen_below + ndegen_above > 1) ) { if( maxdim < m+ndegen_above ) // If the maximum dimension falls within the degenerate subspace { // Truncate the subspace m -= ndegen_below; ndegen_above += ndegen_below; ndegen_below = 0; } else // Otherwise, keep the subspace { m += ndegen_above; ndegen_below += ndegen_above; ndegen_above = 0; } } resize(P,m); // Put back the normalization factor P *= P0; truncerr *= P0; docut_lower *= P0; docut_upper *= P0; return std::make_tuple(truncerr,docut_lower,docut_upper,ndegen_below); } // truncate void showEigs(Vector const& P, Real truncerr, LogNum const& scale, Args args) { if( args.defined("Minm") ) { if( args.defined("MinDim") ) { Global::warnDeprecated("Args Minm and MinDim are both defined. Minm is deprecated in favor of MinDim, MinDim will be used."); } else { Global::warnDeprecated("Arg Minm is deprecated in favor of MinDim."); args.add("MinDim",args.getInt("Minm")); } } if( args.defined("Maxm") ) { if( args.defined("MaxDim") ) { Global::warnDeprecated("Args Maxm and MaxDim are both defined. Maxm is deprecated in favor of MaxDim, MaxDim will be used."); } else { Global::warnDeprecated("Arg Maxm is deprecated in favor of MaxDim."); args.add("MaxDim",args.getInt("Maxm")); } } auto do_truncate = args.getBool("Truncate",true); auto cutoff = args.getReal("Cutoff",0.); auto maxdim = args.getInt("MaxDim",P.size()); auto mindim = args.getInt("MinDim",1); auto doRelCutoff = args.getBool("DoRelCutoff",true); auto absoluteCutoff = args.getBool("AbsoluteCutoff",false); println(); printfln("mindim = %d, maxdim = %d, cutoff = %.2E, truncate = %s",mindim,maxdim,cutoff,do_truncate); printfln("Kept m=%d states, trunc. err. = %.3E", P.size(),truncerr); printfln("doRelCutoff = %s, absoluteCutoff = %s",doRelCutoff,absoluteCutoff); IF_USESCALE(printfln("Scale is = %sexp(%.2f)",scale.sign() > 0 ? "" : "-",scale.logNum());) auto stop = std::min(size_t{10},P.size()); auto Ps = Vector(subVector(P,0,stop)); #ifndef USESCALE print("Eigenvalues:"); #else if(scale.logNum() < 10 && scale.isFiniteReal()) { Ps *= sqr(scale.real0()); print("Eigenvalues:"); } else { print("Eigenvalues [not including scale = ",scale.logNum(),"]:"); } #endif for(auto n : range(Ps)) { auto eig = Ps(n); printf(( eig > 1E-3 && eig < 1000) ? (" %.4f") : (" %.3E") , eig); } println(); } // showEigs Real absSqrt(Real x) { return std::sqrt(std::fabs(x)); } Spectrum factor(ITensor const& T, ITensor & A, ITensor & B, Args args) { auto itagset = getTagSet(args,"Tags","Link"); ITensor D; auto spec = svd(T,A,D,B,{args,"LeftTags=",itagset}); auto dl = commonIndex(A,D); auto dr = commonIndex(B,D); D.apply(absSqrt); A *= D; B *= D; //Replace index dl with dr A *= delta(dl,dr); return spec; } std::tuple factor(ITensor const& T, IndexSet const& Ais, IndexSet const& Bis, Args const& args) { if( !hasSameInds(inds(T),IndexSet(Ais,Bis)) ) Error("In factor, A indices and B indices must match the indices of the input ITensor"); return factor(T,Ais,args); } std::tuple factor(ITensor const& T, IndexSet const& Ais, Args const& args) { ITensor A(Ais),B; factor(T,A,B,args); return std::tuple(A,B); } template void eigDecompImpl(ITensor T, ITensor & L, ITensor & R, ITensor & D, Args const& args) { auto itagset = getTagSet(args,"Tags","Link"); // New index listing eigenvectors Index newmid; if(not hasQNs(T)) { auto full = args.getBool("FullDecomp",false); if(order(T) != 2) { Print(order(T)); Print(T); Error("eig_decomp requires 2-index tensor as input"); } auto lind = noPrime(T.inds().front()); //Do the diagonalization auto MM = toMatRefc(T,prime(lind),lind); Vector Dr, Di; Matrix Rr, Ri; Matrix Lr, Li; if(!full) { eigen(MM,Rr,Ri,Dr,Di); } else { eigDecomp(MM,Lr,Li,Dr,Di,Rr,Ri); } newmid = Index(dim(lind),itagset); //put right eigenvectors into an ITensor if(norm(Ri) > 1E-16*norm(Rr)) { //complex eigenvectors auto store = DenseCplx(Rr.size()); auto ri = Rr.begin(); auto ii = Ri.begin(); for(decltype(Rr.size()) n = 0; n < Rr.size(); ++ri, ++ii, ++n) { #ifdef DEBUG if(ri == Rr.end() || ii == Ri.end()) Error("out of range iterator"); #endif store[n] = Cplx(*ri,*ii); } R = ITensor({lind,newmid},move(store)); } else { //real eigenvectors R = ITensor({lind,newmid},DenseReal{move(Rr.storage())}); } if(norm(Di) > 1E-16*norm(Dr)) { //complex eigenvalues auto store = DiagCplx(Dr.size()); for(auto n : range(Dr.size())) { store.store.at(n) = Cplx(Dr(n),Di(n)); } D = ITensor({prime(newmid),newmid},move(store),T.scale()); } else { //real eigenvectors D = ITensor({prime(newmid),newmid},DiagReal{move(Dr.storage())},T.scale()); } if(full) { // If doing full decomp, prime R R.prime(); //put left eigenvectors into an ITensor if(norm(Li) > 1E-16*norm(Lr)) { //complex eigenvectors auto store = DenseCplx(Lr.size()); auto ri = Lr.begin(); auto ii = Li.begin(); for(decltype(Lr.size()) n = 0; n < Lr.size(); ++ri, ++ii, ++n) { #ifdef DEBUG if(ri == Lr.end() || ii == Li.end()) Error("out of range iterator"); #endif store[n] = Cplx(*ri,*ii); } L = ITensor({lind,newmid},move(store)); } else { //real eigenvectors L = ITensor({lind,newmid},DenseReal{move(Lr.storage())}); } } } else { Error("eigDecompImpl not implemented for QN ITensor"); } } Spectrum denmatDecomp(ITensor const& AA, ITensor & A, ITensor & B, Direction dir, Args const& args) { return denmatDecomp(AA,A,B,dir,NoOp(),args); } std::tuple denmatDecomp(ITensor const& T, IndexSet const& Ais, IndexSet const& Bis, Direction dir, Args const& args) { if( !hasSameInds(inds(T),IndexSet(Ais,Bis)) ) Error("In svd, A indices and B indices must match the indices of the input ITensor"); return denmatDecomp(T,Ais,dir,args); } std::tuple denmatDecomp(ITensor const& T, IndexSet const& Ais, Direction dir, Args const& args) { ITensor A(Ais),B; denmatDecomp(T,A,B,dir,args); return std::tuple(A,B); } Spectrum diagPosSemiDef(ITensor const& M, ITensor & U, ITensor & D, Args args) { if(!args.defined("Tags")) args.add("Tags","Link"); // // Pick an arbitrary index and do some analysis // on its prime level spacing // auto k = M.inds().front(); auto kps = stdx::reserve_vector(order(M)); for(auto& i : M.inds()) if( noPrime(i)==noPrime(k) ) kps.push_back(i.primeLevel()); if(kps.size() <= 1ul || kps.size()%2 != 0ul) { Error("Input tensor to diagHermitian should have pairs of indices with equally spaced prime levels"); } auto nk = kps.size(); std::sort(kps.begin(),kps.end()); //idiff == "inner" difference between cluster of low-prime-level copies // of k, if more than one auto idiff = kps.at(nk/2-1)-kps.front(); //mdiff == max prime-level difference of copies of k auto mdiff = kps.back()-kps.front(); //pdiff == spacing between lower and higher prime level index pairs auto pdiff = mdiff-idiff; auto inds = stdx::reserve_vector(order(M)/2); for(auto& i : M.inds()) for(auto& j : M.inds()) { if( noPrime(i)==noPrime(j) && i.primeLevel()+pdiff == j.primeLevel() ) { inds.push_back(i); } } if(inds.empty() || order(M)/2 != (long)inds.size()) { Error("Input tensor to diagHermitian should have pairs of indices with equally spaced prime levels"); } auto [comb,cind] = combiner(std::move(inds),args); (void)cind; auto Mc = M*comb; auto combP = dag(prime(comb,pdiff)); try { Mc = combP * Mc; } catch(ITError const& e) { println("Diagonalize expects opposite arrow directions for primed and unprimed indices."); throw e; } auto spec = diag_hermitian(Mc,U,D,args); U = comb * U; return spec; } //diagHermitian std::tuple diagPosSemiDef(ITensor const& T, Args const& args) { ITensor U,D; diagPosSemiDef(T,U,D,args); return std::tuple(U,D); } Spectrum diagHermitian(ITensor const& M, ITensor & U, ITensor & D, Args args) { args.add("Truncate",false); return diagPosSemiDef(M,U,D,args); } std::tuple diagHermitian(ITensor const& T, Args const& args) { ITensor U,D; diagHermitian(T,U,D,args); return std::tuple(U,D); } void eigen(ITensor const& T, ITensor & V, ITensor & D, Args args) { if(!args.defined("Tags")) args.add("Tags","Link"); auto colinds = std::vector{}; for(auto& I : T.inds()) { if(I.primeLevel() == 0) colinds.push_back(I); } auto [comb,cind] = combiner(std::move(colinds),args); auto Tc = prime(dag(comb)) * T * comb; // The version where Tc is ordered // {cind,prime(cind)} does not work, // here we will explicitly permute the // ITensor so the indices are ordered // {prime(cind),cind}. // This is not great since it is an // extra reordering of the data, // look into fixing eigen properly. if(inds(Tc)(1) != prime(cind)) Tc = permute(Tc,{prime(cind),cind}); ITensor L; Index eigvec_ind; if(isComplex(T)) { eigDecompImpl(Tc,L,V,D,args); } else { eigDecompImpl(Tc,L,V,D,args); } V *= comb; } std::tuple eigen(ITensor const& T, Args const& args) { ITensor V,D; eigen(T,V,D,args); return std::tuple(V,D); } void eigDecomp(ITensor const& T, ITensor & R, ITensor & D, ITensor & Rinv, Args const& args) { auto colinds = std::vector{}; for(auto& I : T.inds()) { if(I.primeLevel() == 0) colinds.push_back(I); } auto [comb,cind] = combiner(std::move(colinds)); (void)cind; auto Tc = prime(comb) * T * comb; if(isComplex(Tc)) { eigDecompImpl(Tc,Rinv,R,D,{args,"FullDecomp",true}); } else { eigDecompImpl(Tc,Rinv,R,D,{args,"FullDecomp",true}); } R = R * prime(comb); Rinv = Rinv * comb; } template struct Exp { T tt = 0.; Exp(T t_) : tt(t_) { } T operator()(Real x) const { return exp(tt*x); } }; ITensor expHermitian(ITensor const& T, Cplx t) { ITensor U,d; diagHermitian(T,U,d); if(t.imag()==0.) { d.apply(Exp(t.real())); } else { d.apply(Exp(t)); } return prime(U)*d*dag(U); } void qr(ITensor const& AA, ITensor & Q, ITensor & R, Args args) { #ifdef DEBUG if(!Q && !R) Error("Q and R default-initialized in qr, must indicate at least one index on Q or R"); #endif //Combiners which transform AA //into a order 2 tensor std::vector Qinds, Rinds; Qinds.reserve(AA.order()); Rinds.reserve(AA.order()); //Divide up indices based on Q //If Q is null, use R instead auto &A = (Q ? Q : R); auto &Ainds = (Q ? Qinds : Rinds), &Binds = (Q ? Rinds : Qinds); for(const auto& I : AA.inds()) { if(hasIndex(A,I)) Ainds.push_back(I); else Binds.push_back(I); } ITensor Qcomb, Rcomb; Index qi, ri; auto AAcomb = AA; if(!Qinds.empty()) { std::tie(Qcomb,qi) = combiner(std::move(Qinds)); AAcomb *= Qcomb; } if(!Rinds.empty()) { std::tie(Rcomb,ri) = combiner(std::move(Rinds)); AAcomb *= Rcomb; } qrOrd2(AAcomb,qi,ri,Q,R,args); Q = dag(Qcomb) * Q; R = R * dag(Rcomb); } //qr std::tuple qr(ITensor const& AA, IndexSet const& Qis, IndexSet const& Ris, Args args) { if( !hasSameInds(inds(AA),IndexSet(Qis,Ris)) ) Error("In QR, Q indices and R indices must match the indices of the input ITensor"); return qr(AA,Qis,args); } std::tuple qr(ITensor const& AA, IndexSet const& Qis, Args args) { ITensor Q(Qis),R; qr(AA,Q,R,args); auto q = commonIndex(Q,R); return std::tuple(Q,R); } template void qrImpl(ITensor const& A, Index const& qI, Index const& rI, ITensor & Q, ITensor & R, Args args) { auto internaltagset = getTagSet(args,"InternalTags","Link,QR"); auto uppertriangular = args.getBool("UpperTriangular",true); auto complete = args.getBool("Complete",false); if (not complete and not uppertriangular) Error("Cannot construct thin QR without R being upper triangular."); if(not hasQNs(A)) { auto matA = toMatRefc(A,qI,rI); Mat QQ,RR; QR(matA, QQ, RR, args); auto qL = Index(nrows(RR),internaltagset); auto rL = setTags(qL,internaltagset); Q = ITensor({qI,qL},Dense(move(QQ.storage()))); R = ITensor({rL,rI},Dense(move(RR.storage()))); } else { auto blocks = doTask(GetBlocks{A.inds(),qI,rI},A.store()); auto Nblock = blocks.size(); if(Nblock == 0) throw ResultIsZero("IQTensor has no blocks"); vector> indLiq; auto Liq = Index::qnstorage{}; Liq.reserve(Nblock); if (uppertriangular) indLiq.reserve(Nblock); //TODO: optimize allocation/lookup of Qmats,Rmats // etc. by allocating memory ahead of time (see algs.cc) // and making Qmats a vector of MatrixRef's to this memory auto Qmats = vector>(Nblock); auto Rmats = vector>(Nblock); //Set for completely zero blocks in A std::set zerob; for (int i = 0; i < nblock(qI); i++) zerob.emplace(i); for(auto b : range(Nblock)) { auto& B = blocks[b]; auto& matA = B.M; zerob.erase(B.i1); auto & QQ = Qmats.at(b); auto & RR = Rmats.at(b); QR(matA, QQ, RR, args); if (uppertriangular) { int subQcols = nrows(RR) > ncols(RR) ? ncols(RR) : nrows(RR); indLiq.emplace_back(B.i2, QNInt(qn(qI,1+B.i1),subQcols), b); } else { Liq.emplace_back(qn(qI,1+B.i1), nrows(RR)); } } if(uppertriangular) { //Sort the blocks by block column index so R is upper triangular sort(begin(indLiq),end(indLiq)); std::transform(begin(indLiq), end(indLiq), std::back_inserter(Liq), [](auto & triplet){ return std::get<1>(triplet);}); if (complete) { //Add filler rows of zero to R and extra orthonormal rows to Q for(auto b : range(Nblock)) { auto& B = blocks[b]; auto& matA = B.M; int fillrows = nrows(matA)- ncols(matA); if (fillrows > 0) { Liq.emplace_back(qn(qI,1+B.i1),fillrows); } } } } if (complete) { //For any blocks not appearing in A, Q is identity. for (const auto& b: zerob) { Liq.emplace_back(qn(qI,1+b), blocksize(qI,1+b)); } } auto qL = Index(move(Liq),qI.dir(),internaltagset); auto rL = qL; rL.setTags(internaltagset); auto Qis = IndexSet(qI,dag(qL)); auto Ris = IndexSet(rL, rI); auto Qstore = QDense(Qis,QN()); auto Rstore = QDense(Ris,QN(div(A))); int extrab = 0; for(auto b : range(Nblock)) { int n = b; if(uppertriangular) n = std::get<2>(indLiq[b]); auto& B = blocks[b]; auto & QQ = Qmats.at(b); auto & RR = Rmats.at(b); int Rrows = nrows(RR) > ncols(RR) ? ncols(RR) : nrows(RR); int fillrows = nrows(QQ) - Rrows; auto qind = Block(2); qind[0] = B.i1; qind[1] = n; auto pQ = getBlock(Qstore,Qis,qind); assert(pQ.data() != nullptr); auto Qref = makeMatRef(pQ,nrows(QQ), Rrows); Qref &= columns(QQ,0,Rrows); //Filler columns of Q due to reshuffling to make uppertriangular if (uppertriangular and complete and fillrows > 0) { auto qfind = Block(2); qfind[0] = B.i1; qfind[1] = extrab + Nblock; auto pQf = getBlock(Qstore,Qis,qfind); assert(pQf.data() != nullptr); auto Qfref = makeMatRef(pQf,nrows(QQ), fillrows); Qfref &= columns(QQ,Rrows,Rrows + fillrows); extrab++; } auto rind = Block(2); rind[0] = n; rind[1] = B.i2; auto pR = getBlock(Rstore,Ris,rind); assert(pR.data() != nullptr); auto Rref = makeMatRef(pR.data(),pR.size(),Rrows,ncols(RR)); Rref &= rows(RR,0,Rrows); } //Blocks not appearing in A are identity in Q if (complete) { for (const auto& b : zerob) { auto sqind = Block(2); sqind[0] = b; sqind[1] = extrab + Nblock; auto psQ = getBlock(Qstore,Qis,sqind); int sQdim = blocksize(qI,1+b); assert(psQ.data() != nullptr); auto sQref = makeMatRef(psQ,sQdim, sQdim); auto id = Mat(sQdim,sQdim); for(auto j : range(sQdim)) id(j,j) = 1.0; sQref &= id; extrab++; } } Q = ITensor(Qis,move(Qstore)); R = ITensor(Ris,move(Rstore)); } } void qrOrd2(ITensor const& A, Index const& qI, Index const& rI, ITensor & Q, ITensor & R, Args args) { if(A.order() != 2) { Error("A must be matrix-like (order 2)"); } if(isComplex(A)) { qrImpl(A,qI,rI,Q,R,args); } else { qrImpl(A,qI,rI,Q,R,args); } } } //namespace itensor