1 /*
2  *  Copyright (C) 2004-2021 Edward F. Valeev
3  *
4  *  This file is part of Libint.
5  *
6  *  Libint is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  Libint is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with Libint.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include <cmath>
22 #include <cstdlib>
23 #include <ctime>
24 #include <smart_ptr.h>
25 #include <time.h>
26 
27 #ifndef _libint2_src_bin_libint_tactic_h_
28 #define _libint2_src_bin_libint_tactic_h_
29 
30 namespace libint2 {
31 
32   class DirectedGraph;
33   class RecurrenceRelation;
34 
35   class DummyRandomizePolicy;
36   class StdRandomizePolicy;
37 
38   /** Tactic is used to choose the optimal (in some sense) recurrence relation to reduce
39       a vertex.
40   */
41   class Tactic {
42     public:
43     typedef SafePtr<RecurrenceRelation> RR;
44     typedef std::vector<RR> rr_stack;
45 
Tactic()46     Tactic() {}
~Tactic()47     virtual ~Tactic() {}
48 
49     virtual RR optimal_rr(const rr_stack& stack) const =0;
50 
51     // make return true if need to debug Tactic classes
class_debug()52     static constexpr bool class_debug() {
53       return false;
54     }
55   };
56 
57   /** FirstChoiceTactic simply chooses the first RR
58     */
59   template <class RandomizePolicy = DummyRandomizePolicy>
60     class FirstChoiceTactic : public Tactic {
61     public:
Tactic()62     FirstChoiceTactic(const SafePtr<RandomizePolicy>& rpolicy = SafePtr<RandomizePolicy>(new RandomizePolicy)) : Tactic(), rpolicy_(rpolicy) {}
~FirstChoiceTactic()63     virtual ~FirstChoiceTactic() {}
64 
optimal_rr(const rr_stack & stack)65     RR optimal_rr(const rr_stack& stack) const {
66       if (!stack.empty())
67         return stack[0 + rpolicy_->noise(stack.size())];
68       else
69         return RR();
70     }
71 
72     private:
73     SafePtr<RandomizePolicy> rpolicy_;
74   };
75 
76   /** FewestNewVerticesTactic chooses RR which adds fewest new vertices to
77       DirectedGraph dg
78     */
79   class FewestNewVerticesTactic : public Tactic {
80     public:
FewestNewVerticesTactic(const SafePtr<DirectedGraph> & dg)81     FewestNewVerticesTactic(const SafePtr<DirectedGraph>& dg) : Tactic(), dg_(dg) {}
~FewestNewVerticesTactic()82     virtual ~FewestNewVerticesTactic() {}
83 
84     RR optimal_rr(const rr_stack& stack) const;
85 
86     private:
87     SafePtr<DirectedGraph> dg_;
88   };
89 
90   /** ZeroNewVerticesTactic chooses first RR which adds no new vertices on
91       DirectedGraph dg
92     */
93   class ZeroNewVerticesTactic : public Tactic {
94     public:
ZeroNewVerticesTactic(const SafePtr<DirectedGraph> & dg)95     ZeroNewVerticesTactic(const SafePtr<DirectedGraph>& dg) : Tactic(), dg_(dg) {}
~ZeroNewVerticesTactic()96     virtual ~ZeroNewVerticesTactic() {}
97 
98     RR optimal_rr(const rr_stack& stack) const;
99 
100     private:
101     SafePtr<DirectedGraph> dg_;
102   };
103 
104   /** RandomChoiceTactic chooses randomly among the applicable RRs
105     */
106   class RandomChoiceTactic : public Tactic {
107     public:
108     RandomChoiceTactic();
~RandomChoiceTactic()109     virtual ~RandomChoiceTactic() {}
110 
111     RR optimal_rr(const rr_stack& stack) const;
112   };
113 
114   /** NullTactic always returns null RecurrenceRelation
115     */
116   class NullTactic : public Tactic {
117     public:
NullTactic()118     NullTactic() : Tactic() {}
~NullTactic()119     virtual ~NullTactic() {}
120 
121     RR optimal_rr(const rr_stack& stack) const;
122   };
123 
124   /**
125    * ParticleDirectionTactic returns the first RR that transfers the quantum numbers between particles in
126    * the desired direction.
127    */
128   class ParticleDirectionTactic : public Tactic {
129     public:
130       /**
131        * @param increasing if true, quanta should be transferred from lower to higher particle indices.
132        */
ParticleDirectionTactic(bool increase)133       ParticleDirectionTactic(bool increase) : Tactic(), increase_(increase) {}
~ParticleDirectionTactic()134       virtual ~ParticleDirectionTactic() {}
135 
136       RR optimal_rr(const rr_stack& stack) const;
137     private:
138       bool increase_;
139   };
140 
141   /**
142    * TwoCenter_OS_Tactic decides graph build for <bra0|ket0>
143    */
144   class TwoCenter_OS_Tactic : public Tactic {
145     public:
146       /**
147        * @param lbra0
148        * @param lket0
149        */
TwoCenter_OS_Tactic(unsigned lbra0,unsigned lket0)150     TwoCenter_OS_Tactic(unsigned lbra0,
151                         unsigned lket0) : Tactic(), lbra0_(lbra0), lket0_(lket0) {}
~TwoCenter_OS_Tactic()152       virtual ~TwoCenter_OS_Tactic() {}
153 
154       RR optimal_rr(const rr_stack& stack) const;
155     private:
156       unsigned lbra0_;
157       unsigned lket0_;
158   };
159 
160   /**
161    * FourCenter_OS_Tactic decides graph build for (bra0 ket0| bra1 ket1) = <bra0 bra1|ket0 ket1>
162    */
163   class FourCenter_OS_Tactic : public Tactic {
164     public:
165       /**
166        * @param lbra0
167        * @param lbra1
168        * @param lket0
169        * @param lket1
170        */
FourCenter_OS_Tactic(unsigned lbra0,unsigned lket0,unsigned lbra1,unsigned lket1)171       FourCenter_OS_Tactic(unsigned lbra0,
172                            unsigned lket0,
173                            unsigned lbra1,
174                            unsigned lket1) : Tactic(), lbra0_(lbra0), lket0_(lket0),
175                            lbra1_(lbra1), lket1_(lket1) {}
~FourCenter_OS_Tactic()176       virtual ~FourCenter_OS_Tactic() {}
177 
178       RR optimal_rr(const rr_stack& stack) const;
179     private:
180       unsigned lbra0_;
181       unsigned lket0_;
182       unsigned lbra1_;
183       unsigned lket1_;
184   };
185 
186   /////////////////////////////////
187 
188   struct DummyRandomizePolicy {
noiseDummyRandomizePolicy189     unsigned int noise(unsigned int nrrs) const { return 0; }
190   };
191 
192   /** The shift parameter is computed as follows:
193       delta = floor(nrrs*scale*random()/RAND_MAX)
194       where nrrs is the number of possibilities, scale
195       is the user-specified parameter.
196   */
197   class StdRandomizePolicy {
198     public:
StdRandomizePolicy(double scale)199     StdRandomizePolicy(double scale) : scale_(scale) {
200       // Initialize state randomly
201       time_t t;
202       srandom(time(&t));
203     }
204 
noise(unsigned int nrrs)205     unsigned int noise(unsigned int nrrs) const {
206       unsigned long rand = random();
207       const unsigned long range = RAND_MAX;
208       const unsigned int result = static_cast<unsigned int>(std::floor(nrrs*scale_*rand/range));
209       return result;
210     }
211 
212     private:
213     double scale_;
214   };
215 
216 
217 };
218 
219 #endif
220 
221