Tensor Network Theory Library  Beta release 1.0
A library of routines for performing TNT-based operations
Variational Minimisation

Functions

double tntMpsVarMinMpo2sSweep (tntNetwork mps, int dir, int chi, tntNetwork mpo, tntNodeArray *OeffL, tntNodeArray *OeffR, double *err)
 
void tntMpsVarMinMpoInit (tntNetwork mps, tntNetwork mpo, tntNodeArray *OeffL, tntNodeArray *OeffR)
 
void tntMpsVarMinMpoUpdate (tntNode tnA, tntNode tno, unsigned dir, unsigned j, tntNodeArray *Oeff)
 
tntNetwork tntMpsVarMinMpo2sBuild (tntNode beta, tntNode gamma, tntNode theta, tntNode tno)
 
tntNode tntMpsVarMinMpo2sContract (tntNode theta, tntNetwork nwMV)
 

Detailed Description

This section describes MPS functions which are useful for performing variational minimisation algorithms e.g. DMRG, although they are not limited to this.

Function Documentation

tntNetwork tntMpsVarMinMpo2sBuild ( tntNode  beta,
tntNode  gamma,
tntNode  theta,
tntNode  tno 
)

Builds the network representing the effective operator for two sites \(j, j+1\), which will later be used in the variational minimisation routine. The function does not need to have knowledge of \(j\), since the correct nodes are chosen by the calling function.

mps_dmrg_heff.png

The network is built using copies of all the operators passed as arguments, so they are unchanged by the function. This means that when this network is no longer required it should be freed.

Returns
The network for the effective operator times vector.
Parameters
betaNode formed from contracting all nodes to the left of the theta node. Unchanged by function.
gammaNode formed from contracting all nodes to the right of the theta node. Unchanged by function.
thetaNode formed from contracting the two MPS nodes to be varied. Unchanged by function.
tnoNode formed from contracting the two MPO nodes on the sites of the two MPS nodes to be varied. Unchanged by function.
352 {
353 
354  tntNetwork Oeff; /* Network representing the effective Hamiltonian */
355 
356  /* Create the new network */
357  Oeff = tntNetworkCreate();
358 
359  /* Assign all the pointers to copies of the nodes that they currently point to */
360  beta = tntNodeCopy(beta,0);
361  gamma = tntNodeCopy(gamma,0);
362  theta = tntNodeCopy(theta, 0);
363  tno = tntNodeCopy(tno,0);
364 
365  /* Insert the theta node in the network */
367 
368  /* Insert the beta node at the start of the network */
370 
371  /* Insert the gamma node at the end of the network */
372  tntNodeInsertAtEnd(gamma, TNT_MPS_L, TNT_MPS_R, Oeff);
373 
374  /* Join the left upwards leg (5) of tno to the left downwards leg (3) of theta */
375  tntNodeJoin(theta, 3, tno, 5);
376 
377  /* Join the left upwards leg (6) of tno to the left downwards leg (4) of theta */
378  tntNodeJoin(theta, 4, tno, 6);
379 
380  /* Join the left internal leg (TNT_MPS_L) of tno to the right middle leg (4) of beta */
381  tntNodeJoin(tno, TNT_MPS_L, beta, 4);
382 
383  /* Join the left internal leg (TNT_MPS_R) of tno to the left middle leg (3) of gamma */
384  tntNodeJoin(tno, TNT_MPS_R, gamma, 3);
385 
386  /* return the network */
387  return Oeff;
388 
389 }
void tntNodeInsertAtEnd(tntNode tnI, int legIlast, int legInwend, tntNetwork tnw)
Definition: tntNode_funcs.c:945
#define TNT_MPS_L
Definition: tntMps.h:56
void tntNodeInsertAtStart(tntNode tnI, int legInwstart, int legIfirst, tntNetwork tnw)
Definition: tntNode_funcs.c:905
struct tnetwork * tntNetwork
Definition: tnt.h:123
tntNetwork tntNetworkCreate(void)
Definition: tntNetwork_funcs.c:62
tntNode tntNodeCopy(tntNode tn, int conj)
Definition: tntNode_funcs.c:486
void tntNodeJoin(tntNode tnA, int legA, tntNode tnB, int legB)
Definition: tntNode_funcs.c:462
#define TNT_MPS_R
Definition: tntMps.h:61
tntNode tntMpsVarMinMpo2sContract ( tntNode  theta,
tntNetwork  nwMV 
)

Performs the network contraction of \(\beta_j\), \(\hat{o}_{j,j+1}\), \(\gamma_{j+1}\) and \(\Theta_{j,j+1}\) as defined in the network below, needed for each iteration of the eigenvalue routine to variationally minimise the expecation value of the operator over two sites \(j,j+1\).

mps_dmrg_heff.png

This contraction is equal to a matrix-times-vector multiplication, where \(\beta_j\), \(\hat{o}_{j,j+1}\), \(\gamma_{j+1}\) form the matrix \(O_{\mathrm{eff}}\) and \(\Theta_{j,j+1}\) is the vector. The contraction is carried out in the order which leads to the smallest intermediate tensors, rather than forming the matrix first.

Returns
The node that is the result of the matrix times vector contraction, the node having the same form as theta.
Parameters
thetaThe node to contract with \(O_{\mathrm{eff}}\). Unchanged by the function.
nwMVNetwork representing a term in the matrix * vector multiplication.
408 {
409 
410  /* Maps for the leg id's on contraction */
411  int idmap_theta[5] = {0,1,2,7,8}; /* Map used when contracting theta with beta */
412  int idmap_h[5] = {0,3,4,7,8}; /* Used for the operator h when contracting with the updated A */
413  int idmap_thetaup[9] = {0,5,6,7,8,9,1,3,4}; /* Map used when contracted theta with gamma to for the final updated tensor */
414  int idmap_gamma[7] = {0,5,6,7,8,2,10}; /* Map used when contracted theta with gamma to for the final updated tensor */
415 
416  tntNetwork nwMVc; /* Pointers to the start and end of the copy of the network */
417  tntNode theta_o; /* the original node in the MPS */
418  tntNode theta_up; /* The updated node */
419  tntNode beta, gamma; /* The nodes that are the result of all previously contracted nodes from the left or right respectively */
420  tntNode tno; /* The nodes representing the operator for these two sites in the Hamiltonian. Prepared using tntMpsVarMinMpo2sBuild().
421  Legs are labelled as 1 and 2 for left and right internal legs, 3 and 4 as downwards facing physical legs (L & R), 5 and 6 as upwards facing physical legs (L & R). */
422  unsigned legsq[6] = {5,6,7,8,9,10}; /* legs to remove from the final updated gamma */
423 
424  /* Use a copy of the original network, so that the orinal network is unchanged by the function */
425  nwMVc = tntNetworkCopy(nwMV);
426 
427  /* Indentify the first node, the node to be varied, the operator on the side and the last node */
428  beta = tntNodeFindFirst(nwMVc);
429  theta_o = tntNodeFindConn(beta,TNT_MPS_R);
430  tno = tntNodeFindConn(theta_o, TNT_MPS_D);
431  gamma = tntNodeFindConn(theta_o, TNT_MPS_R);
432 
433  /* Make a copy of node to contract with Oeff */
434  theta_up = tntNodeCopy(theta,0);
435 
436  /* Replace this in the network to be contracted */
437  tntNodeReplace(theta_o,theta_up);
438 
439  /* Delete the original node */
440  tntNodeFree(&theta_o);
441 
442  /* The first step is to contract beta with theta. */
443  theta_up = tntNodeContract(beta,theta_up,NULL,idmap_theta);
444 
445  /* The next step is to contract theta with tno. */
446  theta_up = tntNodeContract(theta_up,tno,NULL,idmap_h);
447 
448  /* Theta is then contracted with gamma */
449  theta_up = tntNodeContract(theta_up,gamma,idmap_thetaup,idmap_gamma);
450 
451  /* Make a copy of tnA_up */
452  theta_o = tntNodeCopy(theta_up,0);
453 
454  /* Delete the copy of the network */
455  tntNetworkFree(&nwMVc);
456 
457  /* all singleton legs are removed on the copy of the node. */
458  tntNodeSqueeze(theta_o,6,legsq);
459 
460  /* Return the updated node */
461  return theta_o;
462 
463 }
tntNode tntNodeFindConn(tntNode tn, unsigned legA)
Definition: tntNode_funcs.c:438
struct tnode * tntNode
Definition: tnt.h:118
void tntNodeReplace(tntNode tnA, tntNode tnB)
Definition: tntNode_funcs.c:726
void tntNetworkFree(tntNetwork *tnwp)
Definition: tntNetwork_funcs.c:84
tntNode tntNodeContract(tntNode tnA, tntNode tnB, int *legMapAC, int *legMapBC)
Definition: tntDummy_funcs.c:34
struct tnetwork * tntNetwork
Definition: tnt.h:123
void tntNodeSqueeze(tntNode tn, unsigned squeezeNum, unsigned *squeezeLegs)
Definition: tntNode_funcs.c:1033
void tntNodeFree(tntNode *tn)
Definition: tntNode_funcs.c:420
tntNode tntNodeCopy(tntNode tn, int conj)
Definition: tntNode_funcs.c:486
tntNode tntNodeFindFirst(tntNetwork tnw)
Definition: tntNode_funcs.c:664
tntNetwork tntNetworkCopy(tntNetwork tnw)
Definition: tntNetwork_funcs.c:36
#define TNT_MPS_D
Definition: tntMps.h:66
#define TNT_MPS_R
Definition: tntMps.h:61
double tntMpsVarMinMpo2sSweep ( tntNetwork  mps,
int  dir,
int  chi,
tntNetwork  mpo,
tntNodeArray OeffL,
tntNodeArray OeffR,
double *  err 
)

Performs a sweep varying the elements of the supplied MPS, two at a time, in order to minimise the result of the MPS-MPO-MPS network contraction. If the MPS represents a wave function \(|\psi\rangle\) and the MPO represents a site-wide operator \(\hat{O}\), the network to contract represents \(\langle\psi|\hat{O}|\psi\rangle \). In this case the operator will usually by the system Hamiltonian, so this sweep can be used in a routine to minimise the energy i.e. find the ground state of the system. The function applies updates to each pair of sites in either a left-to-right or right-to-left sweep depending on the direction specified in the arguments.

The sweep consists of first building the network representing the MPS-MPO-MPS product, illustrated below for a six site system.

mps_mpo_mps.png

The two sites to be updated are then contracted with one another to form a \(\Theta\) node, as are the corresponding operators (illustrated below for sites 2 and 3).

mps_dmrg_theta.png

The effective operator for this pair of sites is then built by contracting all nodes to the left and to the right of the sites to be updated. However, rather than contracting the nodes each time, the pre-contracted nodes that are passed as arguments are used to build a new network. The resulting network has the following form:

mps_dmrg_heff.png

It corresponds to a matrix-times-vector multiplication, where \(\Theta\) is the vector and \(\beta\), \(h\) and \(\gamma\) together form the matrix that represents the effective operator. Rather than contracting the latter 3 nodes to form the matrix explicitly, this network is passed to the core function tntNetworkMinSite(). This function uses an efficient contraction pattern (defined in tntMpsVarMinMpo2sContract()) to iteratively perform the matrix-times-vector multiplication. In doing so it finds the \(\Theta\) node which represents the eigenvector of the matrix having the smallest eigenvalue.

Once this \(\Theta\) node is found, it is replaced in the network, and an SVD is performed to reform two \(A\) nodes. This SVD will be trunctated to \(\chi\) (passed in the input argument chi), and the sum of all truncation errors is passed to the value pointed to by argument err.

The twist or orthogonality centre is moved as the two-site optimisations are performed. Note that after completion of a left to right sweep the orthogonality centre will be on the last site, i.e. all sites apart from the last site (site \(L-1\)) will obey the condition.

left_orth.png

and after the completion of a right to left sweep the orthogonality centre will be on the first site, i.e. all sites apart from the first site (site 0) will obey following the condition.

right_orth.png
Returns
The current value of the MPS-MPO-MPS product.
Parameters
mpsNetwork representing the MPS. Will be changed by the function to one that gives a smaller expectation of the operator given in mpo
dirThe direction for performing the time-step - either left to right using TNT_MPS_L (1) or right to left using TNT_MPS_R (2).
chiThe maximum internal dimension. All SVD's will be truncated to this value.
mpoThe network representing the MPO representation of the site-wide operator whose expectation to minimise (usually the system Hamiltonian). Can be generated using tntMpsCreateMpo().
OeffLPrecontracted nodes in the MPS-MPO-MPS network. Can be generated using tntMpsVarMinMpoInit() if this function is being called for the first time. The nodes are updated during the routine for use in subsequent sweeps.
OeffRPrecontracted nodes in the MPS-MPO-MPS network. Can be generated using tntMpsVarMinMpoInit() if this function is being called for the first time. The nodes are updated during the routine for use in subsequent sweeps.
errGives the sum of all the errors returned from the SVD.
Examples:
dmrg.c.
70 {
71  tntNetwork Oeff; /* The network representing the effective operator for the current site */
72  tntNode tnA1, tnA2; /* The current nodes to be minimised */
73  tntNode tno1,tno2,tno1c,tno2c,tno; /* MPO nodes corresponding to the current nodes to be minimised, copies of them, and the contracted node of them. */
74  tntNode theta; /* Node that is result of contraction of the two nodes to be minimised */
75  tntNode beta,gamma; /* Precontracted nodes in full network of all nodes to the left and right of theta node */
76  tntNode tnS, tnVT; /* The nodes returned from an SVD of theta, and node resulting in contraction from S and VT */
77  int L, j, jstart, jend, dj; /* number of sites, site index, starting site, finishing site, difference in site number */
78  int opdir; /* direction opposite to direction of sweep */
79  int startlegs[2], legmapsvd[5] = {0,1,2,3,3}; /* The leg-ids for the SVD of each node and leg map for SVD */
80  int legmap1[5] = {0,1,2,3,5}, legmap2[5] = {0,1,2,3,5}; /* leg maps for contraction -> will be modified depending on the direction of moving through the network below */
81  double errc = 0.0; /* Error returned from each SVD */
82  tntNode theta_eig; /* the eigennode */
83  tntComplex E_eig; /* the eigenvalue */
84 
85  /* Find the length of the MPS */
86  L = tntMpsLength(mps);
87 
88  if (TNT_MPS_R == dir) {
89  /* Set the current tensor equal to the first tensor in the network if moving right */
90  tnA1 = tntNodeFindFirst(mps);
91  tno1 = tntNodeFindFirst(mpo);
92  opdir = TNT_MPS_L;
93  jstart = 0;
94  jend = L-2;
95  dj = 1;
96  legmap2[3] = 4; /* Second tensor is right tensor, so downwards physical leg is renumbered 4 on contraction */
97  legmap2[4] = 6; /* Second tensor is right tensor, so upwards physical leg is renumbered 6 on contraction */
98  startlegs[0] = 1; /* Left hand legs go to U */
99  startlegs[1] = 3; /* Left hand legs go to U */
100  } else {
101  /* Set the current tensor equal to the last tensor in the network if moving left */
102  tnA1 = tntNodeFindLast(mps);
103  tno1 = tntNodeFindLast(mpo);
104  opdir = TNT_MPS_R;
105  jstart = L-2;
106  jend = 0;
107  dj = -1;
108  legmap1[3] = 4; /* First tensor is right tensor, so downwards physical leg is renumbered 4 on contraction */
109  legmap1[4] = 6; /* First tensor is right tensor, so upwards physical leg is renumbered 6 on contraction */
110  startlegs[0] = 2; /* Right hand legs go to U */
111  startlegs[1] = 4; /* Right hand legs go to U */
112  }
113 
114  /* The function loops over the first N-1 sites of the MPS, i.e. site 0 to site L-2 if sweeping left to right, and site L-2 to site 0 is sweeping right to left (where the site index starts fom 0). Here j is chosen to always refer to the left-most node in the pair of nodes being updated. */
115  for (j = jstart; j != jend; j+=dj) {
116 
117  /* the next MPS node along becomes tnA2 */
118  tnA2 = tntNodeFindConn(tnA1, dir);
119 
120  /* the next MPO node along becomes tno2 */
121  tno2 = tntNodeFindConn(tno1, dir);
122 
123  /* Contract tnA1 and tnA2 to find theta */
124  theta = tntNodeContract(tnA1,tnA2,legmap1,legmap2);
125 
126  /* Make copies of the operators to be contracted (make copies rather than contracting directly in the network as unlike theta, these will not be updated or SVD'd afterwards) */
127  tno1c = tntNodeCopy(tno1,0);
128  tno2c = tntNodeCopy(tno2,0);
129 
130  /* Join the operators, then contract to form a two site operator */
131  tntNodeJoin(tno1c,dir,tno2c,opdir);
132  tno = tntNodeContract(tno1c,tno2c,legmap1,legmap2);
133 
134  /* Choose the correct beta and gamma */
135  beta = OeffL->vals[j];
136  gamma = OeffR->vals[j+1];
137 
138  /* Build the network representing the effective Hamiltonian for the two sites to be updated */
139  Oeff = tntMpsVarMinMpo2sBuild(beta,gamma,theta,tno);
140 
141  /* The eigenvector with the smallest eigenvalue is found in tntNetworkMinSite() by iteratively contracting the effective Hamiltonian with an updated theta each iteration, using the current tensor as a starting point. */
142  theta_eig = tntNetworkMinSite(theta, &Oeff, 1, &tntMpsVarMinMpo2sContract, NULL, &E_eig);
143 
144  /* Free the effective Hamiltonian: the minimisation function uses a copy of it so it is unchanged by that function */
145  tntNetworkFree(&Oeff);
146 
147  /* Free the two-site operator: once again the minimisation function and this function use a copy of it */
148  tntNodeFree(&tno);
149 
150  /* Replace theta_eig in the network */
151  tntNodeReplace(theta,theta_eig);
152 
153  /* Delete the original theta node */
154  tntNodeFree(&theta);
155 
156  /* take the svd of the theta node found, to reform two tensors in the MPS form. */
157  tnA1 = tntNodeSVD(theta_eig, startlegs, 2, dir, opdir, legmapsvd, chi, &errc);
158  /* U (the first unitary tensor) now refers to the tnA1, which is finished with */
159 
160  /* Update the node for Oeff with the new value of tnA1 */
161  if (TNT_MPS_R == dir) {
162  tntMpsVarMinMpoUpdate(tnA1, tno1, dir, j, OeffL);
163  } else {
164  tntMpsVarMinMpoUpdate(tnA1, tno1, dir, j+1, OeffR);
165  }
166 
167  /* Find tnS and tnVT by moving along the network */
168  tnS = tntNodeFindConn(tnA1,dir);
169  tnVT = tntNodeFindConn(tnS,dir);
170 
171  /* Contract S and VT to reform A2: this tensor will contain the orthogonality twist, and will take the place of tnA1 in the next two site update */
172  tnA2 = tntNodeContract(tnS, tnVT, NULL, NULL);
173 
174  /* tnA2 becomes tnA1 */
175  tnA1 = tnA2;
176 
177  /* the operator tno2 beomes tno1 */
178  tno1 = tno2;
179 
180  /* add to the total error */
181  *err += errc;
182 
183  /* The process then repeats for the next pair of nodes */
184  }
185 
186  /* Move the orthogonality centre to the last site (not strictly necessary, but will probably be expected by users by analogy with TEBD algorithm) */
187 
188  /* the next MPS node along becomes tnA2 - this should be the end site (i.e. site zero or site L-1, depending on direction) */
189  tnA2 = tntNodeFindConn(tnA1, dir);
190 
191  /* Change the leg map: we are SVD-ing a 3-leg A node rather than a 4-leg theta node, so there will only ever be one physical leg */
192  startlegs[1] = TNT_MPS_D;
193 
194  /* Take SVD of tnA1, assigning U back to tnA1 */
195  tnA1 = tntNodeSVD(tnA1, startlegs, 2, dir, opdir, legmapsvd, chi, &errc);
196 
197  /* Find tnS and tnVT by moving along the network */
198  tnS = tntNodeFindConn(tnA1,dir);
199  tnVT = tntNodeFindConn(tnS,dir);
200 
201  /* Contract S and VT with A2 to reform A2: this tensor will now contain the orthogonality twist */
202  tnS = tntNodeContract(tnS, tnVT, NULL, NULL);
203  tnA2 = tntNodeContract(tnS, tnA2, NULL, NULL);
204 
205  /* \return The current energy of the system. */
206  return E_eig.re;
207 }
void tntMpsVarMinMpoUpdate(tntNode tnA, tntNode tno, unsigned dir, unsigned j, tntNodeArray *Oeff)
Definition: tntMpsDmrgBlocks.c:256
tntNode tntNodeFindConn(tntNode tn, unsigned legA)
Definition: tntNode_funcs.c:438
tntNode tntMpsVarMinMpo2sContract(tntNode theta, tntNetwork nwMV)
Definition: tntMpsDmrgBlocks.c:406
unsigned tntMpsLength(tntNetwork mps)
Definition: tntMpsUtil.c:208
struct tnode * tntNode
Definition: tnt.h:118
tntNode tntNodeFindLast(tntNetwork tnw)
Definition: tntNode_funcs.c:685
void tntNodeReplace(tntNode tnA, tntNode tnB)
Definition: tntNode_funcs.c:726
void tntNetworkFree(tntNetwork *tnwp)
Definition: tntNetwork_funcs.c:84
tntNode tntNodeContract(tntNode tnA, tntNode tnB, int *legMapAC, int *legMapBC)
Definition: tntDummy_funcs.c:34
#define TNT_MPS_L
Definition: tntMps.h:56
tntNode tntNodeSVD(tntNode tn, int *startLegs, unsigned startNum, int legUS, int legSU, int *legMap, int connDim, double *err)
Definition: tntNode_funcs.c:64
tntNetwork tntMpsVarMinMpo2sBuild(tntNode beta, tntNode gamma, tntNode theta, tntNode tno)
Definition: tntMpsDmrgBlocks.c:348
struct tnetwork * tntNetwork
Definition: tnt.h:123
tntNode * vals
Pointer to the first element in the tntNode array.
Definition: tnt.h:179
void tntNodeFree(tntNode *tn)
Definition: tntNode_funcs.c:420
double re
Definition: tnt.h:137
tntNode tntNetworkMinSite(tntNode tnA, tntNetwork *nwMV, unsigned NM, tntNode(*eig_contract)(tntNode, tntNetwork), void(*eig_prep)(tntNode, tntNetwork), tntComplex *eigval)
Definition: tntNetwork_funcs.c:111
tntNode tntNodeCopy(tntNode tn, int conj)
Definition: tntNode_funcs.c:486
tntNode tntNodeFindFirst(tntNetwork tnw)
Definition: tntNode_funcs.c:664
void tntNodeJoin(tntNode tnA, int legA, tntNode tnB, int legB)
Definition: tntNode_funcs.c:462
#define TNT_MPS_D
Definition: tntMps.h:66
#define TNT_MPS_R
Definition: tntMps.h:61
Use this type to define complex numbers. Any complex arithmetic needs to be done manually. For example to add two complex numbers A and B to give C write:
Definition: tnt.h:136
void tntMpsVarMinMpoInit ( tntNetwork  mps,
tntNetwork  mpo,
tntNodeArray OeffL,
tntNodeArray OeffR 
)

Prepares the nodes required for performing a variational minimisation sweep using tntMpsVarMinMpo2sSweep() by pre-contracting nodes in the MPS-MPO-MPS network. If the MPS represents a wave function \(|\psi\rangle\) and the MPO represents a site-wide operator \(\hat{O}\), the returned network represents \(\langle\psi|\hat{O}|\psi\rangle \).

mps_mpo_mps_fullnw.png

The function performs contractions of this network to form the arrays of pre-contracted nodes OeffL and OeffR.

The array entry OeffL->vals[j] is the node that represents all nodes to the left of site \(j\) contracted to form the \(\beta\) node.

mps_dmrg_beta.png

Note that for \(j=0\) the beta node will simply be a tensor with all leg dimensions 1 and containing the entry 1.

The array entry OeffR->vals[j] is the node that represents all nodes to the right of site \(j\) contracted to form the \(\gamma\) node.

mps_dmrg_gamma.png

Note that if \(j=L-1\), then the gamma node will simply be a tensor with all leg dimensions 1 and containing the entry 1.

Returns
No return value.
Parameters
mpsThe wave function of the system
mpoThe MPO representing the site-wide operator
OeffLA pointer to the uninitialised array that will contain the left hand nodes
OeffRA pointer to the uninitialised array that will contain the right hand nodes
Examples:
dmrg.c.
44 {
45  tntNetwork Hfull; /* The full MPS-MPO-MPS network */
46  tntNode tnA, tnAf; /* the current node A of the MPS and the flipped (hermitian conjugate) of the current node Af */
47  tntNode tno; /* The node representing the operator tno on site j of the current term in the Hamiltonian */
48  tntNode beta; /* The node that is the results of all previously contracted nodes from the right and left */
49  tntNode eye; /* Identity node having one element to act as terminating node */
50  tntComplexArray eyevals; /* Values for the identity node to act as termninating node */
51  unsigned legid_term[6] = {1,2,3,4,5,6}, legdim_term[6] = {1,1,1,1,1,1}; /* Leg id's and dimensions for the terminating node */
52  unsigned L; /* the length of the MPS network */
53  int j; /* site counter */
54  int i; /* used for looping */
55 
56  /* Maps for the leg id's on contraction */
57  int idmap_A[4] = {0,1,2,7}; /* Map used when contracting A with Oeff */
58  int idmap_op[4] = {0,3,4,7}; /* Used for the operator when contracting with A or Oeff */
59  int idmap_Afl[3] = {0,5,6}; /* Used for the flipped tensor contracting with the result of the A-operator contraction or Oeff */
60 
61  /* Create the required identity node: it will have the same form as a beta or gamma node, but all legs will have dimension 1, and the value of it will be 1. */
62  /* First create the array values */
63  eyevals = tntComplexArrayAlloc(1);
64  eyevals.vals[0].re = 1.0;
65  /* Then create the node, adding an extra leg for contracting with the MPO base */
66  eye = tntNodeCreate(&eyevals, 6, legid_term, legdim_term);
67 
68 
69  /* Free the array values */
70  tntComplexArrayFree(&eyevals);
71 
72  /* Find the length of the network */
73  L = tntMpsLength(mps);
74 
75  /* Allocate the arrays - one entry for each site in the network */
76  *OeffL = tntNodeArrayAlloc(L);
77  *OeffR = tntNodeArrayAlloc(L);
78 
79  /* ------ First start contracting from the left to the right to form the nodes required for OeffL ---- */
80 
81  /* Create the full network */
82  Hfull = tntMpsMpoMpsConnect(mps,mpo);
83 
84  /* Find the MPS node corresponding to site 0 */
85  tnA = tntNodeFindFirst(Hfull);
86 
87  /* Find the MPO node for site 0 */
88  tno = tntNodeFindConn(tnA, TNT_MPS_D);
89 
90  /* Find flipped MPS node corresponding to site 0 */
91  tnAf = tntNodeFindConn(tno, TNT_MPS_D);
92 
93  /* Let beta be equal to the identity node */
94  beta = tntNodeCopy(eye,0);
95 
96  /* ----------------- SETTING QN INFO HERE ---------------- */
97  /* Put quantum number info (all zeros) on the legs of the node */
98  if (tntSymmTypeGet()) {
99  for (i = 1; i < 7; i++) {
100  /* Set the quantum numbers for the identity node. */
101  tntNodeSetQN(beta,i,NULL,2*(i%2)-1); /* Set legs to alternate as incoming and outgoing legs - odd legs are incoming, even legs outgoing */
102  }
103  }
104  /* ----------------- END OF SETTING QN INFO ----------------- */
105 
106  /* Insert a copy of the identity node at the start of the network */
107  tntNodeInsertAtStart(beta,TNT_MPS_L,TNT_MPS_R,Hfull); /* insert between terminating node and first MPS node */
108  tntNodeJoin(beta,4,tno,TNT_MPS_L); /* Join it to the MPO node */
109  tntNodeJoin(beta,6,tnAf,TNT_MPS_L); /* Join it to the flipped MPS node */
110 
111  /* Let beta be the first element in the array */
112  OeffL->vals[0] = tntNodeCopy(beta,0);
113 
114  /* Now loop through the network from left to right performing contractions */
115  for (j = 1; j < (int) L; j++) {
116 
117  /* Contract beta with the MPS node */
118  beta = tntNodeContract(beta,tnA,NULL,idmap_A);
119 
120  /* Contract beta with the MPO term */
121  beta = tntNodeContract(beta,tno,NULL,idmap_op);
122 
123  /* Contract beta with the flipped MPS node (final contraction step) */
124  beta = tntNodeContract(beta,tnAf,NULL,idmap_Afl);
125 
126  /* Copy the resulting node to the array */
127  OeffL->vals[j] = tntNodeCopy(beta, 0);
128 
129  /* Find the next node along in the unflipped wave function */
130  tnA = tntNodeFindConn(beta, TNT_MPS_R);
131 
132  /* identify the operator for this term in the Hamiltonian tno */
133  tno = tntNodeFindConn(tnA, TNT_MPS_D);
134 
135  /* identify the flipped node tnf */
136  tnAf = tntNodeFindConn(tno, TNT_MPS_D);
137 
138  }
139 
140  /* Free the network */
141  tntNetworkFree(&Hfull);
142 
143 
144  /* ------ Now contract from right to left to form the nodes required for OeffR ---- */
145 
146  /* Create the full network */
147  Hfull = tntMpsMpoMpsConnect(mps,mpo);
148 
149  /* Find the MPS node corresponding to site L-1 */
150  tnA = tntNodeFindLast(Hfull);
151 
152  /* Find the MPO node for site L-1 */
153  tno = tntNodeFindConn(tnA, TNT_MPS_D);
154 
155  /* Find flipped MPS node corresponding to site 0 */
156  tnAf = tntNodeFindConn(tno, TNT_MPS_D);
157 
158  /* Let beta be equal to the identity node */
159  beta = tntNodeCopy(eye,0);
160 
161  /* ----------------- SETTING QN INFO HERE ---------------- */
162  /* Put quantum number info (all zeros) on the legs of the node */
163  if (tntSymmTypeGet()) {
164 
165  tntIntArray legqn; /* Contains the quantum number for the relevant leg */
166 
167  /* quantum numbers for the first two legs come from the last MPS */
168  legqn = tntNodeGetQN(tnA,TNT_MPS_R);
169  tntNodeSetQN(beta,1,&legqn,1);
170  tntNodeSetQN(beta,2,&legqn,-1);
171  tntIntArrayFree(&legqn);
172 
173  /* quantum numbers for the first middle legs come from the last MPO */
174  legqn = tntNodeGetQN(tno,TNT_MPS_R);
175  tntNodeSetQN(beta,3,&legqn,1);
176  tntNodeSetQN(beta,4,&legqn,-1);
177  tntIntArrayFree(&legqn);
178 
179  /* quantum numbers for the last two legs come from the last flipped MPS */
180  legqn = tntNodeGetQN(tnAf,TNT_MPS_R);
181  tntNodeSetQN(beta,5,&legqn,-1);
182  tntNodeSetQN(beta,6,&legqn,1);
183  tntIntArrayFree(&legqn);
184 
185  }
186  /* ----------------- END OF SETTING QN INFO ----------------- */
187 
188  /* Insert a copy of the identity node at the end of the network */
189  tntNodeInsertAtEnd(beta,TNT_MPS_L,TNT_MPS_R,Hfull); /* insert between terminating node and first MPS node */
190  tntNodeJoin(beta,3,tno,TNT_MPS_R); /* Join it to the MPO node */
191  tntNodeJoin(beta,5,tnAf,TNT_MPS_R); /* Join it to the flipped MPS node */
192 
193  /* Let beta be the last element in the array */
194  OeffR->vals[L-1] = tntNodeCopy(beta,0);
195 
196  /* Now loop through the network from right to left performing contractions */
197  for (j = L-2; j >= 0; j--) {
198 
199  /* Contract beta with the MPS node */
200  beta = tntNodeContract(beta,tnA,NULL,idmap_A);
201 
202  /* Contract beta with the MPO term */
203  beta = tntNodeContract(beta,tno,NULL,idmap_op);
204 
205  /* Contract beta with the flipped MPS node (final contraction step) */
206  beta = tntNodeContract(beta,tnAf,NULL,idmap_Afl);
207 
208  /* Copy the resulting node to the array */
209  OeffR->vals[j] = tntNodeCopy(beta, 0);
210 
211  /* Find the next node along in the unflipped wave function */
212  tnA = tntNodeFindConn(beta, TNT_MPS_L);
213 
214  /* identify the operator for this term in the Hamiltonian tno */
215  tno = tntNodeFindConn(tnA, TNT_MPS_D);
216 
217  /* identify the flipped node tnf */
218  tnAf = tntNodeFindConn(tno, TNT_MPS_D);
219 
220  }
221 
222  /* Free the network */
223  tntNetworkFree(&Hfull);
224 
225  /* Free the identity node */
226  tntNodeFree(&eye);
227 }
Simple 1D array for integer values.
Definition: tnt.h:148
tntNode tntNodeFindConn(tntNode tn, unsigned legA)
Definition: tntNode_funcs.c:438
tntComplex * vals
Pointer to the first element in the tntComplex array.
Definition: tnt.h:169
unsigned tntMpsLength(tntNetwork mps)
Definition: tntMpsUtil.c:208
struct tnode * tntNode
Definition: tnt.h:118
tntNode tntNodeFindLast(tntNetwork tnw)
Definition: tntNode_funcs.c:685
tntNode tntNodeCreate(tntComplexArray *nodeVals, unsigned numLegs, unsigned legId[], unsigned legDim[])
Definition: tntNode_funcs.c:523
void tntNetworkFree(tntNetwork *tnwp)
Definition: tntNetwork_funcs.c:84
int tntSymmTypeGet(void)
Definition: tntMisc_funcs.c:699
void tntNodeInsertAtEnd(tntNode tnI, int legIlast, int legInwend, tntNetwork tnw)
Definition: tntNode_funcs.c:945
tntNodeArray tntNodeArrayAlloc(unsigned num_elems)
Definition: tntArray_funcs.c:110
tntIntArray tntNodeGetQN(tntNode tn, unsigned legid)
Definition: tntNode_funcs.c:1442
tntNode tntNodeContract(tntNode tnA, tntNode tnB, int *legMapAC, int *legMapBC)
Definition: tntDummy_funcs.c:34
#define TNT_MPS_L
Definition: tntMps.h:56
void tntComplexArrayFree(tntComplexArray *arr)
Definition: tntArray_funcs.c:192
void tntIntArrayFree(tntIntArray *arr)
Definition: tntArray_funcs.c:162
void tntNodeInsertAtStart(tntNode tnI, int legInwstart, int legIfirst, tntNetwork tnw)
Definition: tntNode_funcs.c:905
void tntNodeSetQN(tntNode tn, unsigned legid, tntIntArray *qvals, int legdir)
Definition: tntNode_funcs.c:1324
struct tnetwork * tntNetwork
Definition: tnt.h:123
Simple 1D array for complex values.
Definition: tnt.h:168
tntNode * vals
Pointer to the first element in the tntNode array.
Definition: tnt.h:179
tntNetwork tntMpsMpoMpsConnect(tntNetwork mps, tntNetwork mpo)
Definition: tntMpsMpoMps.c:30
void tntNodeFree(tntNode *tn)
Definition: tntNode_funcs.c:420
double re
Definition: tnt.h:137
tntNode tntNodeCopy(tntNode tn, int conj)
Definition: tntNode_funcs.c:486
tntNode tntNodeFindFirst(tntNetwork tnw)
Definition: tntNode_funcs.c:664
tntComplexArray tntComplexArrayAlloc(unsigned numrows, unsigned numcols)
Definition: tntDummy_funcs.c:96
void tntNodeJoin(tntNode tnA, int legA, tntNode tnB, int legB)
Definition: tntNode_funcs.c:462
#define TNT_MPS_D
Definition: tntMps.h:66
#define TNT_MPS_R
Definition: tntMps.h:61
void tntMpsVarMinMpoUpdate ( tntNode  tnA,
tntNode  tno,
unsigned  dir,
unsigned  j,
tntNodeArray Oeff 
)

Prepares the nodes required for performing a variational minimisation sweep using tntMpsVarMinMpo2sSweep() by pre-contracting nodes in the MPS-MPO-MPS network. If the MPS represents a wave function \(|\psi\rangle\) and the MPO represents a site-wide operator \(\hat{O}\), the returned network represents \(\langle\psi|\hat{O}|\psi\rangle \).

mps_mpo_mps_fullnw.png

The function updates a single entry of one of the arrays of pre-contracted nodes OeffL (if dir is TNT_MPS_R) and OeffR (if dir is TNT_MPS_L) using the new updated MPS node for site \(j\), \(A_j\), and the corresponding MPO node \(h_j\).

The array OeffL->vals[j] is the node that represents all nodes to the left of site \(j\) contracted to form the node \(\beta_j\), so in this case \(A_j\), its Hermitian conjugate, \(h_j\) and \(\beta_j\) are contracted to update the entry OeffL->vals[j+1] as follows.

mps_dmrg_betaup.png

The array OeffR->vals[j] is the node that represents all nodes to the right of site \(j\) contracted to form the node \(\gamma_j\), so in this case \(A_j\), its Hermitian conjugate, \(h_j\) and \(\gamma_j\) are contracted to update the entry OeffR->vals[j-1] as follows.

mps_dmrg_gammaup.png
Returns
No return value.
Parameters
tnAThe new MPS tensor to update with. Unchanged by the function.
tnoThe node in the MPO corresponding to the MPS node tnA. Unchanged by the function.
dirThe direction that the sweep is taking place in i.e. if sweeping left, use TNT_MPS_L which will update OeffR and if sweeping right use TNT_MPS_R which will update OeffL.
jThe site number of tnA
OeffA pointer to the array that will contain the node to be updated. The precontracted node relating to site j will be updated, and the other nodes are not changed.
263 {
264 
265  tntNode tnAc, tnoc, tnAf; /* copies of tnA and tno for contracting, and the flipped (hermitian conjugate) of the current node Af */
266  tntNode beta; /* The node that is the results of all previously contracted nodes from the right or left */
267  unsigned jup; /* The array entry that needs to be updated using the new tnA (will depend on direction) */
268  unsigned opdir; /* Direction opposite to dir */
269  unsigned legh, legAf; /* Leg ids of beta to join i.e. are we joining the right or left legs? */
270 
271  /* Maps for the leg id's on contraction */
272  int idmap_A[4] = {0,1,2,7}; /* Map used when contracting A with Oeff */
273  int idmap_op[4] = {0,3,4,7}; /* Used for the operator when contracting with A or Oeff */
274  int idmap_Afl[3] = {0,5,6}; /* Used for the flipped tensor contracting with the result of the A-operator contraction or Oeff */
275 
276 
277  /* Make copies of the nodes for contracting */
278  tnAc = tntNodeCopy(tnA,0);
279  tnoc = tntNodeCopy(tno,0);
280  tnAf = tntNodeCopy(tnA,1); /* Taking complex conjugate for the flipped node */
281 
282  /* Set the correct site numbers and directions, depending on the direction of the update */
283  if (TNT_MPS_L == dir) {
284  /* if the site number of tnA is j, and we are sweeping left, then the array entry to change is j-1, and we will use the precontracted node in array entry j to do this */
285  jup = j-1;
286  opdir = TNT_MPS_R;
287 
288  /* set the leg id's of beta to join to tno and tnAf */
289  legh = 3;
290  legAf = 5;
291 
292  } else {
293  /* if the site number of tnA is j, and we are sweeping right, then the array entry to change is j+1, and we will use the precontracted node in array entry j to do this */
294  jup = j+1;
295  opdir = TNT_MPS_L;
296 
297  /* set the leg id's of beta to join to tno and tnAf */
298  legh = 4;
299  legAf = 6;
300 
301  }
302 
303  /* Set beta to be a copy the previous precontracted node */
304  beta = tntNodeCopy(Oeff->vals[j],0);
305 
306  /* Now connect up these nodes to the relevant beta node */
307  tntNodeJoin(beta,dir,tnAc,opdir); /* Join it to the MPO node */
308  tntNodeJoin(beta,legh,tnoc,opdir); /* Join it to the MPO node */
309  tntNodeJoin(beta,legAf,tnAf,opdir); /* Join it to the flipped MPS node */
310 
311  /* Connect up these nodes along the vertical direction */
312  tntNodeJoin(tnAc,TNT_MPS_D,tnoc,TNT_MPS_U);
313  tntNodeJoin(tnoc,TNT_MPS_D,tnAf,TNT_MPS_D);
314 
315  /* Now contract all these nodes:*/
316 
317  /* Contract beta with the MPS node */
318  beta = tntNodeContract(beta,tnAc,NULL,idmap_A);
319 
320  /* Contract beta with the MPO term */
321  beta = tntNodeContract(beta,tnoc,NULL,idmap_op);
322 
323  /* Contract beta with the flipped MPS node (final contraction step) */
324  beta = tntNodeContract(beta,tnAf,NULL,idmap_Afl);
325 
326  /* Free the node that is currently in the array */
327  tntNodeFree(Oeff->vals+jup);
328 
329  /* Put the resulting new node in the array */
330  Oeff->vals[jup] = beta;
331 
332 }
struct tnode * tntNode
Definition: tnt.h:118
tntNode tntNodeContract(tntNode tnA, tntNode tnB, int *legMapAC, int *legMapBC)
Definition: tntDummy_funcs.c:34
#define TNT_MPS_L
Definition: tntMps.h:56
#define TNT_MPS_U
Definition: tntMps.h:71
tntNode * vals
Pointer to the first element in the tntNode array.
Definition: tnt.h:179
void tntNodeFree(tntNode *tn)
Definition: tntNode_funcs.c:420
tntNode tntNodeCopy(tntNode tn, int conj)
Definition: tntNode_funcs.c:486
void tntNodeJoin(tntNode tnA, int legA, tntNode tnB, int legB)
Definition: tntNode_funcs.c:462
#define TNT_MPS_D
Definition: tntMps.h:66
#define TNT_MPS_R
Definition: tntMps.h:61