Turbo Constraint Solver
Loading...
Searching...
No Matches
common_solving.hpp
Go to the documentation of this file.
1// Copyright 2023 Pierre Talbot
2
3#ifndef TURBO_COMMON_SOLVING_HPP
4#define TURBO_COMMON_SOLVING_HPP
5
6#include <atomic>
7#include <algorithm>
8#include <chrono>
9#include <thread>
10#include <csignal>
11
12#include "config.hpp"
13#include "statistics.hpp"
14
15#include "battery/utility.hpp"
16#include "battery/allocator.hpp"
17#include "battery/vector.hpp"
18#include "battery/shared_ptr.hpp"
19
20#include "lala/simplifier.hpp"
21#include "lala/vstore.hpp"
22#include "lala/cartesian_product.hpp"
23#include "lala/interval.hpp"
24#include "lala/pc.hpp"
25#include "lala/pir.hpp"
26#include "lala/fixpoint.hpp"
27#include "lala/search_tree.hpp"
28#include "lala/bab.hpp"
29#include "lala/split_strategy.hpp"
30#include "lala/interpretation.hpp"
31
32#include "lala/flatzinc_parser.hpp"
33
34#ifdef WITH_XCSP3PARSER
35 #include "lala/XCSP3_parser.hpp"
36#endif
37
38using namespace lala;
39
40#ifndef TURBO_ITV_BITS
41 #define TURBO_ITV_BITS 32
42#endif
43
44#if (TURBO_ITV_BITS == 64)
45 using bound_value_type = long long int;
46#elif (TURBO_ITV_BITS == 16)
47 using bound_value_type = short int;
48#elif (TURBO_ITV_BITS == 32)
49 using bound_value_type = int;
50#else
51 #error "Invalid value for TURBO_ITV_BITS: must be 16, 32 or 64."
52#endif
53using Itv = Interval<ZLB<bound_value_type, battery::local_memory>>;
54
55static std::atomic<bool> got_signal;
56static void (*prev_sigint)(int);
57static void (*prev_sigterm)(int);
58
59void signal_handler(int signum)
60{
61 std::signal(SIGINT, signal_handler); // re-arm
62 std::signal(SIGTERM, signal_handler); // re-arm
63 got_signal = true; // volatile
64 if (signum == SIGINT && prev_sigint != SIG_DFL && prev_sigint != SIG_IGN) {
65 (*prev_sigint)(signum);
66 }
67 if (signum == SIGTERM && prev_sigterm != SIG_DFL && prev_sigterm != SIG_IGN) {
68 (*prev_sigterm)(signum);
69 }
70}
71
73 prev_sigint = std::signal(SIGINT, signal_handler);
74 prev_sigterm = std::signal(SIGTERM, signal_handler);
75}
76
77template <class A>
78bool must_quit(A& a) {
79 if(static_cast<bool>(got_signal)) {
80 a.prune();
81 return true;
82 }
83 return false;
84}
85
86/** Check if the timeout of the current execution is exceeded and returns `false` otherwise.
87 * It also update the statistics relevant to the solving duration and the exhaustive flag if we reach the timeout.
88 */
89template <class A, class Timepoint>
90bool check_timeout(A& a, const Timepoint& start) {
91 a.stats.update_timer(Timer::OVERALL, start);
92 if(a.config.timeout_ms == 0) {
93 return true;
94 }
95 if(a.stats.time_ms_of(Timer::OVERALL) >= static_cast<int64_t>(a.config.timeout_ms)) {
96 if(a.config.verbose_solving) {
97 printf("%% CPU: Timeout reached.\n");
98 }
99 a.prune();
100 return false;
101 }
102 return true;
103}
104
105/** This is a simple wrapper aimed at giving a unique type to the allocator, to use them in AbstractDeps. */
106template <class Alloc, size_t n>
109 UniqueAlloc() = default;
110 CUDA UniqueAlloc(const Alloc& alloc): allocator(alloc) {}
111 UniqueAlloc(const UniqueAlloc& alloc) = default;
112 UniqueAlloc(UniqueAlloc&& alloc) = default;
113 UniqueAlloc& operator=(const UniqueAlloc& alloc) = default;
114 UniqueAlloc& operator=(UniqueAlloc&& alloc) = default;
115 CUDA void* allocate(size_t bytes) {
116 return allocator.allocate(bytes);
117 }
118 CUDA void deallocate(void* data) {
119 allocator.deallocate(data);
120 }
121};
122
123template <class Alloc, size_t n>
125 CUDA void* allocate(size_t bytes) {
126 return Alloc{}.allocate(bytes);
127 }
128 CUDA void deallocate(void* data) {
129 Alloc{}.deallocate(data);
130 }
131};
132
133/** This class is parametrized by a universe of discourse, which is the domain of the variables in the store and various allocators:
134 * - BasicAllocator: default allocator, used to allocate abstract domains, the environment, storing intermediate results, etc.
135 * - PropAllocator: allocator used for the PC abstract domain, to allocate the propagators.
136 * - StoreAllocator: allocator used for the store, to allocate the variables.
137 *
138 * Normally, you should use the fastest memory for the store, then for the propagators and then for the rest.
139 */
140template <class Universe,
141 class BasicAllocator,
142 class PropAllocator,
143 class StoreAllocator>
145 using universe_type = typename Universe::local_type;
146
147 /** Version of the abstract domains with a simple allocator, to represent the best solutions. */
148 using LIStore = VStore<universe_type, BasicAllocator>;
149
150 using IStore = VStore<Universe, StoreAllocator>;
151#ifdef TURBO_IPC_ABSTRACT_DOMAIN
152 using IProp = PC<IStore, PropAllocator>; // Interval Propagators using general propagator completion.
153#else
154 using IProp = PIR<IStore, PropAllocator>; // Interval Propagators using the TNF representation of propagators.
155#endif
156 using ISimplifier = Simplifier<IProp, BasicAllocator>;
157 using Split = SplitStrategy<IProp, BasicAllocator>;
158 using IST = SearchTree<IProp, Split, BasicAllocator>;
159 using IBAB = BAB<IST, LIStore>;
160
161 using basic_allocator_type = BasicAllocator;
162 using prop_allocator_type = PropAllocator;
163 using store_allocator_type = StoreAllocator;
164
166
167 using F = TFormula<basic_allocator_type>;
168
171
172 /** We copy `other` in a new element, and ignore every variable not used in a GPU block.
173 * This is because copying everything in each block is very slow.
174 *
175 * NOTE: It is not the allocation itself that is slow, I think it calling many copy constructors for atomic variables (note that in simplifier we have an atomic memory if the underlying domain has one).
176 */
177 template <class U2, class BasicAlloc2, class PropAllocator2, class StoreAllocator2>
179 bool enable_sharing, // `true` if the propagators are not in the shared memory.
181 const BasicAllocator& basic_allocator = BasicAllocator(),
182 const PropAllocator& prop_allocator = PropAllocator(),
183 const StoreAllocator& store_allocator = StoreAllocator())
189 , stats(other.stats)
199 {
200 AbstractDeps<BasicAllocator, PropAllocator, StoreAllocator> deps{enable_sharing, basic_allocator, prop_allocator, store_allocator};
201 store = deps.template clone<IStore>(other.store);
202 iprop = deps.template clone<IProp>(other.iprop);
203 split = deps.template clone<Split>(other.split);
204 search_tree = deps.template clone<IST>(other.search_tree);
205 bab = deps.template clone<IBAB>(other.bab);
206 best = bab->optimum_ptr();
207 }
208
209 template <class U2, class BasicAlloc2, class PropAllocator2, class StoreAllocator2>
211 const BasicAllocator& basic_allocator = BasicAllocator(),
212 const PropAllocator& prop_allocator = PropAllocator(),
213 const StoreAllocator& store_allocator = StoreAllocator(),
214 const tag_copy_cons& tag = tag_copy_cons{})
215 : AbstractDomains(tag_gpu_block_copy{}, false, other, basic_allocator, prop_allocator, store_allocator)
216 {
218 env = other.env;
219 simplifier = battery::allocate_shared<ISimplifier, BasicAllocator>(basic_allocator, *other.simplifier, typename ISimplifier::light_copy_tag{}, iprop, basic_allocator);
220 }
221
222 CUDA AbstractDomains(const this_type& other,
223 const BasicAllocator& basic_allocator = BasicAllocator(),
224 const PropAllocator& prop_allocator = PropAllocator(),
225 const StoreAllocator& store_allocator = StoreAllocator())
227 {}
228
229 template <class Alloc>
231 const BasicAllocator& basic_allocator = BasicAllocator(),
232 const PropAllocator& prop_allocator = PropAllocator(),
233 const StoreAllocator& store_allocator = StoreAllocator())
238 , stats(0,0,false,config.print_statistics)
248 {
249 size_t num_subproblems = 1;
250 num_subproblems <<= config.subproblems_power;
251 stats.eps_num_subproblems = num_subproblems;
252 }
253
255
256 BasicAllocator basic_allocator;
257 PropAllocator prop_allocator;
258 StoreAllocator store_allocator;
259
260 abstract_ptr<IStore> store;
261 abstract_ptr<IProp> iprop;
262 abstract_ptr<ISimplifier> simplifier;
263 abstract_ptr<Split> split;
264 abstract_ptr<IST> search_tree;
265 abstract_ptr<LIStore> best;
266 abstract_ptr<IBAB> bab;
267
268 // The environment of variables, storing the mapping between variable's name and their representation in the abstract domains.
269 VarEnv<BasicAllocator> env;
270
271 // Information about the output of the solutions expected by MiniZinc.
272 SolverOutput<BasicAllocator> solver_output;
273
274 // The barebones architecture only supports minimization.
275 // In case of maximization, we create a new objective variable that is the negation of the original one.
277
280
281 CUDA void allocate(int num_vars, bool with_simplifier) {
282 env = VarEnv<basic_allocator_type>{basic_allocator};
283 store = battery::allocate_shared<IStore, StoreAllocator>(store_allocator, env.extends_abstract_dom(), num_vars, store_allocator);
284 iprop = battery::allocate_shared<IProp, PropAllocator>(prop_allocator, env.extends_abstract_dom(), store, prop_allocator);
285 if(with_simplifier) {
286 simplifier = battery::allocate_shared<ISimplifier, BasicAllocator>(basic_allocator, env.extends_abstract_dom(), store->aty(), iprop, basic_allocator);
287 }
288 split = battery::allocate_shared<Split, BasicAllocator>(basic_allocator, env.extends_abstract_dom(), iprop, basic_allocator);
289 search_tree = battery::allocate_shared<IST, BasicAllocator>(basic_allocator, env.extends_abstract_dom(), iprop, split, basic_allocator);
290 // Note that `best` must have the same abstract type then store (otherwise projection of the variables will fail).
291 best = battery::allocate_shared<LIStore, BasicAllocator>(basic_allocator, store->aty(), num_vars, basic_allocator);
292 bab = battery::allocate_shared<IBAB, BasicAllocator>(basic_allocator, env.extends_abstract_dom(), search_tree, best);
294 printf("%% Abstract domain allocated.\n");
295 }
296 }
297
298 // This force the deallocation of shared memory inside a kernel.
299 CUDA void deallocate() {
300 store = nullptr;
301 iprop = nullptr;
302 simplifier = nullptr;
303 split = nullptr;
304 search_tree = nullptr;
305 bab = nullptr;
306 env = VarEnv<BasicAllocator>{basic_allocator}; // this is to release the memory used by `VarEnv`.
307 }
308
309private:
310 // Mainly to interpret the IN constraint in IProp instead of only over-approximating in intervals.
311 template <class F>
312 CUDA void typing(F& f, bool toplevel = true) const {
313 if(toplevel && config.verbose_solving) {
314 printf("%% Typing the formula...\n");
315 }
316 switch(f.index()) {
317 case F::Seq:
318 if(f.sig() == ::lala::IN && f.seq(1).is(F::S) && f.seq(1).s().size() > 1) {
319 f.type_as(iprop->aty());
320 return;
321 }
322 for(int i = 0; i < f.seq().size(); ++i) {
323 typing(f.seq(i), false);
324 }
325 break;
326 case F::ESeq:
327 for(int i = 0; i < f.eseq().size(); ++i) {
328 typing(f.eseq(i), false);
329 }
330 break;
331 }
332 if(toplevel && config.print_ast) {
333 printf("%% Typed AST:\n");
334 f.print(true);
335 printf("\n");
336 }
337 }
338
339 // We first try to interpret, and if it does not work, we interpret again with the diagnostics mode turned on.
340 template <class F, class Env, class A>
341 CUDA bool interpret_and_diagnose_and_tell(const F& f, Env& env, A& a) {
342 IDiagnostics diagnostics;
343 if(!interpret_and_tell(f, env, a, diagnostics)) {
344 IDiagnostics diagnostics2;
345 interpret_and_tell<true>(f, env, a, diagnostics2);
346 diagnostics2.print();
347 return false;
348 }
349 return true;
350 }
351
352public:
353 template <class F>
354 CUDA bool interpret(const F& f) {
356 printf("%% Interpreting the formula...\n");
357 }
358 if(!interpret_and_diagnose_and_tell(f, env, *bab)) {
359 return false;
360 }
361 if(config.print_ast) {
362 printf("%% Interpreted AST:\n");
363 iprop->deinterpret(env).print();
364 printf("\n");
365 }
367 printf("%% Formula has been interpreted.\n");
368 }
369 /** If some variables were added during the interpretation, we must resize `best` as well.
370 * If we don't do it now, it will be done during the solving (when calling bab.extract) which will lead to a resize of the underlying store.
371 * The problem is that the resize will be done on the device! If it was allocated in managed memory, it will be now reallocated in device memory, leading to a segfault later on.
372 */
373 if(store->vars() != best->vars()) {
374 store->extract(*best);
375 best->join_top();
376 }
378 if(bab->is_minimization()) {
379 minimize_obj_var = bab->objective_var();
380 }
381 else if(bab->is_maximization()) {
382 auto minobj = env.variable_of("__MINIMIZE_OBJ");
383 assert(minobj.has_value());
384 assert(minobj->get().avar_of(store->aty()).has_value());
385 minimize_obj_var = minobj->get().avar_of(store->aty()).value();
386 }
387 }
388 stats.variables = store->vars();
389 stats.constraints = iprop->num_deductions();
390 bool can_interpret = true;
391 /** We add a search strategy by default for the variables that potentially do not occur in the previous strategies.
392 * Note necessary with barebones architecture: it is taken into account by the algorithm.
393 */
394 can_interpret &= interpret_default_strategy<F>();
395 return can_interpret;
396 }
397
398 using FormulaPtr = battery::shared_ptr<TFormula<basic_allocator_type>, basic_allocator_type>;
399
400 /** Parse a constraint network in the FlatZinc or XCSP3 format.
401 * The parsed formula is then syntactically simplified (`eval` function).
402 */
404 FormulaPtr f;
406 f = parse_flatzinc(config.problem_path.data(), solver_output);
407 }
408#ifdef WITH_XCSP3PARSER
410 f = parse_xcsp3(config.problem_path.data(), solver_output);
411 }
412#endif
413 if(!f) {
414 std::cerr << "Could not parse input file." << std::endl;
415 exit(EXIT_FAILURE);
416 }
417
419 printf("%% Input file parsed\n");
420 }
421 if(config.print_ast) {
422 printf("%% Parsed AST:\n");
423 f->print();
424 printf("\n");
425 }
426 stats.print_stat("parsed_variables", num_quantified_vars(*f));
427 stats.print_stat("parsed_constraints", num_constraints(*f));
428 *f = eval(*f);
430 printf("%% Formula syntactically simplified.\n");
431 }
432 return f;
433 }
434
435 template <class F>
436 void initialize_simplifier(const F& f) {
437 IDiagnostics diagnostics;
438 typename ISimplifier::template tell_type<basic_allocator_type> tell{basic_allocator};
439 if(!top_level_ginterpret_in<IKind::TELL>(*simplifier, f, env, tell, diagnostics)) {
440 printf("%% ERROR: Could not simplify the formula because:\n");
441 IDiagnostics diagnostics2;
442 top_level_ginterpret_in<IKind::TELL, true>(*simplifier, f, env, tell, diagnostics2);
443 diagnostics2.print();
444 exit(EXIT_FAILURE);
445 }
446 simplifier->deduce(std::move(tell));
447 }
448
449 void preprocess_ipc(F& f) {
450 size_t num_vars = num_quantified_vars(f);
451 allocate(num_vars, true);
452 typing(f);
453 if(!interpret(f)) {
454 exit(EXIT_FAILURE);
455 }
456 GaussSeidelIteration fp_engine;
457 fp_engine.fixpoint(iprop->num_deductions(), [&](size_t i) { return iprop->deduce(i); });
458 /* We need to initialize the simplifier even if we don't simplify.
459 This is because the simplifier equivalence classes is used in SolverOutput. */
462 return;
463 }
465 printf("%% Simplifying the formula...\n");
466 }
467 fp_engine.fixpoint(simplifier->num_deductions(), [&](size_t i) { return simplifier->deduce(i); });
468 f = simplifier->deinterpret();
470 printf("%% Formula simplified.\n");
471 }
472 f = normalize(f);
473 num_vars = num_quantified_vars(f);
474 stats.print_stat("variables_after_simplification", num_vars);
475 stats.print_stat("constraints_after_simplification", num_constraints(f));
476 allocate(num_vars, false);
477 typing(f);
478 if(!interpret(f)) {
479 exit(EXIT_FAILURE);
480 }
481 }
482
483 // Given maximize(x), add the variable __MINIMIZE_OBJ with constraint __MINIMIZE_OBJ = -x.
484 void add_minimize_objective_var(F& f, const F::Existential& max_var) {
485 if(f.is(F::Seq)) {
486 if(f.sig() == Sig::MAXIMIZE && f.seq(0).is_variable()) {
487 LVar<basic_allocator_type> minimize_obj("__MINIMIZE_OBJ");
488 f = F::make_binary(f,
489 Sig::AND,
490 F::make_binary(
491 F::make_exists(f.seq(0).type(), minimize_obj, battery::get<1>(max_var)),
492 Sig::AND,
493 F::make_binary(
494 F::make_lvar(f.seq(0).type(), minimize_obj),
495 Sig::EQ,
496 F::make_unary(Sig::NEG, f.seq(0)))));
497 }
498 else if(f.sig() == Sig::AND) {
499 for(int i = 0; i < f.seq().size(); ++i) {
500 add_minimize_objective_var(f.seq(i), max_var);
501 }
502 }
503 }
504 }
505
506 void preprocess_tcn(F& f) {
507 f = ternarize(f, VarEnv<BasicAllocator>(), {0,1,2});
508 battery::vector<F> extra;
509 f = normalize(f, extra);
510 size_t num_vars = num_quantified_vars(f);
511 stats.print_stat("tnf_variables", num_vars);
512 stats.print_stat("tnf_constraints", num_tnf_constraints(f));
513 allocate(num_vars, true);
514 if(!interpret(f)) {
515 exit(EXIT_FAILURE);
516 }
517 simplifier->init_env(env);
519 /** Even when we don't simplify, we still need to initialize the equivalence classes.
520 * This is necessary to call `print_variable` on `simplifier` when finding a solution. */
521 simplifier->initialize(num_vars, 0);
522 return;
523 }
524 auto& tnf = f.seq();
525 simplifier->initialize_tnf(num_vars, tnf);
526 size_t preprocessing_fixpoint_iterations = 0;
527 SimplifierStats preprocessing_stats;
528 size_t eliminated_variables = 0;
529 local::B has_changed = true;
530 GaussSeidelIteration fp_engine;
531 /** We apply several preprocessing steps until we reach a fixpoint. */
532 while(!iprop->is_bot() && has_changed) {
533 has_changed = false;
534 preprocessing_fixpoint_iterations++;
535 SimplifierStats local_preprocessing_stats;
536 fp_engine.fixpoint(iprop->num_deductions(), [&](size_t i) { return iprop->deduce(i); }, has_changed);
537 if(has_changed) {
538 simplifier->meet_equivalence_classes();
539 }
540 has_changed |= simplifier->algebraic_simplify(tnf, local_preprocessing_stats);
541 simplifier->eliminate_entailed_constraints(*iprop, tnf, local_preprocessing_stats);
542 has_changed |= simplifier->i_cse(tnf, local_preprocessing_stats);
543 if(has_changed) {
544 simplifier->meet_equivalence_classes();
545 local_preprocessing_stats.print(stats, preprocessing_fixpoint_iterations);
546 }
547 preprocessing_stats.merge(local_preprocessing_stats);
548 }
549 // simplifier->eliminate_entailed_constraints(*iprop, tnf, preprocessing_stats);
550 simplifier->eliminate_useless_variables(tnf, eliminated_variables);
551 f = simplifier->deinterpret(tnf, true);
552 F extra_f = F::make_nary(AND, std::move(extra));
553 simplifier->substitute(extra_f);
555 printf("%% Formula simplified.\n");
556 }
557 F f2 = F::make_binary(std::move(f), AND, std::move(extra_f));
558 num_vars = num_quantified_vars(f2);
559 preprocessing_stats.print(stats);
560 stats.print_stat("eliminated_variables", eliminated_variables);
561 stats.print_stat("preprocessing_fixpoint_iterations", preprocessing_fixpoint_iterations);
562 stats.print_stat("variables_after_simplification", num_vars);
563 stats.print_stat("constraints_after_simplification", num_tnf_constraints(f2));
564 if(iprop->is_bot()) {
565 return;
566 }
567 allocate(num_vars, false);
568 if(!interpret(f2)) {
569 exit(EXIT_FAILURE);
570 }
571 }
572
573 const char* name_of_abstract_domain() const {
574 #define STR_(x) #x
575 #define STR(x) STR_(x)
576 #ifdef TURBO_IPC_ABSTRACT_DOMAIN
577 return "ipc_itv" STR(TURBO_ITV_BITS) "_z";
578 #else
579 return "pir_itv" STR(TURBO_ITV_BITS) "_z";
580 #endif
581 }
582
583 const char* name_of_entailed_removal() const {
584 #ifdef TURBO_NO_ENTAILED_PROP_REMOVAL
585 return "deactivated";
586 #else
587 return "by_indexes_scan";
588 #endif
589 }
590
591 void preprocess() {
592 auto start = stats.start_timer_host();
593 FormulaPtr f_ptr = parse_cn();
594 stats.print_stat("abstract_domain", name_of_abstract_domain());
595 stats.print_stat("entailed_prop_removal", name_of_entailed_removal());
597 auto max_var = find_maximize_var(*f_ptr);
598 if(max_var.has_value()) {
599 auto max_var_decl = find_existential_of(*f_ptr, max_var.value());
600 if(max_var_decl.has_value()) {
601 add_minimize_objective_var(*f_ptr, max_var_decl.value());
602 }
603 }
604 }
605 #ifdef TURBO_IPC_ABSTRACT_DOMAIN
606 constexpr bool use_ipc = true;
607 #else
608 constexpr bool use_ipc = false;
609 #endif
610 if(use_ipc && !config.force_ternarize) {
611 preprocess_ipc(*f_ptr);
612 }
613 else {
614 preprocess_tcn(*f_ptr);
615 }
617 if constexpr(use_ipc) {
618 printf("%% WARNING: -network_analysis option is only valid with the PIR abstract domain.\n");
619 }
620 else {
621 analyze_pir();
622 }
623 }
625 stats.print_timing_stat("preprocessing_time", Timer::PREPROCESSING);
627 }
628
629private:
630 template <class F>
631 CUDA bool interpret_default_strategy() {
632 typename F::Sequence seq;
633 seq.push_back(F::make_nary("first_fail", {}));
634 seq.push_back(F::make_nary("indomain_min", {}));
635 F search_strat = F::make_nary("search", std::move(seq));
636 if(!interpret_and_diagnose_and_tell(search_strat, env, *bab)) {
637 return false;
638 }
639 return true;
640 }
641
642 struct vstat {
643 size_t num_occurrences;
644 bool infinite_domain;
645 size_t domain_size;
646 vstat() = default;
647 };
648
649 /** Only for PIR abstract domain. */
650 void analyze_pir() const {
652 printf("%% Analyzing the constraint network...\n");
653 }
654 if(iprop->is_bot()) {
655 printf("%% Constraint network UNSAT at root, analysis cancelled...\n");
656 return;
657 }
658 std::vector<vstat> vstats(store->vars(), vstat{});
659 for(int i = 0; i < store->vars(); ++i) {
660 auto width = (*store)[i].width().lb();
661 vstats[i].infinite_domain = width.is_top();
662 if(!vstats[i].infinite_domain) {
663 vstats[i].domain_size = width.value() + 1;
664 }
665 }
666
667 std::map<Sig, size_t> op_stats{{{Sig::EQ,0}, {Sig::LEQ,0}, {Sig::NEQ,0}, {Sig::GT,0}, {ADD,0}, {MUL,0}, {EMOD,0}, {EDIV,0}, {MIN,0}, {MAX,0}}};
668 std::map<Sig, size_t> reified_op_stats{{{Sig::EQ,0}, {Sig::LEQ,0}}};
669
670 for(int i = 0; i < iprop->num_deductions(); ++i) {
671 bytecode_type bytecode = iprop->load_deduce(i);
672 vstats[bytecode.x.vid()].num_occurrences++;
673 vstats[bytecode.y.vid()].num_occurrences++;
674 vstats[bytecode.z.vid()].num_occurrences++;
675 if(!op_stats.contains(bytecode.op)) {
676 printf("%% WARNING: operator not explicitly managed in PIR: %d\n", bytecode.op);
677 op_stats[bytecode.op] = 0;
678 }
679 auto dom = iprop->project(bytecode.x);
680 if((is_arithmetic_comparison(bytecode.op)) &&
681 (dom.lb().value() != dom.ub().value() || dom.lb().value() == 0))
682 {
683 if(dom.lb().value() != dom.ub().value()) {
684 reified_op_stats[bytecode.op]++;
685 }
686 else {
687 op_stats[negate_arithmetic_comparison(bytecode.op)]++;
688 }
689 }
690 else {
691 op_stats[bytecode.op]++;
692 }
693 }
694
695 size_t num_constants = 0;
696 size_t num_2bits_vars = 0;
697 size_t num_64bits_vars = 0;
698 size_t num_128bits_vars = 0;
699 size_t num_256bits_vars = 0;
700 size_t num_512bits_vars = 0;
701 size_t num_65536bits_vars = 0;
702 size_t num_infinites = 0;
703 double average_occ_vars = 0;
704 size_t sum_props_of_vars = 0;
705 size_t max_occ_vars = 0;
706 size_t num_vars_in_2_constraints = 0;
707 size_t num_vars_in_3_constraints = 0;
708 size_t num_vars_in_4_to_10_constraints = 0;
709 size_t num_vars_in_more_than_10_constraints = 0;
710 size_t largest_dom = 0;
711 size_t sum_domain_size = 0;
712 for(int i = 0; i < vstats.size(); ++i) {
713 if(vstats[i].infinite_domain) {
714 ++num_infinites;
715 }
716 else {
717 largest_dom = battery::max(largest_dom, vstats[i].domain_size);
718 sum_domain_size += vstats[i].domain_size;
719 }
720 if(vstats[i].domain_size == 1) {
721 ++num_constants;
722 }
723 else {
724 num_2bits_vars += vstats[i].domain_size == 2;
725 num_64bits_vars += vstats[i].domain_size <= 64;
726 num_128bits_vars += vstats[i].domain_size <= 128;
727 num_256bits_vars += vstats[i].domain_size <= 256;
728 num_512bits_vars += vstats[i].domain_size <= 512;
729 num_65536bits_vars += vstats[i].domain_size <= 65536;
730 sum_props_of_vars += vstats[i].num_occurrences;
731 max_occ_vars = battery::max(max_occ_vars, vstats[i].num_occurrences);
732 }
733 num_vars_in_2_constraints += vstats[i].num_occurrences == 2;
734 num_vars_in_3_constraints += vstats[i].num_occurrences == 3;
735 num_vars_in_4_to_10_constraints += vstats[i].num_occurrences > 3 && vstats[i].num_occurrences <= 10;
736 num_vars_in_more_than_10_constraints += vstats[i].num_occurrences > 10;
737 }
738 average_occ_vars = static_cast<double>(sum_props_of_vars) / static_cast<double>(vstats.size() - num_constants);
739
740 stats.print_stat("num_constants", num_constants);
741 stats.print_stat("num_infinite_domains", num_infinites);
742 // Print the number of average occurrence of variables in the constraints.
743 stats.print_stat("sum_props_of_vars", sum_props_of_vars);
744 stats.print_memory_statistics(config.verbose_solving, "sum_props_of_vars", sum_props_of_vars*4); // 1 integer per propagator indexes.
745 stats.print_stat("avg_constraints_per_unassigned_var", average_occ_vars);
746 // Print the maximum number of occurrence of a single variable in the constraints.
747 stats.print_stat("max_constraints_per_unassigned_var", max_occ_vars);
748 // Print the number of variables occurring only twice in the constraints.
749 stats.print_stat("num_vars_in_2_constraints", num_vars_in_2_constraints);
750 stats.print_stat("num_vars_in_3_constraints", num_vars_in_3_constraints);
751 stats.print_stat("num_vars_in_4_to_10_constraints", num_vars_in_4_to_10_constraints);
752 stats.print_stat("num_vars_in_more_than_10_constraints", num_vars_in_more_than_10_constraints);
753 // Print the number of bits required to represent the bounded variables in a bitset.
754 stats.print_stat("sum_domain_size", sum_domain_size);
755 stats.print_memory_statistics(config.verbose_solving, "sum_domain_size", sum_domain_size/8);
756 // Print the largest variable in the constraint network.
757 stats.print_stat("largest_domain", largest_dom);
758 // Print the number of variables with a width of 2, 64, 128, ... bits or less in the constraint network.
759 stats.print_stat("num_2bits_vars", num_2bits_vars);
760 stats.print_stat("num_64bits_vars", num_64bits_vars);
761 stats.print_stat("num_128bits_vars", num_128bits_vars);
762 stats.print_stat("num_256bits_vars", num_256bits_vars);
763 stats.print_stat("num_512bits_vars", num_512bits_vars);
764 stats.print_stat("num_65536bits_vars", num_65536bits_vars);
765 // For each operator, print how many times they each occur in the constraint network.
766 for(auto& [sig,count] : op_stats) {
767 std::string stat_name("num_op_");
768 stat_name += string_of_sig_txt(sig);
769 stats.print_stat(stat_name.c_str(), count);
770 }
771 for(auto& [sig,count] : reified_op_stats) {
772 std::string stat_name("num_op_reified_");
773 stat_name += string_of_sig_txt(sig);
774 stats.print_stat(stat_name.c_str(), count);
775 }
776 }
777
778public:
779 CUDA bool on_node() {
780 stats.nodes++;
781 stats.depth_max = battery::max(stats.depth_max, search_tree->depth());
783 prune();
784 return true;
785 }
786 return false;
787 }
788
790 return bab->is_satisfaction() || config.print_intermediate_solutions;
791 }
792
793 CUDA void print_solution() {
795 }
796
797 template <class BestStore>
798 CUDA void print_solution(const BestStore& best_store) {
799 solver_output.print_solution(env, best_store, *simplifier);
801 }
802
803 CUDA void prune() {
804 stats.exhaustive = false;
805 }
806
807 /** Return `true` if the search state must be pruned. */
810 if(bab->is_satisfaction() && config.stop_after_n_solutions != 0 &&
812 {
813 prune();
814 return true;
815 }
816 return false;
817 }
818
819 CUDA bool on_solution_node() {
822 }
823 return update_solution_stats();
824 }
825
826 CUDA void on_failed_node() {
827 stats.fails += 1;
828 }
829
836
841 if(!bab->objective_var().is_untyped() && !best->is_top()) {
842 stats.print_mzn_objective(best->project(bab->objective_var()), bab->is_minimization());
843 }
845 }
846 }
847
848 /** Extract in `this` the content of `other`. */
849 template <class U2, class BasicAlloc2, class PropAlloc2, class StoreAlloc2>
851 if(bab->is_optimization() && !other.best->is_top() && bab->compare_bound(*other.best, *best)) {
852 other.best->extract(*best);
853 }
854 stats.meet(other.stats);
855 }
856};
857
858template <class Universe, class Allocator = battery::standard_allocator>
859using CP = AbstractDomains<Universe,
860 battery::statistics_allocator<Allocator>,
861 battery::statistics_allocator<UniqueLightAlloc<Allocator, 0>>,
862 battery::statistics_allocator<UniqueLightAlloc<Allocator, 1>>>;
863
864#endif
#define STR(x)
#define TURBO_ITV_BITS
Definition common_solving.hpp:41
bool must_quit(A &a)
Definition common_solving.hpp:78
int bound_value_type
Definition common_solving.hpp:49
Interval< ZLB< bound_value_type, battery::local_memory > > Itv
Definition common_solving.hpp:53
void block_signal_ctrlc()
Definition common_solving.hpp:72
bool check_timeout(A &a, const Timepoint &start)
Definition common_solving.hpp:90
void signal_handler(int signum)
Definition common_solving.hpp:59
@ BAREBONES
@ PREPROCESSING
Definition common_solving.hpp:169
Definition common_solving.hpp:170
Definition common_solving.hpp:144
abstract_ptr< IBAB > bab
Definition common_solving.hpp:266
Configuration< BasicAllocator > config
Definition common_solving.hpp:278
CUDA bool interpret(const F &f)
Definition common_solving.hpp:354
CUDA void deallocate()
Definition common_solving.hpp:299
VStore< universe_type, BasicAllocator > LIStore
Definition common_solving.hpp:148
CUDA void print_final_solution()
Definition common_solving.hpp:830
StoreAllocator store_allocator
Definition common_solving.hpp:258
CUDA AbstractDomains(const tag_gpu_block_copy &, bool enable_sharing, const AbstractDomains< U2, BasicAlloc2, PropAllocator2, StoreAllocator2 > &other, const BasicAllocator &basic_allocator=BasicAllocator(), const PropAllocator &prop_allocator=PropAllocator(), const StoreAllocator &store_allocator=StoreAllocator())
Definition common_solving.hpp:178
SplitStrategy< IProp, BasicAllocator > Split
Definition common_solving.hpp:157
BasicAllocator basic_allocator
Definition common_solving.hpp:256
BAB< IST, LIStore > IBAB
Definition common_solving.hpp:159
CUDA void meet(AbstractDomains< U2, BasicAlloc2, PropAlloc2, StoreAlloc2 > &other)
Definition common_solving.hpp:850
CUDA bool on_solution_node()
Definition common_solving.hpp:819
CUDA bool on_node()
Definition common_solving.hpp:779
void initialize_simplifier(const F &f)
Definition common_solving.hpp:436
Statistics< BasicAllocator > stats
Definition common_solving.hpp:279
abstract_ptr< LIStore > best
Definition common_solving.hpp:265
AVar minimize_obj_var
Definition common_solving.hpp:276
CUDA void allocate(int num_vars, bool with_simplifier)
Definition common_solving.hpp:281
battery::shared_ptr< TFormula< basic_allocator_type >, basic_allocator_type > FormulaPtr
Definition common_solving.hpp:398
AbstractDomains(AbstractDomains &&other)=default
const char * name_of_entailed_removal() const
Definition common_solving.hpp:583
abstract_ptr< IStore > store
Definition common_solving.hpp:260
VarEnv< BasicAllocator > env
Definition common_solving.hpp:269
CUDA bool is_printing_intermediate_sol()
Definition common_solving.hpp:789
CUDA AbstractDomains(const AbstractDomains< U2, BasicAlloc2, PropAllocator2, StoreAllocator2 > &other, const BasicAllocator &basic_allocator=BasicAllocator(), const PropAllocator &prop_allocator=PropAllocator(), const StoreAllocator &store_allocator=StoreAllocator(), const tag_copy_cons &tag=tag_copy_cons{})
Definition common_solving.hpp:210
Simplifier< IProp, BasicAllocator > ISimplifier
Definition common_solving.hpp:156
abstract_ptr< IST > search_tree
Definition common_solving.hpp:264
CUDA void on_failed_node()
Definition common_solving.hpp:826
PropAllocator prop_allocator_type
Definition common_solving.hpp:162
BasicAllocator basic_allocator_type
Definition common_solving.hpp:161
CUDA void print_solution(const BestStore &best_store)
Definition common_solving.hpp:798
abstract_ptr< ISimplifier > simplifier
Definition common_solving.hpp:262
void preprocess()
Definition common_solving.hpp:591
void preprocess_tcn(F &f)
Definition common_solving.hpp:506
void preprocess_ipc(F &f)
Definition common_solving.hpp:449
const char * name_of_abstract_domain() const
Definition common_solving.hpp:573
abstract_ptr< Split > split
Definition common_solving.hpp:263
SolverOutput< BasicAllocator > solver_output
Definition common_solving.hpp:272
CUDA bool update_solution_stats()
Definition common_solving.hpp:808
PropAllocator prop_allocator
Definition common_solving.hpp:257
typename Universe::local_type universe_type
Definition common_solving.hpp:145
VStore< Universe, StoreAllocator > IStore
Definition common_solving.hpp:150
FormulaPtr parse_cn()
Definition common_solving.hpp:403
void add_minimize_objective_var(F &f, const F::Existential &max_var)
Definition common_solving.hpp:484
StoreAllocator store_allocator_type
Definition common_solving.hpp:163
PIR< IStore, PropAllocator > IProp
Definition common_solving.hpp:154
abstract_ptr< IProp > iprop
Definition common_solving.hpp:261
CUDA void print_solution()
Definition common_solving.hpp:793
CUDA void prune()
Definition common_solving.hpp:803
CUDA AbstractDomains(const this_type &other, const BasicAllocator &basic_allocator=BasicAllocator(), const PropAllocator &prop_allocator=PropAllocator(), const StoreAllocator &store_allocator=StoreAllocator())
Definition common_solving.hpp:222
TFormula< basic_allocator_type > F
Definition common_solving.hpp:167
SearchTree< IProp, Split, BasicAllocator > IST
Definition common_solving.hpp:158
CUDA AbstractDomains(const Configuration< Alloc > &config, const BasicAllocator &basic_allocator=BasicAllocator(), const PropAllocator &prop_allocator=PropAllocator(), const StoreAllocator &store_allocator=StoreAllocator())
Definition common_solving.hpp:230
CUDA void print_mzn_statistics()
Definition common_solving.hpp:837
Definition config.hpp:34
size_t stop_after_n_nodes
Definition config.hpp:38
battery::string< allocator_type > problem_path
Definition config.hpp:54
CUDA InputFormat input_format() const
Definition config.hpp:245
bool print_intermediate_solutions
Definition config.hpp:36
bool disable_simplify
Definition config.hpp:45
bool print_ast
Definition config.hpp:42
CUDA void print_mzn_statistics() const
Definition config.hpp:219
Arch arch
Definition config.hpp:51
size_t or_nodes
Definition config.hpp:48
size_t stop_after_n_solutions
Definition config.hpp:37
bool force_ternarize
Definition config.hpp:44
bool network_analysis
Definition config.hpp:46
bool print_statistics
Definition config.hpp:40
int verbose_solving
Definition config.hpp:41
size_t subproblems_power
Definition config.hpp:49
Definition statistics.hpp:110
CUDA void print_timing_stat(const char *name, Timer timer) const
Definition statistics.hpp:283
size_t nodes
Definition statistics.hpp:118
CUDA void print_mzn_end_stats() const
Definition statistics.hpp:318
CUDA void print_mzn_statistics(int verbose=0) const
Definition statistics.hpp:287
size_t constraints
Definition statistics.hpp:115
int depth_max
Definition statistics.hpp:121
CUDA void print_mzn_final_separator() const
Definition statistics.hpp:339
size_t fails
Definition statistics.hpp:119
std::chrono::steady_clock::time_point start_timer_host() const
Definition statistics.hpp:183
bool exhaustive
Definition statistics.hpp:122
std::chrono::steady_clock::time_point stop_timer(Timer timer, std::chrono::steady_clock::time_point start)
Definition statistics.hpp:193
size_t variables
Definition statistics.hpp:114
size_t solutions
Definition statistics.hpp:120
size_t eps_num_subproblems
Definition statistics.hpp:123
CUDA void meet(const Statistics< Alloc > &other)
Definition statistics.hpp:158
CUDA void print_mzn_separator() const
Definition statistics.hpp:335
CUDA void print_stat(const char *name, const char *value) const
Definition statistics.hpp:206
CUDA void print_mzn_objective(const auto &obj, bool is_minimization) const
Definition statistics.hpp:323
CUDA void print_memory_statistics(int verbose, const char *key, size_t bytes) const
Definition statistics.hpp:256
Definition common_solving.hpp:107
CUDA void deallocate(void *data)
Definition common_solving.hpp:118
UniqueAlloc & operator=(UniqueAlloc &&alloc)=default
UniqueAlloc(const UniqueAlloc &alloc)=default
UniqueAlloc & operator=(const UniqueAlloc &alloc)=default
Alloc allocator
Definition common_solving.hpp:108
CUDA void * allocate(size_t bytes)
Definition common_solving.hpp:115
UniqueAlloc()=default
UniqueAlloc(UniqueAlloc &&alloc)=default
CUDA UniqueAlloc(const Alloc &alloc)
Definition common_solving.hpp:110
Definition common_solving.hpp:124
CUDA void * allocate(size_t bytes)
Definition common_solving.hpp:125
CUDA void deallocate(void *data)
Definition common_solving.hpp:128