v2DM-DOCI  1.0
SimulatedAnnealing.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 <cassert>
25 #include <chrono>
26 #include <fstream>
27 #include <iomanip>
28 #include <iostream>
29 #include <numeric>
30 #include <sstream>
31 #include <stdexcept>
32 #include <unistd.h>
33 #include <hdf5.h>
34 #include <mpi.h>
35 
36 #include "SimulatedAnnealing.h"
37 #include "OptIndex.h"
38 #include "BoundaryPoint.h"
39 #include "PotentialReducation.h"
40 
47 {
48  ham.reset(new CheMPS2::Hamiltonian(mol));
49 
50  OptIndex index(*ham);
51 
52  opt_unitary.reset(new UnitaryMatrix(index));
53 
54  orbtrans.reset(new OrbitalTransform(*ham));
55 
56  method.reset(new doci2DM::BoundaryPoint(*ham));
57 
58  mt = std::mt19937_64(rd());
59 
60  steps = 0;
61  energy = 0;
62  max_steps = 20000;
63  unaccepted = 0;
64  stop_running = false;
65 }
66 
68 {
69  ham.reset(new CheMPS2::Hamiltonian(mol));
70 
71  OptIndex index(*ham);
72 
73  opt_unitary.reset(new UnitaryMatrix(index));
74 
75  orbtrans.reset(new OrbitalTransform(*ham));
76 
77  method.reset(new doci2DM::BoundaryPoint(*ham));
78 
79  mt = std::mt19937_64(rd());
80 
81  steps = 0;
82  energy = 0;
83  max_steps = 20000;
84  unaccepted = 0;
85  stop_running = false;
86 }
87 
89 
94 {
95  return energy + ham->getEconst();
96 }
97 
99 {
100  this->max_angle = max_angle;
101 }
102 
104 {
105  this->delta_angle = delta_angle;
106 }
107 
109 {
110  this->start_temp = start_temp;
111 }
112 
114 {
115  this->delta_temp = delta_temp;
116 }
117 
124 {
125  std::uniform_real_distribution<double> dist_accept(0, 1);
126 
127  if(e_new < energy)
128  return true;
129  else
130  {
131  double chance = std::exp((energy - e_new) / cur_temp);
132 
133  if ( dist_accept(mt) * (1+chance) > chance)
134  //biggest chance for failing
135  return false;
136  else
137  return true;
138  }
139 }
140 
146 {
147  method->BuildHam(*ham);
148  method->Run();
149 
150  energy = method->getEnergy();
151 }
152 
158 {
159  orbtrans->fillHamCI(*ham);
160 
161  method->BuildHam(*ham);
162  method->Run();
163 
164  return method->getEnergy();
165 }
166 
171 {
172  std::uniform_int_distribution<int> dist(0, ham->getL()-1);
173  std::uniform_real_distribution<double> dist_angles(0, 1);
174 
175  // unaccepted = 0;
176  double lowest_energy = energy;
177  cur_temp = start_temp;
178 
179  int size, rank;
180  MPI_Comm_size(MPI_COMM_WORLD, &size);
181  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
182 
183  std::stringstream out_name;
184  out_name << getenv("SAVE_H5_PATH") << "/output-" << rank << ".txt";
185 
186  std::ostream* fp = &std::cout;
187  std::ofstream fout;
188  if(size > 1)
189  {
190  fout.open(out_name.str(), std::ios::out | std::ios::app);
191  fp = &fout;
192  }
193  std::ostream &out = *fp;
194  out.precision(10);
195 
196  helpers::matrix sample_pairs(ham->getL(), ham->getL());
197  sample_pairs = 0;
198 
199  for(int i=0;i<ham->getL();i++)
200  for(int j=i+1;j<ham->getL();j++)
201  if(ham->getOrbitalIrrep(i) != ham->getOrbitalIrrep(j))
202  sample_pairs(i,j) = sample_pairs(j,i) = -1;
203 
204  doci2DM::BoundaryPoint *bp_meth = dynamic_cast<doci2DM::BoundaryPoint *>(method.get());
205 
206  for(unsigned int i=0;i<max_steps;i++)
207  {
208  auto orb1 = dist(mt);
209  auto orb2 = dist(mt);
210 
211  if(orb1 != orb2 && ham->getOrbitalIrrep(orb1) == ham->getOrbitalIrrep(orb2))
212  {
213  sample_pairs(orb1, orb2) += 1;
214  sample_pairs(orb2, orb1) += 1;
215 
216  // between -1 and 1 but higher probablity to be close to zero (seems to work better)
217  double cur_angle = max_angle * (dist_angles(mt) - dist_angles(mt));
218 
219  out << "P=" << rank << "\t" << i << "\tT=" << cur_temp << "\tOrb1=" << orb1 << "\tOrb2=" << orb2 << " Over " << cur_angle << std::endl;
220 
221  auto uni_copy = orbtrans->get_unitary();
222  orbtrans->get_unitary().jacobi_rotation(ham->getOrbitalIrrep(orb1), orb1, orb2, cur_angle);
223 
224  auto new_energy = calc_new_energy();
225 
226  if(new_energy < lowest_energy)
227  lowest_energy = new_energy;
228 
229  out << "P=" << rank << "\t" << i << "\tT=" << cur_temp << "\tNew energy = " << new_energy + ham->getEconst() << "\t Old energy = " << get_energy();
230 
231  if(accept_function(new_energy))
232  {
233  energy = new_energy;
234  out << "\t=> Accepted" << std::endl;
235  }
236  else
237  {
238  unaccepted++;
239  out << "\t=> Unaccepted, " << unaccepted << std::endl;
240 // orbtrans->get_unitary().jacobi_rotation(ham->getOrbitalIrrep(orb1), orb1, orb2, -1*cur_angle);
241  orbtrans->get_unitary() = std::move(uni_copy);
242  }
243 
244  cur_temp *= delta_temp;
245  max_angle *= delta_angle;
246 
247 // if(unaccepted > 1000)
248 // {
249 // out << "Too many unaccepted, stopping" << std::endl;
250 // stop_running = true;
251 // break;
252 // }
253 
254 
255  if(bp_meth)
256  {
257  // in a 1000 steps to final accuracy
258  // bp_meth->set_tol_PD(std::max(3e-6,1e-5-i*7e-9));
259  auto tol_pd = bp_meth->get_tol_PD() * 0.99;
260  bp_meth->set_tol_PD(std::max(3e-6,tol_pd));
261  }
262  }
263  else
264  i = (i>0) ? --i : i;
265  }
266 
267  if(bp_meth)
268  out << "Accuracy down to " << bp_meth->get_tol_PD() << std::endl;
269  out << "Bottom was " << lowest_energy + ham->getEconst() << std::endl;
270  out << "Final energy = " << get_energy() << std::endl;
271 
272  for(int i=0;i<ham->getL();i++)
273  for(int j=i+1;j<ham->getL();j++)
274  if(sample_pairs(i,j) >= 0)
275  out << ham->getOrbitalIrrep(i) << "\t" << i << "\t" << j << "\t" << sample_pairs(i,j) << std::endl;
276 
277  if(size > 1)
278  fout.close();
279 }
280 
282 {
283  return orbtrans->get_unitary();
284 }
285 
287 {
288  return *ham;
289 }
290 
292 {
293  return *orbtrans;
294 }
295 
297 {
298  return *method;
299 }
300 
308 {
309  doci2DM::PotentialReduction &meth = dynamic_cast<doci2DM::PotentialReduction &> (*method);
310 
311  return meth;
312 }
313 
321 {
322  doci2DM::BoundaryPoint &meth = dynamic_cast<doci2DM::BoundaryPoint &> (*method);
323 
324  return meth;
325 }
326 
328 {
329  method.reset(new doci2DM::BoundaryPoint(*ham));
330 }
331 
333 {
334  method.reset(new doci2DM::PotentialReduction(*ham));
335 }
336 
338 {
339  max_steps = 500;
340 
341  int size, rank;
342  MPI_Comm_size(MPI_COMM_WORLD, &size);
343  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
344 
345  std::stringstream out_name;
346  out_name << getenv("SAVE_H5_PATH") << "/output-" << rank << ".txt";
347  if(size > 1)
348  method->set_outfile(out_name.str());
349  std::ofstream myfile;
350  myfile.open(out_name.str(), std::ios::out | std::ios::trunc | std::ios::binary);
351  myfile.close();
352 
353  std::vector<double> energies(size);
354 
355  int count_iters = 0;
356 
357  doci2DM::BoundaryPoint *bp_meth = dynamic_cast<doci2DM::BoundaryPoint *>(method.get());
358  if(bp_meth)
359  {
360  bp_meth->set_max_iter(2);
361  bp_meth->set_tol_PD(5e-4);
362  }
363 
364  while(!stop_running)
365  {
366  auto start = std::chrono::high_resolution_clock::now();
367 
368  optimize();
369 
370  auto end = std::chrono::high_resolution_clock::now();
371 
372  MPI_Gather(&energy, 1, MPI_DOUBLE, energies.data(), 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
373 
374  int min_rank = 0;
375 
376  if(rank == 0)
377  {
378  double energy_avg = std::accumulate(energies.begin(), energies.end(), 0.0) * 1.0/energies.size();
379 
380  std::cout << "Avg energy = " << energy_avg + ham->getEconst() << std::endl;
381  std::cout << "Cur temp = " << cur_temp << std::endl;
382 
383  stop_running = true;
384 
385  for(int i=0;i<size;i++)
386  {
387  std::cout << i << "\t" << energies[i] + ham->getEconst() << "\t" << fabs(energies[i]-energy_avg) << std::endl;
388 
389  // if all values are super close together, stop calculating
390  if(fabs(energies[i]-energy_avg) > 1e-5)
391  stop_running = false;
392 
393  if(energies[i] < energies[min_rank])
394  min_rank = i;
395  }
396 
397  if(size == 1)
398  stop_running = false;
399 
400  if(stop_running)
401  std::cout << "Gonna stop!" << std::endl;
402 
403  energy = energies[min_rank];
404  std::cout << "Rank " << min_rank << " has the lowest energy => " << get_energy() << std::endl;
405  }
406 
407  MPI_Bcast(&energy, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
408  MPI_Bcast(&min_rank, 1, MPI_INT, 0, MPI_COMM_WORLD);
409 
410  std::stringstream h5_name;
411  h5_name << getenv("SAVE_H5_PATH") << "/unitary-" << rank << "-" << count_iters*max_steps << ".h5";
412  orbtrans->get_unitary().saveU(h5_name.str());
413 
414  orbtrans->get_unitary().sendreceive(min_rank);
415 
416  max_steps -= 10;
417  if(max_steps<100)
418  max_steps = 100;
419 
420  if(rank == 0)
421  std::cout << "New max steps is " << max_steps << std::endl;
422 
423  start_temp = cur_temp / delta_temp;
424 
425  // sleep rank seconds to avoid overlap in printing (nasty, I know)
426  sleep(rank+1);
427  std::cout << "P=" << rank << "\tMPI Step runtime: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
428 
429  count_iters++;
430 
431  unsigned int total_unaccepted = 0;
432  MPI_Reduce(&unaccepted, &total_unaccepted, 1, MPI_UNSIGNED, MPI_SUM, 0, MPI_COMM_WORLD);
433 
434  unsigned int min_unaccepted = 0;
435  MPI_Reduce(&unaccepted, &min_unaccepted, 1, MPI_UNSIGNED, MPI_MIN, 0, MPI_COMM_WORLD);
436 
437  if(rank == 0)
438  {
439  std::cout << "Total unaccepted = " << total_unaccepted << std::endl;
440  std::cout << "Minimal unaccepted = " << min_unaccepted << std::endl;
441  std::cout << "Run: " << count_iters << std::endl;
442 
443  if(min_unaccepted > 1000)
444  stop_running = true;
445  }
446 
447  MPI_Bcast(&stop_running, 1, MPI::BOOL, 0, MPI_COMM_WORLD);
448  }
449 
450  // wait on everybody before exiting.
451  MPI_Barrier(MPI_COMM_WORLD);
452 }
453 
454 /* vim: set ts=3 sw=3 expandtab :*/
doci2DM::Method & getMethod() const
unsigned int steps
the number of steps done
OrbitalTransform & getOrbitalTf() const
std::unique_ptr< simanneal::OrbitalTransform > orbtrans
the actual orbital transform
SimulatedAnnealing(const CheMPS2::Hamiltonian &)
unsigned int unaccepted
number of unaccepted steps
std::random_device rd
the real random input (hopefully)
std::unique_ptr< simanneal::UnitaryMatrix > opt_unitary
the current unitary
doci2DM::BoundaryPoint & getMethod_BP() const
bool stop_running
bool to indicate if we should quite the optimalisation
void set_max_iter(unsigned int)
unsigned int max_steps
max number of steps
simanneal::UnitaryMatrix & get_Optimal_Unitary()
double get_tol_PD() const
std::mt19937_64 mt
our pseudo-random generator
doci2DM::PotentialReduction & getMethod_PR() const
std::unique_ptr< doci2DM::Method > method
double energy
energy of current iteration
std::unique_ptr< CheMPS2::Hamiltonian > ham
Holds the current hamiltonian.
CheMPS2::Hamiltonian & getHam() const