def _mul_mps_(self,other): ''' The multiplication of an mpo and an mps. Parameters ---------- other : MPS The mps. Returns ------- MPS The product. ''' assert self.nsite==other.nsite u,Lambda=other._merge_ABL_() ms=[] for i,(m1,m2) in enumerate(zip(self,other)): L1,U1,D1,R1=m1.labels L2,S,R2=m2.labels assert S==D1 L=L2.replace(qns=QuantumNumbers.kron([L1.qns,L2.qns]) if L1.qnon else L1.qns*L2.qns) R=R2.replace(qns=QuantumNumbers.kron([R1.qns,R2.qns]) if R1.qnon else R1.qns*R2.qns) m=(m1*m2).transpose((L1,L2,U1,R1,R2)).merge(([L1,L2],L),([R1,R2],R)) m.relabel(olds=[U1],news=[S]) ms.append(m) other._set_ABL_(u,Lambda) return MPS(mode=other.mode,ms=ms)
def _mul_mpo_(self,other): ''' The multiplication of two mpos. Parameters ---------- other : MPO The other mpo. Returns ------- MPO The product. ''' assert self.nsite==other.nsite ms=[] for i,(m1,m2) in enumerate(zip(self,other)): assert m1.labels==m2.labels m1,m2=copy(m1),copy(m2) L1,U1,D1,R1=m1.labels L2,U2,D2,R2=m2.labels L=L1.replace(qns=QuantumNumbers.kron([L1.qns,L2.qns]) if L1.qnon else L1.qns*L2.qns) R=R1.replace(qns=QuantumNumbers.kron([R1.qns,R2.qns]) if R1.qnon else R1.qns*R2.qns) s=Label('__MPO_MUL__',qns=U1.qns) l1,r1=Label('__MPO_MUL_L1__',qns=L1.qns,flow=L1.flow),Label('__MPO_MUL_R1__',qns=R1.qns,flow=R1.flow) l2,r2=Label('__MPO_MUL_L2__',qns=L2.qns,flow=L2.flow),Label('__MPO_MUL_R2__',qns=R2.qns,flow=R2.flow) m1.relabel(olds=[L1,D1,R1],news=[l1,s.replace(flow=D1.flow),r1]) m2.relabel(olds=[L2,U2,R2],news=[l2,s.replace(flow=U2.flow),r2]) ms.append((m1*m2).transpose((l1,l2,U1,D2,r1,r2)).merge(([l1,l2],L),([r1,r2],R))) return MPO(ms)
def union(labels, identifier, flow=0, prime=False, mode=0): ''' The union of a set of labels as a new label. Parameters ---------- labels : list of Label The set of labels. identifier : any hashale object The identifier of the union. flow : -1/0/+1/None The flow of the union. prime : logical, optional When True, the union is in the prime form; otherwise not. mode : -2/-1/0/+1/+2, optional * 0: When qnon, use QuantumNumbers.kron to get the qns of the union, no sorting and no returning the permutation array * -1: When qnon, use QuantumNumbers.kron to get the qns of the union with sorting but without returning the permutation array * +1: When qnon, use QuantumNumbers.kron to get the qns of the union with sorting and with returning the permutation array * -2: When qnon, use QuantumNumbers.ukron to get the qns of the union without returning the record dict * +2: When qnon, use QuantumNumbers.ukron to get the qns of the union with returning the record dict Returns ------- result : Label The union of the input set of labels. permutation/record : 1d-ndarray-of-int/dict, optional The permutation/record of the quantum numbers of the union. ''' qnon = labels[0].qnon assert all(label.qnon == qnon for label in labels[1:]) and mode in {-2, -1, 0, 1, 2} if qnon: assert flow in {-1, 1} qnses, signs = [label.qns for label in labels], [ 1 if label.flow == flow else -1 for label in labels ] if mode in {-1, 0, +1}: qns = QuantumNumbers.kron(qnses, signs=signs) if mode == -1: qns = qns.sorted(history=False) if mode == +1: qns, permutation = qns.sorted(history=True) result = Label(identifier, qns, flow=flow, prime=prime) return (result, permutation) if mode == 1 else result else: if mode == -2: qns = QuantumNumbers.ukron(qnses, signs=signs, history=False) if mode == +2: qns, record = QuantumNumbers.ukron(qnses, signs=signs, history=True) result = Label(identifier, qns, flow=flow, prime=prime) return (result, record) if mode == 2 else result else: result = Label(identifier, np.product([label.qns for label in labels]), flow=None if flow is None else 0, prime=prime) return (result, None) if mode in (1, 2) else result
def impsgrowth(self, sites, bonds, osvs, qn=0, ttype=None): ''' Infinite MPS growth. Parameters ---------- sites,bonds : list of Label/str The site/bond labels/identifiers of the new mps. osvs : 1d ndarray The old singular values. qn : QuantumNumber, optional The injected quantum number of the new mps. ttype : NOne/'D'/'S', optional Tensor type. 'D' for dense, 'S' for sparse and None for automatic. Returns ------- MPS The imps after growth. ''' if self.nsite > 0: assert self.cut == self.nsite // 2 and self.nsite % 2 == 0 and len( sites) + 1 == len(bonds) ob, nb = self.nsite // 2 + 1, (len(bonds) + 1) // 2 ns = nb - ob cms = self[ob - ns - 1:ob + ns - 1].impsprediction( sites[ob - 1:2 * nb - ob - 1], bonds[ob - 1:2 * nb - ob], osvs, qn=qn, ttype=ttype) lms = MPS([copy(self[pos]) for pos in range(0, self.cut)]) rms = MPS([copy(self[pos]) for pos in range(self.cut, self.nsite)]) lms.relabel(sites[:ob - 1], bonds[:ob]) rms.relabel(sites[-ob + 1:], bonds[-ob:]) rms.qninject(qn) result = MPS(it.chain(lms, cms, rms), Lambda=cms.Lambda, cut=nb - 1, ttype=ttype) else: bonds = copy(bonds) iqns, oqns = (QNS.mono(qn.zero()), QNS.mono(qn)) if isinstance(qn, QN) else (1, 1) bonds[+0] = bonds[+0].replace(qns=iqns) if isinstance( bonds[+0], Label) else Label(bonds[+0], qns=iqns, flow=None) bonds[-1] = bonds[-1].replace(qns=oqns) if isinstance( bonds[-1], Label) else Label(bonds[-1], qns=oqns, flow=None) result = MPS.random(sites, bonds=bonds, cut=len(sites) // 2, nmax=10, ttype=ttype or 'D') return result
def reset(self, mode=None, layers=None, priority=None, leaves=(), map=None): ''' Reset the DegFreTree. Parameters ---------- mode,layers,priority,leaves,map : Please see DegFreTree.__init__ for details. ''' self.clear() Tree.__init__(self) assert mode in (None, 'QN', 'NB') if mode is not None: self.mode = mode if layers is not None: self.layers = layers if priority is not None: self.priority = priority if map is not None: self.map = map self.cache = {} if len(leaves) > 0: temp = [key for layer in self.layers for key in layer] assert set(range(len(PID._fields))) == set( [temp.index(key) for key in PID._fields]) temp = set(temp) self.add_leaf(parent=None, leaf=tuple([None] * len(leaves[0])), data=None) for layer in self.layers: temp -= set(layer) indices = set([ index.replace(**{key: None for key in temp}) for index in leaves ]) self.cache[('indices', layer)] = sorted( indices, key=lambda index: index.to_tuple(priority=self.priority)) self.cache[('table', layer)] = Table(self.indices(layer=layer)) for index in self.indices(layer=layer): self.add_leaf( parent=index.replace(**{key: None for key in layer}), leaf=index, data=None) for i, layer in enumerate(reversed(self.layers)): if i == 0: for index in self.indices(layer): self[index] = self.map(index) else: for index in self.indices(layer): if self.mode == 'QN': self[index] = QuantumNumbers.kron([ self[child] for child in self.children(index) ]) else: self[index] = np.product([ self[child] for child in self.children(index) ])
def union(labels,identifier,flow=0,prime=False,mode=0): ''' The union of a set of labels as a new label. Parameters ---------- labels : list of Label The set of labels. identifier : any hashale object The identifier of the union. flow : -1/0/+1/None The flow of the union. prime : logical, optional When True, the union is in the prime form; otherwise not. mode : -2/-1/0/+1/+2, optional * 0: When qnon, use QuantumNumbers.kron to get the qns of the union, no sorting and no returning the permutation array * -1: When qnon, use QuantumNumbers.kron to get the qns of the union with sorting but without returning the permutation array * +1: When qnon, use QuantumNumbers.kron to get the qns of the union with sorting and with returning the permutation array * -2: When qnon, use QuantumNumbers.ukron to get the qns of the union without returning the record dict * +2: When qnon, use QuantumNumbers.ukron to get the qns of the union with returning the record dict Returns ------- result : Label The union of the input set of labels. permutation/record : 1d-ndarray-of-int/dict, optional The permutation/record of the quantum numbers of the union. ''' qnon=labels[0].qnon assert all(label.qnon==qnon for label in labels[1:]) and mode in {-2,-1,0,1,2} if qnon: assert flow in {-1,1} qnses,signs=[label.qns for label in labels],[1 if label.flow==flow else -1 for label in labels] if mode in {-1,0,+1}: qns=QuantumNumbers.kron(qnses,signs=signs) if mode==-1: qns=qns.sorted(history=False) if mode==+1: qns,permutation=qns.sorted(history=True) result=Label(identifier,qns,flow=flow,prime=prime) return (result,permutation) if mode==1 else result else: if mode==-2: qns=QuantumNumbers.ukron(qnses,signs=signs,history=False) if mode==+2: qns,record=QuantumNumbers.ukron(qnses,signs=signs,history=True) result=Label(identifier,qns,flow=flow,prime=prime) return (result,record) if mode==2 else result else: result=Label(identifier,np.product([label.qns for label in labels]),flow=None if flow is None else 0,prime=prime) return (result,None) if mode in (1,2) else result
def directsum(tensors,labels,axes=()): ''' The directsum of a couple of tensors. Parameters ---------- tensors : list of DTensor/STensor The tensors to be directsummed. labels : list of Label The labels of the directsum. axes : list of int, optional The axes along which the directsum is block diagonal. Returns ------- DTensor/STensor The directsum of the tensors. ''' TENSOR=next(iter(tensors)) assert TENSOR.ndim>len(axes) assert len({tensor.ndim for tensor in tensors})==1 assert len({tuple(tensor.shape[axis] for axis in axes) for tensor in tensors})==1 assert len({tuple(label.flow for label in tensor.labels) for tensor in tensors})==1 alters=set(range(TENSOR.ndim))-set(axes) dtype=np.find_common_type([],[tensor.dtype for tensor in tensors]) if isinstance(TENSOR,DTensor): assert len({tensor.qnon for tensor in tensors})==1 for alter in alters: labels[alter].qns=(QuantumNumbers.union if TENSOR.qnon else np.sum)([tensor.labels[alter].qns for tensor in tensors]) for axis in range(TENSOR.ndim): labels[axis].flow=TENSOR.labels[axis].flow for axis in axes: labels[axis].qns=TENSOR.labels[axis].qns data=np.zeros(tuple(len(label.qns) if isinstance(label.qns,QuantumNumbers) else label.qns for label in labels),dtype=dtype) slices=[slice(0,0,0) if axis in alters else slice(None,None,None) for axis in range(TENSOR.ndim)] for tensor in tensors: for alter in alters: slices[alter]=slice(slices[alter].stop,slices[alter].stop+tensor.shape[alter]) data[tuple(slices)]=tensor.data else: for alter in alters: labels[alter].qns=QuantumNumbers.union([tensor.labels[alter].qns for tensor in tensors]).sorted(history=False) for axis in range(TENSOR.ndim): labels[axis].flow=TENSOR.labels[axis].flow for axis in axes: labels[axis].qns=TENSOR.labels[axis].qns ods=[label.qns.toordereddict(protocol=QuantumNumbers.COUNTS) for label in labels] data,counts={},{alter:{} for alter in alters} for tensor in tensors: for alter in alters: for qn,count in tensor.labels[alter].qns.items(protocol=QuantumNumbers.COUNTS): counts[alter][qn]=counts[alter].get(qn,0)+count for qns,block in tensor.data.items(): if qns not in data: data[qns]=np.zeros(tuple(od[qn] for od,qn in zip(ods,qns)),dtype=dtype) slices=[slice(counts[i][qns[i]]-block.shape[i],counts[i][qns[i]],None) if i in alters else slice(None,None,None) for i in range(TENSOR.ndim)] data[qns][tuple(slices)]=block return type(TENSOR)(data,labels=labels)
def impsgrowth(self,sites,bonds,osvs,qn=0,ttype=None): ''' Infinite MPS growth. Parameters ---------- sites,bonds : list of Label/str The site/bond labels/identifiers of the new mps. osvs : 1d ndarray The old singular values. qn : QuantumNumber, optional The injected quantum number of the new mps. ttype : NOne/'D'/'S', optional Tensor type. 'D' for dense, 'S' for sparse and None for automatic. Returns ------- MPS The imps after growth. ''' if self.nsite>0: assert self.cut==self.nsite//2 and self.nsite%2==0 and len(sites)+1==len(bonds) ob,nb=self.nsite//2+1,(len(bonds)+1)//2 ns=nb-ob cms=self[ob-ns-1:ob+ns-1].impsprediction(sites[ob-1:2*nb-ob-1],bonds[ob-1:2*nb-ob],osvs,qn=qn,ttype=ttype) lms=MPS([copy(self[pos]) for pos in range(0,self.cut)]) rms=MPS([copy(self[pos]) for pos in range(self.cut,self.nsite)]) lms.relabel(sites[:ob-1],bonds[:ob]) rms.relabel(sites[-ob+1:],bonds[-ob:]) rms.qninject(qn) result=MPS(it.chain(lms,cms,rms),Lambda=cms.Lambda,cut=nb-1,ttype=ttype) else: bonds=copy(bonds) iqns,oqns=(QNS.mono(qn.zero()),QNS.mono(qn)) if isinstance(qn,QN) else (1,1) bonds[+0]=bonds[+0].replace(qns=iqns) if isinstance(bonds[+0],Label) else Label(bonds[+0],qns=iqns,flow=None) bonds[-1]=bonds[-1].replace(qns=oqns) if isinstance(bonds[-1],Label) else Label(bonds[-1],qns=oqns,flow=None) result=MPS.random(sites,bonds=bonds,cut=len(sites)//2,nmax=10,ttype=ttype or 'D') return result
def qngenerate(self, flow, axes, qnses, flows, tol=None): ''' Generate the quantum numbers of a tensor. Parameters ---------- flow : +1/-1 The flow of the unknown axis. axes : list of Label/int The labels/axes whose quantum number collections are known. qnses : list of QuantumNumbers The quantum number collections of the known axes. flows : tuple of int The flows of the quantum numbers of the known axes. tol : float64, optional The tolerance of the non-zeros. ''' axes = [ self.axis(axis) if isinstance(axis, Label) else axis for axis in axes ] assert flow in { -1, 1 } and len(axes) == len(qnses) == len(flows) == self.ndim - 1 type = next(iter(qnses)).type for axis, qns, f in zip(axes, qnses, flows): self.labels[axis].qns = qns self.labels[axis].flow = f assert qns.type is type unkownaxis = (set(xrange(self.ndim)) - set(axes)).pop() expansions = [qns.expansion() for qns in qnses] contents = [None] * self.shape[unkownaxis] for index in sorted(np.argwhere( np.abs(self.data) > hm.TOL if tol is None else tol), key=lambda index: index[unkownaxis]): qn = type.regularization( sum([ expansions[i][index[axis]] * f * (-flow) for i, (axis, f) in enumerate(zip(axes, flows)) ])) if contents[index[unkownaxis]] is None: contents[index[unkownaxis]] = qn else: assert (contents[index[unkownaxis]] == qn).all() self.labels[unkownaxis].qns = QuantumNumbers( 'G', (type, contents, np.arange(len(contents) + 1)), protocol=QuantumNumbers.INDPTR) self.labels[unkownaxis].flow = flow
def tompo(self, ttype='D', **karg): ''' Convert to the tensor-formatted mpo. Parameters ---------- ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. karg : dict with keys containing 'nsweep','method' and 'options' Please see MPO.compress for details. Returns ------- MPO The corresponding tensor-formatted MPO. ''' Ms = [] type = self[0][0, 0].site.qns.type if isinstance( self[0][0, 0].site.qns, QuantumNumbers) else None for pos, m in enumerate(self): dim = self.sites[pos].dim L = Label(self.bonds[pos], qns=m.shape[0], flow=0) U = self.sites[pos].replace(prime=True, qns=dim, flow=0) D = self.sites[pos].replace(qns=dim, flow=0) R = Label(self.bonds[pos + 1], qns=m.shape[1], flow=0) Ms.append( DTensor(np.zeros((m.shape[0], dim, dim, m.shape[1]), dtype=self.dtype), labels=[L, U, D, R])) for i, j in it.product(range(m.shape[0]), range(m.shape[1])): Ms[-1].data[i, :, :, j] = m[i, j].matrix result = MPO(Ms) result.compress(**karg) if type is not None: for pos in range(len(result)): lqns, sqns = QuantumNumbers.mono( type.zero()) if pos == 0 else result[pos - 1].labels[ MPO.R].qns, self.sites[pos].qns result[pos].qngenerate(flow=-1, axes=[MPO.L, MPO.U, MPO.D], qnses=[lqns, sqns, sqns], flows=[1, 1, -1]) if ttype == 'S': assert issubclass(type, QuantumNumber) for i, m in enumerate(result): m.qnsort() result[i] = m.tostensor() return result
def tompo(self,degfres,ttype='D'): ''' Convert an optstr to the full-formatted mpo. Parameters ---------- degfres : DegFreTree The tree of the site degrees of freedom. ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. Returns ------- MPO The corresponding MPO. ''' index=self[0].site.identifier type,layer=degfres.dtype,degfres.layers[degfres.level(index)-1] table,sites,bonds=degfres.table(layer),degfres.labels('S',layer),degfres.labels('O',layer) poses,matrices=set(table[opt.site.identifier] for opt in self),[opt.matrix for opt in self] ms,count=[],0 for pos in range(len(sites)): ndegfre=sites[pos].dim if issubclass(type,QuantumNumbers): L=Label(bonds[pos],qns=QuantumNumbers.mono(type.zero()) if pos==0 else ms[-1].labels[MPO.R].qns,flow=None) U=sites[pos].P D=copy(sites[pos]) R=Label(bonds[pos+1],qns=1,flow=None) else: L=Label(bonds[pos],qns=1,flow=0) U=sites[pos].P.replace(flow=0) D=sites[pos].replace(flow=0) R=Label(bonds[pos+1],qns=1,flow=0) if pos in poses: ms.append(DTensor(np.asarray(matrices[count],dtype=self.dtype).reshape((1,ndegfre,ndegfre,1)),labels=[L,U,D,R])) count+=1 else: ms.append(DTensor(np.identity(ndegfre,dtype=self.dtype).reshape((1,ndegfre,ndegfre,1)),labels=[L,U,D,R])) if issubclass(type,QuantumNumbers): ms[-1].qngenerate(flow=-1,axes=[MPO.L,MPO.U,MPO.D],qnses=[L.qns,U.qns,D.qns],flows=[1,1,-1]) if ttype=='S': assert issubclass(type,QuantumNumbers) for m in ms: m.qnsort() ms=[m.tostensor() for m in ms] return MPO(ms)
def tompo(self,ttype='D',**karg): ''' Convert to the tensor-formatted mpo. Parameters ---------- ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. karg : dict with keys containing 'nsweep','method' and 'options' Please see MPO.compress for details. Returns ------- MPO The corresponding tensor-formatted MPO. ''' Ms=[] type=self[0][0,0].site.qns.type if isinstance(self[0][0,0].site.qns,QuantumNumbers) else None for pos,m in enumerate(self): dim=self.sites[pos].dim L=Label(self.bonds[pos],qns=m.shape[0],flow=0) U=self.sites[pos].replace(prime=True,qns=dim,flow=0) D=self.sites[pos].replace(qns=dim,flow=0) R=Label(self.bonds[pos+1],qns=m.shape[1],flow=0) Ms.append(DTensor(np.zeros((m.shape[0],dim,dim,m.shape[1]),dtype=self.dtype),labels=[L,U,D,R])) for i,j in it.product(range(m.shape[0]),range(m.shape[1])): Ms[-1].data[i,:,:,j]=m[i,j].matrix result=MPO(Ms) result.compress(**karg) if type is not None: for pos in range(len(result)): lqns,sqns=QuantumNumbers.mono(type.zero()) if pos==0 else result[pos-1].labels[MPO.R].qns,self.sites[pos].qns result[pos].qngenerate(flow=-1,axes=[MPO.L,MPO.U,MPO.D],qnses=[lqns,sqns,sqns],flows=[1,1,-1]) if ttype=='S': assert issubclass(type,QuantumNumber) for i,m in enumerate(result): m.qnsort() result[i]=m.tostensor() return result
def to_mpo(self,degfres): ''' Convert an optstr to the full-formatted mpo. Parameters ---------- degfres : DegFreTree The tree of the site degrees of freedom. Returns ------- MPO The corresponding MPO. ''' index=self[0].site.identifier type,layer=degfres[index].type if degfres.mode=='QN' else None,degfres.layers[degfres.level(index)-1] table,sites,bonds=degfres.table(layer),degfres.labels('S',layer),degfres.labels('O',layer) poses,matrices=set(table[opt.site.identifier] for opt in self),[opt.matrix for opt in self] ms,count=[],0 for pos in xrange(len(sites)): ndegfre=sites[pos].dim if degfres.mode=='QN': L=bonds[pos].replace(qns=QuantumNumbers.mono(type.zero()) if pos==0 else ms[-1].labels[MPO.R].qns) U=sites[pos].P D=copy(sites[pos]) R=bonds[pos+1].replace(qns=1) else: L=bonds[pos].replace(qns=1,flow=0) U=sites[pos].P.replace(flow=0) D=sites[pos].replace(flow=0) R=bonds[pos+1].replace(qns=1,flow=0) if pos in poses: ms.append(DTensor(np.asarray(matrices[count],dtype=self.dtype).reshape((1,ndegfre,ndegfre,1)),labels=[L,U,D,R])) count+=1 else: ms.append(DTensor(np.identity(ndegfre,dtype=self.dtype).reshape((1,ndegfre,ndegfre,1)),labels=[L,U,D,R])) if degfres.mode=='QN': ms[-1].qngenerate(flow=-1,axes=[MPO.L,MPO.U,MPO.D],qnses=[L.qns,U.qns,D.qns],flows=[1,1,-1]) return MPO(ms)
def to_mpo(self,**karg): ''' Convert to the tensor-formatted mpo. Parameters ---------- karg : dict with keys containing 'nsweep','method' and 'options' Please see MPO.compress for details. Returns ------- MPO The corresponding tensor-formatted MPO. ''' Ms=[] type=self[0][0,0].site.qns.type if isinstance(self[0][0,0].site.qns,QuantumNumbers) else None for pos,m in enumerate(self): dim=self.sites[pos].dim if type is None: L=self.bonds[pos].replace(qns=m.shape[0],flow=0) U=self.sites[pos].replace(prime=True,flow=0) D=self.sites[pos].replace(flow=0) R=self.bonds[pos+1].replace(qns=m.shape[1],flow=0) else: L=copy(self.bonds[pos]) U=self.sites[pos].P D=self.sites[pos] R=copy(self.bonds[pos+1]) Ms.append(DTensor(np.zeros((m.shape[0],dim,dim,m.shape[1]),dtype=dtype),labels=[L,U,D,R])) for i,j in it.product(xrange(m.shape[0]),xrange(m.shape[1])): Ms[-1][i,:,:,j]=m[i,j].matrix result=MPO(Ms) result.compress(**karg) if type is not None: for pos in xrange(len(result)): lqns,sqns=QuantumNumbers.mono(type.zero()) if pos==0 else result[pos-1].labels[MPO.R].qns,self.sites[pos].qns result[pos].qngenerate(flow=-1,axes=[MPO.L,MPO.U,MPO.D],qnses=[lqns,sqns,sqns],flows=[1,1,-1]) return result
def tompo(self, degfres, ttype='D'): ''' Convert an optstr to the full-formatted mpo. Parameters ---------- degfres : DegFreTree The tree of the site degrees of freedom. ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. Returns ------- MPO The corresponding MPO. ''' index = self[0].site.identifier type, layer = degfres.dtype, degfres.layers[degfres.level(index) - 1] table, sites, bonds = degfres.table(layer), degfres.labels( 'S', layer), degfres.labels('O', layer) poses, matrices = set(table[opt.site.identifier] for opt in self), [opt.matrix for opt in self] ms, count = [], 0 for pos in range(len(sites)): ndegfre = sites[pos].dim if issubclass(type, QuantumNumbers): L = Label(bonds[pos], qns=QuantumNumbers.mono(type.zero()) if pos == 0 else ms[-1].labels[MPO.R].qns, flow=None) U = sites[pos].P D = copy(sites[pos]) R = Label(bonds[pos + 1], qns=1, flow=None) else: L = Label(bonds[pos], qns=1, flow=0) U = sites[pos].P.replace(flow=0) D = sites[pos].replace(flow=0) R = Label(bonds[pos + 1], qns=1, flow=0) if pos in poses: ms.append( DTensor(np.asarray(matrices[count], dtype=self.dtype).reshape( (1, ndegfre, ndegfre, 1)), labels=[L, U, D, R])) count += 1 else: ms.append( DTensor(np.identity(ndegfre, dtype=self.dtype).reshape( (1, ndegfre, ndegfre, 1)), labels=[L, U, D, R])) if issubclass(type, QuantumNumbers): ms[-1].qngenerate(flow=-1, axes=[MPO.L, MPO.U, MPO.D], qnses=[L.qns, U.qns, D.qns], flows=[1, 1, -1]) if ttype == 'S': assert issubclass(type, QuantumNumbers) for m in ms: m.qnsort() ms = [m.tostensor() for m in ms] return MPO(ms)
def random(sites, bonds=None, cut=None, nmax=None, dtype=np.float64): ''' Generate a random mps. Parameters ---------- sites : list of Label/int/QuantumNumbers The labels/number-of-degrees-of-freedom/quantum-numbers of the physical legs. bonds : optional * list of Label The labels of the virtual legs. * 2-list of QuantumNumber The quantum number of the first and last virtual legs. cut : int, optional The index of the connecting link. nmax : int, optional The maximum number of singular values to be kept. dtype : np.float64, np.complex128, optional The data type of the random mps. Returns ------- MPS The random mixed-canonical mps. ''' np.random.seed() sites = [ site if isinstance(site, Label) else Label( '__MPS_RANDOM_S_%s__' % i, qns=site) for i, site in enumerate(sites) ] if bonds is not None and all( isinstance(bond, Label) for bond in bonds): assert len(bonds) == len(sites) + 1 else: if bonds is not None: assert len(bonds) == 2 and isinstance( bonds[0], QN) and isinstance(bonds[1], QN) iqns, oqns = (QNS.mono(bonds[0]), QNS.mono(bonds[1])) if bonds is not None else (1, 1) bonds = [ Label( '__MPS_RANDOM_B_%s__' % i, qns=iqns if i == 0 else oqns if i == len(sites) else None) for i in xrange(len(sites) + 1) ] mode, shape = 'QN' if next(iter(sites)).qnon else 'NB', tuple( [site.dim for site in sites]) if mode == 'QN': result = 0 if dtype in (np.float32, np.float64): coeffs = np.random.random(nmax) else: coeffs = np.random.random(nmax) + 1j * np.random.random(nmax) for k, indices in enumerate( QNS.decomposition([site.qns for site in sites], bonds[-1].qns[0] - bonds[0].qns[0], method='monte carlo', nmax=nmax)): ms = [ np.array( [1.0 if i == index else 0.0 for i in xrange(site.dim)], dtype=dtype) for site, index in zip(sites, indices) ] result += MPS.productstate(ms, sites, copy(bonds)) * coeffs[k] else: ms = [] for i in xrange(len(sites)): if dtype in (np.float32, np.float64): ms.append(np.random.random((nmax, shape[i], nmax))) else: ms.append( np.random.random((nmax, shape[i], nmax)) + 1j * np.random.random((nmax, shape[i], nmax))) result = MPS(mode=mode, ms=ms, sites=sites, bonds=bonds) if cut is None: result.canonicalize(cut=len(sites) / 2, nmax=nmax) result._merge_ABL_() else: result.canonicalize(cut=cut, nmax=nmax) return result
def random(sites, bonds=None, cut=None, nmax=None, ttype='D', dtype=np.float64): ''' Generate a random mps. Parameters ---------- sites : list of Label/int/QuantumNumbers The labels/number-of-degrees-of-freedom/quantum-numbers of the physical legs. bonds : optional * list of Label/str The labels/identifiers of the virtual legs. * 2-list of QuantumNumber The quantum number of the first and last virtual legs. cut : int, optional The index of the connecting link. nmax : int, optional The maximum number of singular values to be kept. ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. dtype : np.float64, np.complex128, optional The data type of the random mps. Returns ------- MPS The random mixed-canonical mps. ''' np.random.seed() sites = [ site if isinstance(site, Label) else Label( '__MPS_RANDOM_S_%s__' % i, qns=site) for i, site in enumerate(sites) ] if bonds is None or not isinstance(bonds[+0], Label) or not isinstance( bonds[-1], Label): if bonds is not None: iqns = bonds[+0].qns if isinstance( bonds[+0], Label) else bonds[+0] if isinstance( bonds[+0], QNS) else QNS.mono(bonds[+0]) if isinstance( bonds[+0], QN) else 1 oqns = bonds[-1].qns if isinstance( bonds[-1], Label) else bonds[-1] if isinstance( bonds[-1], QNS) else QNS.mono(bonds[-1]) if isinstance( bonds[-1], QN) else 1 else: iqns, oqns = 1, 1 bonds = [ Label('__MPS_RANDOM_B_%s__' % i, None, None) for i in range(len(sites) + 1) ] bonds[+0] = bonds[+0].replace(qns=iqns) bonds[-1] = bonds[-1].replace(qns=oqns) else: assert len(bonds) == len(sites) + 1 bonds = [ bond if isinstance(bond, Label) else Label(bond, None, None) for bond in bonds ] qnon, shape = next(iter(sites)).qnon, tuple( [site.dim for site in sites]) if ttype == 'S': assert qnon if qnon: result = 0 if dtype in (np.float32, np.float64): coeffs = np.random.random(nmax) else: coeffs = np.random.random(nmax) + 1j * np.random.random(nmax) for k, indices in enumerate( QNS.decomposition([site.qns for site in sites], bonds[-1].qns[0] - bonds[+0].qns[0], method='monte carlo', nmax=nmax)): ms = [ np.array( [1.0 if i == index else 0.0 for i in range(site.dim)], dtype=dtype) for site, index in zip(sites, indices) ] result += MPS.productstate(ms, sites, copy(bonds)) * coeffs[k] if ttype == 'S': for m in result: m.qnsort() result = result.tosparse() else: ms = [] for i in range(len(sites)): if dtype in (np.float32, np.float64): ms.append(np.random.random((nmax, shape[i], nmax))) else: ms.append( np.random.random((nmax, shape[i], nmax)) + 1j * np.random.random((nmax, shape[i], nmax))) result = MPS.compose(ms=ms, sites=sites, bonds=bonds) if cut is None: result.canonicalize(cut=len(sites) / 2, nmax=nmax) result._merge_ABL_() else: result.canonicalize(cut=cut, nmax=nmax) return result
def relayer(self,degfres,layer,nmax=None,tol=None): ''' Construct a new mpo with the site labels living on a specific layer of degfres. Parameters ---------- degfres : DegFreTree The tree of the site degrees of freedom. layer : int/tuple-of-str The layer where the site labels live. nmax : int, optional The maximum number of singular values to be kept. tol : np.float64, optional The tolerance of the singular values. Returns ------- MPO The new mpo. ''' new=layer if type(layer) in (int,long) else degfres.layers.index(layer) old=degfres.level(next(iter(self)).labels[MPO.U].identifier)-1 assert 0<=new<len(degfres.layers) if new==old: return copy(self) else: olayer,nlayer=degfres.layers[old],degfres.layers[new] sites,bonds=[site.replace(flow=-1 if site.qnon else 0) for site in degfres.labels('S',nlayer)],degfres.labels('O',nlayer) Ms=[] if new<old: table=degfres.table(olayer) for i,site in enumerate(sites): ms,ups,dws=[],[],[] for index in degfres.descendants(site.identifier,generation=old-new): m=self[table[index]] ms.append(m) ups.append(m.labels[MPO.U]) dws.append(m.labels[MPO.D]) M=np.product(ms) o1,o2=M.labels[0],M.labels[-1] n1,n2=bonds[i].replace(qns=o1.qns,flow=o1.flow),bonds[i+1].replace(qns=o2.qns,flow=o2.flow) M.relabel(olds=[o1,o2],news=[n1,n2]) Ms.append(M.transpose([n1]+ups+dws+[n2]).merge((ups,site.P),(dws,site))) else: table=degfres.table(nlayer) for i,m in enumerate(self): if i>0: m=s*v*m L,U,D,R=m.labels indices=degfres.descendants(D.identifier,generation=new-old) start,stop=table[indices[0]],table[indices[-1]]+1 ups,dws,orders,labels,qnses=[],[],[],[],[] for j,site in enumerate(sites[start:stop]): ups.append(site.P) dws.append(site) orders.append(ups[-1]) orders.append(dws[-1]) qns=QuantumNumbers.kron([site.qns]*2,signs=(+1,-1)) if site.qnon else site.dim**2 labels.append(Label('__MPO_RELAYER_%s__'%j,qns=qns,flow=+1 if site.qnon else 0)) qnses.append(qns) S=Label('__MPO_RELAYER__',qns=(QuantumNumbers.kron if U.qnon else np.product)(qnses),flow=+1 if U.qnon else 0) m=m.split((U,ups),(D,dws)).transpose([L]+orders+[R]).merge((orders,S)) us,s,v=expanded_svd(m,L=[L],S=S,R=[R],E=labels,I=bonds[start+1:stop+1],cut=stop-start,nmax=nmax,tol=tol) for u,up,dw,label in zip(us,ups,dws,labels): Ms.append(u.split((label,[up,dw]))) Ms[-1]=Ms[-1]*s*v Ms[+0].relabel(olds=[MPO.L],news=[Ms[+0].labels[MPO.L].replace(identifier=bonds[+0].identifier)]) Ms[-1].relabel(olds=[MPO.R],news=[Ms[-1].labels[MPO.R].replace(identifier=bonds[-1].identifier)]) return MPO(Ms)
def svd(tensor,row,new,col,nmax=None,tol=None,returnerr=False,**karg): ''' Perform the svd. Parameters ---------- tensor : DTensor/STensor The tensor to be svded. row,col : list of Label or int The labels or axes to be merged as the row/column during the svd. new : Label The label for the singular values. nmax,tol,returnerr : Please refer to HamiltonianPy.Misc.Linalg.truncatedsvd for details. Returns ------- U,S,V : DTensor/STensor The result tensor. err : float, optional The truncation error. ''' assert len(row)+len(col)==tensor.ndim row=[r if isinstance(r,Label) else tensor.label(r) for r in row] col=[c if isinstance(c,Label) else tensor.label(c) for c in col] if isinstance(tensor,STensor): rowlabel,rowrecord=Label.union(row,'__TENSOR_SVD_ROW__',+1,mode=2) collabel,colrecord=Label.union(col,'__TENSOR_SVD_COL__',-1,mode=2) m=tensor.merge((row,rowlabel,rowrecord),(col,collabel,colrecord)).data us,ss,vs,qns=[],[],[],[] for (rowqn,colqn),block in m.items(): assert rowqn==colqn u,s,v=sl.svd(block,full_matrices=False,lapack_driver='gesvd')[0:3] us.append(u) ss.append(s) vs.append(v) qns.append(rowqn) temp=np.sort(np.concatenate([-s for s in ss])) nmax=len(temp) if nmax is None else min(nmax,len(temp)) tol=temp[nmax-1] if tol is None else min(-tol,temp[nmax-1]) Us,Ss,Vs,contents={},[],{},([],[]) for u,s,v,qn in zip(us,ss,vs,qns): cut=np.searchsorted(-s,tol,side='right') if cut>0: Us[(qn,qn)]=u[:,0:cut] Ss.append(s[0:cut]) Vs[(qn,qn)]=v[0:cut,:] contents[0].append(qn) contents[1].append(cut) new=new.replace(qns=QuantumNumbers('U',contents,protocol=QuantumNumbers.COUNTS),flow=None) U=STensor(Us,labels=[rowlabel,new.replace(flow=-1)]).split((rowlabel,row,rowrecord)) S=DTensor(np.concatenate(Ss),labels=[new]) V=STensor(Vs,labels=[new.replace(flow=+1),collabel]).split((collabel,col,colrecord)) if returnerr: err=(temp[nmax:]**2).sum() elif tensor.qnon: rowlabel,rowpermutation=Label.union(row,'__TENSOR_SVD_ROW__',+1,mode=1) collabel,colpermutation=Label.union(col,'__TENSOR_SVD_COL__',-1,mode=1) m=tensor.merge((row,rowlabel,rowpermutation),(col,collabel,colpermutation)).data rowod,colod=rowlabel.qns.toordereddict(),collabel.qns.toordereddict() us,ss,vs,qns=[],[],[],[] for qn in filter(lambda key: key in rowod,colod): u,s,v=sl.svd(m[rowod[qn],colod[qn]],full_matrices=False,lapack_driver='gesvd')[0:3] us.append(u) ss.append(s) vs.append(v) qns.append(qn) temp=np.sort(np.concatenate([-s for s in ss])) nmax=len(temp) if nmax is None else min(nmax,len(temp)) tol=temp[nmax-1] if tol is None else min(-tol,temp[nmax-1]) Us,Ss,Vs,contents=[],[],[],([],[]) for u,s,v,qn in zip(us,ss,vs,qns): cut=np.searchsorted(-s,tol,side='right') if cut>0: Us.append(u[:,0:cut]) Ss.append(s[0:cut]) Vs.append(v[0:cut,:]) contents[0].append(qn) contents[1].append(cut) S=np.concatenate(Ss) new=new.replace(qns=QuantumNumbers('U',contents,protocol=QuantumNumbers.COUNTS),flow=None) od=new.qns.toordereddict() U=np.zeros((rowlabel.dim,new.dim),dtype=tensor.dtype) V=np.zeros((new.dim,collabel.dim),dtype=tensor.dtype) for u,v,qn in zip(Us,Vs,od): U[rowod[qn],od[qn]]=u V[od[qn],colod[qn]]=v U=DTensor(U,labels=[rowlabel,new.replace(flow=-1)]).split((rowlabel,row,np.argsort(rowpermutation))) S=DTensor(S,labels=[new]) V=DTensor(V,labels=[new.replace(flow=+1),collabel]).split((collabel,col,np.argsort(colpermutation))) if returnerr: err=(temp[nmax:]**2).sum() else: rowlabel=Label('__TENSOR_SVD_ROW__',qns=np.product([label.dim for label in row])) collabel=Label('__TENSOR_SVD_COL__',qns=np.product([label.dim for label in col])) m=tensor.merge((row,rowlabel),(col,collabel)).data temp=hm.truncatedsvd(m,full_matrices=False,nmax=nmax,tol=tol,returnerr=returnerr,**karg) u,s,v=temp[0],temp[1],temp[2] new=new.replace(qns=len(s),flow=None) U=DTensor(u,labels=[rowlabel,new.replace(flow=0)]).split((rowlabel,row)) S=DTensor(s,labels=[new]) V=DTensor(v,labels=[new.replace(flow=0),collabel]).split((collabel,col)) if returnerr: err=temp[3] return (U,S,V,err) if returnerr else (U,S,V)
def partitionedsvd(tensor,L,new,R,nmax=None,tol=None,ttype='D',returnerr=False): ''' Partition a 1d-tensor according to L and R and then perform the Schmitt decomposition. Parameters ---------- tensor : DTensor The tensor to be partitioned svded. L,R : Label The left/right part of the partition. new : Label The label for the singular values. ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. nmax,tol,returnerr : Please refer to HamiltonianPy.Misc.Linalg.truncatedsvd for details. Returns ------- U,S,V : DTensor/STensor The Schmitt decomposition of the 1d tensor. err : float, optional The truncation error. ''' assert tensor.ndim==1 and ttype in 'DS' if tensor.qnon: data,qns=tensor.data,tensor.labels[0].qns assert qns.num==1 and sl.norm(qns.contents)<10**-6 lod,rod=L.qns.toordereddict(),R.qns.toordereddict() us,ss,vs,qns,count=[],[],[],[],0 for qn in filter(lambda key: key in lod,rod): s1,s2=lod[qn],rod[qn] n1,n2=s1.stop-s1.start,s2.stop-s2.start u,s,v=sl.svd(data[count:count+n1*n2].reshape((n1,n2)),full_matrices=False,lapack_driver='gesvd')[0:3] us.append(u) ss.append(s) vs.append(v) qns.append(qn) count+=n1*n2 temp=np.sort(np.concatenate([-s for s in ss])) nmax=len(temp) if nmax is None else min(nmax,len(temp)) tol=temp[nmax-1] if tol is None else min(-tol,temp[nmax-1]) if ttype=='D': Us,Ss,Vs,contents=[],[],[],([],[]) for u,s,v,qn in zip(us,ss,vs,qns): cut=np.searchsorted(-s,tol,side='right') if cut>0: Us.append(u[:,0:cut]) Ss.append(s[0:cut]) Vs.append(v[0:cut,:]) contents[0].append(qn) contents[1].append(cut) new=new.replace(qns=QuantumNumbers('U',contents,QuantumNumbers.COUNTS),flow=None) nod=new.qns.toordereddict() U=np.zeros((L.dim,new.dim),dtype=tensor.dtype) S=np.concatenate(Ss) V=np.zeros((new.dim,R.dim),dtype=tensor.dtype) for u,v,qn in zip(Us,Vs,nod): U[lod[qn],nod[qn]]=u V[nod[qn],rod[qn]]=v U=DTensor(U,labels=[L,new.replace(flow=-1)]) S=DTensor(S,labels=[new]) V=DTensor(V,labels=[new.replace(flow=+1),R]) else: Us,Ss,Vs,contents={},[],{},([],[]) for u,s,v,qn in zip(us,ss,vs,qns): cut=np.searchsorted(-s,tol,side='right') if cut>0: Us[(qn,qn)]=u[:,0:cut] Ss.append(s[0:cut]) Vs[(qn,qn)]=v[0:cut,:] contents[0].append(qn) contents[1].append(cut) new=new.replace(qns=QuantumNumbers('U',contents,QuantumNumbers.COUNTS),flow=None) U=STensor(Us,labels=[L,new.replace(flow=-1)]) S=DTensor(np.concatenate(Ss),labels=[new]) V=STensor(Vs,labels=[new.replace(flow=+1),R]) if returnerr: err=(temp[nmax:]**2).sum() else: m=tensor.data.reshape((L.dim,R.dim)) data=hm.truncatedsvd(m,full_matrices=False,nmax=nmax,tol=tol,returnerr=returnerr) new=new.replace(qns=len(data[1]),flow=None) U=DTensor(data[0],labels=[L,new.replace(flow=0)]) S=DTensor(data[1],labels=[new]) V=DTensor(data[2],labels=[new.replace(flow=0),R]) if returnerr: err=data[3] return (U,S,V,err) if returnerr else (U,S,V)
def random(sites,bonds=None,cut=None,nmax=None,ttype='D',dtype=np.float64): ''' Generate a random mps. Parameters ---------- sites : list of Label/int/QuantumNumbers The labels/number-of-degrees-of-freedom/quantum-numbers of the physical legs. bonds : optional * list of Label/str The labels/identifiers of the virtual legs. * 2-list of QuantumNumber The quantum number of the first and last virtual legs. cut : int, optional The index of the connecting link. nmax : int, optional The maximum number of singular values to be kept. ttype : 'D'/'S', optional Tensor type. 'D' for dense and 'S' for sparse. dtype : np.float64, np.complex128, optional The data type of the random mps. Returns ------- MPS The random mixed-canonical mps. ''' np.random.seed() sites=[site if isinstance(site,Label) else Label('__MPS_RANDOM_S_%s__'%i,qns=site) for i,site in enumerate(sites)] if bonds is None or not isinstance(bonds[+0],Label) or not isinstance(bonds[-1],Label): if bonds is not None: iqns=bonds[+0].qns if isinstance(bonds[+0],Label) else bonds[+0] if isinstance(bonds[+0],QNS) else QNS.mono(bonds[+0]) if isinstance(bonds[+0],QN) else 1 oqns=bonds[-1].qns if isinstance(bonds[-1],Label) else bonds[-1] if isinstance(bonds[-1],QNS) else QNS.mono(bonds[-1]) if isinstance(bonds[-1],QN) else 1 else: iqns,oqns=1,1 bonds=[Label('__MPS_RANDOM_B_%s__'%i,None,None) for i in range(len(sites)+1)] bonds[+0]=bonds[+0].replace(qns=iqns) bonds[-1]=bonds[-1].replace(qns=oqns) else: assert len(bonds)==len(sites)+1 bonds=[bond if isinstance(bond,Label) else Label(bond,None,None) for bond in bonds] qnon,shape=next(iter(sites)).qnon,tuple([site.dim for site in sites]) if ttype=='S': assert qnon if qnon: result=0 if dtype in (np.float32,np.float64): coeffs=np.random.random(nmax) else: coeffs=np.random.random(nmax)+1j*np.random.random(nmax) for k,indices in enumerate(QNS.decomposition([site.qns for site in sites],bonds[-1].qns[0]-bonds[+0].qns[0],method='monte carlo',nmax=nmax)): ms=[np.array([1.0 if i==index else 0.0 for i in range(site.dim)],dtype=dtype) for site,index in zip(sites,indices)] result+=MPS.productstate(ms,sites,copy(bonds))*coeffs[k] if ttype=='S': for m in result: m.qnsort() result=result.tosparse() else: ms=[] for i in range(len(sites)): if dtype in (np.float32,np.float64): ms.append(np.random.random((nmax,shape[i],nmax))) else: ms.append(np.random.random((nmax,shape[i],nmax))+1j*np.random.random((nmax,shape[i],nmax))) result=MPS.compose(ms=ms,sites=sites,bonds=bonds) if cut is None: result.canonicalize(cut=len(sites)/2,nmax=nmax) result._merge_ABL_() else: result.canonicalize(cut=cut,nmax=nmax) return result
def directsum(tensors, labels, axes=()): ''' The directsum of a couple of tensors. Parameters ---------- tensors : list of DTensor/STensor The tensors to be directsummed. labels : list of Label The labels of the directsum. axes : list of int, optional The axes along which the directsum is block diagonal. Returns ------- DTensor/STensor The directsum of the tensors. ''' TENSOR = next(iter(tensors)) assert TENSOR.ndim > len(axes) assert len({tensor.ndim for tensor in tensors}) == 1 assert len( {tuple(tensor.shape[axis] for axis in axes) for tensor in tensors}) == 1 assert len( {tuple(label.flow for label in tensor.labels) for tensor in tensors}) == 1 alters = set(xrange(TENSOR.ndim)) - set(axes) if isinstance(TENSOR, DTensor): assert len({tensor.qnon for tensor in tensors}) == 1 shape, dtypes = [ 0 if axis in alters else TENSOR.shape[axis] for axis in xrange(TENSOR.ndim) ], [] for tensor in tensors: for alter in alters: shape[alter] += tensor.shape[alter] dtypes.append(tensor.dtype) data = np.zeros(tuple(shape), dtype=np.find_common_type([], dtypes)) slices = [ slice(0, 0, 0) if axis in alters else slice(None, None, None) for axis in xrange(TENSOR.ndim) ] for tensor in tensors: for alter in alters: slices[alter] = slice(slices[alter].stop, slices[alter].stop + tensor.shape[alter]) data[tuple(slices)] = tensor.data for alter in alters: labels[alter].qns = (QuantumNumbers.union if TENSOR.qnon else np.sum)([ tensor.labels[alter].qns for tensor in tensors ]) else: content = {} for qns, block in it.chain(*tuple(tensor.iteritems() for tensor in tensors)): if qns not in content: content[qns] = ([ 0 if axis in alters else block.shape[axis] for axis in xrange(block.ndim) ], [], []) for alter in alters: content[qns][0] += block.shape[alter] content[qns][1].append(block.dtype) content[qns][2].append(block) data = {} for qns, (shape, dtypes, blocks) in content.iteritems(): data[qns] = np.zeros(tuple(shape), dtype=np.find_common_type([], dtypes)) slices = [ slice(0, 0, 0) if axis in alters else slice(None, None, None) for axis in xrange(TENSOR.ndim) ] for block in blocks: for alter in alters: slices[alter] = slice( slices[alter].stop, slices[alter].stop + block.shape[alter]) data[tupe(slices)] = block for alter in alters: labels[alter].qns = QuantumNumbers.union([ tensor.labels[alter].qns for tensor in tensors ]).sorted(history=False) for axis in xrange(TENSOR.ndim): labels[axis].flow = TENSOR.labels[axis].flow for axis in axes: labels[axis].qns = tensor.labels[axis].qns return type(TENSOR)(data, labels=labels)
def relayer(self,degfres,layer,nmax=None,tol=None): ''' Construct a new mpo with the site labels living on a specific layer of degfres. Parameters ---------- degfres : DegFreTree The tree of the site degrees of freedom. layer : int/tuple-of-str The layer where the site labels live. nmax : int, optional The maximum number of singular values to be kept. tol : np.float64, optional The tolerance of the singular values. Returns ------- MPO The new mpo. ''' assert all(isinstance(m,DTensor) for m in self) new=layer if type(layer) is int else degfres.layers.index(layer) old=degfres.level(next(iter(self)).labels[MPO.U].identifier)-1 assert 0<=new<len(degfres.layers) if new==old: return copy(self) else: olayer,nlayer=degfres.layers[old],degfres.layers[new] sites,bonds=[site.replace(flow=-1 if site.qnon else 0) for site in degfres.labels('S',nlayer)],degfres.labels('O',nlayer) Ms=[] if new<old: table=degfres.table(olayer) for i,site in enumerate(sites): ms,ups,dws=[],[],[] for index in degfres.descendants(site.identifier,generation=old-new): m=self[table[index]] ms.append(m) ups.append(m.labels[MPO.U]) dws.append(m.labels[MPO.D]) M=np.product(ms) o1,o2=M.labels[0],M.labels[-1] n1,n2=o1.replace(identifier=bonds[i]),o2.replace(identifier=bonds[i+1]) M.relabel(olds=[o1,o2],news=[n1,n2]) Ms.append(M.transpose([n1]+ups+dws+[n2]).merge((ups,site.P),(dws,site))) else: table,s,v=degfres.table(nlayer),1.0,1.0 for i,m in enumerate(self): m=s*v*m L,U,D,R=m.labels indices=degfres.descendants(D.identifier,generation=new-old) start,stop=table[indices[0]],table[indices[-1]]+1 ups,dws,orders,labels,qnses=[],[],[],[],[] for j,site in enumerate(sites[start:stop]): ups.append(site.P) dws.append(site) orders.append(ups[-1]) orders.append(dws[-1]) qns=QuantumNumbers.kron([site.qns]*2,signs=(+1,-1)) if site.qnon else site.dim**2 labels.append(Label('__MPO_RELAYER_%s__'%j,qns=qns,flow=+1 if site.qnon else 0)) qnses.append(qns) S=Label('__MPO_RELAYER__',qns=(QuantumNumbers.kron if U.qnon else np.product)(qnses),flow=+1 if U.qnon else 0) m=m.split((U,ups),(D,dws)).transpose([L]+orders+[R]).merge((orders,S)) us,s,v=expandedsvd(m,L=[L],S=S,R=[R],E=labels,I=[Label(bond,None,None) for bond in bonds[start+1:stop+1]],cut=stop-start,nmax=nmax,tol=tol) for u,up,dw,label in zip(us,ups,dws,labels): Ms.append(u.split((label,[up,dw]))) Ms[-1]=Ms[-1]*s*v Ms[+0].relabel(olds=[MPO.L],news=[Ms[+0].labels[MPO.L].replace(identifier=bonds[+0])]) Ms[-1].relabel(olds=[MPO.R],news=[Ms[-1].labels[MPO.R].replace(identifier=bonds[-1])]) return MPO(Ms)