def IsingSiteTensor(betaJ, dim=4, labels=None): """ The site tensor that can be connected to form Ising tensor network. Parameters ---------- betaJ : float or list of float The interaction combined with inverse temperature, namely, J / T. When a list is given, the length should be dim, and each for one different edge. dim : int, default 4 The degree of sites(the number of edges it is linked to). By default, square lattice value 4. labels : list of str, optional The labels of the result tensor on each leg. If betaJ is a number, since the legs are the same, the order of labels do not matter. Otherwise please be carefully about the order of labels since it corresponds to the order of betaJ. Returns ------- Tensor The tensor of dim legs, labelled with labels, and representing the local interaction around a site(a diagonal site tensor with multiple edge tensors). """ assert (funcs.isRealNumber(betaJ) or (len(betaJ) == dim)), funcs.errorMessage( "betaJ {} do not have required dim {}.".format(betaJ, dim)) assert ((labels is None) or (len(labels) == dim)), funcs.errorMessage( "labels {} do not have required dim {}.".format(labels, dim)) a = xplib.xp.array([1.0, 1.0]) a = funcs.diagonalNDTensor(a, dim=dim) if (funcs.isRealNumber(betaJ)): betaJ = [betaJ] * dim # edgeMat = IsingEdgeMatrix(betaJ) for i in range(dim): edgeMat = IsingEdgeMatrix(betaJ[i]) a = xplib.xp.tensordot(a, edgeMat, (0, 0)) # print(a) return Tensor(data=a, labels=labels)
def deduceDimension(self, data, labels): """ Deduce the dimension of current diagonal tensor from data and labels. Parameters ---------- data : None or 1D array or ndarray The data to be put in the diagonal tensor. labels : None or list of Leg The labels to be added to the legs of this tensor. Returns ------- int The dimension of the current tensor. """ # if the labels is given: then use labels # otherwise, if data is given(as an ndarray), then we return then len(data.shape) # otherwise, error if (data is not None) and (len(data.shape) != 1) and (labels is not None) and ((len(labels) != len(data.shape)) or (len(labels) == 0 and len(data.shape) == 1)): raise ValueError(funcs.errorMessage(location = "DiagonalTensor.deduceDimension", err = "data {} and labels {} are not compatible.".format(data, labels))) # what if len(labels) == 0, len(data.shape) == 1? if (labels is not None): return len(labels) elif (data is not None): # then data must be an numpy array return len(data.shape) else: raise ValueError(funcs.errorMessage(location = "DiagonalTensor.deduceDimension", err = "both data and labels are None."))
def __init__(self, leg1, leg2, name = None): assert (isinstance(leg1, Leg) and (isinstance(leg2, Leg))), errorMessage(err = "Bond must be initialized with 2 Leg elements.", location = 'Bond.__init__') assert (leg1.dim == leg2.dim), errorMessage(err = "{} and {} do not share the same dimension.".format(leg1, leg2), location = 'Bond.__init__') # self.name = getBondName(name) self.name = name self.legs = (leg1, leg2) leg1.bond = self leg2.bond = self
def matrixSchimdtDecomposition(a, dim, chi=None): ''' Schimdt decomposition of matrix a a can be either 1D or 2D 1. if a is 1D: then we reshape a to (dim, -1) and then SVD to obtain u(dim, chi), s(chi, chi), vh(chi, -1), return u and s @ vh 2. if a is 2D: (s0, s1): then we reshape a to (s0 * dim, s1 // dim) SVD to obtain u(s0 * dim, chi), s(chi, chi), vh(chi, -1) return u(s0, dim, chi), s @ vh(chi, s1 // dim) ''' funcName = 'CTL.examples.Schimdt.matrixSchimdtDecomposition' shape = a.shape assert (len(shape) in [1, 2]), funcs.errorMessage( "matrix shape can only be 1D or 2D, {} obtained.".format(shape), location=funcName) if (len(shape) == 1): l = shape[0] assert ((dim != 0) and (l % dim == 0)), funcs.errorMessage( "matrix shape {} cannot be Schimdt decomposed with dimension {}.". format(shape, dim), location=funcName) a = a.reshape((dim, -1)) u, s, vh = xplib.xp.linalg.svd(a) if (chi is None): chi = min([u.shape[0], vh.shape[1], funcs.nonZeroElementN(s)]) else: chi = min([u.shape[0], vh.shape[1], chi, funcs.nonZeroElementN(s)]) u = u[:, :chi] s = s[:chi] vh = vh[:chi] return u, funcs.leftDiagonalProduct(vh, s) else: s0, s1 = shape assert ((dim != 0) and (s1 % dim == 0)), funcs.errorMessage( "matrix shape {} cannot be Schimdt decomposed with dimension {}.". format(shape, dim), location=funcName) a = a.reshape(s0 * dim, -1) u, s, vh = xplib.xp.linalg.svd(a) if (chi is None): chi = min([u.shape[0], vh.shape[1], funcs.nonZeroElementN(s)]) else: chi = min([u.shape[0], vh.shape[1], chi, funcs.nonZeroElementN(s)]) u = u[:, :chi].reshape(s0, dim, chi) s = s[:chi] vh = vh[:chi] return u, funcs.leftDiagonalProduct(vh, s)
def getIndex(d, x, y): # d == 0: "up/bottom" of plaquettes, (n + 1) * m # d == 1: "left/right" of plaquettes, n * (m + 1) # order: up to down, left to right assert (d != 0) or (x >= 0 and x <= n and y >= 0 and y < m), funcs.errorMessage('({}, {}) is not a valid coordinate in ({}, {}) lattice.'.format(x, y, n, m), location = funcName + ".getIndex") assert (d != 1) or (x >= 0 and x < n and y >= 0 and y <= m), funcs.errorMessage('({}, {}) is not a valid coordinate in ({}, {}) lattice.'.format(x, y, n, m), location = funcName + ".getIndex") if (d == 0): return x * m + y else: return (n + 1) * m + x * (m + 1) + y
def makeAdjacent(self, idx1, idx2): funcName = 'FreeBoundaryMPS.makeAdjacent' assert (self.isIndex(idx1) and self.isIndex(idx2)), funcs.errorMessage( "{} or {} is invalid index.".format(idx1, idx2), location=funcName) assert (idx1 != idx2), funcs.errorMessage( "cannot make two identical indices adjacent: {} and {}.".format( idx1, idx2), location=funcName) if (idx1 > idx2): idx1, idx2 = idx2, idx1 for i in range(idx1, idx2 - 1): self.swap(i, i + 1) return idx2 - 1, idx2
def getBondTensorIndices(mpsA, mpsB, bond): funcName = 'CTL.examples.MPS.getBondTensorIndices' legA, legB = bond.legs tensorA, tensorB = legA.tensor, legB.tensor if (not mpsA.hasTensor(tensorA)): tensorA, tensorB = tensorB, tensorA assert (mpsA.hasTensor(tensorA)), funcs.errorMessage( '{} does not contain {}.'.format(mpsA, tensorA), location=funcName) assert (mpsB.hasTensor(tensorB)), funcs.errorMessage( '{} does not contain {}.'.format(mpsA, tensorB), location=funcName) return mpsA.tensorIndex(tensorA), mpsB.tensorIndex(tensorB)
def newString(self, inputStr=None): """ Parameters ---------- inputStr : str, optional The string that is preferred to be added. If None, then generate a new string. Returns ------- str A new string generated, or the input string if it is not None. Raises ------ AssertionError Raised when inputStr is not None, but the string has already appeared in this set. """ if (inputStr is None): res = randomString(self.n) while (res in self.stringSet): res = randomString(self.n) else: assert not (inputStr in self.stringSet), funcs.errorMessage( err="Error: name '{}' is already used.".format(inputStr), location="StringSet.newString") res = inputStr self.stringSet.add(res) return res
def getIsingWeight(g, S): """ Calculate the weight of Ising model for a graph and given spins. Parameters ---------- g : UndirectedGraph The site-graph to add interaction. The weights represent the betaJ on each edge. S : int The bitmask of the Ising spin states. Returns ------- float The Boltzmann weight for this configuration. """ funcName = 'CTL.models.Ising.getIsingWeight' assert (isinstance(g, UndirectedGraph)), funcs.errorMessage( err= "only UndirectedGraph can be trasferred to Ising tensor network, {} obtained." .format(g), location=funcName) E = 0.0 n = len(g.v) spin = funcs.intToBitList(S, n) for edge in g.getEdges(): v1, v2 = edge.vertices s1, s2 = spin[v1.index], spin[v2.index] if (s1 == s2): E -= edge.weight else: E += edge.weight return xplib.xp.exp(-E)
def sideLeg(self, tensor): """ Given tensor on one side, return the corresponding leg. If both legs are from that tensor, return the first. Parameters ---------- tensor : Tensor Tensor on one side. Returns ------- Leg The leg in the given tensor. Raises ------ AssertionError Raised if the both legs are not in given tensor. """ if (self.legs[0] in tensor.legs): return self.legs[0] elif (self.legs[1] in tensor.legs): return self.legs[1] else: raise ValueError(errorMessage("legs {} is not in tensor {}.".format(self.legs, tensor), location = 'Bond.sideLeg')) # getBondName = Bond.bondNameSet.newString
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 canonicalize(self, idx): ''' canonicalize the MPS, and the only non-isometry will be put at 0 <= idx < n after this, activeIdx will be set to idx and we can check the canonicalization of the MPS with self.checkCanonical if None for excepIdx(or set to the idx), it will give true before modified ''' assert (isinstance(idx, int) and (idx >= 0) and (idx < self.n)), funcs.errorMessage( 'index must in [0, n - 1), {} gotten.'.format(idx), location="FreeBoundaryMPS.canonicalize") for i in range(idx): # print('canonicalizing {} to {}'.format(i, i + 1)) u, s, v = SchimdtDecomposition(self._tensors[i], self._tensors[i + 1], self.chi) sv = contractTwoTensors(s, v) self._tensors[i] = u self._tensors[i + 1] = sv for i in range(self.n - 1, idx, -1): # print('canonicalizing {} to {}'.format(i, i - 1)) u, s, v = SchimdtDecomposition(self._tensors[i], self._tensors[i - 1], self.chi) # print('isometry = {}'.format(isIsometry(u, ['o']))) sv = contractTwoTensors(s, v) self._tensors[i] = u self._tensors[i - 1] = sv self.activeIdx = idx
def exactZFromGraphIsing(g): """ Calculate the exact partition function by enumerating configurations of Ising model. Parameters ---------- g : UndirectedGraph The site-graph to add interaction. The weights represent the betaJ on each edge. Returns ------- float The exact Z. """ funcName = 'CTL.models.Ising.exactZFromGraphIsing' assert (isinstance(g, UndirectedGraph)), funcs.errorMessage( err= "only UndirectedGraph can be trasferred to Ising tensor network, {} obtained." .format(g), location=funcName) res = 0.0 n = len(g.v) # for S in range(1 << n): # if (S % 10000 == 0): # print('{}/{}'.format(S, 1 << n)) # res += getIsingWeight(g, S) res = xplib.xp.sum( xplib.xp.array([getIsingWeight(g, S) for S in range(1 << n)])) return res
def trace(self, rows=None, cols=None): """ Trace of the current tensor after making a matrix according to rows and cols. For details, check Tensor.toMatrix Parameters ---------- rows : None or list of str or list of Leg The legs for the rows of the matrix. If None, deducted from cols. cols : None or list of str or list of Leg The legs for the cols of the matrix. If None, deducted from rows. Returns ------- float The trace of the matrix generated by given cols and rows. """ assert (not self.tensorLikeFlag), funcs.errorMessage( 'TensorLike do not have trace since no data contained.', 'Tensor.trace') mat = self.toMatrix(rows=rows, cols=cols) assert ( mat.shape[0] == mat.shape[1] ), "Error: Tensor.trace must have the same dimension for cols and rows, but shape {} gotten.".format( mat.shape) return xplib.xp.trace(mat)
def doubleSquareLatticeFBC(n, m = None, weight = 0.0): """ Create a graph of double square lattice, free boundary condition. Double square lattice: take the plaquette tensor as checker board, and they form a square lattice. The (n, m) lattice contains n(m + 1) + m(n + 1) sites. Parameters ---------- n : int The number of rows of the lattice. m : int, optional The number of columns of the lattice. By default, the same as n. weight : float or tuple of float, optional The weight of edges between vertices. If a tuple is given, then the first for left-up to right-bottom edges and the second for right-up to left-bottom edges. By default, both weights are 0.0. This will be used in models like Ising model, where the weight can represent interactions. Returns ------- UndirectedGraph A graph representing the lattice. """ funcName = 'CTL.funcs.graphFuncs.doubleSquareLatticeFBC' assert isinstance(n, int), funcs.errorMessage('n must be int, {} obtained.'.format(n), location = funcName) if funcs.isRealNumber(weight): weight = (weight, weight) weightTTB, weightBTT = weight if (m is None): m = n nn = n * (m + 1) + m * (n + 1) def getIndex(d, x, y): # d == 0: "up/bottom" of plaquettes, (n + 1) * m # d == 1: "left/right" of plaquettes, n * (m + 1) # order: up to down, left to right assert (d != 0) or (x >= 0 and x <= n and y >= 0 and y < m), funcs.errorMessage('({}, {}) is not a valid coordinate in ({}, {}) lattice.'.format(x, y, n, m), location = funcName + ".getIndex") assert (d != 1) or (x >= 0 and x < n and y >= 0 and y <= m), funcs.errorMessage('({}, {}) is not a valid coordinate in ({}, {}) lattice.'.format(x, y, n, m), location = funcName + ".getIndex") if (d == 0): return x * m + y else: return (n + 1) * m + x * (m + 1) + y g = UndirectedGraph(nn) for x in range(n): for y in range(m): # add edges for square (x, y) g.addEdge(idx1 = getIndex(1, x, y), idx2 = getIndex(0, x, y), weight = weightBTT) g.addEdge(idx1 = getIndex(1, x, y), idx2 = getIndex(0, x + 1, y), weight = weightTTB) g.addEdge(idx1 = getIndex(1, x, y + 1), idx2 = getIndex(0, x, y), weight = weightTTB) g.addEdge(idx1 = getIndex(1, x, y + 1), idx2 = getIndex(0, x + 1, y), weight = weightBTT) # if (y < m - 1): # g.addEdge(idx1 = getIndex(x, y), idx2 = getIndex(x, y + 1), weight = weightH) # if (x < n - 1): # g.addEdge(idx1 = getIndex(x, y), idx2 = getIndex(x + 1, y), weight = weightV) return g
def mergeTensor(self, idx, newTensor): # merge idx & (idx + 1) to newTensor funcName = 'FreeBoundaryMPS.mergeTensor' assert (self.isIndex(idx) and self.isIndex(idx + 1)), funcs.errorMessage( "{} or {} is invalid index.".format(idx, idx + 1), location=funcName) self._tensors = self._tensors[:idx] + [newTensor ] + self._tensors[(idx + 2):]
def getLegsByLabel(self, labelList): indices = funcs.generateIndices(self.labels, labelList) for index, label in zip(indices, labelList): if (index is None): raise ValueError( funcs.errorMessage('{} is not in tensor {}'.format( label, self), location='Tensor.getLegsByLabel')) return [self.legs[index] for index in indices]
def outProduct(self, labelList, newLabel): """ Deprecated Comment ------- The outer product will destroy the shape of diagonal tensor: we cannot easily combine several legs if it is a full diagonal tensor, so a TypeError will be raised. """ raise TypeError(funcs.errorMessage(location = "DiagonalTensor.outProduct", err = "DiagonalTensor cannot perform outProduct, since the diagonal nature will be destroyed."))
def norm(self): """ Norm of the current tensor. O(n). Returns ------- float The norm of data. """ assert (not self.tensorLikeFlag), funcs.errorMessage('DiagonalTensorLike do not have norm since no data contained.', 'DiagonalTensor.norm') return xplib.xp.linalg.norm(self.a)
def checkCanonical(self, excepIdx=None): ''' check if the current MPS is in canonical(isometry except for excepIdx) if the index is not given: check with the index the last time the MPS has been canonicalized for !!! Note that: this will be not true when excepIdx is None if the MPS has been changed directly by self._tensors[...] = ... ''' funcName = 'FreeBoundaryMPS.checkCanonical' assert (excepIdx is not None) or ( self.activeIdx is not None ), funcs.errorMessage( "exception index and MPS.activeIdx cannot be None at the same time.", location=funcName) if (excepIdx is None): excepIdx = self.activeIdx assert (isinstance(excepIdx, int) and (excepIdx >= 0) and (excepIdx < self.n)), funcs.errorMessage( "exception index must in [0, self.n), {} obtained.".format( excepIdx), location=funcName) if (self.n == 0): warnings.warn( funcs.warningMessage( "number of tensors in MPS is 0, return True", location=funcName)) return True # print([isIsometry(tensor, ['o']) for tensor in self._tensors]) for i in range(self.n): if (i == excepIdx): continue if (i == 0) or (i == self.n - 1): labels = ['o'] elif (i < excepIdx): labels = ['l', 'o'] else: labels = ['r', 'o'] # print(i, labels, isIsometry(self._tensors[i], labels)) if not isIsometry(self._tensors[i], labels): return False return True
def __init__(self, a, chi=16): self.a = a.copy() self.chi = chi assert funcs.compareLists(a.labels, [ 'l', 'r', 'u', 'd' ]), funcs.errorMessage( 'CTMRG can only accept tensor with legs ["l", "r", "u", "d"], {} obtained.' .format(a)) self.setRecords() self.setFTNs() self.setInitialTensors()
def toMatrix(self, rows=None, cols=None): """ Make a matrix of the data of this tensor, given the labels or legs of rows and cols. Parameters ---------- rows : None or list of str or list of Leg The legs for the rows of the matrix. If None, deducted from cols. cols : None or list of str or list of Leg The legs for the cols of the matrix. If None, deducted from rows. Returns ------- 2D ndarray of float The data of this tensor, in the form of (rows, cols). """ # print(rows, cols) # print(self.labels) # input two set of legs assert (not self.tensorLikeFlag), funcs.errorMessage( 'TensorLike cannot be transferred to matrix since no data contained.', 'Tensor.toMatrix') assert not ( (rows is None) and (cols is None) ), "Error in Tensor.toMatrix: toMatrix must have at least row or col exist." if (rows is not None) and (isinstance(rows[0], str)): rows = [self.getLeg(label) for label in rows] if (cols is not None) and (isinstance(cols[0], str)): cols = [self.getLeg(label) for label in cols] if (cols is None): cols = funcs.listDifference(self.legs, rows) if (rows is None): rows = funcs.listDifference(self.legs, cols) assert ( funcs.compareLists(rows + cols, self.legs) ), "Error Tensor.toMatrix: rows + cols must contain(and only contain) all legs of tensor." colIndices = self.getLegIndices(cols) rowIndices = self.getLegIndices(rows) colShape = tuple([self.shape[x] for x in colIndices]) rowShape = tuple([self.shape[x] for x in rowIndices]) colTotalSize = funcs.tupleProduct(colShape) rowTotalSize = funcs.tupleProduct(rowShape) moveFrom = rowIndices + colIndices moveTo = list(range(len(moveFrom))) data = xplib.xp.moveaxis(xplib.xp.copy(self.a), moveFrom, moveTo) data = xplib.xp.reshape(data, (rowTotalSize, colTotalSize)) return data
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 toVector(self): """ Flatten the data contained to a 1D-vector. Returns ------- 1D ndarray of float A vector contains the data in this tensor, following the current order of labels. """ assert (not self.tensorLikeFlag), funcs.errorMessage( 'TensorLike cannot be transferred to vector since no data contained.', 'Tensor.toVector') return xplib.xp.copy(xplib.xp.ravel(self.a))
def trace(self, rows = None, cols = None): """ Trace of the current diagonal tensor. To not destroy the property for the diagonal tensors, this function can only be used to calculate the global trace on the main diagonal. Parameters ---------- rows, cols: None Only set to be compatible with the usage for Tensor Returns ------- float The trace of the matrix generated by given cols and rows. """ assert (not self.tensorLikeFlag), funcs.errorMessage('DiagonalTensorLike do not have trace since no data contained.', 'DiagonalTensor.trace') return xplib.xp.sum(self.a)
def doubleMergeByBond(mpsA, mpsB, bond1, bond2): funcName = 'CTL.examples.MPS.doubleMergeByBond' idxA1, idxB1 = getBondTensorIndices(mpsA, mpsB, bond1) idxA2, idxB2 = getBondTensorIndices(mpsA, mpsB, bond2) idxA1, idxA2 = mpsA.makeAdjacent(idxA1, idxA2) idxB1, idxB2 = mpsB.makeAdjacent(idxB1, idxB2) # print('mpsA after swap = {}'.format(mpsA)) # print('mpsB after swap = {}'.format(mpsB)) assert (idxA1 + 1 == idxA2) and (idxB1 + 1 == idxB2), funcs.errorMessage( "index is not adjacent after swapping: ({}, {}) and ({}, {}).".format( idxA1, idxA2, idxB1, idxB2), location=funcName) return doubleMerge(mpsA, mpsB, idxA1, idxB1)
def toMatrix(self, rows, cols): """ Deprecated Make a matrix of the data of this diagonal tensor, given the labels or legs of rows and cols. Deprecated since this function is time comsuming(O(n^d)), and for most of the cases there are much better ways to use the data rather than making a matrix. For details, see CTL.tensor.contract for more information. Parameters ---------- rows : None or list of str or list of Leg The legs for the rows of the matrix. If None, deducted from cols. cols : None or list of str or list of Leg The legs for the cols of the matrix. If None, deducted from rows. Returns ------- 2D ndarray of float The data of this tensor, in the form of (rows, cols). """ assert (not self.tensorLikeFlag), funcs.errorMessage('DiagonalTensorLike cannot be transferred to matrix since no data contained.', 'DiagonalTensor.toMatrix') # print(rows, cols) # print(self.labels) # input two set of legs funcs.deprecatedFuncWarning(funcName = "DiagonalTensor.toMatrix", deprecateMessage = "Diagonal tensors should be used in a better way for linear algebra calculation rather than be made into a matrix.") assert not ((rows is None) and (cols is None)), "Error in Tensor.toMatrix: toMatrix must have at least row or col exist." if (rows is not None) and (isinstance(rows[0], str)): rows = [self.getLeg(label) for label in rows] if (cols is not None) and (isinstance(cols[0], str)): cols = [self.getLeg(label) for label in cols] if (cols is None): cols = funcs.listDifference(self.legs, rows) if (rows is None): rows = funcs.listDifference(self.legs, cols) assert (funcs.compareLists(rows + cols, self.legs)), "Error Tensor.toMatrix: rows + cols must contain(and only contain) all legs of tensor." colIndices = self.getLegIndices(cols) rowIndices = self.getLegIndices(rows) colShape = tuple([self.shape[x] for x in colIndices]) rowShape = tuple([self.shape[x] for x in rowIndices]) colTotalSize = funcs.tupleProduct(colShape) rowTotalSize = funcs.tupleProduct(rowShape) data = funcs.diagonalNDTensor(self.a, self.dim) data = xplib.xp.reshape(data, (rowTotalSize, colTotalSize)) return data
def plaquetteIsingTensor(weight, diamondForm=False): """ A local tensor of Ising model, based on plaquette. Including the interactions on four edges. Parameters ---------- weight : float or length-2 tuple of float The weights(J) of Ising model. If a length-2 tuple, then two values represent (J_vertical, J_horizontal) diamondForm : bool, default False If True, then instead of usual ['lu', 'ru', 'rd', 'ld'] tensor, the tensor will be rotated 45 degrees clockwise, so that the ['lu', 'ru', 'rd', 'ld'] will be ['u', 'r', 'd', 'l'], so vertical weight will become the weight from left-bottom to right-top Returns ------- Tensor Plaquette tensor of labels ['lu', 'ru', 'rd', 'ld'] or ['l', 'd', 'r', 'u'] """ funcName = 'CTL.models.Ising.plaquetteIsingTensor' if isinstance(weight, float): weight = [weight, weight] else: assert len(weight) == 2, funcs.errorMessage( 'Only float or (float, float) is accepted by {}, {} obtained.'. format(funcName, weight), location=funcName) weight = list(weight) data = xplib.xp.zeros((2, 2, 2, 2), dtype=xplib.xp.float64) # ru, rd, ld, lu for s in range(16): idx = funcs.intToBitTuple(s, 4) localE = 0.0 for i in range(4): if (idx[i] == idx[(i + 1) % 4]): localE -= weight[i % 2] # e.g. ru & rd will share a bond of weight[0](J_vertical) else: localE += weight[i % 2] data[idx] = xplib.xp.exp(-localE) labels = ['ru', 'rd', 'ld', 'lu'] if diamondForm: labels = ['r', 'd', 'l', 'u'] return Tensor(labels=labels, data=data, degreeOfFreedom=2)
def swap(self, aIdx, bIdx): # tensorA and tensorB are tensors in tensorList assert ((aIdx >= 0) and (aIdx < self.n) and (bIdx < self.n) and (bIdx >= 0) and (abs(aIdx - bIdx) == 1)), funcs.errorMessage( "index {} and {} are not valid for MPS with {} tensors.". format(aIdx, bIdx, self.n), location="FreeBoundaryMPS.swap") self._tensors[aIdx], _, self._tensors[bIdx] = SchimdtDecomposition( self._tensors[aIdx], self._tensors[bIdx], self.chi, squareRootSeparation=True, swapLabels=(['o'], ['o'])) self.activeIdx = None
def single(self): """ Generate a single value from a shapeless tensor. Returns ------- float A single value of this tensor. """ # return the single value of this tensor # only works if shape == (,) assert (not self.tensorLikeFlag), funcs.errorMessage( 'TensorLike cannot be transferred to single value since no data contained.', 'Tensor.single') assert self.shape == ( ), "Error: cannot get single value from tensor whose shape is not ()." return self.a