RockyML  0.0.1
A High-Performance Scientific Computing Framework
scontainer.h
1 /*
2  Copyright (C) 2022 Amirabbas Asadi , All Rights Reserved
3  distributed under Apache-2.0 license
4 */
5 #ifndef ROCKY_ZAGROS_SCONTAINER_GUARD
6 #define ROCKY_ZAGROS_SCONTAINER_GUARD
7 
8 
9 #include<iostream>
10 #include<cmath>
11 #include<utility>
12 #include<memory>
13 #include<limits>
14 #include<random>
15 #include<vector>
16 #include<set>
17 
18 #include<tbb/tbb.h>
19 #include<Eigen/Core>
20 
21 #include<rocky/zagros/system.h>
22 #include<rocky/utils.h>
23 
24 namespace rocky{
25 namespace zagros{
30 template<typename T_e, int T_dim>
32 protected:
33  int n_particles_;
34  int group_size_;
35 public:
36  basic_scontainer(int n_particles, int group_size){
37  n_particles_ = n_particles;
38  group_size_ = group_size;
39  }
40  int n_particles() const{
41  return n_particles_;
42  }
43  int group_size() const{
44  return group_size_;
45  }
46  int n_groups() const{
47  return n_particles() / group_size();
48  }
49  // holding particles
50  std::vector<std::vector<T_e>> particles;
51  // holding the particles value
52  std::vector<T_e> values;
53  void reset_values(){
54  // initialize particles value
55  std::fill(values.begin(), values.end(), std::numeric_limits<T_e>::max());
56  }
57  // allocate the requred memory
58  void allocate(){
59  particles.resize(n_particles());
60  for(int p=0; p<n_particles(); ++p)
61  particles[p].resize(T_dim);
62  values.resize(n_particles());
63  reset_values();
64  }
71  T_e* particle(int p){
72  return particles[p].data();
73  }
80  int particle_group(int p){
81  return p / group_size();
82  }
89  T_e* group(int g){
90  return particles[g * group_size()].data();
91  }
98  std::pair<int, int> group_range(int g) const{
99  int group_s = g * group_size();
100  int group_e = group_s + group_size();
101  if (g == n_groups() - 1)
102  group_e = n_particles();
103  return std::make_pair(group_s, group_e);
104  }
110  T_e* value(int p){
111  return &values[p];
112  }
119  void sample_n_particles(int* indices, int n=1){
120  auto weighted_dist = weighted_sampler();
121  std::set<int> indices_set;
122  int max_iters = 10 * n;
123  int iters = 0;
124  do{
125  indices_set.insert(weighted_dist(rocky::utils::random::prng()));
126  iters++;
127  } while((indices_set.size() < n) && (iters < max_iters));
128 
129  if(indices_set.size() < n){
130  std::uniform_int_distribution uniform_dist(0, n_particles()-1);
131  do{
132  indices_set.insert(uniform_dist(rocky::utils::random::prng()));
133  }while(indices_set.size() < n);
134  }
135  int i = 0;
136  for(auto index: indices_set)
137  indices[i++] = index;
138  }
145  std::pair<int, int> sample_pair(int group){
146  auto indices = sample_n_particles(2, group);
147  auto el = indices.begin();
148  auto result = std::make_pair(*el, *(std::next(el)));
149  return result;
150  }
158  auto group_rng = group_range(group);
159  std::uniform_int_distribution<> dist(group_rng.first, group_rng.second-1);
160  return dist(rocky::utils::random::prng());
161  }
167  int sample_dim(){
168  std::uniform_int_distribution<> dist(0, T_dim-1);
169  return dist(rocky::utils::random::prng());
170  }
175  size_t space() const{
176  return sizeof(T_e) * (n_particles() * (T_dim + 1));
177  }
182  T_e best_min(){
183  T_e best = *std::min_element(values.begin(), values.end());
184  return best;
185  }
191  std::pair<T_e, int> best_min_index(){
192  auto min_el = std::min_element(values.begin(), values.end());
193  int index = static_cast<int>(min_el - values.begin());
194  T_e best = *min_el;
195  return std::make_pair(best, index);
196  }
205  void evaluate_and_update(system<T_e>* problem, int rng_start, int rng_end){
206  tbb::parallel_for(rng_start, rng_end, [&](int p){
207  T_e obj = problem->objective(this->particle(p));
208  this->values[p] = obj;
209  });
210  }
218  void evaluate_and_update(system<T_e>* problem, int p){
219  evaluate_and_update(problem, p, p+1);
220  }
228  evaluate_and_update(problem, 0, n_particles());
229  }
236  void best_k(int* indices, int k){
237  auto comp_values = [this](int x, int y){
238  return this->values[x] < this->values[y];
239  };
240  std::vector<int> all_ind(n_particles());
241  std::iota(all_ind.begin(), all_ind.end(), 0);
242  std::sort(all_ind.begin(), all_ind.end(), comp_values);
243  std::copy(all_ind.data(), all_ind.data()+k, indices);
244  }
251  void worst_k(int* indices, int k){
252  auto comp_values = [this](int x, int y){
253  return this->values[x] > this->values[y];
254  };
255  std::vector<int> all_ind(n_particles());
256  std::iota(all_ind.begin(), all_ind.end(), 0);
257  std::sort(all_ind.begin(), all_ind.end(), comp_values);
258  std::copy(all_ind.data(), all_ind.data()+k, indices);
259  }
267  // sort the solutions in both containers
268  std::vector<int> src_ind(cnt->n_particles());
269  std::vector<int> des_ind(n_particles());
270  cnt->best_k(src_ind.data(), cnt->n_particles());
271  worst_k(des_ind.data(), n_particles());
272  int src_i=0, des_i=0;
273  T_e src_b, des_w;
274  while((src_i < cnt->n_particles()) && (des_i < n_particles())){
275  // repeat while the best solution in source is better than the worst in destination
276  src_b = cnt->values[src_ind[src_i]];
277  des_w = values[des_ind[des_i]];
278  if(des_w <= src_b)
279  break;
280 
281  std::copy(cnt->particles[src_ind[src_i]].begin(),
282  cnt->particles[src_ind[src_i]].end(),
283  particles[des_ind[des_i]].begin());
284 
285  values[des_ind[des_i]] = cnt->values[src_ind[src_i]];
286  src_i++;
287  des_i++;
288  }
289  }
295  std::discrete_distribution<int> weighted_sampler(){
296  std::vector<T_e> weights;
297  T_e max_el = *std::max_element(values.begin(), values.end());
298  if(max_el == std::numeric_limits<T_e>::max())
299  weights.assign(values.size(), 1.0);
300  else{
301  T_e min_el = *std::min_element(values.begin(), values.end());
302  T_e shift = 0.0001;
303  if (min_el < 0.0)
304  shift += -min_el;
305  max_el += shift;
306  weights.resize(n_particles());
307  for(int p=0; p<n_particles(); p++)
308  weights[p] = max_el - (shift + values[p]);
309  }
310  // construct a distribution for weighted sampling
311  std::discrete_distribution<int> sampling_dist(weights.begin(), weights.end());
312  return sampling_dist;
313  }
314 };
315 
316 }; // end of zagros namespace
317 }; // end of rocky namespace
318 #endif
rocky::zagros::basic_scontainer::group_range
std::pair< int, int > group_range(int g) const
starting and endind point of a group
Definition: scontainer.h:98
rocky::zagros::basic_scontainer::particle_group
int particle_group(int p)
get the group of a particle
Definition: scontainer.h:80
rocky::zagros::basic_scontainer::best_min_index
std::pair< T_e, int > best_min_index()
find the best solution and the corresponding index in the container
Definition: scontainer.h:191
rocky::zagros::basic_scontainer::group
T_e * group(int g)
get the address to the starting point of a group
Definition: scontainer.h:89
rocky::zagros::basic_scontainer::value
T_e * value(int p)
pointer to the value of a particle
Definition: scontainer.h:110
rocky::zagros::basic_scontainer::sample_particle
int sample_particle(int group)
sample a single particle from a group
Definition: scontainer.h:157
rocky::zagros::basic_scontainer::worst_k
void worst_k(int *indices, int k)
find worst-k solutions and fill the indices
Definition: scontainer.h:251
rocky::zagros::basic_scontainer::weighted_sampler
std::discrete_distribution< int > weighted_sampler()
weighted particle sampling
Definition: scontainer.h:295
rocky::zagros::basic_scontainer::evaluate_and_update
void evaluate_and_update(system< T_e > *problem)
evaluate and update all particles
Definition: scontainer.h:227
rocky::zagros::basic_scontainer::sample_n_particles
void sample_n_particles(int *indices, int n=1)
sample n distinct particles from a group efficient when n is small
Definition: scontainer.h:119
rocky::zagros::basic_scontainer::space
size_t space() const
amount of allocated memory in bytes
Definition: scontainer.h:175
rocky::zagros::basic_scontainer::best_k
void best_k(int *indices, int k)
find top-k solutions and fill the indices
Definition: scontainer.h:236
rocky::zagros::basic_scontainer::replace_with
void replace_with(basic_scontainer< T_e, T_dim > *cnt)
replace the best values from another container
Definition: scontainer.h:266
rocky::zagros::basic_scontainer::sample_dim
int sample_dim()
choose a dimension randomly
Definition: scontainer.h:167
rocky::zagros::basic_scontainer::best_min
T_e best_min()
find the best solution in the container
Definition: scontainer.h:182
rocky::zagros::basic_scontainer
a data container representing a scontainer
Definition: scontainer.h:31
rocky::zagros::basic_scontainer::evaluate_and_update
void evaluate_and_update(system< T_e > *problem, int p)
evaluate and update a single particle
Definition: scontainer.h:218
rocky::zagros::system
Definition: system.h:20
rocky::zagros::basic_scontainer::evaluate_and_update
void evaluate_and_update(system< T_e > *problem, int rng_start, int rng_end)
evaluate and update the particles within a range
Definition: scontainer.h:205
rocky::zagros::basic_scontainer::particle
T_e * particle(int p)
get the starting address of a specific particle
Definition: scontainer.h:71
rocky::zagros::basic_scontainer::sample_pair
std::pair< int, int > sample_pair(int group)
sample a pair of distinct particles
Definition: scontainer.h:145