def test_TensorSumOutLeg(self): tensor = Tensor(data = np.zeros((3, 4, 5), dtype = np.float64), labels = ['abc', 'def', 'abc']) tensor.sumOutLegByLabel('abc') tensor.reArrange(['def', 'abc']) self.assertTupleEqual(tensor.shape, (4, 5)) tensor = Tensor(data = np.zeros((3, 4, 5), dtype = np.float64), labels = ['abc', 'def', 'abc']) tensor.sumOutLegByLabel('abc', backward = True) tensor.reArrange(['def', 'abc']) self.assertTupleEqual(tensor.shape, (4, 3)) tensor = Tensor(data = np.zeros((3, 4, 5), dtype = np.float64), labels = ['abc', 'def', 'abc']) with self.assertWarns(RuntimeWarning): tensor.sumOutLegByLabel('abd') tensor.reArrange(['abc', 'abc', 'def']) self.assertTupleEqual(tensor.shape, (3, 5, 4)) a = Tensor(data = np.zeros((3, 4, 5), dtype = np.float64), labels = ['a3', 'a4', 'a5']) b = Tensor(data = np.zeros((4, 5, 6), dtype = np.float64), labels = ['b4', 'b5', 'b6']) makeLink('a4', 'b4', a, b) with self.assertWarns(RuntimeWarning): a.sumOutLegByLabel('a4') a.reArrange(['a5', 'a3']) self.assertEqual(a.shape, (5, 3)) bLeg = b.getLeg('b4') with self.assertWarns(RuntimeWarning): a.sumOutLeg(bLeg)
def test_MPSContraction(self): mpsA = self.createMPSA(tensorLikeFlag=False) mpsB = self.createMPSB(tensorLikeFlag=False) tensorA2 = mpsA.getTensor(2) tensorB2 = mpsB.getTensor(2) makeLink('o', 'o', tensorA2, tensorB2) # print(mpsA, mpsB) mps = contractMPS(mpsA, mpsB) # print(mps) mps.canonicalize(idx=2) self.assertTrue(mps.checkCanonical()) self.assertTrue(mps.n, 7) # 4 + 5 - 2 self.assertTrue(mps.activeIdx, 2) mpsA = self.createMPSA(tensorLikeFlag=True) mpsB = self.createMPSB(tensorLikeFlag=True) tensorA2 = mpsA.getTensor(2) tensorB2 = mpsB.getTensor(2) makeLink('o', 'o', tensorA2, tensorB2) # print(mpsA, mpsB) mps = contractMPS(mpsA, mpsB) # print(mps) mps.canonicalize(idx=2) self.assertTrue(mps.checkCanonical()) self.assertTrue(mps.n, 7) # 4 + 5 - 2 self.assertTrue(mps.activeIdx, 2)
def iterate(self): # print('iterate:') if (self.iterateFTN is None): self.iterateFTN = triangleContractFTN() self.a.addTensorTag('a') self.b.addTensorTag('b') dof = self.a.degreeOfFreedom makeLink(self.a.getLeg('a-1'), self.b.getLeg('b-1')) iTensor = contractTwoTensors(self.a, self.b) a2Dim, b2Dim, a3Dim, b3Dim = iTensor.shapeOfLabels( ['a-2', 'b-2', 'a-3', 'b-3']) iMat = iTensor.toMatrix(rows=['a-2', 'b-2'], cols=['a-3', 'b-3']) u, v, error = SVDDecomposition(iMat, self.chi) self.errors.append(error) # print(u.shape, v.shape) # print(iTensor.shape) u = xplib.xp.reshape(u, (a2Dim, b2Dim, u.shape[1])) v = xplib.xp.reshape(v, (a3Dim, b3Dim, v.shape[1])) uTensor = makeTriangleTensor(u, labels=['2', '3', '1']) vTensor = makeTriangleTensor(v, labels=['2', '3', '1']) self.a = self.iterateFTN.contract(makeTriangleTensorDict(uTensor)) self.b = self.iterateFTN.contract(makeTriangleTensorDict(vTensor)) self.a.degreeOfFreedom = dof * 3 self.b.degreeOfFreedom = dof * 3 # self.normalizeTensors() self.appendToArchive()
def contract(self, tensorDict, removeTensorTag=True): self.lock() if (isinstance(tensorDict, dict)): tensorDict = TensorDict(tensorDict) # print(tensorDict.tensors) assert funcs.compareLists( self.tensorNames, list(tensorDict.tensors.keys()) ), "Error: input tensorDict {} does not compatible with FTN {}.".format( list(tensorDict.tensors.keys()), self.tensorNames) localTensors = dict() for name in tensorDict.tensors: localTensors[name] = tensorDict.tensors[name].copy() for tensor, legName, newName in self.changeNameBefore: localTensors[tensor].renameLabel(legName, newName) for name in localTensors: localTensors[name].addTensorTag(name) for leg1, leg2 in self.links: tensorA, legA = leg1 tensorB, legB = leg2 makeLink(legA, legB, localTensors[tensorA], localTensors[tensorB]) self.changed = False self.loadBondDims(localTensors) tensorList = [localTensors[name] for name in self.tensorNames] if (self.optimalSeq is None) or (self.realCost and self.changed): # print('getting optimal seq') self.optimalSeq = generateOptimalSequence( tensorList, bf=False, typicalDim=self.typicalDim) # print('optimal seq = {}'.format(self.optimalSeq)) res = contractWithSequence(tensorList, seq=self.optimalSeq, inplace=True) # print(res) # print(tensorDict.tensors) for labelList, newLabel in self.outProductAfter: labelList = [ self._dealOutProductLabel(label) for label in labelList ] # print(labelList, newLabel) # print(res.labels) res.outProduct(labelList, 'res-' + newLabel) # print(res.labels) for tensor, legName, newName in self.changeNameAfter: res.renameLabel(tensor + '-' + legName, tensor + '-' + newName) if (removeTensorTag): res.removeTensorTag() return res
def twoTensorsContraction(): shapeA = (3, 4, 5) shapeB = (5, 4) a = Tensor(labels=['a3', 'a4', 'a5'], data=np.ones(shapeA)) b = Tensor(labels=['b5', 'b4'], data=np.ones(shapeB)) makeLink('a4', 'b4', a, b) makeLink('a5', 'b5', a, b) c = a @ b print(c)
def copyTensorList(tensorList, tensorLikeFlag=False, linkOutgoingBonds=False): """ Make a copy of tensor list. Usually used for not-in-place tensor contraction. The difference from [x.copy() for x in tensorList]: it will copy all the bonds between tensors in tensorList. Parameters ---------- tensorList : list of Tensor tensorLikeFlag : bool, default False Whether we are copying a set of TensorLikes(instead of tensors). linkOutgoingBonds : bool, default False Whether we connect the bonds outgoing(not in tensorList). If true, then all the old bonds will be replaced with new bonds generated for the copies. This is not recommended behavior since the function changes something it should not touch, but this is saved for future usage. Returns ------- resTensroList : list of Tensor A copy of the given tensor list. """ resTensorList = [] tensorMap = dict() for tensor in tensorList: if (tensorLikeFlag): resTensorList.append(tensor.toTensorLike()) else: resTensorList.append(tensor.copy()) tensorMap[tensor] = resTensorList[-1] # use the objects themselves as key, so no worry about double name # print(tensorMap) addedBonds = set() for tensor in tensorList: for leg, newLeg1 in zip(tensor.legs, tensorMap[tensor].legs): if (leg.bond is not None) and (leg.bond not in addedBonds): leg2 = leg.anotherSide() addedBonds.add(leg.bond) if (leg2.tensor not in tensorList): if (linkOutgoingBonds): del leg.bond makeLink(newLeg1, leg2) continue newTensorB = tensorMap[leg2.tensor] newLeg2 = newTensorB.legs[leg2.tensor.legs.index(leg2)] makeLink(newLeg1, newLeg2) # no consideration about leg name, only from their relative positions return resTensorList
def createMPSA(self, tensorLikeFlag=False): tensor1L = Tensor(shape=(3, 3), labels=['o', 'internal'], tensorLikeFlag=tensorLikeFlag) tensor11 = Tensor(shape=(3, 5, 4), labels=['itl', 'oo', 'itr'], tensorLikeFlag=tensorLikeFlag) tensor12 = Tensor(shape=(4, 2, 4), labels=['itl', 'oo', 'itr'], tensorLikeFlag=tensorLikeFlag) tensor13 = Tensor(shape=(4, 3, 2), labels=['itl', 'oo', 'itr'], tensorLikeFlag=tensorLikeFlag) tensor1R = Tensor(shape=(2, 5), labels=['internal', 'o'], tensorLikeFlag=tensorLikeFlag) makeLink('internal', 'itl', tensor1L, tensor11) makeLink('itr', 'itl', tensor11, tensor12) makeLink('itr', 'itl', tensor12, tensor13) makeLink('itr', 'internal', tensor13, tensor1R) tensorsA = [tensor1L, tensor11, tensor12, tensor13, tensor1R] mpsA = FreeBoundaryMPS(tensorList=tensorsA, chi=16) return mpsA
def IsingTNFromUndirectedGraph(g): """ Create a tensor network of Ising model basing on an undirected graph. Parameters ---------- g : UndirectedGraph The site-graph to add interaction. The weights represent the betaJ on each edge. Returns ------- list of Tensor A tensor network, each of the tensors represents one site, and the contraction of this tensor network will give the exact partition function Z. """ funcName = 'CTL.models.Ising.IsingTNFromUndirectedGraph' assert (isinstance(g, UndirectedGraph)), funcs.errorMessage( err= "only UndirectedGraph can be trasferred to Ising tensor network, {} obtained." .format(g), location=funcName) nodes = g.v edgeIndex = dict() for ei, edge in enumerate(g.getEdges()): edgeIndex[edge] = ei def getLegName(edge, toV): return str(toV.index) + '-' + str(edgeIndex[edge]) def getLabels(v): return [getLegName(edge=e, toV=e.anotherSide(v)) for e in v.edges] def getWeights(v): return [e.weight for e in v.edges] tensors = [ IsingSiteTensor(betaJ=getWeights(v), dim=len(v.edges), labels=getLabels(v)) for v in nodes ] for ei, edge in enumerate(g.getEdges()): v1, v2 = edge.vertices idx1, idx2 = v1.index, v2.index makeLink(getLegName(edge=edge, toV=v2), getLegName(edge=edge, toV=v1), tensors[idx1], tensors[idx2]) return tensors
def createCompleteGraph(self, n, dimRange=(2, 3)): low, high = dimRange dims = np.random.randint(low=low, high=high, size=(n, n)) for i in range(n): for j in range(i, n): dims[j][i] = dims[i][j] tensors = [] for i in range(n): shape = tuple([dims[i][j] for j in range(n) if (j != i)]) labels = [str(j) for j in range(n) if (j != i)] tensor = Tensor(shape=shape, labels=labels) tensors.append(tensor) for i in range(n): for j in range(i + 1, n): makeLink(str(j), str(i), tensors[i], tensors[j]) return tensors
def test_MPSMerge(self): mpsA = self.createMPSA() mpsB = self.createMPSB() makeLink('o', 'o', mpsA.getTensor(1), mpsB.getTensor(1)) makeLink('o', 'o', mpsA.getTensor(4), mpsB.getTensor(3)) # print(mpsA, mpsB) mergeMPS(mpsA, mpsB) # print(mpsA, mpsB) self.assertTrue(mpsA.checkCanonical(excepIdx=mpsA.n - 1)) self.assertTrue(mpsB.checkCanonical(excepIdx=mpsB.n - 1)) self.assertEqual(mpsA.n, 4) self.assertEqual(mpsB.n, 3) mpsA.moveTensor(mpsA.n - 1, 1) mpsB.moveTensor(mpsB.n - 1, 0) mps = contractMPS(mpsB, mpsA) mps.canonicalize(idx=2) self.assertTrue(mps.checkCanonical()) self.assertEqual(mps.n, 5) mpsA = self.createMPSFromDim(dims=[3, 4, 5, 5, 2]) mpsB = self.createMPSFromDim(dims=[2, 5, 3, 3, 4]) mpsA.canonicalize(idx=1) mpsB.canonicalize(idx=2) # print(mpsA, mpsB) makeLink('o', 'o', mpsA.getTensor(1), mpsB.getTensor(4)) makeLink('o', 'o', mpsA.getTensor(0), mpsB.getTensor(2)) makeLink('o', 'o', mpsB.getTensor(0), mpsA.getTensor(4)) mergeMPS(mpsA, mpsB, beginFlag=True) # print(mpsA, mpsB) self.assertEqual(mpsA.n, 3) self.assertEqual(mpsB.n, 3) mps = contractMPS(mpsA, mpsB) self.assertEqual(mps.n, 4)
def createMPSFromDim(self, dims, itbRange=(3, 10), tensorLikeFlag=False, chi=16): # internal bonds will be automaticall lastDim = -1 tensors = [] n = len(dims) if (n == 1): tensors.append( Tensor(shape=(dims[0], ), labels=['o'], tensorLikeFlag=tensorLikeFlag)) return FreeBoundaryMPS(tensorList=tensors, chi=chi) itbLow, itbHigh = itbRange bondDim = np.random.randint(low=itbLow, high=itbHigh) tensor = Tensor(shape=(dims[0], bondDim), labels=['o', 'r'], tensorLikeFlag=tensorLikeFlag) tensors.append(tensor) lastDim = bondDim for i in range(1, n - 1): bondDim = np.random.randint(low=itbLow, high=itbHigh) newTensor = Tensor(shape=(lastDim, dims[i], bondDim), labels=['l', 'o', 'r'], tensorLikeFlag=tensorLikeFlag) tensors.append(newTensor) makeLink('r', 'l', tensor, newTensor) lastDim = bondDim tensor = newTensor newTensor = Tensor(shape=(lastDim, dims[-1]), labels=['l', 'o'], tensorLikeFlag=tensorLikeFlag) tensors.append(newTensor) makeLink('r', 'l', tensor, newTensor) return FreeBoundaryMPS(tensorList=tensors, chi=chi)
def example(): shapeA = (300, 400, 50) shapeB = (300, 600) shapeC = (400, 600, 50) a = Tensor(labels=['a3', 'b4', 'c5'], data=np.ones(shapeA)) b = Tensor(labels=['a3', 'd6'], data=np.ones(shapeB)) c = Tensor(labels=['e4', 'd6', 'c5'], data=np.ones(shapeC)) # create tensors with labels makeLink('a3', 'a3', a, b) makeLink('c5', 'c5', a, c) makeLink('d6', 'd6', b, c) # make links via labels # note that labels can also be made between the "Leg" objects to avoid reused leg names, but here for simplicity from leg names # now we have a tensor network, we can generate the optimal sequence of this tensor list optimalSeq = generateOptimalSequence([a, b, c]) print('optimal contraction sequence = {}'.format(optimalSeq)) # if we do not have any knowledge in prior, we can contract the tensor list like res = contractTensorList([a, b, c]) print(res) # if you already have a good sequence to use res, cost = contractAndCostWithSequence([a, b, c], seq=optimalSeq) print(res) print('contraction cost = {}'.format(cost)) # if you want to save time / space by contract in place(note that after this you cannot contract them again, since their bonds between have been broken): res = contractWithSequence([a, b, c], seq=optimalSeq, inplace=True) print(res) print('')
def test_TensorGraph(self): shapeA = (300, 4, 5) shapeB = (300, 6) shapeC = (4, 6, 5) a = Tensor(shape=shapeA, labels=['a300', 'b4', 'c5'], data=np.ones(shapeA)) b = Tensor(shape=shapeB, labels=['a300', 'd6'], data=np.ones(shapeB)) c = Tensor(shape=shapeC, labels=['b4', 'd6', 'c5'], data=np.ones(shapeC)) makeLink(a.getLeg('a300'), b.getLeg('a300')) makeLink(a.getLeg('b4'), c.getLeg('b4')) makeLink(a.getLeg('c5'), c.getLeg('c5')) makeLink(b.getLeg('d6'), c.getLeg('d6')) tensorList = [a, b, c] tensorGraph = makeTensorGraph(tensorList) # if we use typical dim, then contract between 0 and 2 first is preferred # and this is not true if we consider the real bond dimension 300 seq = tensorGraph.optimalContractSequence(typicalDim=None) self.assertListEqual(seq, [(0, 1), (2, 0)]) self.assertEqual(tensorGraph.optimalCostResult(), 36120) seq = tensorGraph.optimalContractSequence(typicalDim=None, bf=True) self.assertEqual(tensorGraph.optimalCostResult(), 36120) # res1 = contractWithSequence(tensorList, seq = seq) seq = tensorGraph.optimalContractSequence(typicalDim=10) self.assertListEqual(seq, [(0, 2), (1, 0)]) self.assertEqual(tensorGraph.optimalCostResult(), 10100) seq = tensorGraph.optimalContractSequence(typicalDim=10, bf=True) self.assertEqual(tensorGraph.optimalCostResult(), 10100) res2 = contractWithSequence(tensorList, seq=seq) self.assertEqual( res2.a**2, funcs.tupleProduct(shapeA) * funcs.tupleProduct(shapeB) * funcs.tupleProduct(shapeC))
def triangleTensorTrace(a, b): """ Take the trace of two triangle tensors, usually work for the end of the RG of triangular lattice. Parameters ---------- a, b : Tensor Two tensors of the triangular shape(namely, 3 legs ['1', '2', '3']). Returns ------- Tensor The trace tensor of the two tensors. If a and b only have legs ['1', '2', '3'], then shapeless(so the value can be obtained as res.single()), otherwise the tensor of the remaining legs. """ tensorA = a.copy() tensorB = b.copy() bonds = [] for label in ['1', '2', '3']: bonds += makeLink(label, label, tensorA=tensorA, tensorB=tensorB) res = contractTwoTensors(tensorA, tensorB, bonds=bonds) return res
def createMPSB(self, tensorLikeFlag=False): tensor2L = Tensor(shape=(3, 3), labels=['o', 'internal'], tensorLikeFlag=tensorLikeFlag) tensor21 = Tensor(shape=(3, 5, 4), labels=['itl', 'oo', 'itr'], tensorLikeFlag=tensorLikeFlag) tensor22 = Tensor(shape=(4, 2, 4), labels=['itl', 'oo', 'itr'], tensorLikeFlag=tensorLikeFlag) tensor2R = Tensor(shape=(4, 5), labels=['internal', 'o'], tensorLikeFlag=tensorLikeFlag) makeLink('internal', 'itl', tensor2L, tensor21) makeLink('itr', 'itl', tensor21, tensor22) makeLink('itr', 'internal', tensor22, tensor2R) tensorsB = [tensor2L, tensor21, tensor22, tensor2R] mpsB = FreeBoundaryMPS(tensorList=tensorsB, chi=12) return mpsB
def simplestExample(): shapeA = (300, 4, 5) shapeB = (300, 6) shapeC = (4, 6, 5) a = Tensor(labels=['a300', 'b4', 'c5'], data=xplib.xp.ones(shapeA)) b = Tensor(labels=['a300', 'd6'], data=xplib.xp.ones(shapeB)) c = Tensor(labels=['e4', 'd6', 'c5'], data=xplib.xp.ones(shapeC)) # create tensors with labels makeLink('a300', 'a300', a, b) makeLink('c5', 'c5', a, c) makeLink('d6', 'd6', b, c) # make links via labels # note that labels can also be made between the "Leg" objects to avoid reused leg names, but here for simplicity from leg names # now we have a tensor network, we can generate the optimal sequence of this tensor list optimalSeq = generateOptimalSequence([a, b, c]) print('optimal contraction sequence = {}'.format(optimalSeq)) # if we do not have any knowledge in prior, we can contract the tensor list like res = contractTensorList([a, b, c]) print(res) # if you already have a good sequence to use res = contractWithSequence([a, b, c], seq=optimalSeq) print(res) # if you want to save time / space by contract in place(note that after this you cannot contract them again, since their bonds between have been broken): res = contractWithSequence([a, b, c], seq=optimalSeq, inplace=True) print(res) print('') # for reusable inplace contraction(which is our goal), refer to the use of CTL.tensornetwork.tensornetwork.FiniteTensorNetwork return res
def test_outerProduct(self): a = Tensor(shape=(2, ), labels=['a']) b = Tensor(shape=(2, ), labels=['b']) op = contractTwoTensors(a, b, outProductWarning=False) self.assertTrue(funcs.compareLists(op.labels, ['a', 'b'])) a = Tensor(shape=(2, 2, 2), labels=['a', 'b', 'c']) b = Tensor(shape=(2, ), labels=['x']) c = Tensor(shape=(2, ), labels=['y']) makeLink('a', 'x', a, b) makeLink('b', 'y', a, c) prod = contractTensorList([a, b, c], outProductWarning=False) self.assertTrue(funcs.compareLists(prod.labels, ['c'])) dataA = np.random.random_sample((2, 2)) dataB = np.random.random_sample((3, 3)) a = Tensor(shape=(2, 2), labels=['a1', 'a2'], data=dataA) b = Tensor(shape=(3, 3), labels=['b1', 'b2'], data=dataB) prod = contractTensorList([a, b], outProductWarning=False) prod.reArrange(['a1', 'a2', 'b1', 'b2']) res = np.zeros((2, 2, 3, 3)) for i in range(2): for j in range(2): for k in range(3): for l in range(3): res[(i, j, k, l)] = dataA[(i, j)] * dataB[(k, l)] # print(res, prod.a) self.assertTrue(funcs.floatArrayEqual(res, prod.a)) a = Tensor(shape=(2, 2), labels=['a1', 'a2'], data=dataA) b = DiagonalTensor(shape=(3, 3), labels=['b1', 'b2'], data=np.diag(dataB)) prod = contractTensorList([a, b], outProductWarning=False) prodData = prod.toTensor(['a1', 'a2', 'b1', 'b2']) # prod.reArrange(['a1', 'a2', 'b1', 'b2']) res = np.zeros((2, 2, 3, 3)) for i in range(2): for j in range(2): for k in range(3): # for l in range(3): res[(i, j, k, k)] = dataA[(i, j)] * dataB[(k, k)] # print(res, prod.a) self.assertTrue(funcs.floatArrayEqual(res, prodData)) dataA = np.random.random_sample((2, 2)) dataB = np.random.random_sample(3) a = Tensor(shape=(2, 2), labels=['a1', 'a2'], data=dataA) b = DiagonalTensor(shape=(3, 3, 3), labels=['b1', 'b2', 'b3'], data=dataB) prod = contractTensorList([a, b], outProductWarning=False) prodData = prod.toTensor(['a1', 'a2', 'b1', 'b2', 'b3']) # prod.reArrange(['a1', 'a2', 'b1', 'b2']) res = np.zeros((2, 2, 3, 3, 3)) for i in range(2): for j in range(2): for k in range(3): # for l in range(3): res[(i, j, k, k, k)] = dataA[(i, j)] * dataB[k] # print(res, prod.a) self.assertTrue(funcs.floatArrayEqual(res, prodData))
def test_merge(self): ''' test the merge(ta, tb) for merge the bonds between ta and tb ''' # normal tensor merge case: 2 shared bonds ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) makeLink('a3', 'b3', ta, tb) makeLink('a4', 'b4', ta, tb) ta, tb = merge(ta, tb) self.assertTrue(funcs.compareLists(ta.labels, ['a3|a4', 'a5'])) self.assertTrue(funcs.compareLists(tb.labels, ['b3|b4', 'b6'])) ta.reArrange(['a3|a4', 'a5']) self.assertTupleEqual(ta.shape, (12, 5)) tb.reArrange(['b3|b4', 'b6']) self.assertTupleEqual(tb.shape, (12, 6)) self.assertEqual(len(shareBonds(ta, tb)), 1) # normal tensor merge case, order changed ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) makeLink('a3', 'b3', ta, tb) makeLink('a4', 'b4', ta, tb) tb, ta = merge(tb, ta) self.assertTrue(funcs.compareLists(ta.labels, ['a4|a3', 'a5'])) self.assertTrue(funcs.compareLists(tb.labels, ['b4|b3', 'b6'])) # test single shared bond: with warning, and do nothing but rename ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) makeLink('a3', 'b3', ta, tb) # tb, ta = merge(tb, ta, bondName = 'o') with self.assertWarns(RuntimeWarning) as cm: tb, ta = merge(tb, ta, bondName='o') self.assertIn('link.py', cm.filename) message = cm.warning.__str__() self.assertIn('mergeLink cannot merge links', message) self.assertIn('sharing one bond', message) self.assertTrue(funcs.compareLists(['o', 'a4', 'a5'], ta.labels)) self.assertTrue(funcs.compareLists(['o', 'b4', 'b6'], tb.labels)) # test for normal merge, tensorLike ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5'], tensorLikeFlag=True) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6'], tensorLikeFlag=True) makeLink('a3', 'b3', ta, tb) makeLink('a4', 'b4', ta, tb) ta, tb = merge(ta, tb) self.assertTrue(funcs.compareLists(ta.labels, ['a3|a4', 'a5'])) self.assertTrue(funcs.compareLists(tb.labels, ['b3|b4', 'b6'])) ta.reArrange(['a3|a4', 'a5']) self.assertTupleEqual(ta.shape, (12, 5)) tb.reArrange(['b3|b4', 'b6']) self.assertTupleEqual(tb.shape, (12, 6)) self.assertEqual(len(shareBonds(ta, tb)), 1) self.assertTrue(ta.tensorLikeFlag and tb.tensorLikeFlag) # test for single bond merge, tensorLike ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5'], tensorLikeFlag=True) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6'], tensorLikeFlag=True) makeLink('a3', 'b3', ta, tb) # tb, ta = merge(tb, ta, bondName = 'o') with self.assertWarns(RuntimeWarning) as cm: tb, ta = merge(tb, ta, bondName='o') self.assertIn('link.py', cm.filename) message = cm.warning.__str__() self.assertIn('mergeLink cannot merge links', message) self.assertIn('sharing one bond', message) self.assertTrue(ta.tensorLikeFlag and tb.tensorLikeFlag) ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) with self.assertWarns(RuntimeWarning) as cm: ta, tb = merge(ta, tb, bondName='o') self.assertIn('link.py', cm.filename) ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) with self.assertWarns(RuntimeWarning) as cm: ta, tb = merge(ta, tb, bondName='o', chi=2) # print(cm.__dict__) self.assertIn('link.py', cm.filename) ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) makeLink('a3', 'b3', ta, tb) makeLink('a4', 'b4', ta, tb) ta, tb = merge(ta, tb, chi=2) self.assertTrue(funcs.compareLists(ta.labels, ['a3|a4', 'a5'])) self.assertTrue(funcs.compareLists(tb.labels, ['b3|b4', 'b6'])) ta.reArrange(['a3|a4', 'a5']) self.assertTupleEqual(ta.shape, (2, 5)) tb.reArrange(['b3|b4', 'b6']) self.assertTupleEqual(tb.shape, (2, 6)) self.assertEqual(len(shareBonds(ta, tb)), 1) ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5'], tensorLikeFlag=True) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6'], tensorLikeFlag=True) makeLink('a3', 'b3', ta, tb) makeLink('a4', 'b4', ta, tb) ta, tb = merge(ta, tb, chi=2) # print(ta, tb) self.assertTrue(funcs.compareLists(ta.labels, ['a3|a4', 'a5'])) self.assertTrue(funcs.compareLists(tb.labels, ['b3|b4', 'b6'])) ta.reArrange(['a3|a4', 'a5']) self.assertTupleEqual(ta.shape, (2, 5)) tb.reArrange(['b3|b4', 'b6']) self.assertTupleEqual(tb.shape, (2, 6)) self.assertEqual(len(shareBonds(ta, tb)), 1) self.assertTrue(ta.tensorLikeFlag and tb.tensorLikeFlag) ta = Tensor(shape=(3, 4, 5), labels=['a3', 'a4', 'a5']) tb = Tensor(shape=(4, 3, 6), labels=['b4', 'b3', 'b6']) makeLink('a3', 'b3', ta, tb) makeLink('a4', 'b4', ta, tb) # large chi test ta, tb = merge(ta, tb, chi=15) # the real internal bond size is chosen by min(chi, ta.remainShape, tb.remainShape, mergeShape) self.assertTrue(funcs.compareLists(ta.labels, ['a3|a4', 'a5'])) self.assertTrue(funcs.compareLists(tb.labels, ['b3|b4', 'b6'])) ta.reArrange(['a3|a4', 'a5']) self.assertTupleEqual(ta.shape, (5, 5)) tb.reArrange(['b3|b4', 'b6']) self.assertTupleEqual(tb.shape, (5, 6)) self.assertEqual(len(shareBonds(ta, tb)), 1)
def selfTrace(tensor): a, b = tensor.copyN(2) for leg1, leg2 in zip(a.legs, b.legs): makeLink(leg1, leg2) return a @ b
def test_contraction(self): self.showTestCaseBegin("diagonal tensor contraction") # print('Begin test diagonalTensor contraction.') a = DiagonalTensor(shape = (2, 2), labels = ['a', 'b']) b = Tensor(shape = (2, ), labels = ['x']) c = Tensor(shape = (2, ), labels = ['y']) makeLink('a', 'x', a, b) makeLink('b', 'y', a, c) seq = generateOptimalSequence([a, b, c], typicalDim = 10) # print('optimal sequence = {}'.format(seq)) prod, cost = contractAndCostWithSequence([a, b, c], seq = seq) # print('cost = {}'.format(cost)) # prod = contractTensorList([a, b, c], outProductWarning = False) self.assertTrue(funcs.compareLists(prod.labels, [])) self.assertListEqual(seq, [(0, 2), (1, 0)]) self.assertEqual(cost, 4.0) # if we use Tensor instead of DiagonalTensor for a # then the cost should be 12.0, and the order should be (1, 2), (0, 1) # the optimal cost of diagonal tensors can be achieved if we use diagonal nature for contraction a = DiagonalTensor(shape = (2, 2, 2), labels = ['a', 'b', 'c']) b = DiagonalTensor(shape = (2, 2), labels = ['x', 'y']) makeLink('a', 'x', a, b) prod, cost = contractAndCostWithSequence([a, b]) self.assertEqual(cost, 2) self.assertTrue(funcs.compareLists(prod.labels, ['b', 'c', 'y'])) aData = np.array([[[1, 0], [0, 0]], [[0, 0], [0, 3]]]) bData = np.random.random_sample(2) cData = np.random.random_sample(2) a = DiagonalTensor(data = aData, labels = ['a', 'b', 'c']) b = Tensor(data = bData, labels = ['x']) c = Tensor(data = cData, labels = ['y']) makeLink('a', 'x', a, b) makeLink('b', 'y', a, c) res1, _ = contractAndCostWithSequence([a, b, c]) # print('seq = {}'.format(generateOptimalSequence([a, b, c]))) a = Tensor(data = aData, labels = ['a', 'b', 'c']) b = Tensor(data = bData, labels = ['x']) c = Tensor(data = cData, labels = ['y']) makeLink('a', 'x', a, b) makeLink('b', 'y', a, c) res2, _ = contractAndCostWithSequence([a, b, c]) # self.assertListEqual(list(res1.a), list(res2.a)) self.assertTrue(funcs.floatArrayEqual(res1.a, res2.a)) # print(cost1, cost2) # print(res1.a, res2.a) # test diagonal tensor contraction a = DiagonalTensor(shape = (2, 2), labels = ['a1', 'a2']) b = DiagonalTensor(shape = (2, 2, 2), labels = ['b1', 'b2', 'b3']) makeLink('a1', 'b1', a, b) res = a @ b self.assertTupleEqual(res.shape, (2, 2, 2)) self.assertEqual(res.dim, 3) self.assertTrue(res.diagonalFlag) self.assertTrue((res.a == np.ones(2)).all()) # test for diagonal * diagonal contraction cost(just O(length)) a = DiagonalTensor(shape = (2, 2), labels = ['a1', 'a2']) b = DiagonalTensor(shape = 2, labels = ['b1', 'b2']) # deduce dim makeLink('a1', 'b2', a, b) cost, _ = contractCost(a, b) self.assertEqual(cost, 2.0) res, cost = contractAndCostWithSequence([a, b]) self.assertEqual(res.dim, 2) self.assertEqual(res._length, 2) self.assertTupleEqual(res.shape, (2, 2)) self.assertEqual(cost, 2.0) self.assertTrue(res.diagonalFlag) a = DiagonalTensor(shape = (2, 2), labels = ['a1', 'a2']) b = Tensor(shape = (2, 3, 3), labels = ['b1', 'b2', 'b3']) # deduce dim makeLink('a1', 'b1', a, b) cost, _ = contractCost(a, b) self.assertEqual(cost, 18.0) res, cost = contractAndCostWithSequence([a, b]) # print(res) self.assertEqual(res.dim, 3) self.assertTrue(funcs.compareLists(list(res.shape), [2, 3, 3])) self.assertEqual(cost, 18.0) self.showTestCaseEnd("diagonal tensor contraction")
def test_extremeContraction(self): self.showTestCaseBegin("diagonal tensor extreme contraction") aData = np.random.random_sample(2) aTensorData = np.array([[[aData[0], 0], [0, 0]], [[0, 0], [0, aData[1]]]]) bData = np.random.random_sample((2, 2)) a = DiagonalTensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aData) b = Tensor(shape = (2, 2), labels = ['b1', 'b2'], data = bData) makeLink('a1', 'b1', a, b) makeLink('a3', 'b2', a, b) res1 = a @ b a = Tensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aTensorData) b = Tensor(shape = (2, 2), labels = ['b1', 'b2'], data = bData) makeLink('a1', 'b1', a, b) makeLink('a3', 'b2', a, b) res2 = b @ a self.assertTrue(funcs.compareLists(list(res1.labels), ['a2'])) self.assertTrue(funcs.compareLists(list(res2.labels), ['a2'])) # self.assertListEqual(list(res1.a), list(res2.a)) self.assertTrue(funcs.floatArrayEqual(res1.a, res2.a)) aData = np.random.random_sample(2) aTensorData = np.array([[aData[0], 0], [0, aData[1]]]) bData = np.random.random_sample((2, 2)) a = DiagonalTensor(shape = (2, 2), labels = ['a1', 'a2'], data = aData) b = Tensor(shape = (2, 2), labels = ['b1', 'b2'], data = bData) makeLink('a1', 'b1', a, b) makeLink('a2', 'b2', a, b) res1 = a @ b a = Tensor(shape = (2, 2), labels = ['a1', 'a2'], data = aTensorData) b = Tensor(shape = (2, 2), labels = ['b1', 'b2'], data = bData) makeLink('a1', 'b1', a, b) makeLink('a2', 'b2', a, b) res2 = b @ a self.assertTrue(funcs.compareLists(res1.labels, [])) self.assertTrue(funcs.compareLists(res2.labels, [])) # self.assertListEqual(list(res1.single(), list(res2.a)) self.assertTrue(funcs.floatEqual(res1.single(), res2.single())) aData = np.random.random_sample(2) aTensorData = np.array([[[aData[0], 0], [0, 0]], [[0, 0], [0, aData[1]]]]) bData = np.random.random_sample((2, 2, 2)) a = DiagonalTensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aData) b = Tensor(shape = (2, 2, 2), labels = ['b1', 'b2', 'b3'], data = bData) makeLink('a1', 'b1', a, b) makeLink('a3', 'b2', a, b) res1 = a @ b a = Tensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aTensorData) b = Tensor(shape = (2, 2, 2), labels = ['b1', 'b2', 'b3'], data = bData) makeLink('a1', 'b1', a, b) makeLink('a3', 'b2', a, b) res2 = b @ a self.assertTrue(funcs.compareLists(list(res1.labels), ['a2', 'b3'])) self.assertTrue(funcs.compareLists(list(res2.labels), ['a2', 'b3'])) # print(res1.labels, res2.labels) # print(res1.a, res2.a) res2.reArrange(res1.labels) self.assertTrue(funcs.floatArrayEqual(res1.a, res2.a)) # self.assertListEqual(list(np.ravel(res1.a)), list(np.ravel(res2.a))) aData = np.random.random_sample(2) aTensorData = np.array([[[aData[0], 0], [0, 0]], [[0, 0], [0, aData[1]]]]) bData = np.random.random_sample((2, 2, 2)) a = DiagonalTensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aData) b = Tensor(shape = (2, 2, 2), labels = ['b1', 'b2', 'b3'], data = bData) makeLink('a1', 'b1', a, b) # makeLink('a3', 'b2', a, b) res1 = a @ b a = Tensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aTensorData) b = Tensor(shape = (2, 2, 2), labels = ['b1', 'b2', 'b3'], data = bData) makeLink('a1', 'b1', a, b) # makeLink('a3', 'b2', a, b) res2 = b @ a # print('ndEye(2, 2) = {}'.format(funcs.ndEye(2, 2))) # print('ndEye(1, 2) = {}'.format(funcs.ndEye(1, 2))) # print('ndEye(3, 2) = {}'.format(funcs.ndEye(3, 2))) self.assertTrue(funcs.compareLists(list(res1.labels), ['a2', 'a3', 'b2', 'b3'])) self.assertTrue(funcs.compareLists(list(res2.labels), ['a2', 'a3', 'b2', 'b3'])) # print(res1.labels, res2.labels) res2.reArrange(res1.labels) # print('res1 = {}, res2 = {}'.format(res1.a, res2.a)) self.assertTrue(funcs.floatArrayEqual(res1.a, res2.a)) # self.assertListEqual(list(np.ravel(res1.a)), list(np.ravel(res2.a))) aData = np.random.random_sample(2) aTensorData = np.array([[[aData[0], 0], [0, 0]], [[0, 0], [0, aData[1]]]]) bData = np.random.random_sample((2, 4, 7)) a = DiagonalTensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aData) b = Tensor(shape = (2, 4, 7), labels = ['b1', 'b2', 'b3'], data = bData) makeLink('a1', 'b1', a, b) # makeLink('a3', 'b2', a, b) res1 = a @ b a = Tensor(shape = (2, 2, 2), labels = ['a1', 'a2', 'a3'], data = aTensorData) b = Tensor(shape = (2, 4, 7), labels = ['b1', 'b2', 'b3'], data = bData) makeLink('a1', 'b1', a, b) # makeLink('a3', 'b2', a, b) res2 = b @ a # print('ndEye(2, 2) = {}'.format(funcs.ndEye(2, 2))) # print('ndEye(1, 2) = {}'.format(funcs.ndEye(1, 2))) # print('ndEye(3, 2) = {}'.format(funcs.ndEye(3, 2))) self.assertTrue(funcs.compareLists(list(res1.labels), ['a2', 'a3', 'b2', 'b3'])) self.assertTrue(funcs.compareLists(list(res2.labels), ['a2', 'a3', 'b2', 'b3'])) # print(res1.labels, res2.labels) res2.reArrange(res1.labels) # print('res1 = {}, res2 = {}'.format(res1.a, res2.a)) self.assertTrue(funcs.floatArrayEqual(res1.a, res2.a)) self.showTestCaseEnd("diagonal tensor extreme contraction")
def createSpecialTN(self): a = Tensor(shape=(3, 5, 7), labels=['a3', 'a5', 'a7']) b = Tensor(shape=(2, 4, 5), labels=['b2', 'b4', 'b5']) c = Tensor(shape=(2, 7, 7, 7), labels=['c2', 'c71', 'c72', 'c73']) d = Tensor(shape=(7, 7, 3, 4), labels=['d71', 'd72', 'd3', 'd4']) makeLink('a3', 'd3', a, d) makeLink('a5', 'b5', a, b) makeLink('a7', 'c72', a, c) makeLink('b2', 'c2', b, c) makeLink('b4', 'd4', b, d) makeLink('c71', 'd72', c, d) makeLink('c73', 'd71', c, d) return [a, b, d, c]
def createSpecialTN2(self): a = Tensor(shape=(3, 5, 7), labels=['a3', 'a5', 'a7']) b = Tensor(shape=(2, 4, 5), labels=['b2', 'b4', 'b5']) c = Tensor(shape=(2, 7, 7, 7), labels=['c2', 'c71', 'c72', 'c73']) d = Tensor(shape=(7, 7, 3, 4), labels=['d71', 'd72', 'd3', 'd4']) e = Tensor(shape=(3, 3, 5), labels=['e31', 'e32', 'e5']) f = Tensor(shape=(2, 2, 5), labels=['f21', 'f22', 'f5']) g = Tensor(shape=(4, 4, 3, 3), labels=['g41', 'g42', 'g31', 'g32']) makeLink('a3', 'e31', a, e) makeLink('a5', 'b5', a, b) makeLink('a7', 'c72', a, c) makeLink('b2', 'f21', b, f) makeLink('b4', 'g41', b, g) makeLink('c2', 'f22', c, f) makeLink('c71', 'd72', c, d) makeLink('c73', 'd71', c, d) makeLink('d3', 'g31', d, g) makeLink('d4', 'g42', d, g) makeLink('e5', 'f5', e, f) makeLink('e32', 'g32', e, g) return [a, b, d, c, g, f, e]
def createMPSFromTensor(tensor, chi=16): ''' tensor is a real Tensor with n outer legs transfer it into an MPS with Schimdt decomposition after this, we can manage the tensor network decomposition by MPS network decomposition finally consider if tensor is only a TensorLike object ''' # TODO: make this function work for tensorLike funcName = 'CTL.examples.MPS.createMPSFromTensor' legs = [leg for leg in tensor.legs] # xp = tensor.xp n = len(legs) assert (n > 0), funcs.errorMessage( "cannot create MPS from 0-D tensor {}.".format(tensor), location=funcName) if (n == 1): warnings.warn( funcs.warningMessage( "creating MPS for 1-D tensor {}.".format(tensor), location=funcName), RuntimeWarning) return FreeBoundaryMPS([tensor], chi=chi) a = xplib.xp.ravel(tensor.toTensor(labels=None)) lastDim = -1 tensors = [] lastRightLeg = None for i in range(n - 1): u, v = matrixSchimdtDecomposition(a, dim=legs[i].dim, chi=chi) leg = legs[i] if (i == 0): dim1 = u.shape[1] rightLeg = Leg(None, dim=dim1, name='r') tensor = Tensor(shape=(leg.dim, u.shape[1]), legs=[leg, rightLeg], data=u) lastRightLeg = rightLeg lastDim = dim1 else: dim1 = u.shape[-1] leftLeg = Leg(None, dim=lastDim, name='l') rightLeg = Leg(None, dim=dim1, name='r') tensor = Tensor(shape=(lastDim, leg.dim, u.shape[-1]), legs=[leftLeg, leg, rightLeg], data=u) makeLink(leftLeg, lastRightLeg) lastRightLeg = rightLeg lastDim = dim1 tensors.append(tensor) a = v leftLeg = Leg(None, dim=lastDim, name='l') tensor = Tensor(shape=(lastDim, legs[-1].dim), legs=[leftLeg, legs[-1]], data=a) makeLink(leftLeg, lastRightLeg) tensors.append(tensor) # print(tensors) return FreeBoundaryMPS(tensorList=tensors, chi=chi)
def copyOfTensors(self): tensors = [self.getTensor(i).copy() for i in range(self.n)] for i in range(self.n - 1): makeLink('r', 'l', tensors[i], tensors[i + 1]) return tensors
def makeRandomMPS(n, virtualDim, physicalDim, chi=-1): ''' Create an MPS consisting of n tensors, each has one physical leg. Parameters ---------- n : int Number of tensors(physical legs) in MPS. virtualDim : int Bond dimension of bonds between tensors. physicalDim : int Bond dimension for physical legs. chi : int, optional The chi for MPS. If -1, then decided according to virtualDim. Returns ------- mps : FreeBoundaryMPS MPS of n tensors. ''' location = 'CTL.examples.MPS.makeRandomMPS' assert (n > 1), funcs.errorMessage( err= 'cannot make random MPS whose number of tensors is not greater than 1, got {}' .format(n), location=location) tensors = [] if isinstance(virtualDim, int): virtualDim = [virtualDim] * (n - 1) else: assert len(virtualDim) == n - 1, funcs.errorMessage( err='virtual dim must be int or length-(n - 1) list, {} found.'. format(virtualDim), location=location) if isinstance(physicalDim, int): physicalDim = [physicalDim] * n else: assert len(physicalDim) == n, funcs.errorMessage( err='physical dim must be int or length-n list, {} found.'.format( physicalDim), location=location) if chi == -1: chi = virtualDim[0] # endsShape = (physicalDim, virtualDim) endsLabels = ['p', 'v'] # middleShape = (physicalDim, virtualDim, virtualDim) middleLabels = ['p', 'vl', 'vr'] leftEndShape = (physicalDim[0], virtualDim[0]) tensors.append(Tensor(shape=leftEndShape, labels=endsLabels)) for i in range(n - 2): middleShape = (physicalDim[i + 1], virtualDim[i], virtualDim[i + 1]) tensors.append(Tensor(shape=middleShape, labels=middleLabels)) rightEndShape = (physicalDim[-1], virtualDim[-1]) tensors.append(Tensor(shape=rightEndShape, labels=endsLabels)) for tensor in tensors: tensor.a /= tensor.norm() if (n == 2): makeLink('v', 'v', tensors[0], tensors[-1]) else: makeLink('v', 'vl', tensors[0], tensors[1]) makeLink('v', 'vr', tensors[-1], tensors[-2]) for i in range(1, n - 2): makeLink('vr', 'vl', tensors[i], tensors[i + 1]) return FreeBoundaryMPS(tensors, chi=chi, inplace=True)
def contractHandmadeTN(): print('contractHandmadeTN():') a = Tensor(shape=(3, 5, 7), labels=['a3', 'a5', 'a7']) b = Tensor(shape=(2, 4, 5), labels=['b2', 'b4', 'b5']) c = Tensor(shape=(2, 7, 7, 7), labels=['c2', 'c71', 'c72', 'c73']) d = Tensor(shape=(7, 7, 3, 4), labels=['d71', 'd72', 'd3', 'd4']) e = Tensor(shape=(3, 3, 5), labels=['e31', 'e32', 'e5']) f = Tensor(shape=(2, 2, 5), labels=['f21', 'f22', 'f5']) g = Tensor(shape=(4, 4, 3, 3), labels=['g41', 'g42', 'g31', 'g32']) makeLink('a3', 'e31', a, e) makeLink('a5', 'b5', a, b) makeLink('a7', 'c72', a, c) makeLink('b2', 'f21', b, f) makeLink('b4', 'g41', b, g) makeLink('c2', 'f22', c, f) makeLink('c71', 'd72', c, d) makeLink('c73', 'd71', c, d) makeLink('d3', 'g31', d, g) makeLink('d4', 'g42', d, g) makeLink('e5', 'f5', e, f) makeLink('e32', 'g32', e, g) tensors = [a, b, d, c, g, f, e] res, _ = contractAndCostWithSequence(tensors) print('res from direct contraction = {}'.format(res.single())) mpsRes = contractWithMPS(tensors, chi=32) print('res from mps = {}'.format(mpsRes.single())) print('')
def merge(ta, tb, chi=None, bondName=None, renameWarning=True): """ Merge the shared bonds of two tensors. If not connected, make a warning and do nothing. Parameters ---------- ta, tb : Tensor chi : int, optional The upper-bound of the bond dimension of the bond after merged. If None, then no truncation. bondName : str, optional The name of bond after merging. If None, then for a list of [name1, name2, ... nameN], the name will be "{name1}|{name2}| .... |{nameN}". renameWarning : bool, default True If only one bond is shared, then the two Returns ------- ta, tb : Tensor The two tensors after merging all the common bonds to one bond. """ funcName = "CTL.tensor.contract.contract.truncate" # assert (ta.xp == tb.xp), funcs.errorMessage("Truncation cannot accept two tensors with different xp: {} and {} gotten.".format(ta.xp, tb.xp), location = funcName) assert (ta.tensorLikeFlag == tb.tensorLikeFlag), funcs.errorMessage( 'two tensors to be merged must be either Tensor or TensorLike simultaneously, {} and {} obtained.' .format(ta, tb), location=funcName) tensorLikeFlag = ta.tensorLikeFlag # xp = ta.xp ta, tb = mergeLink(ta, tb, bondName=bondName, renameWarning=renameWarning) if (chi is None): # no need for truncation return ta, tb sb = shareBonds(ta, tb) # assert (len(sb) > 0), funcs.errorMessage("Truncation cannot work on two tensors without common bonds: {} and {} gotten.".format(ta, tb), location = funcName) # if (bondName is None): # bondNameListA = [bond.sideLeg(ta).name for bond in sb] # bondNameListB = [bond.sideLeg(tb).name for bond in sb] # bondNameA = '|'.join(bondNameListA) # bondNameB = '|'.join(bondNameListB) # elif (isinstance(bondName, str)): # bondNameA = bondName # bondNameB = bondName # else: # bondNameA, bondNameB = bondName # tuple/list # if (renameFlag): if (len(sb) == 0): if (renameWarning): warnings.warn( funcs.warningMessage( warn= 'mergeLink cannot merge links between two tensors {} and {} not sharing any bond' .format(ta, tb), location=funcName), RuntimeWarning) return ta, tb assert (len(sb) == 1), funcs.errorMessage( "There should only be one common leg between ta and tb after mergeLink, {} obtained." .format(sb), location=funcName) legA = [bond.sideLeg(ta) for bond in sb] legB = [bond.sideLeg(tb) for bond in sb] bondNameA = legA[0].name bondNameB = legB[0].name remainLegA = ta.complementLegs(legA) remainLegB = tb.complementLegs(legB) if (not tensorLikeFlag): matA = ta.toMatrix(rows=None, cols=legA) matB = tb.toMatrix(rows=legB, cols=None) mat = matA @ matB u, s, vh = xplib.xp.linalg.svd(mat) chi = min( [chi, funcs.nonZeroElementN(s), matA.shape[0], matB.shape[1]]) u = u[:, :chi] s = s[:chi] vh = vh[:chi] uOutLeg = Leg(tensor=None, dim=chi, name=bondNameA) vOutLeg = Leg(tensor=None, dim=chi, name=bondNameB) # print(legA, legB) sqrtS = xplib.xp.sqrt(s) uS = funcs.rightDiagonalProduct(u, sqrtS) vS = funcs.leftDiagonalProduct(vh, sqrtS) uTensor = Tensor(data=uS, legs=remainLegA + [uOutLeg]) vTensor = Tensor(data=vS, legs=[vOutLeg] + remainLegB) else: chi = min([ chi, legA[0].dim, ta.totalSize // legA[0].dim, tb.totalSize // legB[0].dim ]) uOutLeg = Leg(tensor=None, dim=chi, name=bondNameA) vOutLeg = Leg(tensor=None, dim=chi, name=bondNameB) uTensor = Tensor(tensorLikeFlag=True, legs=remainLegA + [uOutLeg]) vTensor = Tensor(tensorLikeFlag=True, legs=[vOutLeg] + remainLegB) makeLink(uOutLeg, vOutLeg) return uTensor, vTensor
def SchimdtDecomposition(ta, tb, chi, squareRootSeparation=False, swapLabels=([], []), singularValueEps=1e-10): ''' Schimdt decomposition between tensor ta and tb return ta, s, tb ta should be in canonical form, that is, a a^dagger = I to do this, first contract ta and tb, while keeping track of legs from a and legs from b then SVD over the matrix, take the required chi singular values take first chi eigenvectors for a and b, create a diagonal tensor for singular value tensor if squareRootSeparation is True: then divide s into two square root diagonal tensors and contract each into ta and tb, return ta, None, tb if swapLabels is not ([], []): swap the two set of labels for output, so we swapped the locations of two tensors on MPS e.g. t[i], t[i + 1] = SchimdtDecomposition(t[i], t[i + 1], chi = chi, squareRootSeparation = True, swapLabels = (['o'], ['o'])) we can swap the two tensors t[i] & t[i + 1], both have an "o" leg connected to outside while other legs(e.g. internal legs in MPS, usually 'l' and 'r') will not be affected ''' funcName = 'CTL.examples.Schimdt.SchimdtDecomposition' sb = shareBonds(ta, tb) assert (len(sb) > 0), funcs.errorMessage( "Schimdt Decomposition cannot accept two tensors without common bonds, {} and {} gotten." .format(ta, tb), location=funcName) assert (ta.tensorLikeFlag == tb.tensorLikeFlag), funcs.errorMessage( "Schimdt Decomposition must havge two objects being either Tensor or TensorLike simultaneously, but {} and {} obtained." .format(ta, tb), location=funcName) TLFlag = ta.tensorLikeFlag sbDim = funcs.tupleProduct(tuple([bond.legs[0].dim for bond in sb])) sharedLabelA = sb[0].sideLeg(ta).name sharedLabelB = sb[0].sideLeg(tb).name # if (sharedLabelA.startswith('a-')): # raise ValueError(funcs.errorMessage(err = "shared label {} of tensor A starts with 'a-'.".format(sharedLabelA), location = funcName)) # if (sharedLabelB.startswith('b-')): # raise ValueError(funcs.errorMessage(err = "shared label {} of tensor B starts with 'b-'.".format(sharedLabelB), location = funcName)) # assert (ta.xp == tb.xp), funcs.errorMessage("Schimdt Decomposition cannot accept two tensors with different xp: {} and {} gotten.".format(ta.xp, tb.xp), location = funcName) assert (len(swapLabels[0]) == len(swapLabels[1])), funcs.errorMessage( err="invalid swap labels {}.".format(swapLabels), location=funcName) assert ta.labelsInTensor(swapLabels[0]), funcs.errorMessage( err="{} not in tensor {}.".format(swapLabels[0], ta), location=funcName) assert tb.labelsInTensor(swapLabels[1]), funcs.errorMessage( err="{} not in tensor {}.".format(swapLabels[1], tb), location=funcName) ta.addTensorTag('a') tb.addTensorTag('b') for swapLabel in swapLabels[0]: ta.renameLabel('a-' + swapLabel, 'b-' + swapLabel) for swapLabel in swapLabels[1]: tb.renameLabel('b-' + swapLabel, 'a-' + swapLabel) tot = contractTwoTensors(ta, tb) legA = [leg for leg in tot.legs if leg.name.startswith('a-')] legB = [leg for leg in tot.legs if leg.name.startswith('b-')] labelA = [leg.name for leg in legA] labelB = [leg.name for leg in legB] # not remove a- and b- here, since we need to add an internal leg, and we need to distinguish it from others shapeA = tuple([leg.dim for leg in legA]) shapeB = tuple([leg.dim for leg in legB]) totShapeA = funcs.tupleProduct(shapeA) totShapeB = funcs.tupleProduct(shapeB) if (TLFlag): u = None vh = None s = None chi = min([chi, totShapeA, totShapeB, sbDim]) else: mat = tot.toMatrix(rows=labelA, cols=labelB) # np = ta.xp # default numpy u, s, vh = xplib.xp.linalg.svd(mat) chi = min([ chi, totShapeA, totShapeB, funcs.nonZeroElementN(s, singularValueEps) ]) u = u[:, :chi] s = s[:chi] vh = vh[:chi] if (squareRootSeparation): if (TLFlag): uS = None vS = None else: sqrtS = xplib.xp.sqrt(s) uS = funcs.rightDiagonalProduct(u, sqrtS) vS = funcs.leftDiagonalProduct(vh, sqrtS) outLegForU = Leg(None, chi, name=sharedLabelA) # inLegForU = Leg(None, chi, name = sharedLabelB) # internalLegForS1 = Leg(None, chi, name = 'o') # internalLegForS2 = Leg(None, chi, name = 'o') # inLegForV = Leg(None, chi, name = sharedLabelA) outLegForV = Leg(None, chi, name=sharedLabelB) uTensor = Tensor(data=uS, legs=legA + [outLegForU], shape=shapeA + (chi, ), tensorLikeFlag=TLFlag) # s1Tensor = DiagonalTensor(data = xplib.xp.sqrt(s), legs = [inLegForU, internalLegForS1], shape = (chi, chi)) # s2Tensor = DiagonalTensor(data = xplib.xp.sqrt(s), legs = [internalLegForS2, inLegForV], shape = (chi, chi)) vTensor = Tensor(data=vS, legs=[outLegForV] + legB, shape=(chi, ) + shapeB, tensorLikeFlag=TLFlag) # legs should be automatically set by Tensor / DiagonalTensor, so no need for setTensor # outLegForU.setTensor(uTensor) # outLegForV.setTensor(vTensor) # inLegForU.setTensor(sTensor) # inLegForV.setTensor(sTensor) # remove a- and b- for leg in legA: if (leg.name.startswith('a-')): leg.name = leg.name[2:] for leg in legB: if (leg.name.startswith('b-')): leg.name = leg.name[2:] makeLink(outLegForU, outLegForV) # makeLink(outLegForU, inLegForU) # makeLink(outLegForV, inLegForV) # makeLink(internalLegForS1, internalLegForS2) # uTensor = contractTwoTensors(uTensor, s1Tensor) # vTensor = contractTwoTensors(vTensor, s2Tensor) return uTensor, None, vTensor outLegForU = Leg(None, chi, name=sharedLabelA) inLegForU = Leg(None, chi, name=sharedLabelB) inLegForV = Leg(None, chi, name=sharedLabelA) outLegForV = Leg(None, chi, name=sharedLabelB) uTensor = Tensor(data=u, legs=legA + [outLegForU], shape=shapeA + (chi, ), tensorLikeFlag=TLFlag) sTensor = DiagonalTensor(data=s, legs=[inLegForU, inLegForV], shape=(chi, chi), tensorLikeFlag=TLFlag) vTensor = Tensor(data=vh, legs=[outLegForV] + legB, shape=(chi, ) + shapeB, tensorLikeFlag=TLFlag) # legs should be automatically set by Tensor / DiagonalTensor, so no need for setTensor # outLegForU.setTensor(uTensor) # outLegForV.setTensor(vTensor) # inLegForU.setTensor(sTensor) # inLegForV.setTensor(sTensor) # remove a- and b- for leg in legA: if (leg.name.startswith('a-')): leg.name = leg.name[2:] for leg in legB: if (leg.name.startswith('b-')): leg.name = leg.name[2:] makeLink(outLegForU, inLegForU) makeLink(outLegForV, inLegForV) return uTensor, sTensor, vTensor