v2DM-DOCI  1.0
BoundaryPoint.cpp
Go to the documentation of this file.
1 /*
2  * @BEGIN LICENSE
3  *
4  * Copyright (C) 2014-2015 Ward Poelmans
5  *
6  * This file is part of v2DM-DOCI.
7  *
8  * v2DM-DOCI is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * Foobar is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Foobar. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * @END LICENSE
22  */
23 
24 #include <fstream>
25 #include <cassert>
26 #include <iomanip>
27 #include <chrono>
28 #include <functional>
29 #include <signal.h>
30 #include "BoundaryPoint.h"
31 #include "Hamiltonian.h"
32 #include "OptIndex.h"
33 
34 #define BP_AVG_ITERS_START 500000
35 
36 // if set, the signal has been given to stop the calculation and write current step to file
37 extern sig_atomic_t stopping;
38 
41 
42 BoundaryPoint::BoundaryPoint(const CheMPS2::Hamiltonian &hamin)
43 {
44  N = hamin.getNe();
45  L = hamin.getL();
46  nuclrep = hamin.getEconst();
47 
48  ham.reset(new TPM(L,N));
49 
50  X.reset(new SUP(L,N));
51  Z.reset(new SUP(L,N));
52 
53  useprevresult = false;
54  (*X) = 0.0;
55  (*Z) = 0.0;
56 
57  lineq.reset(new Lineq(L,N));
58 
59  BuildHam(hamin);
60 
61  // some default values
62  sigma = 1.0;
63 
64  tol_PD = 1.0e-6;
65  tol_en = 1.0e-3;
66 
67  mazzy = 1.0;
68 
69  max_iter = 5;
70 
71  avg_iters = BP_AVG_ITERS_START; // first step we don't really limited anything
72  iters = 0;
73  runs = 0;
74 
75  returnhigh = false;
76 
77  D_conv = 1;
78  P_conv = 1;
79  convergence = 1;
80 }
81 
83 {
84  N = hamin.gN();
85  L = hamin.gL();
86  nuclrep = 0;
87 
88  ham.reset(new TPM(hamin));
89 
90  X.reset(new SUP(L,N));
91  Z.reset(new SUP(L,N));
92 
93  useprevresult = false;
94  (*X) = 0.0;
95  (*Z) = 0.0;
96 
97  lineq.reset(new Lineq(L,N));
98 
99  BuildHam(hamin);
100 
101  // some default values
102  sigma = 1.0;
103 
104  tol_PD = 3.0e-6;
105  tol_en = 1.0e-3;
106 
107  mazzy = 1.0;
108 
109  max_iter = 5;
110 
111  avg_iters = 1000000; // first step we don't really limited anything
112  iters = 0;
113  runs = 0;
114 
115  returnhigh = false;
116 
117  D_conv = 1;
118  P_conv = 1;
119  convergence = 1;
120 }
121 
123 {
124  N = orig.N;
125  L = orig.L;
126  nuclrep = orig.nuclrep;
127 
128  ham.reset(new TPM(*orig.ham));
129 
130  X.reset(new SUP(*orig.X));
131  Z.reset(new SUP(*orig.Z));
132 
133  lineq.reset(new Lineq(*orig.lineq));
134 
136 
137  sigma = orig.sigma;;
138 
139  tol_PD = orig.tol_PD;
140  tol_en = orig.tol_en;
141 
142  mazzy = orig.mazzy;
143 
144  max_iter = orig.max_iter;
145 
146  energy = orig.energy;
147 
148  avg_iters = orig.avg_iters;
149  iters = orig.iters;
150  runs = orig.runs;
151 
152  returnhigh = orig.returnhigh;
153 
154  D_conv = orig.D_conv;
155  P_conv = orig.P_conv;
156  convergence = orig.convergence;
157 }
158 
160 {
161  N = orig.N;
162  L = orig.L;
163  nuclrep = orig.nuclrep;
164 
165  (*ham) = *orig.ham;
166 
167  (*X) = *orig.X;
168  (*Z) = *orig.Z;
169 
170  (*lineq) = *orig.lineq;
171 
173 
174  sigma = orig.sigma;;
175 
176  tol_PD = orig.tol_PD;
177  tol_en = orig.tol_en;
178 
179  mazzy = orig.mazzy;
180 
181  max_iter = orig.max_iter;
182 
183  energy = orig.energy;
184 
185  avg_iters = orig.avg_iters;
186  iters = orig.iters;
187  runs = orig.runs;
188 
189  returnhigh = orig.returnhigh;
190 
191  D_conv = orig.D_conv;
192  P_conv = orig.P_conv;
193  convergence = orig.convergence;
194 
195  return *this;
196 }
197 
199 {
200  return new BoundaryPoint(*this);
201 }
202 
204 {
205  return new BoundaryPoint(std::move(*this));
206 }
207 
214 {
215  std::function<double(int,int)> getT = [&hamin] (int a, int b) -> double { return hamin.getTmat(a,b); };
216  std::function<double(int,int,int,int)> getV = [&hamin] (int a, int b, int c, int d) -> double { return hamin.getVmat(a,b,c,d); };
217 
218  ham->ham(getT, getV);
219 }
220 
225 void BoundaryPoint::BuildHam(const TPM &hamin)
226 {
227  (*ham) = hamin;
228 }
229 
234 unsigned int BoundaryPoint::Run()
235 {
236  TPM ham_copy(*ham);
237 
238  //only traceless hamiltonian needed in program.
239  ham_copy.Proj_E(*lineq);
240 
241  if(!useprevresult)
242  {
243  (*X) = 0;
244  (*Z) = 0;
245  }
246 
247  //Lagrange multiplier
248  SUP V(L,N);
249 
250  //just dubya
251  SUP W(L,N);
252 
253  SUP u_0(L,N);
254 
255  //little help
256  TPM hulp(L,N);
257 
258  u_0.init_S(*lineq);
259 
260  D_conv = 1;
261  P_conv = 1;
262  convergence = 1;
263 
264  unsigned int iter_dual(0),iter_primal(0);
265 
266  unsigned int tot_iter = 0;
267 
268  unsigned int go_up = 0;
269  double P_conv_prev = 10; // something big so compare will be false first time
270 
271  std::ostream* fp = &std::cout;
272  std::ofstream fout;
273  if(!outfile.empty())
274  {
275  fout.open(outfile, std::ios::out | std::ios::app);
276  fp = &fout;
277  }
278  std::ostream &out = *fp;
279  out.precision(10);
280  out.setf(std::ios::scientific | std::ios::fixed, std::ios_base::floatfield);
281 
282 
283  auto start = std::chrono::high_resolution_clock::now();
284 
285  while(P_conv > tol_PD || D_conv > tol_PD || fabs(convergence) > tol_en)
286  {
287  ++iter_primal;
288 
289  D_conv = 1.0;
290 
291  iter_dual = 0;
292 
293  while(D_conv > tol_PD && iter_dual <= max_iter)
294  {
295  ++tot_iter;
296 
297  ++iter_dual;
298 
299  //solve system
300  SUP B(*Z);
301 
302  B -= u_0;
303 
304  B.daxpy(1.0/sigma,*X);
305 
306  TPM b(L,N);
307 
308  b.collaps(B, *lineq);
309 
310  b.daxpy(-1.0/sigma,ham_copy);
311 
312  hulp.InverseS(b, *lineq);
313 
314  hulp.Proj_E(*lineq);
315 
316  //construct W
317  W.fill(hulp);
318 
319  W += u_0;
320 
321  W.daxpy(-mazzy/sigma,*X);
322 
323  //update Z and V with eigenvalue decomposition:
324  W.sep_pm(*Z,V);
325 
326  V.dscal(-sigma);
327 
328  //check infeasibility of the primal problem:
329  TPM v(L,N);
330 
331  v.collaps(V, *lineq);
332 
333  v -= ham_copy;
334 
335  D_conv = sqrt(v.ddot(v));
336  }
337 
338  //update primal:
339  *X = V;
340 
341  //check dual feasibility (W is a helping variable now)
342  W.fill(hulp);
343 
344  W += u_0;
345 
346  W -= *Z;
347 
348  P_conv = sqrt(W.ddot(W));
349 
350  convergence = Z->getI().ddot(ham_copy) + X->ddot(u_0);
351 
352  energy = ham->ddot(Z->getI());
353 
354  if(do_output && iter_primal%500 == 0)
355  {
356  if(P_conv_prev < P_conv)
357  go_up++;
358 
359  P_conv_prev = P_conv;
360 
361  out << std::setw(16) << P_conv << "\t" << std::setw(16) << D_conv << "\t" << std::setw(16) << sigma << "\t" << std::setw(16) << convergence << "\t" << std::setw(16) << energy + nuclrep << "\t" << std::setw(16) << Z->getI().S_2() << "\t" << go_up << std::endl;
362 
363  if(iter_primal>avg_iters*10 || stopping || go_up > 20)
364  {
365  std::cout << "Bailing out: too many iterations! " << std::endl;
366  if(returnhigh)
367  energy = 1e90; // something big so we're sure the step will be rejected
368  break;
369  }
370  }
371 
372  if(D_conv < P_conv)
373  sigma *= 1.01;
374  else
375  sigma /= 1.01;
376  }
377 
378  auto end = std::chrono::high_resolution_clock::now();
379 
380 
381  if((iter_primal<=avg_iters*10 || go_up<=20) && !stopping)
382  {
383  runs++;
384  iters += iter_primal;
385  avg_iters = iters/runs;
386  if(avg_iters<10000)
387  avg_iters = 10000; // never go below 1e4 iterations
388  }
389 
390  out << std::endl;
391  out << "Energy: " << ham->ddot(Z->getI()) + nuclrep << std::endl;
392  out << "Trace: " << Z->getI().trace() << std::endl;
393  out << "pd gap: " << Z->ddot(*X) << std::endl;
394  out << "S^2: " << Z->getI().S_2() << std::endl;
395  out << "dual conv: " << D_conv << std::endl;
396  out << "primal conv: " << P_conv << std::endl;
397  out << "Runtime: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
398  out << "Primal iters: " << iter_primal << std::endl;
399  out << "avg primal iters: " << avg_iters << std::endl;
400 
401  out << std::endl;
402  out << "total nr of iterations = " << tot_iter << std::endl;
403 
404  if(!outfile.empty())
405  fout.close();
406 
407  return iter_primal;
408 }
409 
414 {
415  return energy + nuclrep;
416 }
417 
419 {
420  this->tol_PD = tol;
421 }
422 
424 {
425  this->tol_en = tol;
426 }
427 
428 void BoundaryPoint::set_mazzy(double maz)
429 {
430  this->mazzy = maz;
431 }
432 
433 void BoundaryPoint::set_sigma(double sig)
434 {
435  this->sigma = sig;
436 }
437 
438 void BoundaryPoint::set_max_iter(unsigned int iters)
439 {
440  this->max_iter = iters;
441 }
442 
444 {
445  return (*X);
446 }
447 
449 {
450  return (*Z);
451 }
452 
454 {
455  return (*lineq);
456 }
457 
458 
460 {
461  return Z->getI();
462 }
463 
470 {
471  useprevresult = new_val;
472 }
473 
475 {
476  return tol_PD;
477 }
478 
480 {
481  return *ham;
482 }
483 
489 {
490  return ham->ddot(Z->getI()) + nuclrep;
491 }
492 
500 {
501  returnhigh = set;
502 }
503 
509 {
511 }
512 
514 {
515  return P_conv;
516 }
517 
519 {
520  return D_conv;
521 }
522 
524 {
525  return convergence;
526 }
527 
533 {
534  return !(P_conv > tol_PD || D_conv > tol_PD || fabs(convergence) > tol_en);
535 }
536 
541 std::vector<double> BoundaryPoint::energyperirrep(const CheMPS2::Hamiltonian &hamin, bool print)
542 {
543  CheMPS2::Irreps symgroup(hamin.getNGroup());
544  simanneal::OptIndex index(hamin);
545 
546  std::vector<double> results(symgroup.getNumberOfIrreps(), 0);
547 
548  BuildHam(hamin);
549 
550  const auto& rdm = getRDM();
551  const auto orbtoirrep = index.get_irrep_each_orbital();
552 
553  // Ag: the LxL block
554  results[0] = ham->getMatrix(0).ddot(rdm.getMatrix(0));
555 
556  // run over orbitals
557  for(int a=0;a<L;a++)
558  for(int b=a+1;b<L;b++)
559  {
560  auto irrep_a = orbtoirrep[a];
561  auto irrep_b = orbtoirrep[b];
562 
563  auto firrep = symgroup.directProd(irrep_a, irrep_b);
564 
565  results[firrep] += 4 * (*ham)(a,b,a,b) * rdm(a,b,a,b);
566  }
567 
568 
569  if(print)
570  {
571  std::cout << "Group: " << symgroup.getGroupName() << std::endl;
572  for(int i=0;i<symgroup.getNumberOfIrreps();i++)
573  std::cout << symgroup.getIrrepName(i) << ":\t" << results[i] << std::endl;
574 
575  double check = 0;
576  for(auto &elem: results)
577  check += elem;
578 
579  std::cout << "Check: " << rdm.ddot(*ham)+hamin.getEconst() << "\t" << check+hamin.getEconst() << std::endl;
580  }
581 
582  return results;
583 }
584 
585 /* vim: set ts=3 sw=3 expandtab :*/
int gN() const
Definition: TPM.cpp:110
std::string outfile
Definition: Method.h:77
std::unique_ptr< SUP > Z
void daxpy(double, const SUP &)
Definition: SUP.cpp:323
double ddot(const SUP &) const
Definition: SUP.cpp:306
BoundaryPoint & operator=(const BoundaryPoint &)
double get_P_conv() const
std::unique_ptr< SUP > X
#define BP_AVG_ITERS_START
Lineq & getLineq() const
std::vector< double > energyperirrep(const CheMPS2::Hamiltonian &, bool print=false)
sig_atomic_t stopping
Definition: doci.cpp:38
double getVmat(const int index1, const int index2, const int index3, const int index4) const
Get a Vmat element.
double ddot(const Container &) const
Definition: Container.cpp:255
double getFullEnergy() const
int InverseS(TPM &, const Lineq &)
Definition: TPM.cpp:981
bool do_output
Definition: Method.h:75
double getTmat(const int index1, const int index2) const
Get a Tmat element.
double get_convergence() const
bool FullyConverged() const
void init_S(const Lineq &)
Definition: SUP.cpp:352
bool returnhigh
when true, return very high value for the energy if the calculation takes too many iterations ...
void dscal(double)
Definition: SUP.cpp:161
std::unique_ptr< Lineq > lineq
void sep_pm(SUP &, SUP &)
Definition: SUP.cpp:336
BoundaryPoint(const CheMPS2::Hamiltonian &)
BoundaryPoint * Move()
Container & daxpy(double alpha, const Container &)
Definition: Container.cpp:116
void Proj_E(const Lineq &, int option=0)
Definition: TPM.cpp:852
void fill(const TPM &)
Definition: SUP.cpp:231
double evalEnergy() const
void ReturnHighWhenBailingOut(bool)
double get_D_conv() const
void set_max_iter(unsigned int)
std::unique_ptr< TPM > ham
BoundaryPoint * Clone() const
double energy
Definition: Method.h:73
int gL() const
Definition: TPM.cpp:115
int getNGroup() const
Get the group number.
double getEconst() const
Get the constant energy.
double get_tol_PD() const
void BuildHam(const CheMPS2::Hamiltonian &)
void collaps(const SUP &, const Lineq &)
Definition: TPM.cpp:568
int getL() const
Get the number of orbitals.
double D_conv
the 3 convergence criteria