38 #endif // GUM_NO_INLINE 40 #ifndef DOXYGEN_SHOULD_SKIP_THIS 57 const NodeProperty< double >* log_domain_sizes,
58 NodeProperty< double >* log_weights,
60 double theThreshold) :
61 __graph(graph != nullptr
64 "SimplicialSet requires a valid graph")),
65 __log_weights(log_weights != nullptr
69 "requires non-null log weights")),
70 __log_domain_sizes(log_domain_sizes != nullptr
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) {
84 GUM_CONSTRUCTOR(SimplicialSet);
95 const NodeProperty< double >* log_domain_sizes,
96 NodeProperty< double >* log_weights,
98 __graph(graph != nullptr
101 "SimplicialSet requires a valid graph")),
102 __log_weights(log_weights != nullptr
106 "requires non-null log weights")),
108 log_domain_sizes != nullptr
112 "requires non-null domain sizes")) {
116 if ((__graph == from.__graph) || (__log_weights == from.__log_weights)
117 || (*__graph != *from.__graph)
118 || (*__log_domain_sizes != *from.__log_domain_sizes)) {
120 "SimplicialSet requires fresh copies of " 121 "graph, log weights and log domain sizes");
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;
141 GUM_CONS_CPY(SimplicialSet);
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)) {
160 GUM_CONS_MOV(SimplicialSet);
166 GUM_DESTRUCTOR(SimplicialSet);
172 if (__changed_status.contains(
id)) __updateList(
id);
185 if (__simplicial_nodes.contains(
id)) {
187 }
else if (__almost_simplicial_nodes.contains(
id)) {
192 const NodeSet& nei = __graph->neighbours(
id);
193 Size nb_adj = nei.size();
194 Size nb = __nb_adjacent_neighbours[id];
201 Size nb_almost = ((nb_adj - 1) * (nb_adj - 2)) / 2;
204 for (
const auto current_node : nei) {
205 if (nb_almost == nb - __nb_triangles[Edge(current_node,
id)]) {
215 node1 = current_node;
220 double log_domain_size_node1 = (*__log_domain_sizes)[node1];
221 double& __log_weights_node1 = (*__log_weights)[node1];
229 unsigned int nb_n1 = 0;
233 for (
const auto node2 : nei) {
234 if ((node2 != node1) && !__graph->existsEdge(node1, node2)) {
236 const Edge e1_2(node1, node2);
237 __graph->addEdge(node1, node2);
239 if (__we_want_fill_ins) __fill_ins_list.insert(e1_2);
241 if (!__changed_status.contains(node2)) __changed_status.insert(node2);
243 __log_weights_node1 += (*__log_domain_sizes)[node2];
244 (*__log_weights)[node2] += log_domain_size_node1;
245 __nb_triangles.insert(e1_2, 0);
250 unsigned int nb_n2 = 0;
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)) {
263 ++__nb_adjacent_neighbours[neighbor];
264 ++__nb_triangles[Edge(node1, neighbor)];
265 ++__nb_triangles[Edge(node2, neighbor)];
267 if (!__changed_status.contains(neighbor))
268 __changed_status.insert(neighbor);
272 for (
const auto neighbor : __graph->neighbours(node2)) {
273 if (__graph->existsEdge(neighbor, node1)) {
279 ++__nb_adjacent_neighbours[neighbor];
280 ++__nb_triangles[Edge(node2, neighbor)];
281 ++__nb_triangles[Edge(node1, neighbor)];
283 if (!__changed_status.contains(neighbor))
284 __changed_status.insert(neighbor);
289 __nb_adjacent_neighbours[node2] += nb_n2;
290 __nb_triangles[e1_2] += nb_n2;
294 if (!__changed_status.contains(node1)) __changed_status.insert(node1);
296 __nb_adjacent_neighbours[node1] += nb_n1;
302 const NodeSet& nei = __graph->neighbours(
id);
304 for (
auto iter1 = nei.begin(); iter1 != nei.end(); ++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;
311 auto iterEdge2 = iter1;
313 for (++iterEdge2; iterEdge2 != nei.end(); ++iterEdge2) {
314 const NodeId node2 = *iterEdge2;
315 const Edge e1_2(node1, node2);
317 if (!__graph->existsEdge(e1_2)) {
319 __graph->addEdge(node1, node2);
321 if (__we_want_fill_ins) __fill_ins_list.insert(e1_2);
323 if (!__changed_status.contains(node2)) __changed_status.insert(node2);
326 __log_weights_node1 += (*__log_domain_sizes)[node2];
327 (*__log_weights)[node2] += log_domain_size_node1;
328 __nb_triangles.insert(e1_2, 0);
332 unsigned int nb_n2 = 0;
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)) {
342 ++__nb_adjacent_neighbours[neighbor];
343 ++__nb_triangles[Edge(node1, neighbor)];
344 ++__nb_triangles[Edge(node2, neighbor)];
346 if (!__changed_status.contains(neighbor))
347 __changed_status.insert(neighbor);
350 for (
const auto neighbor : __graph->neighbours(node2)) {
351 if (__graph->existsEdge(neighbor, node1)) {
356 ++__nb_adjacent_neighbours[neighbor];
357 ++__nb_triangles[Edge(node2, neighbor)];
358 ++__nb_triangles[Edge(node1, neighbor)];
360 if (!__changed_status.contains(neighbor))
361 __changed_status.insert(neighbor);
366 __nb_adjacent_neighbours[node2] += nb_n2;
367 __nb_triangles[e1_2] += nb_n2;
371 __nb_adjacent_neighbours[node1] += nb_n1;
373 if (node1_status && !__changed_status.contains(node1))
374 __changed_status.insert(node1);
379 if (!__simplicial_nodes.contains(
id)) {
380 if (__changed_status.contains(
id)) __changed_status.erase(
id);
382 switch (__containing_list[
id]) {
383 case __Belong::ALMOST_SIMPLICIAL:
384 __almost_simplicial_nodes.erase(
id);
387 case __Belong::QUASI_SIMPLICIAL: __quasi_simplicial_nodes.erase(
id);
break;
392 __simplicial_nodes.insert(
id, (*__log_weights)[
id]);
393 __containing_list[id] = __Belong::SIMPLICIAL;
395 if (__changed_status.contains(
id)) { __changed_status.erase(
id); }
402 if (!__graph->exists(
id)) {
403 GUM_ERROR(NotFound,
"Node " <<
id <<
" does not belong to the graph");
406 const NodeSet nei = __graph->neighbours(
id);
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");
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;
421 if (!__changed_status.contains(node1)) __changed_status.insert(node1);
423 __nb_triangles.erase(Edge(node1,
id));
426 for (++iter2; iter2 != nei.end(); ++iter2)
427 --__nb_triangles[Edge(node1, *iter2)];
430 __log_tree_width = std::max(__log_tree_width, (*__log_weights)[
id]);
432 switch (__containing_list[
id]) {
433 case __Belong::SIMPLICIAL: __simplicial_nodes.erase(
id);
break;
435 case __Belong::ALMOST_SIMPLICIAL: __almost_simplicial_nodes.erase(
id);
break;
437 case __Belong::QUASI_SIMPLICIAL: __quasi_simplicial_nodes.erase(
id);
break;
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);
452 if (!__graph->exists(
id)) {
453 GUM_ERROR(NotFound,
"Node " <<
id <<
" does not belong to the graph");
457 const NodeSet& nei = __graph->neighbours(
id);
459 for (
auto iter = nei.beginSafe(); iter != nei.endSafe();
461 eraseEdge(Edge(*iter,
id));
463 switch (__containing_list[
id]) {
464 case __Belong::SIMPLICIAL: __simplicial_nodes.
erase(
id);
break;
466 case __Belong::ALMOST_SIMPLICIAL: __almost_simplicial_nodes.erase(
id);
break;
468 case __Belong::QUASI_SIMPLICIAL: __quasi_simplicial_nodes.erase(
id);
break;
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);
483 if (!__graph->existsEdge(edge)) {
484 GUM_ERROR(NotFound,
"Edge " << edge <<
" does not belong to the graph");
488 const NodeId node1 = edge.first();
489 const NodeId node2 = edge.second();
492 __graph->eraseEdge(edge);
493 __nb_triangles.erase(edge);
496 (*__log_weights)[node1] -= (*__log_domain_sizes)[node2];
497 (*__log_weights)[node2] -= (*__log_domain_sizes)[node1];
500 unsigned int nb_neigh_n1_n2 = 0;
501 for (
const auto othernode : __graph->neighbours(node1)) {
502 if (__graph->existsEdge(node2, othernode)) {
505 --__nb_triangles[Edge(node1, othernode)];
506 --__nb_triangles[Edge(node2, othernode)];
509 --__nb_adjacent_neighbours[othernode];
511 if (!__changed_status.contains(othernode))
512 __changed_status.insert(othernode);
516 __nb_adjacent_neighbours[node1] -= nb_neigh_n1_n2;
517 __nb_adjacent_neighbours[node2] -= nb_neigh_n1_n2;
519 if (!__changed_status.contains(node1)) __changed_status.insert(node1);
520 if (!__changed_status.contains(node2)) __changed_status.insert(node2);
526 const Edge edge(node1, node2);
528 if (__graph->existsEdge(edge))
return;
531 (*__log_weights)[node1] += (*__log_domain_sizes)[node2];
532 (*__log_weights)[node2] += (*__log_domain_sizes)[node1];
534 unsigned int nb_triangle_in_new_edge = 0;
535 unsigned int nb_neigh_n1_n2 = 0;
537 for (
const auto othernode : __graph->neighbours(node1)) {
538 if (__graph->existsEdge(node2, othernode)) {
541 ++__nb_triangles[Edge(node1, othernode)];
542 ++__nb_triangles[Edge(node2, othernode)];
543 ++nb_triangle_in_new_edge;
547 ++__nb_adjacent_neighbours[othernode];
549 if (!__changed_status.contains(othernode))
550 __changed_status.insert(othernode);
554 __nb_adjacent_neighbours[node1] += nb_neigh_n1_n2;
555 __nb_adjacent_neighbours[node2] += nb_neigh_n1_n2;
558 __graph->addEdge(node1, node2);
559 __nb_triangles.insert(edge, nb_triangle_in_new_edge);
561 if (!__changed_status.contains(node1)) __changed_status.insert(node1);
562 if (!__changed_status.contains(node2)) __changed_status.insert(node2);
569 if (!__graph->exists(
id)) {
570 GUM_ERROR(NotFound,
"Node " <<
id <<
" could not be found");
574 if (!__changed_status.contains(
id))
return;
576 __changed_status.erase(
id);
578 __Belong& belong = __containing_list[id];
579 const NodeSet& nei = __graph->neighbours(
id);
580 Size nb_adj = nei.size();
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);
590 __simplicial_nodes.insert(
id, (*__log_weights)[
id]);
591 belong = __Belong::SIMPLICIAL;
598 Size nb_almost = ((nb_adj - 1) * (nb_adj - 2)) / 2;
599 Size nb = __nb_adjacent_neighbours[id];
601 for (
const auto cur : nei) {
602 if (nb_almost == nb - __nb_triangles[Edge(cur,
id)]) {
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);
610 __almost_simplicial_nodes.insert(
id, (*__log_weights)[
id]);
611 belong = __Belong::ALMOST_SIMPLICIAL;
613 __almost_simplicial_nodes.setPriority(
id, (*__log_weights)[
id]);
620 if (__nb_adjacent_neighbours[
id] / ((nb_adj * (nb_adj - 1)) / 2)
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);
628 __quasi_simplicial_nodes.insert(
id, (*__log_weights)[
id]);
629 belong = __Belong::QUASI_SIMPLICIAL;
631 __quasi_simplicial_nodes.setPriority(
id, (*__log_weights)[
id]);
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);
645 belong = __Belong::NO_LIST;
651 double limit = __log_tree_width + __log_threshold;
655 for (
auto iter = __changed_status.beginSafe();
656 iter != __changed_status.endSafe();
658 if (__almost_simplicial_nodes.contains(*iter)) __updateList(*iter);
662 if (!__almost_simplicial_nodes.empty()
663 && ((*__log_weights)[__almost_simplicial_nodes.top()] <= limit))
668 for (
auto iter = __changed_status.beginSafe();
669 iter != __changed_status.endSafe();
673 if (!__almost_simplicial_nodes.empty()
674 && ((*__log_weights)[__almost_simplicial_nodes.top()] <= limit))
685 for (
auto iter = __changed_status.beginSafe();
686 iter != __changed_status.endSafe();
688 if (__simplicial_nodes.contains(*iter)) __updateList(*iter);
692 if (!__simplicial_nodes.empty())
return true;
696 for (
auto iter = __changed_status.beginSafe();
697 iter != __changed_status.endSafe();
701 if (!__simplicial_nodes.empty())
return true;
710 double limit = __log_tree_width + __log_threshold;
714 for (
auto iter = __changed_status.beginSafe();
715 iter != __changed_status.endSafe();
717 if (__quasi_simplicial_nodes.contains(*iter)) __updateList(*iter);
721 if (!__quasi_simplicial_nodes.empty()
722 && ((*__log_weights)[__quasi_simplicial_nodes.top()] <= limit))
727 for (
auto iter = __changed_status.beginSafe();
728 iter != __changed_status.endSafe();
732 if (!__quasi_simplicial_nodes.empty()
733 && ((*__log_weights)[__quasi_simplicial_nodes.top()] <= limit))
744 if (__graph->size() == 0)
return;
748 __log_tree_width = std::numeric_limits< double >::max();
749 __log_weights->clear();
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];
756 __log_weights->insert(nodeX, log_weight);
757 if (__log_tree_width > log_weight) __log_tree_width = log_weight;
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();
771 for (
const auto nodeX : *__graph) {
772 Size& nb_adjacent_neighbors_idX = __nb_adjacent_neighbours[nodeX];
773 const NodeSet& nei = __graph->neighbours(nodeX);
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];
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)];
797 const NodeProperty< double >* log_domain_sizes,
798 NodeProperty< double >* log_weights,
800 double theThreshold) {
802 if ((graph ==
nullptr) || (log_domain_sizes ==
nullptr)
803 || (log_weights ==
nullptr)) {
804 GUM_ERROR(OperationNotAllowed,
"SimplicialSet requires non-null pointers");
809 __log_weights = log_weights;
810 __log_domain_sizes = log_domain_sizes;
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());
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());
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();
839 NodeProperty< double >* new_weights) {
841 if (old_weights != __log_weights)
843 "the old set of weights shall be identical " 844 "to the current one");
846 __log_weights = new_weights;
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 'id' 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' 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
void erase(const Key &k)
Erases an element from the set.
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
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.
~SimplicialSet()
destructor
Size NodeId
Type for node ids.
#define GUM_ERROR_IN_EXPR(type, msg)
#define GUM_ERROR(type, msg)
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