DOCI-Exact  1.0
LocalMinimizer.cpp
Go to the documentation of this file.
1 #include <cassert>
2 #include <chrono>
3 #include <iostream>
4 #include <numeric>
5 #include <stdexcept>
6 #include <algorithm>
7 #include <hdf5.h>
8 #include <signal.h>
9 #include <cstring>
10 #include <sstream>
11 
12 #include "LocalMinimizer.h"
13 #include "Hamiltonian.h"
14 #include "OptIndex.h"
15 
20 {
21  orbtrans.reset(new simanneal::OrbitalTransform(mol.getHamObject()));
22 
23  method.reset(new doci::DOCIHamiltonian(mol));
24 
25  rdm.reset(new doci::DM2(mol));
26 
27  energy = 0;
28 
29  conv_crit = 1e-6;
30  conv_steps = 25;
31 
32  std::random_device rd;
33  mt = std::mt19937(rd());
34 
35  // expect to be comma seperated list of allowed irreps
36  char *irreps_env = getenv("v2DM_DOCI_ALLOWED_IRREPS");
37  if(irreps_env && strlen(irreps_env) > 0)
38  {
39  std::string irreps_string = irreps_env;
40  const std::string delim = ",";
41  CheMPS2::Irreps syminfo(mol.getHamObject().getNGroup());
42 
43  auto start = 0U;
44  auto end = irreps_string.find(delim);
45  try
46  {
47  while (true)
48  {
49  auto elem = irreps_string.substr(start, end - start);
50  if(elem.empty())
51  break;
52 
53  int cur_irrep = std::stoi(elem);
54 
55  if(cur_irrep >= 0 && cur_irrep < syminfo.getNumberOfIrreps())
56  allow_irreps.push_back(cur_irrep);
57 
58  start = end + delim.length();
59 
60  if(end >= std::string::npos)
61  break;
62 
63  end = irreps_string.find(delim, start);
64  }
65  } catch (std::exception& e) {
66  std::cout << "Invalid value in v2DM_DOCI_ALLOWED_IRREPS" << std::endl;
67  }
68 
69  std::sort(allow_irreps.begin(), allow_irreps.end());
70  std::cout << "Allowed irreps: ";
71  for(auto &elem: allow_irreps)
72  std::cout << elem << " ";
73  std::cout << std::endl;
74  }
75 }
76 
78 {
79  method.reset(new doci::DOCIHamiltonian(mol));
80 
81  orbtrans.reset(new simanneal::OrbitalTransform(mol.getHamObject()));
82 
83  rdm.reset(new doci::DM2(mol));
84 
85  energy = 0;
86 
87  conv_crit = 1e-6;
88  conv_steps = 50;
89 
90  std::random_device rd;
91  mt = std::mt19937(rd());
92 }
93 
95 
100 {
101  return energy + method->getMolecule().get_nucl_rep();
102 }
103 
109 {
110  auto start = std::chrono::high_resolution_clock::now();
111  method->Build();
112  auto end = std::chrono::high_resolution_clock::now();
113 
114  std::cout << "Building took: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
115 
116  start = std::chrono::high_resolution_clock::now();
117  auto eig = method->Diagonalize();
118  energy = eig.first;
119  end = std::chrono::high_resolution_clock::now();
120  std::cout << "E = " << eig.first + method->getMolecule().get_nucl_rep() << std::endl;
121 
122  std::cout << "Diagonalization took: " << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
123 
124  auto perm = method->getPermutation();
125  start = std::chrono::high_resolution_clock::now();
126  rdm->Build(perm, eig.second);
127  end = std::chrono::high_resolution_clock::now();
128 
129  std::cout << "Building 2DM took: " << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
130 }
131 
137 {
138  auto *mol = static_cast<Sym_Molecule *> (&method->getMolecule());
139  assert(mol && "Shit, NULL pointer");
140 
141  orbtrans->fillHamCI(mol->getHamObject());
142 
143  auto start = std::chrono::high_resolution_clock::now();
144  method->Build();
145  auto end = std::chrono::high_resolution_clock::now();
146 
147  std::cout << "Building took: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
148 
149  start = std::chrono::high_resolution_clock::now();
150  auto eig = method->Diagonalize();
151  end = std::chrono::high_resolution_clock::now();
152  std::cout << "E = " << eig.first + mol->get_nucl_rep() << std::endl;
153 
154  std::cout << "Diagonalization took: " << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
155 
156  auto perm = method->getPermutation();
157  start = std::chrono::high_resolution_clock::now();
158  rdm->Build(perm, eig.second);
159  end = std::chrono::high_resolution_clock::now();
160 
161  std::cout << "Building 2DM took: " << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
162 
163  return eig.first;
164 }
165 
167 {
168  auto *mol = static_cast<Sym_Molecule *> (&method->getMolecule());
169  assert(mol && "Shit, NULL pointer");
170 
171  mol->getHamObject() = new_ham.getHamObject();
172 
173  auto start = std::chrono::high_resolution_clock::now();
174  method->Build();
175  auto end = std::chrono::high_resolution_clock::now();
176 
177  std::cout << "Building took: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
178 
179  start = std::chrono::high_resolution_clock::now();
180  auto eig = method->Diagonalize();
181  end = std::chrono::high_resolution_clock::now();
182  std::cout << "E = " << eig.first + mol->get_nucl_rep() << std::endl;
183 
184  std::cout << "Diagonalization took: " << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
185 
186  auto perm = method->getPermutation();
187  start = std::chrono::high_resolution_clock::now();
188  rdm->Build(perm, eig.second);
189  end = std::chrono::high_resolution_clock::now();
190 
191  std::cout << "Building 2DM took: " << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
192 
193  return eig.first;
194 }
195 
197 {
198  return orbtrans->get_unitary();
199 }
200 
202 {
203  auto *mol = static_cast<Sym_Molecule *> (&method->getMolecule());
204  assert(mol && "Shit, NULL pointer");
205 
206  return *mol;
207 }
208 
210 {
211  return *orbtrans;
212 }
213 
214 std::vector< std::tuple<int,int,double,double> > doci::LocalMinimizer::scan_orbitals()
215 {
216  auto start = std::chrono::high_resolution_clock::now();
217 
218  auto *mol = static_cast<Sym_Molecule *> (&method->getMolecule());
219  assert(mol && "Shit, NULL pointer");
220 
221  const auto& ham2 = mol->getHamObject();
222  std::function<double(int,int)> getT = [&ham2] (int a, int b) -> double { return ham2.getTmat(a,b); };
223  std::function<double(int,int,int,int)> getV = [&ham2] (int a, int b, int c, int d) -> double { return ham2.getVmat(a,b,c,d); };
224 
225  std::vector< std::tuple<int,int,double,double> > pos_rotations;
226  // worst case: c1 symmetry
227  pos_rotations.reserve(ham2.getL()*(ham2.getL()-1)/2);
228 
229  for(int k_in=0;k_in<ham2.getL();k_in++)
230  for(int l_in=k_in+1;l_in<ham2.getL();l_in++)
231  if(ham2.getOrbitalIrrep(k_in) == ham2.getOrbitalIrrep(l_in))
232  {
233  if(!allow_irreps.empty() && std::find(allow_irreps.begin(), allow_irreps.end(), ham2.getOrbitalIrrep(k_in)) == allow_irreps.end() )
234  continue;
235 
236  auto found = rdm->find_min_angle(k_in,l_in,0.3,getT,getV);
237 
238  if(!found.second)
239  // we hit a maximum
240  found = rdm->find_min_angle(k_in,l_in,0.01,getT,getV);
241 
242  if(!found.second)
243  // we're still stuck in a maximum, skip this!
244  continue;
245 
246  // skip angles larger than Pi/2
247  if(fabs(found.first)>M_PI/2.0)
248  continue;
249 
250  double new_en = rdm->calc_rotate(k_in,l_in,found.first,getT,getV);
251 
252  assert(found.second && "Shit, maximum!");
253 
254  pos_rotations.push_back(std::make_tuple(k_in,l_in,found.first,new_en));
255  }
256 
257  auto end = std::chrono::high_resolution_clock::now();
258 
259  std::cout << "Orbital scanning took: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
260 
261  assert(pos_rotations.size()>0);
262 
263  return pos_rotations;
264 }
265 
271 void doci::LocalMinimizer::Minimize(bool dist_choice)
272 {
273  int converged = 0;
274  double new_energy;
275 
276  // first run
277  energy = calc_new_energy();
278 
279  auto start = std::chrono::high_resolution_clock::now();
280 
281  std::pair<int,int> prev_pair(0,0);
282 
283  int iters = 1;
284 
285  auto *mol = static_cast<Sym_Molecule *> (&method->getMolecule());
286  assert(mol && "Shit, NULL pointer");
287 
288  auto& ham2 = mol->getHamObject();
289 
290  while(converged<conv_steps)
291  {
292  auto list_rots = scan_orbitals();
293 
294  std::sort(list_rots.begin(), list_rots.end(),
295  [](const std::tuple<int,int,double,double> & a, const std::tuple<int,int,double,double> & b) -> bool
296  {
297  return std::get<3>(a) < std::get<3>(b);
298  });
299 
300  for(auto& elem: list_rots)
301  std::cout << std::get<0>(elem) << "\t" << std::get<1>(elem) << "\t" << std::get<3>(elem)+ham2.getEconst() << "\t" << std::get<2>(elem) << std::endl;
302 
303  int idx = 0;
304  std::pair<int,int> tmp;
305 
306  if(dist_choice)
307  {
308  idx = choose_orbitalpair(list_rots);
309 
310  tmp = std::make_pair(std::get<0>(list_rots[idx]), std::get<1>(list_rots[idx]));
311 
312  if(tmp==prev_pair)
313  idx = choose_orbitalpair(list_rots);
314 
315  tmp = std::make_pair(std::get<0>(list_rots[idx]), std::get<1>(list_rots[idx]));
316 
317  if(tmp==prev_pair)
318  idx = 0;
319  }
320 
321  tmp = std::make_pair(std::get<0>(list_rots[idx]), std::get<1>(list_rots[idx]));
322 
323  // don't do the same pair twice in a row
324  if(tmp==prev_pair)
325  idx++;
326 
327  const auto& new_rot = list_rots[idx];
328  prev_pair = std::make_pair(std::get<0>(new_rot), std::get<1>(new_rot));
329 
330  if(dist_choice)
331  std::cout << iters << " (" << converged << ") Chosen: " << idx << std::endl;
332 
333  assert(ham2.getOrbitalIrrep(std::get<0>(new_rot)) == ham2.getOrbitalIrrep(std::get<1>(new_rot)));
334  // do Jacobi rotation twice: once for the Hamiltonian data and once for the Unitary Matrix
335  orbtrans->DoJacobiRotation(ham2, std::get<0>(new_rot), std::get<1>(new_rot), std::get<2>(new_rot));
336  orbtrans->get_unitary().jacobi_rotation(ham2.getOrbitalIrrep(std::get<0>(new_rot)), std::get<0>(new_rot), std::get<1>(new_rot), std::get<2>(new_rot));
337 
338  new_energy = calc_new_energy();
339 
340  std::stringstream h5_name;
341 
342  if(iters%10==0)
343  {
344  h5_name << getenv("SAVE_H5_PATH") << "/unitary-" << iters << ".h5";
345  orbtrans->get_unitary().saveU(h5_name.str());
346  }
347 
348  if(iters%25==0)
349  {
350  h5_name.str("");
351  h5_name << getenv("SAVE_H5_PATH") << "/ham-" << iters << ".h5";
352  ham2.save2(h5_name.str());
353 
354  h5_name.str("");
355  h5_name << getenv("SAVE_H5_PATH") << "/rdm-" << iters << ".h5";
356  rdm->WriteToFile(h5_name.str());
357  }
358 
359  if(fabs(energy-new_energy)<conv_crit)
360  converged++;
361 
362  std::cout << iters << " (" << converged << ")\tRotation between " << std::get<0>(new_rot) << " " << std::get<1>(new_rot) << " over " << std::get<2>(new_rot) << " E_rot = " << std::get<3>(new_rot)+ham2.getEconst() << " E = " << new_energy+ham2.getEconst() << "\t" << fabs(energy-new_energy) << std::endl;
363 
364  energy = new_energy;
365 
366  iters++;
367 
368  if(iters>1000)
369  {
370  std::cout << "Done 1000 steps, quiting..." << std::endl;
371  break;
372  }
373  }
374 
375  auto end = std::chrono::high_resolution_clock::now();
376 
377  std::cout << "Minimization took: " << std::fixed << std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1>>>(end-start).count() << " s" << std::endl;
378 
379  std::stringstream h5_name;
380  h5_name << getenv("SAVE_H5_PATH") << "/optimale-uni.h5";
381  get_Optimal_Unitary().saveU(h5_name.str());
382 }
383 
385 {
386  return conv_crit;
387 }
388 
390 {
391  conv_crit = crit;
392 }
393 
395 {
396  conv_steps = steps;
397 }
398 
405 int doci::LocalMinimizer::choose_orbitalpair(std::vector<std::tuple<int,int,double,double>> &orbs)
406 {
407  std::uniform_real_distribution<double> dist(0, 1);
408 
409  const double choice = dist(mt);
410 
411  double norm = 0;
412 
413  for(auto &orb_pair: orbs)
414  norm += (energy - std::get<3>(orb_pair));
415 
416  double cum = 0;
417  for(int i=0;i<orbs.size();i++)
418  {
419  cum += (energy - std::get<3>(orbs[i]))/norm;
420  if(choice < cum)
421  return i;
422  }
423 
424  assert(0 && "Should never ever be reached!");
425  return -1;
426 }
427 
429 {
430  return *rdm;
431 }
432 
433 /* vim: set ts=3 sw=3 expandtab :*/
simanneal::OrbitalTransform & getOrbitalTf() const
void Minimize(bool dist_choice=false)
simanneal::UnitaryMatrix & get_Optimal_Unitary()
int choose_orbitalpair(std::vector< std::tuple< int, int, double, double >> &)
Definition: DM2.h:24
void set_conv_crit(double)
const doci::DM2 & get_DM2() const
virtual ~LocalMinimizer()
doci::Sym_Molecule & getHam() const
LocalMinimizer(const doci::Sym_Molecule &)
double get_energy() const
int getNGroup() const
Get the group number.
std::vector< std::tuple< int, int, double, double > > scan_orbitals()
CheMPS2::Hamiltonian & getHamObject() const
Definition: SymMolecule.cpp:67
double get_conv_crit() const