LIBINT 2.7.2
buildtest.h
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#ifndef _libint2_src_bin_libint_buildtest_h_
22#define _libint2_src_bin_libint_buildtest_h_
23
24#include <iostream>
25#include <fstream>
26#include <sstream>
27#include <string>
28#include <deque>
29#include <iterator>
30#include <dg.h>
31#include <integral_11_11.h>
32#include <strategy.h>
33#include <iface.h>
34#include <dims.h>
35#include <graph_registry.h>
36
37namespace libint2 {
38
39 // defined in buildtest.cc
40 void generate_rr_code(std::ostream& os, const SafePtr<CompilationParameters>& cparams,
41 std::deque<std::string>& decl_filenames,
42 std::deque<std::string>& def_filenames);
43
45 void
46 GenerateCode(const SafePtr<DirectedGraph>& dg,
47 const SafePtr<CodeContext>& context,
48 const SafePtr<CompilationParameters>& cparams,
49 const SafePtr<Strategy>& strat,
50 const SafePtr<Tactic>& tactic,
51 const SafePtr<MemoryManager>& memman,
52 std::deque<std::string>& decl_filenames,
53 std::deque<std::string>& def_filenames,
54 const std::string& prefix,
55 const std::string& label,
56 bool have_parent);
57
59 template <unsigned int N>
61 public:
62 TesterCmdLine(int argc, char* argv[]);
64
65 const std::vector<unsigned int>& am() const { return am_; }
66 unsigned int size_to_unroll() const { return size_to_unroll_; }
67 unsigned int veclen() const { return veclen_; }
68 bool vectorize_by_line() const { return vectorize_by_line_; }
69 bool do_cse() const { return do_cse_; }
70
71 private:
72 static const unsigned int max_am = 10;
73 std::vector<unsigned int> am_;
74 unsigned int size_to_unroll_;
75 unsigned int veclen_;
76 bool vectorize_by_line_;
77 bool do_cse_;
78 };
79
84 template <class Integral, bool GenAllCode>
85 void BuildTest(const std::vector< SafePtr<Integral> >& targets, unsigned int size_to_unroll, unsigned int veclen,
86 bool vec_by_line, bool do_cse, const std::string& complabel = "buildtest",
87 std::ostream& os = std::cout);
88
93 template <class Integral, bool GenAllCode>
94 void __BuildTest(const std::vector< SafePtr<Integral> >& targets, const SafePtr<CompilationParameters>& cparams,
95 unsigned int size_to_unroll, std::ostream& os = std::cout,
96 const SafePtr<Tactic>& tactic = SafePtr<Tactic>(new FirstChoiceTactic<DummyRandomizePolicy>),
97 const SafePtr<MemoryManager>& memman = SafePtr<MemoryManager>(new WorstFitMemoryManager),
98 const std::string& complabel = "general_integral");
99
100 template <class Integral, bool GenAllCode>
101 void
102 __BuildTest(const std::vector< SafePtr<Integral> >& targets, const SafePtr<CompilationParameters>& cparams,
103 unsigned int size_to_unroll, std::ostream& os,
104 const SafePtr<Tactic>& tactic, const SafePtr<MemoryManager>& memman,
105 const std::string& complabel)
106 {
107 const std::string prefix("");
108 const std::string label = cparams->api_prefix() + complabel;
109 SafePtr<Strategy> strat(new Strategy);
110 SafePtr<CodeContext> context(new CppCodeContext(cparams));
111
113 taskmgr.add(complabel);
114 taskmgr.current(complabel);
115
116 //
117 // do CSE only if max_am <= cparams->max_am_opt()
118 //
119 unsigned int max_am = 0;
120 for(unsigned int t=0; t<targets.size(); ++t) {
121 const SafePtr<Integral>& target = targets[t];
122 const unsigned int np = target->bra().num_part();
123 // bra
124 for(unsigned int p=0; p<np; p++) {
125 const unsigned int nf = target->bra().num_members(p);
126 for(unsigned int f=0; f<nf; f++) {
127 // Assuming shells here
128 const unsigned int am = target->bra(p,f).qn();
129 using std::max;
130 max_am = max(max_am,am);
131 }
132 }
133 // ket
134 for(unsigned int p=0; p<np; p++) {
135 const unsigned int nf = target->ket().num_members(p);
136 for(unsigned int f=0; f<nf; f++) {
137 // Assuming shells here
138 const unsigned int am = target->ket(p,f).qn();
139 using std::max;
140 max_am = max(max_am,am);
141 }
142 }
143 }
144 const bool need_to_optimize = (max_am <= cparams->max_am_opt(complabel));
145
146 std::deque<std::string> decl_filenames;
147 std::deque<std::string> def_filenames;
148
149 os << "Building " << complabel << std::endl;
150
151 SafePtr<DirectedGraph> dg_xxxx(new DirectedGraph);
152 dg_xxxx->set_label(complabel);
153
154 // configure the graph
155 dg_xxxx->registry()->do_cse(need_to_optimize);
156 dg_xxxx->registry()->condense_expr(condense_expr(size_to_unroll,cparams->max_vector_length()>1));
157 // Need to accumulate integrals?
158 dg_xxxx->registry()->accumulate_targets(cparams->accumulate_targets());
159 dg_xxxx->registry()->unroll_threshold(size_to_unroll);
160
161 for(unsigned int t=0; t<targets.size(); ++t) {
162 const SafePtr<Integral>& target = targets[t];
163 SafePtr<DGVertex> target_ptr = dynamic_pointer_cast<DGVertex,Integral>(target);
164 assert(target_ptr != 0);
165 dg_xxxx->append_target(target_ptr);
166 }
167
168 // this will generate code for this targets, and potentially generate code for its prerequisites
169 GenerateCode(dg_xxxx, context, cparams, strat, tactic, memman,
170 decl_filenames, def_filenames,
171 prefix, label, false);
172
173 // update max stack size
174 taskmgr.current().params()->max_stack_size(max_am, memman->max_memory_used());
175 taskmgr.current().params()->max_ntarget(targets.size());
176 os << "Max memory used = " << memman->max_memory_used() << std::endl;
177
178 if (GenAllCode) {
179 // initialize code context to produce library API
180 SafePtr<CodeContext> icontext(new CppCodeContext(cparams));
181 // initialize object to generate interface
182 SafePtr<Libint2Iface> iface(new Libint2Iface(cparams,icontext));
183
184 // generate interface
185 std::ostringstream oss;
186 for(std::deque<std::string>::const_iterator i = decl_filenames.begin(); i != decl_filenames.end(); ++i) {
187 oss << "#include <" << *i << ">" << std::endl;
188 }
189 iface->to_int_iface(oss.str());
190
191 // transfer some configuration parameters to the generated library API
192 iface->to_params(iface->macro_define("CARTGAUSS_MAX_AM",LIBINT_CARTGAUSS_MAX_AM));
193 iface->to_params(iface->macro_define("CGSHELL_ORDERING",LIBINT_CGSHELL_ORDERING));
194 iface->to_params(iface->macro_define("CGSHELL_ORDERING_STANDARD",LIBINT_CGSHELL_ORDERING_STANDARD));
195 iface->to_params(iface->macro_define("CGSHELL_ORDERING_INTV3",LIBINT_CGSHELL_ORDERING_INTV3));
196 iface->to_params(iface->macro_define("CGSHELL_ORDERING_GAMESS",LIBINT_CGSHELL_ORDERING_GAMESS));
197 iface->to_params(iface->macro_define("CGSHELL_ORDERING_ORCA",LIBINT_CGSHELL_ORDERING_ORCA));
198 iface->to_params(iface->macro_define("CGSHELL_ORDERING_BAGEL",LIBINT_CGSHELL_ORDERING_BAGEL));
199 iface->to_params(iface->macro_define("SHELLQUARTET_SET",LIBINT_SHELL_SET));
200 iface->to_params(iface->macro_define("SHELLQUARTET_SET_STANDARD",LIBINT_SHELL_SET_STANDARD));
201 iface->to_params(iface->macro_define("SHELLQUARTET_SET_ORCA",LIBINT_SHELL_SET_ORCA));
202
203 // Generate set-level RR code
204 generate_rr_code(os,cparams,
205 decl_filenames, def_filenames);
206
207 // Print log
208 std::cout << "Generated headers: ";
209 std::copy(decl_filenames.begin(), decl_filenames.end(), std::ostream_iterator<std::string>(std::cout, " "));
210 std::cout << std::endl << "Generated sources: ";
211 std::copy(def_filenames.begin(), def_filenames.end(), std::ostream_iterator<std::string>(std::cout, " "));
212 std::cout << std::endl << "Top compute function: " << context->label_to_name(label_to_funcname(label)) << std::endl;
213
214 }
215 }
216
217 void
218 GenerateCode(const SafePtr<DirectedGraph>& dg,
219 const SafePtr<CodeContext>& context,
220 const SafePtr<CompilationParameters>& cparams,
221 const SafePtr<Strategy>& strat,
222 const SafePtr<Tactic>& tactic,
223 const SafePtr<MemoryManager>& memman,
224 std::deque<std::string>& decl_filenames,
225 std::deque<std::string>& def_filenames,
226 const std::string& prefix,
227 const std::string& label,
228 bool have_parent) {
229
230 dg->apply(strat,tactic);
231#if PRINT_DAG_GRAPHVIZ
232 {
233 std::basic_ofstream<char> dotfile(dg->label() + ".strat.dot");
234 dg->print_to_dot(false,dotfile);
235 }
236#endif
237 dg->optimize_rr_out(context);
238#if DEBUG
239 std::cout << "The number of vertices = " << dg->num_vertices() << std::endl;
240#endif
241
242 // if there are missing prerequisites -- make a list of them
244 if (dg->missing_prerequisites()) {
245 //std::cout << "missing some prerequisites!" << std::endl;
246 dg->foreach(pe);
247 }
248 std::deque< SafePtr<DGVertex> > prereq_list = pe.vertices;
249
250 dg->traverse();
251 //dg->debug_print_traversal(cout);
252
253#if PRINT_DAG_GRAPHVIZ
254 {
255 std::basic_ofstream<char> dotfile(dg->label() + ".expr.dot");
256 dg->print_to_dot(false,dotfile);
257 }
258#endif
259
260 std::string decl_filename(prefix + context->label_to_name(label)); decl_filename += ".h";
261 std::string def_filename(prefix + context->label_to_name(label)); def_filename += ".cc";
262 std::basic_ofstream<char> declfile(decl_filename.c_str());
263 std::basic_ofstream<char> deffile(def_filename.c_str());
264 // if have parent graph, it will pass its stack where this graph will put its results
265 SafePtr<CodeSymbols> args(new CodeSymbols);
266 if (have_parent)
267 args->append_symbol("parent_stack");
268 dg->generate_code(context,memman,ImplicitDimensions::default_dims(),args,
269 label,declfile,deffile);
270 declfile.close();
271 deffile.close();
272
273 // extract all external symbols
274 extract_symbols(dg);
275
276#if PRINT_DAG_GRAPHVIZ
277 {
278 std::basic_ofstream<char> dotfile(dg->label() + ".symb.dot");
279 dg->print_to_dot(true,dotfile);
280 }
281#endif
282
283 decl_filenames.push_back(decl_filename);
284 def_filenames.push_back(def_filename);
285
286 // last: missing prerequisites? create new graph computing prereqs and move them onto it
287 if (dg->missing_prerequisites()) {
288
289 SafePtr<DirectedGraph> dg_prereq(new DirectedGraph);
290 // configure identically
291 dg_prereq->registry() = SafePtr<GraphRegistry>(dg->registry()->clone());
292 // except:
293 // - allow uncontraction
294 // - no need to return targets via inteval->targets_ -- their locations are known by the parent graph (see allocate_mem)
295 dg_prereq->registry()->uncontract(true);
296 assert(cparams->contracted_targets());
297 dg_prereq->registry()->return_targets(false);
298 dg_prereq->registry()->accumulate_targets(true);
299 dg_prereq->registry()->stack_name("stack");
300 if (dg->registry()->current_timer() >= 0) {
301 dg_prereq->registry()->current_timer( dg->registry()->current_timer() + 1 );
302 }
303
304 // now is the "right" time to reset dg
305 // reset graph of the previous computation so that the vertices that will be targets on the new graph
306 // are not attached still to the vertices from the old graph
307 dg->reset();
308 memman->reset();
309
310 while (!prereq_list.empty()) {
311 dg_prereq->append_target(prereq_list.front());
312 prereq_list.pop_front();
313 }
314
315 const std::string label_prereq = label + "_prereq";
316 GenerateCode(dg_prereq, context, cparams, strat, tactic, memman,
317 decl_filenames, def_filenames,
318 prefix, label_prereq, true);
319
320 }
321 dg->reset();
322 memman->reset();
323
324 }
325
326 template <class Integral, bool GenAllCode>
327 void BuildTest(const std::vector< SafePtr<Integral> >& targets, unsigned int size_to_unroll, unsigned int veclen,
328 bool vec_by_line, bool do_cse, const std::string& complabel,
329 std::ostream& os)
330 {
331 const unsigned int max_am = 10;
332 os << "generating code to compute " << complabel << std::endl;
333
335 taskmgr.add(complabel);
336 taskmgr.current(complabel);
337
338 // initialize cparams
339 SafePtr<CompilationParameters> cparams(new CompilationParameters);
340 cparams->max_am(complabel,max_am);
341 cparams->num_bf(complabel,4u);
342 cparams->max_vector_length(veclen);
343 cparams->vectorize_by_line(vec_by_line);
344#if LIBINT_ALIGN_SIZE
345 cparams->align_size(LIBINT_ALIGN_SIZE);
346#endif
347 cparams->count_flops(true);
348#if LIBINT_ACCUM_INTS
349 cparams->accumulate_targets(true);
350#else
351 cparams->accumulate_targets(false);
352#endif
353#ifdef LIBINT_API_PREFIX
354 {
355 const std::string api_prefix(LIBINT_API_PREFIX);
356 cparams->api_prefix(api_prefix);
357 }
358#endif
359#if LIBINT_CONTRACTED_INTS
360 cparams->contracted_targets(true);
361#else
362 cparams->contracted_targets(false);
363#endif
364#ifdef LIBINT_USER_DEFINED_REAL
365 {
366 const std::string realtype(LIBINT_USER_DEFINED_REAL);
367 cparams->realtype(realtype);
368 }
369#endif
370
371 if (do_cse) {
372 cparams->max_am_opt(complabel,max_am);
373 }
374 else {
375 cparams->max_am_opt(complabel,0);
376 }
377 cparams->default_task_name(complabel);
378
379 // set default dims
381
382 SafePtr<StdRandomizePolicy> rpolicy(new StdRandomizePolicy(0.00));
383 // use 4-center OS if the target is a 4-center integral
384 SafePtr<Tactic> tactic;
385 {
386 typedef GenIntegralSet_11_11<typename Integral::BasisFunctionType,
387 typename Integral::OperatorType,
388 typename Integral::AuxIndexType> genint_11_11_t;
389 SafePtr< genint_11_11_t > cast_ptr = dynamic_pointer_cast<genint_11_11_t>(targets.front());
390 if (cast_ptr) {
391 const unsigned int la = cast_ptr->bra(0, 0).norm();
392 const unsigned int lb = cast_ptr->ket(0, 0).norm();
393 const unsigned int lc = cast_ptr->bra(1, 0).norm();
394 const unsigned int ld = cast_ptr->ket(1, 0).norm();
395 tactic = SafePtr<Tactic>(new FourCenter_OS_Tactic(la, lb, lc, ld));
396 }
397 else {
398 tactic = SafePtr<Tactic>(new FirstChoiceTactic<StdRandomizePolicy>(rpolicy));
399 }
400 }
401 const SafePtr<MemoryManager> memman(new WorstFitMemoryManager);
402 __BuildTest<Integral,true>(targets,cparams,size_to_unroll,os,tactic,memman,complabel);
403 }
404
405 template <unsigned int N>
406 TesterCmdLine<N>::TesterCmdLine(int argc, char* argv[])
407 {
408 if (N == 0)
409 throw ProgrammingError("TesterCmdLine<N>::TesterCmdLine but N is 0");
410 const int argc_min = N + 2;
411 const int argc_max = N + 5;
412 if (argc < argc_min || argc > argc_max) {
413 std::cerr << "Usage: " << argv[0] << " <am> size_to_unroll [vector_length] [vector_method] [do_cse]" << std::endl
414 << " <am> -- angular momenta on each center, e.g. 4 nonnegative integers for a 4-center ERI" << std::endl
415 << " size_to_unroll -- size of the largest integral set to be unrolled" << std::endl
416 << " vector_length -- (optional) max vector length. Defaults to 1." << std::endl
417 << " vector_method -- (optional) vectorization method. Valid choices are 0 (by-block) and 1 (by-line). Defaults to 0." << std::endl
418 << " do_cse -- (optional) do Common Subexpression Elimination? Valid choices are 0 (no) and 1 (yes). Defaults to 0." << std::endl << std::endl;
419 throw InputError("TesterCmdLine<N>::TesterCmdLine -- incorrect number of command-line arguments");
420 }
421 for(unsigned int i=1; i<N+1; ++i) {
422 const unsigned int am = atoi(argv[i]);
423 if (am > max_am)
424 throw InputError("TesterCmdLine<N>::TesterCmdLine -- angular momentum limit exceeded");
425 am_.push_back(am);
426 }
427 size_to_unroll_ = atoi(argv[N+1]);
428
429 veclen_ = 1;
430 if (argc >= N+3) {
431 veclen_ = atoi(argv[N+2]);
432 }
433 vectorize_by_line_ = false;
434 if (argc >= N+4) {
435 vectorize_by_line_ = (1 == atoi(argv[N+3]));
436 }
437 do_cse_ = false;
438 if (argc >= N+5) {
439 do_cse_ = (1 == atoi(argv[N+4]));
440 }
441 }
442
443};
444
445#endif
Class CodeSymbols specifies a set of symbols used in a code.
Definition: code.h:32
These are the parameters received by the compiler.
Definition: default_params.h:38
CppCodeContext is an implementation of CodeContext for C++.
Definition: context.h:207
DirectedGraph is an implementation of a directed graph composed of vertices represented by DGVertex o...
Definition: dg.h:63
FirstChoiceTactic simply chooses the first RR.
Definition: tactic.h:59
FourCenter_OS_Tactic decides graph build for (bra0 ket0| bra1 ket1) = <bra0 bra1|ket0 ket1>
Definition: tactic.h:162
Generic integral over a two-body operator with one bfs for each particle in bra and ket.
Definition: integral_11_11.h:33
static SafePtr< ImplicitDimensions > default_dims()
Default ImplicitDimension object.
Definition: dims.cc:38
static void set_default_dims(const SafePtr< CompilationParameters > &cparams)
Sets default ImplicitDimension object.
Definition: dims.cc:31
Libint2Iface is used to generate Libint2 interfaces.
Definition: iface.h:41
Manages tasks. This is a Singleton.
Definition: task.h:63
void current(const std::string &task_label)
Makes this task current (must have been added already)
Definition: task.cc:76
void add(const std::string &task_label)
Adds a new task. Do nothing if the task exists already.
Definition: task.cc:40
static LibraryTaskManager & Instance()
LibraryTaskManager is a Singleton.
Definition: task.cc:34
The shift parameter is computed as follows: delta = floor(nrrs*scale*random()/RAND_MAX) where nrrs is...
Definition: tactic.h:196
Strategy specifies how to apply recurrence relations.
Definition: strategy.h:39
Command-line parser for the standard build tester – N is the number of centers, i....
Definition: buildtest.h:60
WorstFitMemoryManager allocates memory by trying to find the largest-possible free block.
Definition: src/bin/libint/memory.h:206
Defaults definitions for various parameters assumed by Libint.
Definition: algebra.cc:24
void extract_symbols(const SafePtr< DirectedGraph > &dg)
extracts external symbols and RRs from the graph
Definition: dg.cc:2357
void GenerateCode(const SafePtr< DirectedGraph > &dg, const SafePtr< CodeContext > &context, const SafePtr< CompilationParameters > &cparams, const SafePtr< Strategy > &strat, const SafePtr< Tactic > &tactic, const SafePtr< MemoryManager > &memman, std::deque< std::string > &decl_filenames, std::deque< std::string > &def_filenames, const std::string &prefix, const std::string &label, bool have_parent)
defined below generates code for dg; dg and memman are reset at the end
Definition: buildtest.h:218
void BuildTest(const std::vector< SafePtr< Integral > > &targets, unsigned int size_to_unroll, unsigned int veclen, bool vec_by_line, bool do_cse, const std::string &complabel="buildtest", std::ostream &os=std::cout)
This is a user-friendly generic test of building an Integral using specified size_to_unroll,...
Definition: buildtest.h:327
std::string label_to_funcname(const std::string &label)
Converts a label, e.g. name of the target node, to the name of the function to compute it.
Definition: default_params.cc:216
bool condense_expr(unsigned int unroll_threshold, bool vectorize)
need to condense expressions? Makes sense if vectorizing the code or the compiler somehow prefers lon...
Definition: default_params.cc:229
void __BuildTest(const std::vector< SafePtr< Integral > > &targets, const SafePtr< CompilationParameters > &cparams, unsigned int size_to_unroll, std::ostream &os=std::cout, const SafePtr< Tactic > &tactic=SafePtr< Tactic >(new FirstChoiceTactic< DummyRandomizePolicy >), const SafePtr< MemoryManager > &memman=SafePtr< MemoryManager >(new WorstFitMemoryManager), const std::string &complabel="general_integral")
This is a generic test of building an Integral using specified cparams, memman, size_to_unroll,...
Definition: buildtest.h:102