QuIDS: Quantum Irregular Dynamic Simulator
quids.hpp
1#pragma once
2
3typedef unsigned uint;
4
5#include <parallel/algorithm>
6#include <parallel/numeric>
7#include <limits>
8
9#include <complex>
10#include <cstddef>
11#include <vector>
12
13#include "utils/libs/robin_hood.h"
14
15#include "utils/vector.hpp"
16#include "utils/load_balancing.hpp"
17#include "utils/algorithm.hpp"
18#include "utils/memory.hpp"
19#include "utils/random.hpp"
20
21#ifndef PROBA_TYPE
22 #define PROBA_TYPE double
23#endif
24#ifndef HASH_MAP_OVERHEAD
25 #define HASH_MAP_OVERHEAD 1.7
26#endif
27#ifndef ALIGNMENT_BYTE_LENGTH
28 #define ALIGNMENT_BYTE_LENGTH 8
29#endif
30#ifndef TOLERANCE
31 #define TOLERANCE 1e-30;
32#endif
33#ifndef SAFETY_MARGIN
34 #define SAFETY_MARGIN 0.2
35#endif
36#ifndef LOAD_BALANCING_BUCKET_PER_THREAD
37 #define LOAD_BALANCING_BUCKET_PER_THREAD 32
38#endif
39
40#define ITERATION_MEMORY_SIZE 2*sizeof(PROBA_TYPE) + 3*sizeof(size_t) + sizeof(float) + 2*sizeof(uint)
41#define SYMBOLIC_ITERATION_MEMORY_SIZE 2*sizeof(PROBA_TYPE) + 4*sizeof(size_t) + 2*sizeof(uint) + sizeof(float)
42
43/*
44defining openmp function's return values if openmp isn't installed or loaded
45*/
46#ifndef _OPENMP
47 #define omp_set_nested(i)
48 #define omp_get_thread_num() 0
49 #define omp_get_num_threads() 1
50#else
51 #include <omp.h>
52#endif
53
55namespace quids {
57 uint align_byte_length = ALIGNMENT_BYTE_LENGTH;
59 PROBA_TYPE tolerance = TOLERANCE;
61 float safety_margin = SAFETY_MARGIN;
63 int load_balancing_bucket_per_thread = LOAD_BALANCING_BUCKET_PER_THREAD;
64 #ifdef SIMPLE_TRUNCATION
66 bool simple_truncation = true;
67 #else
69 bool simple_truncation = false;
70 #endif
71
73 typedef std::complex<PROBA_TYPE> mag_t;
75 typedef class iteration it_t;
79 typedef class rule rule_t;
81 typedef std::function<void(char* parent_begin, char* parent_end, mag_t &mag)> modifier_t;
83 typedef std::function<PROBA_TYPE(char const *object_begin, char const *object_end)> observable_t;
85 typedef std::function<void(const char* step)> debug_t;
86
88 uint inline get_alignment_offset(const uint size) {
89 if (align_byte_length <= 1)
90 return 0;
91
92 uint alignment_offset = align_byte_length - size%align_byte_length;
93 if (alignment_offset == align_byte_length)
94 alignment_offset = 0;
95
96 return alignment_offset;
97 }
98
100 class rule {
101 public:
103 rule() {};
105
110 virtual inline void get_num_child(char const *parent_begin, char const *parent_end, uint &num_child, uint &max_child_size) const = 0;
112
119 virtual inline void populate_child(char const *parent_begin, char const *parent_end, char* const child_begin, uint const child_id, uint &size, mag_t &mag) const = 0;
121
126 virtual inline void populate_child_simple(char const *parent_begin, char const *parent_end, char* const child_begin, uint const child_id) const { //can be overwritten
127 uint size_placeholder;
128 mag_t mag_placeholder;
129 populate_child(parent_begin, parent_end, child_begin, child_id,
130 size_placeholder, mag_placeholder);
131 }
133
138 virtual inline size_t hasher(char const *object_begin, char const *object_end) const {
139 return std::hash<std::string_view>()(std::string_view(object_begin, std::distance(object_begin, object_end)));
140 }
141 };
142
144 class iteration {
145 public:
147 size_t num_object = 0;
149 PROBA_TYPE total_proba = 1;
150
153 resize(0);
154 allocate(0);
155 object_begin[0] = 0;
156 }
158
161 iteration(char* object_begin_, char* object_end_) : iteration() {
162 append(object_begin_, object_end_);
163 }
165
169 void append(char const *object_begin_, char const *object_end_, mag_t const mag=1) {
170 size_t offset = object_begin[num_object];
171 size_t size = std::distance(object_begin_, object_end_);
172 uint alignment_offset = get_alignment_offset(size);
173
174 resize(++num_object);
175 allocate(offset + size + alignment_offset);
176
177 for (size_t i = 0; i < size; ++i)
178 objects[offset + i] = object_begin_[i];
179
180 magnitude[num_object - 1] = mag;
181 object_size[num_object - 1] = size;
182 object_begin[num_object] = offset + size + alignment_offset;
183 }
185
189 void pop(size_t n=1, bool normalize_=true) {
190 if (n < 1)
191 return;
192
193 num_object -= n;
194 allocate(object_begin[num_object]);
195 resize(num_object);
196
197 if (normalize_) normalize();
198 }
200
203 PROBA_TYPE average_value(const observable_t observable) const {
204 PROBA_TYPE avg = 0;
205 if (num_object == 0)
206 return avg;
207
208 #pragma omp parallel
209 {
210 /* compute average per thread */
211 PROBA_TYPE local_avg = 0;
212 #pragma omp for
213 for (size_t oid = 0; oid < num_object; ++oid) {
214 uint size;
215 std::complex<PROBA_TYPE> mag;
216
217 /* get object and accumulate */
218 char const *this_object_begin;
219 get_object(oid, this_object_begin, size, mag);
220 local_avg += observable(this_object_begin, this_object_begin + size) * std::norm(mag);
221 }
222
223 /* accumulate thread averages */
224 #pragma omp critical
225 avg += local_avg;
226 }
227
228 return avg;
229 }
231
237 void get_object(size_t const object_id, char *& object_begin_, uint &object_size_, mag_t *&mag) {
238 object_size_ = object_size[object_id];
239 mag = &magnitude[object_id];
240 object_begin_ = &objects[object_begin[object_id]];
241 }
243
249 void get_object(size_t const object_id, char const *& object_begin_, uint &object_size_, mag_t &mag) const {
250 object_size_ = object_size[object_id];
251 mag = magnitude[object_id];
252 object_begin_ = &objects[object_begin[object_id]];
253 }
254
255 private:
256 friend symbolic_iteration;
257 friend void inline simulate(it_t &iteration, rule_t const *rule, it_t &next_iteration, sy_it_t &symbolic_iteration, size_t max_num_object, debug_t mid_step_function);
258 friend void inline simulate(it_t &iteration, modifier_t const rule);
259
260 protected:
261 mutable size_t truncated_num_object = 0;
262 mutable uint ub_symbolic_object_size = 0;
263
264 mutable utils::fast_vector<mag_t> magnitude;
265 mutable utils::fast_vector<char> objects;
266 mutable utils::fast_vector<size_t> object_begin;
267 mutable utils::fast_vector<uint> object_size;
268 mutable utils::fast_vector<uint> num_childs;
269 mutable utils::fast_vector<size_t> child_begin;
270 mutable utils::fast_vector<size_t> truncated_oid;
271 mutable utils::fast_vector<float> random_selector;
272
274 void inline resize(size_t num_object) const {
275 #pragma omp parallel sections
276 {
277 #pragma omp section
278 magnitude.resize(num_object);
279
280 #pragma omp section
281 num_childs.resize(num_object);
282
283 #pragma omp section
284 object_size.resize(num_object);
285
286 #pragma omp section
287 object_begin.resize(num_object + 1);
288
289 #pragma omp section
290 child_begin.resize(num_object + 1);
291
292 #pragma omp section
293 {
294 truncated_oid.resize(num_object);
295 utils::parallel_iota(&truncated_oid[0], &truncated_oid[0] + num_object, 0);
296 }
297
298 #pragma omp section
299 random_selector.resize(num_object);
300 }
301 }
302 void inline allocate(size_t size) const {
303 objects.resize(size, align_byte_length);
304 }
305
306
307 /*
308 utils functions
309 */
310 size_t get_mem_size() const {
311 static const size_t iteration_memory_size = ITERATION_MEMORY_SIZE;
312 return magnitude.size()*iteration_memory_size + objects.size();
313 }
314 size_t get_object_length() const {
315 return object_begin[num_object];
316 }
317 size_t get_num_symbolic_object() const {
318 return __gnu_parallel::accumulate(&num_childs[0], &num_childs[0] + num_object, (size_t)0);
319 }
320
321
322 void compute_num_child(rule_t const *rule, debug_t mid_step_function=[](const char*){}) const;
323 void prepare_truncate(debug_t mid_step_function=[](const char*){}) const;
324 size_t get_truncated_mem_size(size_t begin_num_object=0) const;
325 void truncate(size_t begin_num_object, size_t max_num_object, debug_t mid_step_function=[](const char*){}) const;
326 void generate_symbolic_iteration(rule_t const *rule, sy_it_t &symbolic_iteration, debug_t mid_step_function=[](const char*){}) const;
327 void apply_modifier(modifier_t const rule);
328 void normalize(debug_t mid_step_function=[](const char*){});
330 };
331
334 public:
337
339 size_t num_object = 0;
342
343 private:
344 friend iteration;
345 friend void inline simulate(it_t &iteration, rule_t const *rule, it_t &next_iteration, sy_it_t &symbolic_iteration, size_t max_num_object, debug_t mid_step_function);
346
347 protected:
348 size_t next_iteration_num_object = 0;
349
350 std::vector<char*> placeholder;
351
358 utils::fast_vector<float> random_selector;
359 utils::fast_vector<size_t> next_oid_partitioner_buffer;
360
362 void inline resize(size_t num_object) {
363 #pragma omp parallel sections
364 {
365 #pragma omp section
366 magnitude.resize(num_object);
367
368 #pragma omp section
369 next_oid.resize(num_object);
370
371 #pragma omp section
372 size.resize(num_object);
373
374 #pragma omp section
375 hash.resize(num_object);
376
377 #pragma omp section
378 parent_oid.resize(num_object);
379
380 #pragma omp section
381 child_id.resize(num_object);
382
383 #pragma omp section
384 random_selector.resize(num_object);
385
386 #pragma omp section
387 next_oid_partitioner_buffer.resize(num_object);
388 }
389
390 utils::parallel_iota(&next_oid[0], &next_oid[num_object], 0);
391 }
392 void inline reserve(size_t max_size) {
393 int num_threads;
394 #pragma omp parallel
395 #pragma omp single
396 num_threads = omp_get_num_threads();
397
398 placeholder.resize(num_threads);
399
400 #pragma omp parallel
401 {
402 auto &buffer = placeholder[omp_get_thread_num()];
403 if (buffer == NULL)
404 free(buffer);
405 buffer = new char[max_size];
406 }
407 }
408
409
410 /*
411 utils functions
412 */
413 size_t get_mem_size() const {
414 static const size_t symbolic_iteration_memory_size = SYMBOLIC_ITERATION_MEMORY_SIZE;
415 return magnitude.size()*symbolic_iteration_memory_size;
416 }
417
418 void compute_collisions(debug_t mid_step_function=[](const char*){});
419 size_t get_truncated_mem_size(size_t begin_num_object=0) const;
420 void truncate(size_t begin_num_object, size_t max_num_object, debug_t mid_step_function=[](const char*){});
421 void prepare_truncate(debug_t mid_step_function=[](const char*){});
422 void finalize(rule_t const *rule, it_t const &last_iteration, it_t &next_iteration, debug_t mid_step_function=[](const char*){});
424 };
425
427
431 void inline simulate(it_t &iteration, modifier_t const rule) {
432 iteration.apply_modifier(rule);
433 }
435
443 void inline simulate(it_t &iteration, rule_t const *rule, it_t &next_iteration, sy_it_t &symbolic_iteration, size_t max_num_object=0, debug_t mid_step_function=[](const char*){}) {
444 /* compute the number of child */
445 iteration.compute_num_child(rule, mid_step_function);
446 iteration.truncated_num_object = iteration.num_object;
447
448 /* prepare truncate */
449 mid_step_function("truncate_symbolic - prepare");
450 iteration.prepare_truncate(mid_step_function);
451
452 /* max_num_object */
453 mid_step_function("truncate_symbolic");
454 if (max_num_object == 0) {
455
456 /* available memory */
457 size_t avail_memory = next_iteration.get_mem_size() + symbolic_iteration.get_mem_size() + utils::get_free_mem();
458 size_t target_memory = avail_memory*(1 - safety_margin);
459
460 /* actually truncate by binary search */
461 if (iteration.get_truncated_mem_size() > target_memory) {
462 size_t begin = 0, end = iteration.num_object;
463 while (end > begin + 1) {
464 size_t middle = (end + begin) / 2;
465 iteration.truncate(begin, middle, mid_step_function);
466
467 size_t used_memory = iteration.get_truncated_mem_size(begin);
468 if (used_memory < target_memory) {
469 target_memory -= used_memory;
470 begin = middle;
471 } else
472 end = middle;
473 }
474 }
475 } else
476 iteration.truncate(0, max_num_object, mid_step_function);
477
478 /* downsize if needed */
479 if (iteration.num_object > 0) {
480 if (iteration.truncated_num_object < next_iteration.num_object)
481 next_iteration.resize(iteration.truncated_num_object);
482 size_t next_object_size = iteration.truncated_num_object*iteration.get_object_length()/iteration.num_object;
483 if (next_object_size < next_iteration.objects.size())
484 next_iteration.allocate(next_object_size);
485 }
486
487 /* generate symbolic iteration */
488 iteration.generate_symbolic_iteration(rule, symbolic_iteration, mid_step_function);
489 symbolic_iteration.compute_collisions(mid_step_function);
490 symbolic_iteration.next_iteration_num_object = symbolic_iteration.num_object_after_interferences;
491
492
493 /* prepare truncate */
494 mid_step_function("truncate - prepare");
495 symbolic_iteration.prepare_truncate(mid_step_function);
496
497 /* second max_num_object */
498 mid_step_function("truncate");
499 if (max_num_object == 0) {
500
501 /* available memory */
502 size_t avail_memory = next_iteration.get_mem_size() + utils::get_free_mem();
503 size_t target_memory = avail_memory*(1 - safety_margin);
504
505 /* actually truncate by binary search */
506 if (symbolic_iteration.get_truncated_mem_size() > target_memory) {
507 size_t begin = 0, end = symbolic_iteration.num_object_after_interferences;
508 while (end > begin + 1) {
509 size_t middle = (end + begin) / 2;
510 symbolic_iteration.truncate(begin, middle, mid_step_function);
511
512 size_t used_memory = symbolic_iteration.get_truncated_mem_size(begin);
513 if (used_memory < target_memory) {
514 target_memory -= used_memory;
515 begin = middle;
516 } else
517 end = middle;
518 }
519 }
520 } else
521 symbolic_iteration.truncate(0, max_num_object, mid_step_function);
522
523 /* finish simulation */
524 symbolic_iteration.finalize(rule, iteration, next_iteration, mid_step_function);
525 next_iteration.normalize(mid_step_function);
526 }
527
528 /*
529 compute num child
530 */
531 void iteration::compute_num_child(rule_t const *rule, debug_t mid_step_function) const {
532 /* !!!!!!!!!!!!!!!!
533 num_child
534 !!!!!!!!!!!!!!!! */
535 mid_step_function("num_child");
536
537 if (num_object == 0)
538 return;
539
540 ub_symbolic_object_size = 0;
541
542 #pragma omp parallel for reduction(max:ub_symbolic_object_size)
543 for (size_t oid = 0; oid < num_object; ++oid) {
544 uint size;
545 rule->get_num_child(&objects[object_begin[oid]],
546 &objects[object_begin[oid] + object_size[oid]],
547 num_childs[oid], size);
548 ub_symbolic_object_size = std::max(ub_symbolic_object_size, size);
549 }
550
551 __gnu_parallel::partial_sum(num_childs.begin(), num_childs.begin() + num_object, child_begin.begin() + 1);
552 }
553
554 /*
555 get the truncated memory size
556 */
557 size_t iteration::get_truncated_mem_size(size_t begin_num_object) const {
558 static const size_t iteration_memory_size = ITERATION_MEMORY_SIZE;
559
560 static const float hash_map_size = HASH_MAP_OVERHEAD*2*sizeof(size_t);
561 static const size_t symbolic_iteration_memory_size = SYMBOLIC_ITERATION_MEMORY_SIZE;
562
563 size_t mem_size = iteration_memory_size*(truncated_num_object - begin_num_object);
564 for (size_t i = begin_num_object; i < truncated_num_object; ++i) {
565 size_t oid = truncated_oid[i];
566
567 mem_size += object_begin[oid + 1] - object_begin[oid];
568 mem_size += num_childs[oid]*(symbolic_iteration_memory_size + hash_map_size);
569 }
570
571 return mem_size;
572 }
573
574 /*
575 prepare pre-truncate
576 */
577 void iteration::prepare_truncate(debug_t mid_step_function) const {
578 /* !!!!!!!!!!!!!!!!
579 prepare pre_truncate
580 !!!!!!!!!!!!!!!! */
581
583 #pragma omp parallel
584 {
585 utils::random_generator rng;
586
587 #pragma omp for
588 for (size_t oid = 0; oid < num_object; ++oid)
589 random_selector[oid] = rng() / std::norm(magnitude[oid]); //-std::log(1 - rng()) / std::norm(magnitude[oid]);
590 }
591 }
592
593 /*
594 pre-truncate
595 */
596 void iteration::truncate(size_t begin_num_object, size_t max_num_object, debug_t mid_step_function) const {
597 /* !!!!!!!!!!!!!!!!
598 pre_truncate
599 !!!!!!!!!!!!!!!! */
600
601 if (max_num_object >= num_object) {
602 truncated_num_object = num_object;
603 return;
604 }
605
606 /* delimitations */
607 auto begin = truncated_oid.begin() + begin_num_object;
608 auto middle = truncated_oid.begin() + max_num_object;
609 auto end = truncated_oid.begin() + truncated_num_object;
610
611 if (simple_truncation) {
612 /* truncation */
613 __gnu_parallel::nth_element(begin, middle, end,
614 [&](size_t const &oid1, size_t const &oid2) {
615 return std::norm(magnitude[oid1]) > std::norm(magnitude[oid2]);
616 });
617 } else
618 /* select graphs according to random selectors */
619 __gnu_parallel::nth_element(begin, middle, end,
620 [&](size_t const &oid1, size_t const &oid2) {
621 return random_selector[oid1] < random_selector[oid2];
622 });
623
624 truncated_num_object = max_num_object;
625 }
626
627 /*
628 generate symbolic iteration
629 */
630 void iteration::generate_symbolic_iteration(rule_t const *rule, sy_it_t &symbolic_iteration, debug_t mid_step_function) const {
631 if (truncated_num_object == 0) {
632 symbolic_iteration.num_object = 0;
633 mid_step_function("prepare_index");
634 mid_step_function("symbolic_iteration");
635 return;
636 }
637
638
639
640
641
642
643
644 /* !!!!!!!!!!!!!!!!
645 prepare_index
646 !!!!!!!!!!!!!!!! */
647 mid_step_function("prepare_index");
648
649 child_begin[0] = 0;
650 for (size_t i = 0; i < truncated_num_object; ++i) {
651 size_t oid = truncated_oid[i];
652
653 child_begin[i + 1] = child_begin[i] + num_childs[oid];
654 }
655
656 symbolic_iteration.num_object = child_begin[truncated_num_object];
657
658 /* resize symbolic iteration */
659 symbolic_iteration.resize(symbolic_iteration.num_object);
660 symbolic_iteration.reserve(ub_symbolic_object_size);
661
662 #pragma omp parallel
663 {
664 auto thread_id = omp_get_thread_num();
665
666 #pragma omp for
667 for (size_t i = 0; i < truncated_num_object; ++i) {
668 size_t oid = truncated_oid[i];
669
670 /* assign parent ids and child ids for each child */
671 std::fill(&symbolic_iteration.parent_oid[child_begin[i]],
672 &symbolic_iteration.parent_oid[child_begin[i + 1]],
673 oid);
674 std::iota(&symbolic_iteration.child_id[child_begin[i]],
675 &symbolic_iteration.child_id[child_begin[i + 1]],
676 0);
677 }
678
679
680
681
682 /* !!!!!!!!!!!!!!!!
683 symbolic_iteration
684 !!!!!!!!!!!!!!!! */
685 #pragma omp single
686 mid_step_function("symbolic_iteration");
687
688 #pragma omp for
689 for (size_t oid = 0; oid < symbolic_iteration.num_object; ++oid) {
690 auto id = symbolic_iteration.parent_oid[oid];
691
692 /* generate graph */
693 symbolic_iteration.magnitude[oid] = magnitude[id];
694 rule->populate_child(&objects[object_begin[id]],
695 &objects[object_begin[id] + object_size[id]],
696 symbolic_iteration.placeholder[thread_id], symbolic_iteration.child_id[oid],
697 symbolic_iteration.size[oid], symbolic_iteration.magnitude[oid]);
698
699 /* compute hash */
700 symbolic_iteration.hash[oid] = rule->hasher(symbolic_iteration.placeholder[thread_id],
701 symbolic_iteration.placeholder[thread_id] + symbolic_iteration.size[oid]);
702 }
703 }
704 }
705
706 /*
707 compute interferences
708 */
709 void symbolic_iteration::compute_collisions(debug_t mid_step_function) {
710 if (num_object == 0) {
712 mid_step_function("compute_collisions - prepare");
713 mid_step_function("compute_collisions - insert");
714 mid_step_function("compute_collisions - finalize");
715 return;
716 }
717
718 int num_threads;
719 #pragma omp parallel
720 #pragma omp single
721 num_threads = omp_get_num_threads();
722
723 int const num_bucket = utils::nearest_power_of_two(load_balancing_bucket_per_thread*num_threads);
724 size_t const offset = 8*sizeof(size_t) - utils::log_2_upper_bound(num_bucket);
725
726 std::vector<int> load_balancing_begin(num_threads + 1, 0);
727 std::vector<size_t> partition_begin(num_bucket + 1, 0);
728
729
730
731
732
733
734 /* !!!!!!!!!!!!!!!!
735 partition
736 !!!!!!!!!!!!!!!! */
737 mid_step_function("compute_collisions - prepare");
739 &partition_begin[0], &partition_begin[num_bucket + 1],
740 [&](size_t const oid) {
741 return hash[oid] >> offset;
742 });
743
744
745
746
747
748 /* !!!!!!!!!!!!!!!!
749 load-balance
750 !!!!!!!!!!!!!!!! */
751#ifndef SKIP_CCP
752 quids::utils::load_balancing_from_prefix_sum(partition_begin.begin(), partition_begin.end(),
753 load_balancing_begin.begin(), load_balancing_begin.end());
754#else
755 for (size_t i = 0; i <= num_threads; ++i)
756 load_balancing_begin[i] = i*num_bucket/num_threads;
757#endif
758
759
760
761
762
763
764 /* !!!!!!!!!!!!!!!!
765 compute-collision
766 !!!!!!!!!!!!!!!! */
767 mid_step_function("compute_collisions - insert");
768 #pragma omp parallel
769 {
770 int thread_id = omp_get_thread_num();
771 int load_begin = load_balancing_begin[thread_id], load_end = load_balancing_begin[thread_id + 1];
772 for (int j = load_begin; j < load_end; ++j) {
773 robin_hood::unordered_map<size_t, size_t> elimination_map;
774
775 size_t begin = partition_begin[j], end = partition_begin[j + 1];
776
777 elimination_map.reserve(end - begin);
778 for (size_t i = begin; i < end; ++i) {
779 size_t oid = next_oid[i];
780
781 /* accessing key */
782 auto [it, unique] = elimination_map.insert({hash[oid], oid});
783 if (!unique) {
784 const size_t other_oid = it->second;
785
786 /* if it exist add the probabilities */
787 magnitude[other_oid] += magnitude[oid];
788 magnitude[oid] = 0;
789 }
790 }
791 }
792 }
793 mid_step_function("compute_collisions - finalize");
794
795
796
797
798
799 /* !!!!!!!!!!!!!!!!
800 partition
801 !!!!!!!!!!!!!!!! */
802 size_t* partitioned_it = __gnu_parallel::partition(&next_oid[0], &next_oid[0] + num_object,
803 [&](size_t const &oid) {
804 return std::norm(magnitude[oid]) > tolerance;
805 });
806 num_object_after_interferences = std::distance(&next_oid[0], partitioned_it);
807 }
808
809 /*
810 prepare truncate
811 */
812 void symbolic_iteration::prepare_truncate(debug_t mid_step_function) {
813 /* !!!!!!!!!!!!!!!!
814 prepare truncate
815 !!!!!!!!!!!!!!!! */
816
818 #pragma omp parallel
819 {
820 utils::random_generator rng;
821
822 #pragma omp for
823 for (size_t i = 0; i < num_object_after_interferences; ++i) {
824 size_t oid = next_oid[i];
825 random_selector[oid] = rng() / std::norm(magnitude[oid]); //-std::log(1 - rng()) / std::norm(magnitude[oid]);
826 }
827 }
828 }
829
830 /*
831 get the truncated memory size
832 */
833 size_t symbolic_iteration::get_truncated_mem_size(size_t begin_num_object) const {
834 static const size_t iteration_memory_size = ITERATION_MEMORY_SIZE;
835
836 size_t mem_size = iteration_memory_size*(next_iteration_num_object - begin_num_object);
837 for (size_t i = begin_num_object; i < next_iteration_num_object; ++i) {
838 size_t this_mem_size = size[next_oid[i]];
839 uint alignment_offset = get_alignment_offset(this_mem_size);
840 mem_size += this_mem_size + alignment_offset;
841 }
842
843 return mem_size;
844 }
845
846 /*
847 truncate
848 */
849 void symbolic_iteration::truncate(size_t begin_num_object, size_t max_num_object, debug_t mid_step_function) {
850 if (next_iteration_num_object == 0)
851 return;
852
853 /* !!!!!!!!!!!!!!!!
854 truncate
855 !!!!!!!!!!!!!!!! */
856
857 if (max_num_object >= num_object_after_interferences) {
858 next_iteration_num_object = num_object_after_interferences;
859 return;
860 }
861
862 /* delimitations */
863 auto begin = next_oid.begin() + begin_num_object;
864 auto middle = next_oid.begin() + max_num_object;
865 auto end = next_oid.begin() + next_iteration_num_object;
866
867 if (simple_truncation) {
868 /* truncation */
869 __gnu_parallel::nth_element(begin, middle, end,
870 [&](size_t const &oid1, size_t const &oid2) {
871 return std::norm(magnitude[oid1]) > std::norm(magnitude[oid2]);
872 });
873 } else {
874 /* select graphs according to random selectors */
875 __gnu_parallel::nth_element(begin, middle, end,
876 [&](size_t const &oid1, size_t const &oid2) {
877 return random_selector[oid1] < random_selector[oid2];
878 });
879 }
880
881
882 next_iteration_num_object = max_num_object;
883 }
884
885 /*
886 finalize iteration
887 */
888 void symbolic_iteration::finalize(rule_t const *rule, it_t const &last_iteration, it_t &next_iteration, debug_t mid_step_function) {
889 if (next_iteration_num_object == 0) {
890 next_iteration.num_object = 0;
891 mid_step_function("prepare_final");
892 mid_step_function("final");
893 return;
894 }
895
896
897
898
899
900
901
902
903 /* !!!!!!!!!!!!!!!!
904 prepare_final
905 !!!!!!!!!!!!!!!! */
906 mid_step_function("prepare_final");
907 next_iteration.num_object = next_iteration_num_object;
908
909 /* sort to make memory access more continuous */
910 __gnu_parallel::sort(&next_oid[0], &next_oid[0] + next_iteration.num_object);
911
912 /* resize new step variables */
913 next_iteration.resize(next_iteration.num_object);
914
915 /* prepare for partial sum */
916 #pragma omp parallel for
917 for (size_t oid = 0; oid < next_iteration.num_object; ++oid) {
918 auto id = next_oid[oid];
919
920 /* assign magnitude and size */
921 uint alignment_offset = get_alignment_offset(size[id]);
922 next_iteration.object_size[oid] = size[id];
923 next_iteration.object_begin[oid + 1] = size[id] + alignment_offset;
924 next_iteration.magnitude[oid] = magnitude[id];
925 }
926
927 __gnu_parallel::partial_sum(&next_iteration.object_begin[1],
928 &next_iteration.object_begin[1] + next_iteration.num_object + 1,
929 &next_iteration.object_begin[1]);
930
931 next_iteration.allocate(next_iteration.object_begin[next_iteration.num_object]);
932
933
934
935
936 /* !!!!!!!!!!!!!!!!
937 final
938 !!!!!!!!!!!!!!!! */
939 mid_step_function("final");
940
941 #pragma omp parallel for
942 for (size_t oid = 0; oid < next_iteration.num_object; ++oid) {
943 auto id = next_oid[oid];
944 auto this_parent_oid = parent_oid[id];
945
946 rule->populate_child_simple(&last_iteration.objects[last_iteration.object_begin[this_parent_oid]],
947 &last_iteration.objects[last_iteration.object_begin[this_parent_oid] + last_iteration.object_size[this_parent_oid]],
948 &next_iteration.objects[next_iteration.object_begin[oid]],
949 child_id[id]);
950 }
951 }
952
953 /*
954 apply modifier
955 */
956 void iteration::apply_modifier(modifier_t const rule) {
957 #pragma omp parallel for
958 for (size_t oid = 0; oid < num_object; ++oid)
959 /* generate graph */
960 rule(&objects[object_begin[oid]],
961 &objects[object_begin[oid] + object_size[oid]],
962 magnitude[oid]);
963 }
964
965 /*
966 normalize
967 */
968 void iteration::normalize(debug_t mid_step_function) {
969 total_proba = 0;
970
971 if (num_object == 0) {
972 mid_step_function("normalize");
973 mid_step_function("end");
974 return;
975 }
976
977
978
979
980 /* !!!!!!!!!!!!!!!!
981 normalize
982 !!!!!!!!!!!!!!!! */
983 mid_step_function("normalize");
984
985 #pragma omp parallel
986 {
987 #pragma omp for reduction(+:total_proba)
988 for (size_t oid = 0; oid < num_object; ++oid)
989 total_proba += std::norm(magnitude[oid]);
990
991 PROBA_TYPE normalization_factor = std::sqrt(total_proba);
992
993 if (normalization_factor != 1)
994 #pragma omp for
995 for (size_t oid = 0; oid < num_object; ++oid)
996 magnitude[oid] /= normalization_factor;
997 }
998
999 mid_step_function("end");
1000 }
1001}
iteration (wave function) representation class
Definition: quids.hpp:144
friend void simulate(it_t &iteration, rule_t const *rule, it_t &next_iteration, sy_it_t &symbolic_iteration, size_t max_num_object, debug_t mid_step_function)
function to apply a dynamic to a wavefunction
Definition: quids.hpp:443
void get_object(size_t const object_id, char *&object_begin_, uint &object_size_, mag_t *&mag)
function to access a particular object (read-write access)
Definition: quids.hpp:237
iteration(char *object_begin_, char *object_end_)
constructor that insert a single object with magnitude 1
Definition: quids.hpp:161
void get_object(size_t const object_id, char const *&object_begin_, uint &object_size_, mag_t &mag) const
function to access a particular object (read-only access)
Definition: quids.hpp:249
PROBA_TYPE average_value(const observable_t observable) const
function to get the average value of a custom observable
Definition: quids.hpp:203
iteration()
simple empty wavefunction constructor
Definition: quids.hpp:152
void pop(size_t n=1, bool normalize_=true)
function that removes a given number of object from the "tail" of the memory representation
Definition: quids.hpp:189
void append(char const *object_begin_, char const *object_end_, mag_t const mag=1)
function that insert a single object with a given magnitude
Definition: quids.hpp:169
PROBA_TYPE total_proba
total probability retained after previous truncation (if any).
Definition: quids.hpp:149
size_t num_object
number of objects contained in the wave function
Definition: quids.hpp:147
class represneting a dynamic (or rule).
Definition: quids.hpp:100
virtual void populate_child_simple(char const *parent_begin, char const *parent_end, char *const child_begin, uint const child_id) const
function generating a child, without computing its magnitude and size.
Definition: quids.hpp:126
virtual void populate_child(char const *parent_begin, char const *parent_end, char *const child_begin, uint const child_id, uint &size, mag_t &mag) const =0
function generating a child, and computing its magnitude and size.
rule()
base constructor
Definition: quids.hpp:103
virtual void get_num_child(char const *parent_begin, char const *parent_end, uint &num_child, uint &max_child_size) const =0
function geting the number of children
virtual size_t hasher(char const *object_begin, char const *object_end) const
optional function hashing an object.
Definition: quids.hpp:138
symbolic iteration (computation intermediary)
Definition: quids.hpp:333
friend void simulate(it_t &iteration, rule_t const *rule, it_t &next_iteration, sy_it_t &symbolic_iteration, size_t max_num_object, debug_t mid_step_function)
function to apply a dynamic to a wavefunction
Definition: quids.hpp:443
size_t num_object_after_interferences
number of objects obrained after eliminating duplicates
Definition: quids.hpp:341
symbolic_iteration()
simple constructor
Definition: quids.hpp:336
size_t num_object
number of objects considered in the symbolic step
Definition: quids.hpp:339
void resize(const Int n_, const uint align_byte_length_=std::alignment_of< T >()) const
align_byte_length_ should be used to reallign the buffer, which is not yet implemented as realloc doe...
Definition: vector.hpp:111
void load_balancing_from_prefix_sum(const UnsignedIntIterator1 prefixSumLoadBegin, const UnsignedIntIterator1 prefixSumLoadEnd, UnsignedIntIterator2 workSharingIndexesBegin, UnsignedIntIterator2 workSharingIndexesEnd)
simple CCP load balacing implementation.
Definition: load_balancing.hpp:14
int log_2_upper_bound(int n)
returns the upperbound bound of the log2
Definition: algorithm.hpp:16
int nearest_power_of_two(int n)
returns the upper bound as a power of two
Definition: algorithm.hpp:9
size_t get_free_mem()
function that get the total amount of available free memory.
Definition: memory.hpp:47
void parallel_generalized_partition_from_iota(idIteratorType idx_in, idIteratorType idx_in_end, long long int const iotaOffset, countIteratorType offset, countIteratorType offset_end, functionType const partitioner)
linear partitioning algorithm into n partitions without an initialized index list in parallel
Definition: algorithm.hpp:170
void parallel_iota(iteratorType begin, iteratorType end, const valueType value_begin)
parallel iota
Definition: algorithm.hpp:24
QuIDS namespace.
Definition: quids.hpp:55
void simulate(it_t &iteration, modifier_t const rule)
function to apply a modifer to a wave function
Definition: quids.hpp:431
std::complex< PROBA_TYPE > mag_t
complex magnitude type
Definition: quids.hpp:73
class symbolic_iteration sy_it_t
symbolic iteration type
Definition: quids.hpp:77
std::function< void(const char *step)> debug_t
debuging function type
Definition: quids.hpp:85
class iteration it_t
iteration class type
Definition: quids.hpp:75
class rule rule_t
dynamic (or rule) type
Definition: quids.hpp:79
int load_balancing_bucket_per_thread
number of load balancing buckets per thread
Definition: quids.hpp:63
std::function< PROBA_TYPE(char const *object_begin, char const *object_end)> observable_t
observable definition typedef
Definition: quids.hpp:83
float safety_margin
memory safety margin (0.2 = 80% memory usage target)
Definition: quids.hpp:61
PROBA_TYPE tolerance
tolerance for objects (remove objects with a smaller probability)
Definition: quids.hpp:59
uint align_byte_length
amount of byte to align objects to
Definition: quids.hpp:57
uint get_alignment_offset(const uint size)
utility function to get the alignment offset to put at the end of an object of size "size"
Definition: quids.hpp:88
bool simple_truncation
simple truncation toggle - disable probabilistic truncation, increasing "accuracy" but reducing the r...
Definition: quids.hpp:69
std::function< void(char *parent_begin, char *parent_end, mag_t &mag)> modifier_t
simple "modifier" type (single input, single output of same size dynamic)
Definition: quids.hpp:81