aGrUM  0.16.0
simplicialSet.cpp
Go to the documentation of this file.
1 
28 #include <agrum/core/math/math.h>
31 #include <limits>
32 #include <limits>
33 #include <sstream>
34 #include <sstream>
35 
36 #ifdef GUM_NO_INLINE
38 #endif // GUM_NO_INLINE
39 
40 #ifndef DOXYGEN_SHOULD_SKIP_THIS
41 
42 namespace gum {
43 
44  /* ===========================================================================
45  */
46  /* ===========================================================================
47  */
48  /* === CLASS FOR RETRIEVING SIMPLICIAL, ALMOST AND QUASI SIMPLICIAL NODES ===
49  */
50  /* ===========================================================================
51  */
52  /* ===========================================================================
53  */
54 
56  SimplicialSet::SimplicialSet(UndiGraph* graph,
57  const NodeProperty< double >* log_domain_sizes,
58  NodeProperty< double >* log_weights,
59  double theRatio,
60  double theThreshold) :
61  __graph(graph != nullptr
62  ? graph
63  : GUM_ERROR_IN_EXPR(OperationNotAllowed,
64  "SimplicialSet requires a valid graph")),
65  __log_weights(log_weights != nullptr
66  ? log_weights
67  : GUM_ERROR_IN_EXPR(OperationNotAllowed,
68  "SimplicialSet "
69  "requires non-null log weights")),
70  __log_domain_sizes(log_domain_sizes != nullptr
71  ? log_domain_sizes
72  : GUM_ERROR_IN_EXPR(OperationNotAllowed,
73  "SimplicialSet "
74  "requires non-null domain sizes")),
75  __simplicial_nodes(std::less< double >(), __graph->size()),
76  __almost_simplicial_nodes(std::less< double >(), __graph->size()),
77  __quasi_simplicial_nodes(std::less< double >(), __graph->size()),
78  __containing_list(__graph->size()),
79  __nb_triangles(__graph->size() * __graph->size() / 2),
80  __nb_adjacent_neighbours(__graph->size()), __quasi_ratio(theRatio),
81  __log_threshold(std::log(1 + theThreshold)) {
82  if (graph != nullptr) {
83  // for debugging purposes
84  GUM_CONSTRUCTOR(SimplicialSet);
85  }
86 
87  // end of initialization: compute __nb_triangles, __nb_adjacent_neighbours,
88  // etc
89  __initialize();
90  }
91 
93  SimplicialSet::SimplicialSet(const SimplicialSet& from,
94  UndiGraph* graph,
95  const NodeProperty< double >* log_domain_sizes,
96  NodeProperty< double >* log_weights,
97  bool avoid_check) :
98  __graph(graph != nullptr
99  ? graph
100  : GUM_ERROR_IN_EXPR(OperationNotAllowed,
101  "SimplicialSet requires a valid graph")),
102  __log_weights(log_weights != nullptr
103  ? log_weights
104  : GUM_ERROR_IN_EXPR(OperationNotAllowed,
105  "SimplicialSet "
106  "requires non-null log weights")),
107  __log_domain_sizes(
108  log_domain_sizes != nullptr
109  ? log_domain_sizes
110  : GUM_ERROR_IN_EXPR(OperationNotAllowed,
111  "SimplicialSet "
112  "requires non-null domain sizes")) {
113  if (!avoid_check) {
114  // check whether the graph, the log weights and the log_modalities
115  // are similar to those of from
116  if ((__graph == from.__graph) || (__log_weights == from.__log_weights)
117  || (*__graph != *from.__graph)
118  || (*__log_domain_sizes != *from.__log_domain_sizes)) {
119  GUM_ERROR(OperationNotAllowed,
120  "SimplicialSet requires fresh copies of "
121  "graph, log weights and log domain sizes");
122  }
123  }
124 
125  // copy the current content of from
126  *__log_weights = *from.__log_weights;
127  __simplicial_nodes = from.__simplicial_nodes;
128  __almost_simplicial_nodes = from.__almost_simplicial_nodes;
129  __quasi_simplicial_nodes = from.__quasi_simplicial_nodes;
130  __containing_list = from.__containing_list;
131  __nb_triangles = from.__nb_triangles;
132  __nb_adjacent_neighbours = from.__nb_adjacent_neighbours;
133  __log_tree_width = from.__log_tree_width;
134  __quasi_ratio = from.__quasi_ratio;
135  __log_threshold = from.__log_threshold;
136  __changed_status = from.__changed_status;
137  __we_want_fill_ins = from.__we_want_fill_ins;
138  __fill_ins_list = from.__fill_ins_list;
139 
140  // for debugging purposes
141  GUM_CONS_CPY(SimplicialSet);
142  }
143 
145  SimplicialSet::SimplicialSet(SimplicialSet&& from) :
146  __graph(from.__graph), __log_weights(from.__log_weights),
147  __log_domain_sizes(from.__log_domain_sizes),
148  __simplicial_nodes(std::move(from.__simplicial_nodes)),
149  __almost_simplicial_nodes(std::move(from.__almost_simplicial_nodes)),
150  __quasi_simplicial_nodes(std::move(from.__quasi_simplicial_nodes)),
151  __containing_list(std::move(from.__containing_list)),
152  __nb_triangles(std::move(from.__nb_triangles)),
153  __nb_adjacent_neighbours(std::move(from.__nb_adjacent_neighbours)),
154  __log_tree_width(from.__log_tree_width), __quasi_ratio(from.__quasi_ratio),
155  __log_threshold(from.__log_threshold),
156  __changed_status(std::move(from.__changed_status)),
157  __we_want_fill_ins(from.__we_want_fill_ins),
158  __fill_ins_list(std::move(from.__fill_ins_list)) {
159  // for debugging purposes
160  GUM_CONS_MOV(SimplicialSet);
161  }
162 
165  // for debugging purposes
166  GUM_DESTRUCTOR(SimplicialSet);
167  }
168 
170  void SimplicialSet::makeClique(const NodeId id) {
171  // first, update the list to which belongs the node
172  if (__changed_status.contains(id)) __updateList(id);
173 
174  // to make id be a clique, we may have to add edges. Hence, this will create
175  // new triangles and we should update the number of triangles passing
176  // through the new edges. Moreover, we should also update the number of
177  // adjacent neighbors for each node involved in these new triangles as well
178  // as the new weights of the nodes. Finally, we should update the
179  // simplicial,
180  // almost and quasi simplicial lists. However, to optimize the code, we use
181  // a lazy list update: we just update a hashtable indicating whether a list
182  // update should be performed for a given node. This enables performing the
183  // updates only when necessary. if the node is known to be simplicial, there
184  // is nothing to do
185  if (__simplicial_nodes.contains(id)) {
186  return;
187  } else if (__almost_simplicial_nodes.contains(id)) {
188  // get the neighbor that does not form a clique with the other neighbors
189  // recall that id is an almost simplicial node if there exists a node,
190  // say Y, such that, after deleting Y, id and its adjacent nodes form a
191  // clique.
192  const NodeSet& nei = __graph->neighbours(id);
193  Size nb_adj = nei.size();
194  Size nb = __nb_adjacent_neighbours[id];
195 
196  // nb_almost = the number of edges that should link the neighbors of
197  // node id, after node Y mentioned above has been removed. Recall that
198  // these neighbors and id form a clique. Hence this corresponds to the
199  // number of triangles involving id and 2 of its neighbors, after node
200  // Y has been removed.
201  Size nb_almost = ((nb_adj - 1) * (nb_adj - 2)) / 2;
202  NodeId node1 = 0;
203 
204  for (const auto current_node : nei) {
205  if (nb_almost == nb - __nb_triangles[Edge(current_node, id)]) {
206  // we found the neighbor we were looking for: nb = the number of
207  // pairs of neighbors of id that are adjacent. In other words, this
208  // is the number of triangles involving node id. Now remove from it
209  // the
210  // triangles involving edge (id,node1), and you get the number of
211  // triangles involving id, but not node1. If id is almost simplicial,
212  // then this number should be equal to the set of combinations of
213  // all the possible pairs of neighbors of id except node1, hence
214  // to nb_almost.
215  node1 = current_node;
216  break;
217  }
218  }
219 
220  double log_domain_size_node1 = (*__log_domain_sizes)[node1];
221  double& __log_weights_node1 = (*__log_weights)[node1];
222 
223  // now, to make a clique between id and its neighbors, there just remains
224  // to add the missing edges between node1 and the other neighbors of id.
225 
226  // nb_n1 will contain the number of pairs of neighbors of node1 that
227  // will be adjacent after the clique is constructed but that
228  // are not yet adjacent
229  unsigned int nb_n1 = 0;
230 
231  // update the number of triangles of the edges and keep track of the
232  // nodes involved.
233  for (const auto node2 : nei) {
234  if ((node2 != node1) && !__graph->existsEdge(node1, node2)) {
235  // add the edge
236  const Edge e1_2(node1, node2);
237  __graph->addEdge(node1, node2);
238 
239  if (__we_want_fill_ins) __fill_ins_list.insert(e1_2);
240 
241  if (!__changed_status.contains(node2)) __changed_status.insert(node2);
242 
243  __log_weights_node1 += (*__log_domain_sizes)[node2];
244  (*__log_weights)[node2] += log_domain_size_node1;
245  __nb_triangles.insert(e1_2, 0);
246 
247  // nb_n2 will contain the number of pairs of neighbors of node2 that
248  // will be adjacent after the clique is constructed but that
249  // are not yet adjacent
250  unsigned int nb_n2 = 0;
251 
252  // for all common neighbors of node1 and node2, update the number of
253  // triangles as well as the number of adjacent neighbors
254  if (__graph->neighbours(node1).size()
255  <= __graph->neighbours(node2).size()) {
256  for (const auto neighbor : __graph->neighbours(node1)) {
257  if (__graph->existsEdge(neighbor, node2)) {
258  // here iter->other (node1) is a neighbor of node1 and node2
259  // hence there is a new triangle to be taken into account:
260  // that between node1, node2 and iterEdge->other( node1 )
261  ++nb_n1;
262  ++nb_n2;
263  ++__nb_adjacent_neighbours[neighbor];
264  ++__nb_triangles[Edge(node1, neighbor)];
265  ++__nb_triangles[Edge(node2, neighbor)];
266 
267  if (!__changed_status.contains(neighbor))
268  __changed_status.insert(neighbor);
269  }
270  }
271  } else {
272  for (const auto neighbor : __graph->neighbours(node2)) {
273  if (__graph->existsEdge(neighbor, node1)) {
274  // here iter->other (node2) is a neighbor of node1 and node2
275  // hence there is a new triangle to be taken into account:
276  // that between node1, node2 and iterEdge->other( node2 )
277  ++nb_n1;
278  ++nb_n2;
279  ++__nb_adjacent_neighbours[neighbor];
280  ++__nb_triangles[Edge(node2, neighbor)];
281  ++__nb_triangles[Edge(node1, neighbor)];
282 
283  if (!__changed_status.contains(neighbor))
284  __changed_status.insert(neighbor);
285  }
286  }
287  }
288 
289  __nb_adjacent_neighbours[node2] += nb_n2;
290  __nb_triangles[e1_2] += nb_n2;
291  }
292  }
293 
294  if (!__changed_status.contains(node1)) __changed_status.insert(node1);
295 
296  __nb_adjacent_neighbours[node1] += nb_n1;
297  } else {
298  // here, id is neither a simplicial node nor an almost simplicial node
299 
300  // update the number of triangles of the edges and keep track of the
301  // nodes involved.
302  const NodeSet& nei = __graph->neighbours(id);
303 
304  for (auto iter1 = nei.begin(); iter1 != nei.end(); ++iter1) {
305  NodeId node1 = *iter1;
306  double log_domain_size_node1 = (*__log_domain_sizes)[node1];
307  double& __log_weights_node1 = (*__log_weights)[node1];
308  bool node1_status = false;
309  unsigned int nb_n1 = 0;
310 
311  auto iterEdge2 = iter1;
312 
313  for (++iterEdge2; iterEdge2 != nei.end(); ++iterEdge2) {
314  const NodeId node2 = *iterEdge2;
315  const Edge e1_2(node1, node2);
316 
317  if (!__graph->existsEdge(e1_2)) {
318  // add the edge
319  __graph->addEdge(node1, node2);
320 
321  if (__we_want_fill_ins) __fill_ins_list.insert(e1_2);
322 
323  if (!__changed_status.contains(node2)) __changed_status.insert(node2);
324 
325  node1_status = true;
326  __log_weights_node1 += (*__log_domain_sizes)[node2];
327  (*__log_weights)[node2] += log_domain_size_node1;
328  __nb_triangles.insert(e1_2, 0);
329 
330  // for all common neighbors of node1 and node2, update the number
331  // of triangles as well as the number of adjacent neighbors
332  unsigned int nb_n2 = 0;
333 
334  if (__graph->neighbours(node1).size()
335  <= __graph->neighbours(node2).size()) {
336  for (const auto neighbor : __graph->neighbours(node1))
337  if (__graph->existsEdge(neighbor, node2)) {
338  // here iterEdge->other (node1) is a neighbor of
339  // both node1 and node2
340  ++nb_n1;
341  ++nb_n2;
342  ++__nb_adjacent_neighbours[neighbor];
343  ++__nb_triangles[Edge(node1, neighbor)];
344  ++__nb_triangles[Edge(node2, neighbor)];
345 
346  if (!__changed_status.contains(neighbor))
347  __changed_status.insert(neighbor);
348  }
349  } else {
350  for (const auto neighbor : __graph->neighbours(node2)) {
351  if (__graph->existsEdge(neighbor, node1)) {
352  // here iterEdge->other (node2) is a neighbor of
353  // both node1 and node2
354  ++nb_n1;
355  ++nb_n2;
356  ++__nb_adjacent_neighbours[neighbor];
357  ++__nb_triangles[Edge(node2, neighbor)];
358  ++__nb_triangles[Edge(node1, neighbor)];
359 
360  if (!__changed_status.contains(neighbor))
361  __changed_status.insert(neighbor);
362  }
363  }
364  }
365 
366  __nb_adjacent_neighbours[node2] += nb_n2;
367  __nb_triangles[e1_2] += nb_n2;
368  }
369  }
370 
371  __nb_adjacent_neighbours[node1] += nb_n1;
372 
373  if (node1_status && !__changed_status.contains(node1))
374  __changed_status.insert(node1);
375  }
376  }
377 
378  // update the __changed_status of node id as well as its containing list
379  if (!__simplicial_nodes.contains(id)) {
380  if (__changed_status.contains(id)) __changed_status.erase(id);
381 
382  switch (__containing_list[id]) {
383  case __Belong::ALMOST_SIMPLICIAL:
384  __almost_simplicial_nodes.erase(id);
385  break;
386 
387  case __Belong::QUASI_SIMPLICIAL: __quasi_simplicial_nodes.erase(id); break;
388 
389  default: break;
390  }
391 
392  __simplicial_nodes.insert(id, (*__log_weights)[id]);
393  __containing_list[id] = __Belong::SIMPLICIAL;
394  } else {
395  if (__changed_status.contains(id)) { __changed_status.erase(id); }
396  }
397  }
398 
400  void SimplicialSet::eraseClique(const NodeId id) {
401  // check if the node we wish to remove actually belongs to the __graph
402  if (!__graph->exists(id)) {
403  GUM_ERROR(NotFound, "Node " << id << " does not belong to the graph");
404  }
405 
406  const NodeSet nei = __graph->neighbours(id);
407 
408  // check that node id is actually a clique
409  Size nb_adj = nei.size();
410  if (__nb_adjacent_neighbours[id] != (nb_adj * (nb_adj - 1)) / 2) {
411  GUM_ERROR(NotFound, "Node " << id << " is not a clique");
412  }
413 
414  // remove the node and its adjacent edges
415  double log_domain_size_id = (*__log_domain_sizes)[id];
416  for (auto iter1 = nei.begin(); iter1 != nei.end(); ++iter1) {
417  const NodeId node1 = *iter1;
418  __nb_adjacent_neighbours[node1] -= nb_adj - 1;
419  (*__log_weights)[node1] -= log_domain_size_id;
420 
421  if (!__changed_status.contains(node1)) __changed_status.insert(node1);
422 
423  __nb_triangles.erase(Edge(node1, id));
424 
425  auto iter2 = iter1;
426  for (++iter2; iter2 != nei.end(); ++iter2)
427  --__nb_triangles[Edge(node1, *iter2)];
428  }
429 
430  __log_tree_width = std::max(__log_tree_width, (*__log_weights)[id]);
431 
432  switch (__containing_list[id]) {
433  case __Belong::SIMPLICIAL: __simplicial_nodes.erase(id); break;
434 
435  case __Belong::ALMOST_SIMPLICIAL: __almost_simplicial_nodes.erase(id); break;
436 
437  case __Belong::QUASI_SIMPLICIAL: __quasi_simplicial_nodes.erase(id); break;
438 
439  default: break;
440  }
441 
442  __nb_adjacent_neighbours.erase(id);
443  __containing_list.erase(id);
444  __changed_status.erase(id);
445  __graph->eraseNode(id);
446  __log_weights->erase(id);
447  }
448 
450  void SimplicialSet::eraseNode(const NodeId id) {
451  // check if the node we wish to remove actually belongs to the __graph
452  if (!__graph->exists(id)) {
453  GUM_ERROR(NotFound, "Node " << id << " does not belong to the graph");
454  }
455 
456  // remove the node and its adjacent edges
457  const NodeSet& nei = __graph->neighbours(id);
458 
459  for (auto iter = nei.beginSafe(); iter != nei.endSafe();
460  ++iter) // safe iterator needed here for deletions
461  eraseEdge(Edge(*iter, id));
462 
463  switch (__containing_list[id]) {
464  case __Belong::SIMPLICIAL: __simplicial_nodes.erase(id); break;
465 
466  case __Belong::ALMOST_SIMPLICIAL: __almost_simplicial_nodes.erase(id); break;
467 
468  case __Belong::QUASI_SIMPLICIAL: __quasi_simplicial_nodes.erase(id); break;
469 
470  default: break;
471  }
472 
473  __nb_adjacent_neighbours.erase(id);
474  __containing_list.erase(id);
475  __changed_status.erase(id);
476  __graph->eraseNode(id);
477  __log_weights->erase(id);
478  }
479 
481  void SimplicialSet::eraseEdge(const Edge& edge) {
482  // check if the edge we wish to remove actually belongs to the __graph
483  if (!__graph->existsEdge(edge)) {
484  GUM_ERROR(NotFound, "Edge " << edge << " does not belong to the graph");
485  }
486 
487  // get the extremal nodes of the edge
488  const NodeId node1 = edge.first();
489  const NodeId node2 = edge.second();
490 
491  // remove the edge
492  __graph->eraseEdge(edge);
493  __nb_triangles.erase(edge);
494 
495  // update the __log_weights of both nodes
496  (*__log_weights)[node1] -= (*__log_domain_sizes)[node2];
497  (*__log_weights)[node2] -= (*__log_domain_sizes)[node1];
498 
499  // update the number of triangles and the adjacent neighbors
500  unsigned int nb_neigh_n1_n2 = 0;
501  for (const auto othernode : __graph->neighbours(node1)) {
502  if (__graph->existsEdge(node2, othernode)) {
503  // udate the number of triangles passing through the egdes of the
504  // __graph
505  --__nb_triangles[Edge(node1, othernode)];
506  --__nb_triangles[Edge(node2, othernode)];
507  // update the neighbors
508  ++nb_neigh_n1_n2;
509  --__nb_adjacent_neighbours[othernode];
510 
511  if (!__changed_status.contains(othernode))
512  __changed_status.insert(othernode);
513  }
514  }
515 
516  __nb_adjacent_neighbours[node1] -= nb_neigh_n1_n2;
517  __nb_adjacent_neighbours[node2] -= nb_neigh_n1_n2;
518 
519  if (!__changed_status.contains(node1)) __changed_status.insert(node1);
520  if (!__changed_status.contains(node2)) __changed_status.insert(node2);
521  }
522 
524  void SimplicialSet::addEdge(NodeId node1, NodeId node2) {
525  // if the edge already exists, do nothing
526  const Edge edge(node1, node2);
527 
528  if (__graph->existsEdge(edge)) return;
529 
530  // update the __log_weights of both nodes
531  (*__log_weights)[node1] += (*__log_domain_sizes)[node2];
532  (*__log_weights)[node2] += (*__log_domain_sizes)[node1];
533 
534  unsigned int nb_triangle_in_new_edge = 0;
535  unsigned int nb_neigh_n1_n2 = 0;
536 
537  for (const auto othernode : __graph->neighbours(node1)) {
538  if (__graph->existsEdge(node2, othernode)) {
539  // udate the number of triangles passing through the egdes of the
540  // __graph
541  ++__nb_triangles[Edge(node1, othernode)];
542  ++__nb_triangles[Edge(node2, othernode)];
543  ++nb_triangle_in_new_edge;
544 
545  // update the neighbors
546  ++nb_neigh_n1_n2;
547  ++__nb_adjacent_neighbours[othernode];
548 
549  if (!__changed_status.contains(othernode))
550  __changed_status.insert(othernode);
551  }
552  }
553 
554  __nb_adjacent_neighbours[node1] += nb_neigh_n1_n2;
555  __nb_adjacent_neighbours[node2] += nb_neigh_n1_n2;
556 
557  // add the edge
558  __graph->addEdge(node1, node2);
559  __nb_triangles.insert(edge, nb_triangle_in_new_edge);
560 
561  if (!__changed_status.contains(node1)) __changed_status.insert(node1);
562  if (!__changed_status.contains(node2)) __changed_status.insert(node2);
563  }
564 
567  void SimplicialSet::__updateList(const NodeId id) {
568  // check if the node belongs to the __graph
569  if (!__graph->exists(id)) {
570  GUM_ERROR(NotFound, "Node " << id << " could not be found");
571  }
572 
573  // check if the status of the node has changed
574  if (!__changed_status.contains(id)) return;
575 
576  __changed_status.erase(id);
577 
578  __Belong& belong = __containing_list[id];
579  const NodeSet& nei = __graph->neighbours(id);
580  Size nb_adj = nei.size();
581 
582  // check if the node should belong to the simplicial set
583  if (__nb_adjacent_neighbours[id] == (nb_adj * (nb_adj - 1)) / 2) {
584  if (belong != __Belong::SIMPLICIAL) {
585  if (belong == __Belong::ALMOST_SIMPLICIAL)
586  __almost_simplicial_nodes.erase(id);
587  else if (belong == __Belong::QUASI_SIMPLICIAL)
588  __quasi_simplicial_nodes.erase(id);
589 
590  __simplicial_nodes.insert(id, (*__log_weights)[id]);
591  belong = __Belong::SIMPLICIAL;
592  }
593 
594  return;
595  }
596 
597  // check if the node should belong to the almost simplicial set
598  Size nb_almost = ((nb_adj - 1) * (nb_adj - 2)) / 2;
599  Size nb = __nb_adjacent_neighbours[id];
600 
601  for (const auto cur : nei) {
602  if (nb_almost == nb - __nb_triangles[Edge(cur, id)]) {
603  // the node is an almost simplicial node
604  if (belong != __Belong::ALMOST_SIMPLICIAL) {
605  if (belong == __Belong::SIMPLICIAL)
606  __simplicial_nodes.erase(id);
607  else if (belong == __Belong::QUASI_SIMPLICIAL)
608  __quasi_simplicial_nodes.erase(id);
609 
610  __almost_simplicial_nodes.insert(id, (*__log_weights)[id]);
611  belong = __Belong::ALMOST_SIMPLICIAL;
612  } else
613  __almost_simplicial_nodes.setPriority(id, (*__log_weights)[id]);
614 
615  return;
616  }
617  }
618 
619  // check if the node should belong to the quasi simplicial nodes
620  if (__nb_adjacent_neighbours[id] / ((nb_adj * (nb_adj - 1)) / 2)
621  >= __quasi_ratio) {
622  if (belong != __Belong::QUASI_SIMPLICIAL) {
623  if (belong == __Belong::SIMPLICIAL)
624  __simplicial_nodes.erase(id);
625  else if (belong == __Belong::ALMOST_SIMPLICIAL)
626  __almost_simplicial_nodes.erase(id);
627 
628  __quasi_simplicial_nodes.insert(id, (*__log_weights)[id]);
629  belong = __Belong::QUASI_SIMPLICIAL;
630  } else
631  __quasi_simplicial_nodes.setPriority(id, (*__log_weights)[id]);
632 
633  return;
634  }
635 
636  // the node does not belong to any list, so remove the node from
637  // its current list
638  if (belong == __Belong::QUASI_SIMPLICIAL)
639  __quasi_simplicial_nodes.erase(id);
640  else if (belong == __Belong::ALMOST_SIMPLICIAL)
641  __almost_simplicial_nodes.erase(id);
642  else if (belong == __Belong::SIMPLICIAL)
643  __simplicial_nodes.erase(id);
644 
645  belong = __Belong::NO_LIST;
646  }
647 
650  // set the limit weight value
651  double limit = __log_tree_width + __log_threshold;
652 
653  // update the elements currently in the almost simplicial list that may
654  // now be contained in another list
655  for (auto iter = __changed_status.beginSafe(); // safe iterator needed here
656  iter != __changed_status.endSafe();
657  ++iter) {
658  if (__almost_simplicial_nodes.contains(*iter)) __updateList(*iter);
659  }
660 
661  // check the current almost simplicial list
662  if (!__almost_simplicial_nodes.empty()
663  && ((*__log_weights)[__almost_simplicial_nodes.top()] <= limit))
664  return true;
665 
666  // if the almost simplicial list does not contain any node that has a low
667  // weight, check if a node can enter the almost simplicial list
668  for (auto iter = __changed_status.beginSafe(); // safe iterator needed here
669  iter != __changed_status.endSafe();
670  ++iter) {
671  __updateList(*iter);
672 
673  if (!__almost_simplicial_nodes.empty()
674  && ((*__log_weights)[__almost_simplicial_nodes.top()] <= limit))
675  return true;
676  }
677 
678  return false;
679  }
680 
683  // update the elements currently in the simplicial list that may
684  // now be contained in another list
685  for (auto iter = __changed_status.beginSafe(); // safe iterator needed here
686  iter != __changed_status.endSafe();
687  ++iter) {
688  if (__simplicial_nodes.contains(*iter)) __updateList(*iter);
689  }
690 
691  // check the current almost simplicial list
692  if (!__simplicial_nodes.empty()) return true;
693 
694  // if the simplicial list does not contain any node, check if a
695  // node can enter the simplicial list
696  for (auto iter = __changed_status.beginSafe(); // safe iterator needed here
697  iter != __changed_status.endSafe();
698  ++iter) {
699  __updateList(*iter);
700 
701  if (!__simplicial_nodes.empty()) return true;
702  }
703 
704  return false;
705  }
706 
709  // set the limit weight value
710  double limit = __log_tree_width + __log_threshold;
711 
712  // update the elements currently in the quasi simplicial list that may
713  // now be contained in another list
714  for (auto iter = __changed_status.beginSafe(); // safe iterator needed here
715  iter != __changed_status.endSafe();
716  ++iter) {
717  if (__quasi_simplicial_nodes.contains(*iter)) __updateList(*iter);
718  }
719 
720  // check the current quasi simplicial list
721  if (!__quasi_simplicial_nodes.empty()
722  && ((*__log_weights)[__quasi_simplicial_nodes.top()] <= limit))
723  return true;
724 
725  // if the quasi simplicial list does not contain any node that has a low
726  // weight, check if a node can enter the quasi simplicial list
727  for (auto iter = __changed_status.beginSafe(); // safe iterator needed here
728  iter != __changed_status.endSafe();
729  ++iter) {
730  __updateList(*iter);
731 
732  if (!__quasi_simplicial_nodes.empty()
733  && ((*__log_weights)[__quasi_simplicial_nodes.top()] <= limit))
734  return true;
735  }
736 
737  return false;
738  }
739 
743  // if the __graph is empty, do nothing
744  if (__graph->size() == 0) return;
745 
746  // set the weights of the nodes and the initial tree_width (min of the
747  // weights)
748  __log_tree_width = std::numeric_limits< double >::max();
749  __log_weights->clear();
750 
751  for (const auto nodeX : *__graph) {
752  double log_weight = (*__log_domain_sizes)[nodeX];
753  for (const auto& nei : __graph->neighbours(nodeX))
754  log_weight += (*__log_domain_sizes)[nei];
755 
756  __log_weights->insert(nodeX, log_weight);
757  if (__log_tree_width > log_weight) __log_tree_width = log_weight;
758  }
759 
760  // initialize the __nb_triangles so that there is no need to check whether
761  // __nb_triangles need new insertions
762  __nb_triangles = __graph->edgesProperty(Size(0));
763  __nb_adjacent_neighbours = __graph->nodesProperty(Size(0));
764  __containing_list = __graph->nodesProperty(__Belong::NO_LIST);
765  __changed_status = __graph->asNodeSet();
766 
767  // set the __nb_triangles and the __nb_adjacent_neighbours: for each
768  // triangle, update the __nb_triangles. To count the triangles only once,
769  // parse for each node X the set of its neighbors Y,Z that are adjacent to
770  // each other and such that the Id of Y and Z are greater than X.
771  for (const auto nodeX : *__graph) {
772  Size& nb_adjacent_neighbors_idX = __nb_adjacent_neighbours[nodeX];
773  const NodeSet& nei = __graph->neighbours(nodeX);
774 
775  for (auto iterY = nei.begin(); iterY != nei.end(); ++iterY)
776  if (*iterY > nodeX) {
777  const NodeId node_idY = *iterY;
778  Size& nb_adjacent_neighbors_idY = __nb_adjacent_neighbours[node_idY];
779 
780  auto iterZ = iterY;
781  for (++iterZ; iterZ != nei.end(); ++iterZ)
782  if ((*iterZ > nodeX) && __graph->existsEdge(node_idY, *iterZ)) {
783  const NodeId node_idZ = *iterZ;
784  ++nb_adjacent_neighbors_idX;
785  ++nb_adjacent_neighbors_idY;
786  ++__nb_adjacent_neighbours[node_idZ];
787  ++__nb_triangles[Edge(nodeX, node_idY)];
788  ++__nb_triangles[Edge(nodeX, node_idZ)];
789  ++__nb_triangles[Edge(node_idZ, node_idY)];
790  }
791  }
792  }
793  }
794 
796  void SimplicialSet::setGraph(UndiGraph* graph,
797  const NodeProperty< double >* log_domain_sizes,
798  NodeProperty< double >* log_weights,
799  double theRatio,
800  double theThreshold) {
801  // check that the pointers passed in argument are all different from 0
802  if ((graph == nullptr) || (log_domain_sizes == nullptr)
803  || (log_weights == nullptr)) {
804  GUM_ERROR(OperationNotAllowed, "SimplicialSet requires non-null pointers");
805  }
806 
807  // clear the structures used for the previous graph and assign the new graph
808  __graph = graph;
809  __log_weights = log_weights;
810  __log_domain_sizes = log_domain_sizes;
811 
812  __simplicial_nodes.clear();
813  __almost_simplicial_nodes.clear();
814  __quasi_simplicial_nodes.clear();
815  __simplicial_nodes.resize(__graph->size());
816  __almost_simplicial_nodes.resize(__graph->size());
817  __quasi_simplicial_nodes.resize(__graph->size());
818 
819  __containing_list.clear();
820  __containing_list.resize(__graph->size());
821  __nb_triangles.clear();
822  __nb_triangles.resize(__graph->size() * __graph->size() / 2);
823  __nb_adjacent_neighbours.clear();
824  __nb_adjacent_neighbours.resize(__graph->size());
825 
826  __log_tree_width = std::numeric_limits< double >::max();
827  __quasi_ratio = theRatio;
828  __log_threshold = std::log(1 + theThreshold);
829  __changed_status.clear();
830  __fill_ins_list.clear();
831 
832  // end of initialization: compute __nb_triangles, __nb_adjacent_neighbours,
833  // etc
834  __initialize();
835  }
836 
838  void SimplicialSet::replaceLogWeights(NodeProperty< double >* old_weights,
839  NodeProperty< double >* new_weights) {
840  // check that the current weights are the old_weights
841  if (old_weights != __log_weights)
842  GUM_ERROR(InvalidArgument,
843  "the old set of weights shall be identical "
844  "to the current one");
845 
846  __log_weights = new_weights;
847  }
848 
849 } /* namespace gum */
850 
851 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
void makeClique(const NodeId id)
adds the necessary edges so that node &#39;id&#39; and its neighbors form a clique
void __initialize()
initialize: compute __nb_triangles, __nb_adjacent_neighbors, etc when a new graph is set ...
void eraseNode(const NodeId id)
removes a node and its adjacent edges from the underlying graph
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
void setGraph(UndiGraph *graph, const NodeProperty< double > *log_domain_sizes, NodeProperty< double > *log_weights, double theRatio=GUM_QUASI_RATIO, double theThreshold=GUM_WEIGHT_THRESHOLD)
initialize the simplicial set w.r.t. a new graph
bool hasQuasiSimplicialNode()
indicates whether there exists a quasi simplicial node
void replaceLogWeights(NodeProperty< double > *old_weigths, NodeProperty< double > *new_weights)
reassigns a new set of cliques&#39; log weights (with the same content)
bool hasAlmostSimplicialNode()
indicates whether there exists an almost simplicial node
void eraseClique(const NodeId id)
removes a node and its adjacent edges from the underlying graph
STL namespace.
void erase(const Key &k)
Erases an element from the set.
Definition: set_tpl.h:656
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Definition: agrum.h:25
void __updateList(const NodeId id)
put node id in the correct simplicial/almost simplicial/quasi simplicial list
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
void addEdge(NodeId first, NodeId second)
adds a new edge to the graph and recomputes the simplicial set
bool hasSimplicialNode()
indicates whether there exists a simplicial node
SimplicialSet(UndiGraph *graph, const NodeProperty< double > *log_domain_sizes, NodeProperty< double > *log_weights, double theRatio=GUM_QUASI_RATIO, double theThreshold=GUM_WEIGHT_THRESHOLD)
constructor. initializes the simplicial set w.r.t. a given graph
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:48
~SimplicialSet()
destructor
Size NodeId
Type for node ids.
Definition: graphElements.h:98
#define GUM_ERROR_IN_EXPR(type, msg)
Definition: exceptions.h:39
#define GUM_ERROR(type, msg)
Definition: exceptions.h:55
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
void eraseEdge(const Edge &edge)
removes an edge from the graph and recomputes the simplicial set