def doubleMerge(mpsA, mpsB, idxA, idxB): funcName = 'CTL.examples.MPS.doubleMerge' tensorA = contractTwoTensors(mpsA.getTensor(idxA), mpsA.getTensor(idxA + 1)) tensorB = contractTwoTensors(mpsB.getTensor(idxB), mpsB.getTensor(idxB + 1)) tensorA, tensorB = merge(tensorA, tensorB, chi=None, bondName='o') mpsA.mergeTensor(idxA, tensorA) mpsB.mergeTensor(idxB, tensorB) mpsA.canonicalize(idx=idxA) mpsB.canonicalize(idx=idxB) tensorA, tensorB = mpsA.getTensor(idxA), mpsB.getTensor(idxB) tensorA, tensorB = merge(tensorA, tensorB, chi=min(mpsA.chi, mpsB.chi), bondName='o', renameWarning=False) mpsA.setTensor(idxA, tensorA) mpsB.setTensor(idxB, tensorB) sb = shareBonds(tensorA, tensorB) assert (len(sb) == 1), funcs.errorMessage( "{} and {} do not share exactly one bond.".format(tensorA, tensorB), location=funcName) return sb[0]
def contractCost(ta, tb): """ The cost of contraction of two tensors. Parameters ---------- ta, tb : Tensor Two tensors we want to contract. Returns ------- cost : int The exact cost for contraction of two tensors(e.g. for two matrices A * B & B * C, the cost is A * B * C. However, for diagonal tensors, the cost is just the size of output tensor). costLevel : int The order of the cost(how many dimensions). This is used when we want to decide the order of our calculation to a given bond dimension chi. """ diagonalA, diagonalB = ta.diagonalFlag, tb.diagonalFlag if (diagonalA and diagonalB): return ta.bondDimension(), 1 diagonal = diagonalA or diagonalB bonds = shareBonds(ta, tb) intersectionShape = tuple([bond.legs[0].dim for bond in bonds]) if (not diagonal): cost = funcs.tupleProduct(ta.shape) * funcs.tupleProduct( tb.shape) // funcs.tupleProduct(intersectionShape) costLevel = len(ta.shape) + len(tb.shape) - len(intersectionShape) else: cost = funcs.tupleProduct(ta.shape) * funcs.tupleProduct( tb.shape) // (funcs.tupleProduct(intersectionShape)**2) costLevel = len(ta.shape) + len(tb.shape) - 2 * len(intersectionShape) return cost, costLevel
def renameBonds(self): # 'l' and 'r' for internal bonds # 'o' for external bonds self.internalBonds = set() for i in range(self.n - 1): bond = shareBonds(self._tensors[i], self._tensors[i + 1])[0] bond.sideLeg(self._tensors[i]).name = 'r' bond.sideLeg(self._tensors[i + 1]).name = 'l' self.internalBonds.add(bond) for i in range(self.n): for leg in self._tensors[i].legs: if (leg.bond not in self.internalBonds): leg.name = 'o'
def getChi(self, chi): # if chi is None: then take the maximum from bonds shared # otherwise, if bonds sharing larger than chi, then warning and update chi # otherwise, take chi bondChi = -1 for i in range(self.n - 1): bond = shareBonds(self._tensors[i], self._tensors[i + 1])[0] bondChi = min(bondChi, bond.legs[0].dim) if (chi is None): return bondChi elif (bondChi > chi): warnings.warn( funcs.warningMessage( 'required chi {} is lower than real bond chi {}, set to {}.' .format(chi, bondChi, bondChi), location="FreeBoundaryMPS.getChi")) return bondChi else: return chi
def checkMPSProperty(self, tensorList): # MPS property: first and last tensor has one bond out, and another linked to the next # others: one to left, one to right, one out n = len(tensorList) if (n == 0): return False for i in range(n - 1): if (len(shareBonds(tensorList[i], tensorList[i + 1])) != 1): return False if (n > 1) and ((tensorList[0].dim != 2) or (tensorList[-1].dim != 2)): return False for i in range(1, n - 1): if (tensorList[i].dim != 3): return False return True
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
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)