DOCI-Exact  1.0
SimulatedAnnealing.cpp
Go to the documentation of this file.
1 #include <iostream>
2 #include <fstream>
3 #include <sstream>
4 #include <chrono>
5 #include <cassert>
6 #include <hdf5.h>
7 
8 #include "SimulatedAnnealing.h"
9 
10 #include "Hamiltonian.h"
11 #include "UnitaryMatrix.h"
12 #include "OrbitalTransform.h"
13 
14 using namespace simanneal;
15 
22 {
23  ham.reset(new DOCIHamiltonian(mol));
24 
25  OptIndex index(mol.getHamObject());
26 
27  opt_unitary.reset(new UnitaryMatrix(index));
28 
29  orbtrans.reset(new OrbitalTransform(mol.getHamObject()));
30 
31  mt = std::mt19937_64(rd());
32 
33  steps = 0;
34  energy = 0;
35  max_steps = 20000;
36 }
37 
39 {
40  ham.reset(new DOCIHamiltonian(mol));
41 
42  OptIndex index(mol.getHamObject());
43 
44  opt_unitary.reset(new UnitaryMatrix(index));
45 
46  orbtrans.reset(new OrbitalTransform(mol.getHamObject()));
47 
48  mt = std::mt19937_64(rd());
49 
50  steps = 0;
51  energy = 0;
52  max_steps = 20000;
53 }
54 
56 
61 {
62  return energy + ham->getMolecule().get_nucl_rep();
63 }
64 
65 void doci::SimulatedAnnealing::Set_max_angle(double max_angle)
66 {
67  this->max_angle = max_angle;
68 }
69 
70 void doci::SimulatedAnnealing::Set_delta_angle(double delta_angle)
71 {
72  this->delta_angle = delta_angle;
73 }
74 
75 void doci::SimulatedAnnealing::Set_start_temp(double start_temp)
76 {
77  this->start_temp = start_temp;
78 }
79 
80 void doci::SimulatedAnnealing::Set_delta_temp(double delta_temp)
81 {
82  this->delta_temp = delta_temp;
83 }
84 
91 {
92  std::uniform_real_distribution<double> dist_accept(0, 1);
93 
94  if(e_new < energy)
95  return true;
96  else
97  {
98  double chance = std::exp((energy - e_new) / cur_temp);
99 
100  if ( dist_accept(mt) * (1+chance) > chance)
101  //biggest chance for failing
102  return false;
103  else
104  return true;
105  }
106 }
107 
113 {
114  ham->Build();
115 
116  energy = ham->CalcEnergy();
117 }
118 
124 {
125  // cast from Molecule to Sym_Molecule
126  auto *mol = static_cast<Sym_Molecule *> (&ham->getMolecule());
127  assert(mol && "Shit, NULL pointer");
128 
129  orbtrans->fillHamCI(mol->getHamObject());
130 
131  ham->Build();
132 
133  return ham->CalcEnergy();
134 }
135 
140 {
141  std::uniform_int_distribution<int> dist(0, ham->getMolecule().get_n_sp()-1);
142  std::uniform_real_distribution<double> dist_angles(0, 1);
143 
144  auto *mol = static_cast<Sym_Molecule *> (&ham->getMolecule());
145  auto &ham_data = mol->getHamObject();
146 
147  unsigned int unaccepted = 0;
148 
149  energy = calc_new_energy();
150  std::cout << "Starting energy = " << get_energy() << std::endl;
151 
152  double lowest_energy = energy;
153  cur_temp = start_temp;
154 
155  helpers::matrix sample_pairs(ham_data.getL(), ham_data.getL());
156  sample_pairs = 0;
157 
158  for(unsigned int i=0;i<ham_data.getL();i++)
159  for(unsigned int j=i+1;j<ham_data.getL();j++)
160  if(ham_data.getOrbitalIrrep(i) != ham_data.getOrbitalIrrep(j))
161  sample_pairs(i,j) = sample_pairs(j,i) = -1;
162 
163  auto start = std::chrono::high_resolution_clock::now();
164 
165  unsigned int i;
166  for(i=0;i<max_steps;i++)
167  {
168  auto orb1 = dist(mt);
169  auto orb2 = dist(mt);
170 
171  if(orb1 != orb2 && ham_data.getOrbitalIrrep(orb1) == ham_data.getOrbitalIrrep(orb2))
172  {
173  sample_pairs(orb1, orb2) += 1;
174  sample_pairs(orb2, orb1) += 1;
175 
176  // between -1 and 1 but higher probablity to be close to zero (seems to work better)
177  auto cur_angle = max_angle * (dist_angles(mt) - dist_angles(mt));
178 
179  std::cout << i << "\tT=" << cur_temp << "\tOrb1=" << orb1 << "\tOrb2=" << orb2 << " Over " << cur_angle << std::endl;
180 
181  orbtrans->get_unitary().jacobi_rotation(ham_data.getOrbitalIrrep(orb1), orb1, orb2, cur_angle);
182 
183  auto new_energy = calc_new_energy();
184 
185  if(new_energy < lowest_energy)
186  lowest_energy = new_energy;
187 
188  std::cout << "T=" << cur_temp << "\tNew energy = " << new_energy + mol->get_nucl_rep() << "\t Old energy = " << get_energy();
189 
190  if(accept_function(new_energy))
191  {
192  energy = new_energy;
193  std::cout << "\t=> Accepted" << std::endl;
194  }
195  else
196  {
197  unaccepted++;
198  std::cout << "\t=> Unaccepted, " << unaccepted << std::endl;
199  orbtrans->get_unitary().jacobi_rotation(ham_data.getOrbitalIrrep(orb1), orb1, orb2, -1*cur_angle);
200  }
201 
202  cur_temp *= delta_temp;
203  max_angle *= delta_angle;
204 
205  if(unaccepted > 1500)
206  {
207  std::cout << "Too many unaccepted, stopping" << std::endl;
208  break;
209  }
210 
211  }
212 
213  }
214 
215  auto end = std::chrono::high_resolution_clock::now();
216 
217  std::cout << "Bottom was " << lowest_energy + mol->get_nucl_rep() << std::endl;
218  std::cout << "Final energy = " << get_energy() << std::endl;
219  std::cout << "Sim anneal runtime: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
220 
221  std::stringstream h5_name;
222  if(getenv("SAVE_H5_PATH"))
223  h5_name << getenv("SAVE_H5_PATH") << "/unitary-final-" << i << ".h5";
224  else
225  h5_name << "unitary-final-" << i << ".h5";
226  orbtrans->get_unitary().saveU(h5_name.str());
227 
228  for(unsigned int i=0;i<ham_data.getL();i++)
229  for(unsigned int j=i+1;j<ham_data.getL();j++)
230  if(sample_pairs(i,j) >= 0)
231  std::cout << ham_data.getOrbitalIrrep(i) << "\t" << i << "\t" << j << "\t" << sample_pairs(i,j) << std::endl;
232 }
233 
235 {
236  return *ham;
237 }
238 
240 {
241  return *orbtrans;
242 }
243 
244 /* vim: set ts=3 sw=3 expandtab :*/
doci::DOCIHamiltonian & getHam() const
SimulatedAnnealing(doci::Sym_Molecule &)
simanneal::OrbitalTransform & getOrbitaltf() const
double get_nucl_rep() const
Definition: SymMolecule.cpp:48
CheMPS2::Hamiltonian & getHamObject() const
Definition: SymMolecule.cpp:67