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