The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
probabilities.c
1 /*
2  * topology_probabilities.c
3  *
4  * Created on: 02 ago 2018
5  * Author: andrea
6  */
7 
8 /*
9  * topology_custom.c
10  *
11  * Created on: 24 mag 2018
12  * Author: andrea
13  */
14 
15 #include <ROOT-Sim.h>
16 #include <lib/topology.h>
17 
18 #include <scheduler/scheduler.h>
19 #include <lib/numerical.h>
20 #include <datatypes/bitmap.h>
21 #include <scheduler/process.h>
22 
23 typedef struct _topology_t {
24  unsigned neighbours_id[6];
25  bool dirty;
26  double data[];
27 } topology_t;
28 
29 unsigned size_checkpoint_probabilities(void){
30  return sizeof(topology_t) + // the basic struct size
31  sizeof(double) * (topology_global.directions + 1) + // the row of exit probabilities weights
32  sizeof(double); // the cache of the sum of probabilities weights
33 }
34 
35 void *load_topology_file_probabilities(c_jsmntok_t *root_token, const char *json_base){
36  unsigned i;
37  c_jsmntok_t *aux_tok;
38  const unsigned lp_cnt = topology_global.lp_cnt;
39 
40  // number of possible exit regions for this region, we add 1 to take in consideration the self loop
41  const unsigned exit_regions = topology_global.directions + 1;
42 
43  // retrieve the values array
44  c_jsmntok_t *values_tok = get_value_token_by_key(root_token, json_base, root_token, "values");
45  if(!values_tok|| values_tok->type != JSMN_ARRAY || children_count_token(root_token, values_tok) != lp_cnt)
46  rootsim_error(true, "Invalid or missing json value with key \"values\"");
47 
48  // instantiates the array
49  double *ret_data = rsalloc(sizeof(double) * exit_regions * lp_cnt);
50 
51  // we get the array of tokens we are interested in
52  for(i = 0; i < lp_cnt; ++i){
53  aux_tok = get_at_token(root_token, values_tok, i);
54 
55  // we parse the array
56  if(!aux_tok || parse_double_array(root_token, json_base, aux_tok, exit_regions, &ret_data[i * exit_regions]) < 0)
57  rootsim_error(true, "Invalid or missing value in the array of probabilities for this region");
58  }
59 
60  // sanity check on the values of the probability weights
61  for (i = 0; i < exit_regions * lp_cnt; ++i) {
62  if(ret_data[i] < 0)
63  rootsim_error(true, "Found a negative probability weight in the topology file!");
64  }
65  return ret_data;
66 }
67 
68 
69 topology_t *topology_probabilities_init(unsigned this_region_id, void *topology_data){
70  // get number of possible exit regions for this region, we add 1 to take in consideration the self loop
71  const unsigned exit_regions = topology_global.directions + 1;
72  unsigned i;
73  // instantiate the topology struct
74  topology_t *topology = rsalloc(topology_global.chkp_size);
75 
76  if(topology_data)
77  memcpy(topology->data, ((char *) topology_data) + this_region_id * exit_regions, sizeof(double) * exit_regions);
78  else{
79  // most models assume that you don't select the region you are from
80  topology->data[0] = 0.0;
81  for(i = 1; i < exit_regions; ++i)
82  topology->data[i] = 1.0;
83  }
84 
85  if(topology_global.geometry != TOPOLOGY_GRAPH){
86  i = topology_global.directions;
87  while(i--)
88  topology->neighbours_id[i] = get_raw_receiver(this_region_id, i);
89  }
90 
91  topology->dirty = true;
92  return topology;
93 }
94 
95 
96 static void refresh_cache_probabilities(topology_t *topology){
97  if(topology->dirty){
98  // +1: remember we hold also the probability of self loop
99  const unsigned exit_regions = topology_global.directions + 1;
100  topology->data[exit_regions] = NeumaierSum(exit_regions, topology->data);
101  topology->dirty = false;
102  }
103 }
104 
105 struct update_topology_t{
106  unsigned loc_i;
107  double val;
108 };
109 
110 void set_value_topology_probabilities(unsigned from, unsigned to, double value) {
111  topology_t *topology = current->topology;
112  if(current->gid.to_int == from){
113  topology->data[to] = value;
114  topology->dirty = true;
115  }else {
116  struct update_topology_t upd = {to, value};
117  UncheckedScheduleNewEvent(from, current_evt->timestamp, TOPOLOGY_UPDATE, &upd, sizeof(upd));
118  }
119 }
120 
121 bool is_reachable_probabilities(unsigned to){
122  topology_t *topology = current->topology;
123  unsigned i = topology_global.directions;
124  switch (topology_global.geometry) {
125  case TOPOLOGY_HEXAGON:
126  case TOPOLOGY_SQUARE:
127  case TOPOLOGY_TORUS:
128  case TOPOLOGY_BIDRING:
129  while(i--){
130  if(topology->neighbours_id[i] == to){
131  if(topology->data[i] > 0)
132  return true;
133  break;
134  }
135  }
136  break;
137 
138  case TOPOLOGY_RING:
139  rootsim_error(true, "Topology ring still not supported!");
140  break;
141 
142  case TOPOLOGY_GRAPH:
143  return topology->data[i] > 0;
144 
145  default:
146  rootsim_error(true, "This shouldn't happen, report to maintainer");
147 
148  }
149 
150  return false;
151 }
152 
153 void update_topology_probabilities(void){
154  topology_t *topology = current->topology;
155  struct update_topology_t *upd_p = (struct update_topology_t *)current_evt->event_content;
156  if(topology->data[upd_p->loc_i] > upd_p->val || topology->data[upd_p->loc_i] < upd_p->val){
157  topology->data[upd_p->loc_i] = upd_p->val;
158  topology->dirty = true;
159  }
160 }
161 
162 double get_value_topology_probabilities(unsigned from, unsigned to) {
163  if(current->gid.to_int == from){
164  return current->topology->data[to];
165  }else {
166  // TODO remote retrieve value!!
167  // this could be real tricky and costly to implement!!
168  rootsim_error(true, "getting a remote probability value is still not supported... !");
169  }
170 
171  return 0.0;
172 }
173 
174 // this code right here is used multiple times:
175 // it randomly chooses a direction weighted on the probabilities values
176 // if 0 is choosen it means we decided to stay where we were and so we set receiver
177 // and we exit else we continue processing. Notice that the direction value has different
178 // meanings in different geometries. FIXME with better approach, this one uses too many calls to random()
179 #define select_direction() \
180  do{\
181  direction = exit_regions * Random();\
182  }while(Random() * sum >= topology->data[direction]);\
183  if(!direction) \
184  return sender; \
185  direction--;
186 
187 unsigned int find_receiver_probabilities(void) {
188  topology_t *topology = current->topology;
189  unsigned int direction, receiver = DIRECTION_INVALID;
190  const unsigned sender = current->gid.to_int;
191  double sum;
192 
193  refresh_cache_probabilities(topology);
194 
195  // we sum 1 to take into account the possibility to stay where we are
196  const unsigned exit_regions = topology_global.directions + 1;
197  // the sum has been computed during refresh_cache if needed.
198  sum = topology->data[exit_regions];
199  if(sum <= 0)
200  return DIRECTION_INVALID;
201 
202  switch (topology_global.geometry) {
203 
204  case TOPOLOGY_HEXAGON:
205  case TOPOLOGY_SQUARE:
206  // Find a random neighbour
207  do {
208  select_direction();
209 
210  receiver = topology->neighbours_id[direction];
211  // we simply repeat this until we either select ourselves or a valid neighbour
212  } while (receiver == DIRECTION_INVALID);
213  break;
214 
215  case TOPOLOGY_TORUS:
216  case TOPOLOGY_BIDRING:
217  case TOPOLOGY_RING:
218  select_direction();
219 
220  receiver = topology->neighbours_id[direction];
221  break;
222 
223  case TOPOLOGY_GRAPH:
224  // here we don't use the select_direction() macro because we don't map direction 0
225  // to the region we occupy because this way we simplify the logic.
226  do {
227  direction = exit_regions * Random();
228  } while (Random() * sum >= topology->data[direction]);
229 
230  //receiver = GetReceiver(topology, sender, direction);
231  receiver = direction;
232  break;
233 
234  case TOPOLOGY_STAR:
235  select_direction();
236 
237  if(sender == 0) {
238  receiver = direction;
239  } else {
240  receiver = 0;
241  }
242  break;
243 
244  default:
245  rootsim_error(true, "Wrong topology code specified: %d. Aborting...\n", topology);
246  }
247 
248  return receiver;
249 }
250 
251 #undef select_direction
252 
unsigned neighbours_id[6]
Definition: obstacles.c:22
Definition: jsmn.h:43
enum _topology_geometry_t geometry
Definition: topology.h:22
A generic invalid direction.
Definition: ROOT-Sim.h:122
square grid topology
Definition: ROOT-Sim.h:91
ROOT-Sim header for model development.
unsigned chkp_size
Definition: topology.h:18
unsigned int to_int
The GID numerical value.
Definition: core.h:133
unsigned lp_cnt
Definition: topology.h:21
The ROOT-Sim scheduler main module header.
a torus shaped grid topology (a wrapping around square topology)
Definition: ROOT-Sim.h:94
__thread struct lp_struct * current
This is a per-thread variable pointing to the block state of the LP currently scheduled.
Definition: scheduler.c:72
double data[]
a pointer to the cache used to speedup queries on paths (unused in probabilities topology type) ...
Definition: costs.c:25
Numerical Library.
hexagonal grid topology
Definition: ROOT-Sim.h:90
__thread msg_t * current_evt
Definition: scheduler.c:83
topology_t * topology
pointer to the topology struct
Definition: process.h:152
unsigned directions
Definition: topology.h:19
LP control blocks.
union _double_bits_trick upd
where to put the new value
Definition: costs.c:186
an arbitrary shaped topology
Definition: ROOT-Sim.h:96
a ring shaped topology direction
Definition: ROOT-Sim.h:93
GID_t gid
Global ID of the LP.
Definition: process.h:82
bool dirty
Definition: costs.c:23
Bitmap data type.
double NeumaierSum(unsigned cnt, double addendums[cnt])
Definition: numerical.c:432
a ring shaped topology walkable in a single direction
Definition: ROOT-Sim.h:92
double Random(void)
Definition: numerical.c:80
the customised struct for TOPOLOGY_OBSTACLES representation
Definition: costs.c:22