59#ifndef DOXYGEN_SHOULD_SKIP_THIS
79 double theThreshold) :
80 _graph_(graph != nullptr
82 :
GUM_ERROR_IN_EXPR(OperationNotAllowed,
"SimplicialSet requires a valid graph")),
83 _log_weights_(log_weights != nullptr ? log_weights
86 "requires non-null log weights")),
87 _log_domain_sizes_(log_domain_sizes != nullptr
91 "requires non-null domain sizes")),
92 _simplicial_nodes_(std::less< double >(), _graph_->size()),
93 _almost_simplicial_nodes_(std::less< double >(), _graph_->size()),
94 _quasi_simplicial_nodes_(std::less< double >(), _graph_->size()),
95 _containing_list_(_graph_->size()), _nb_triangles_(_graph_->size() * _graph_->size() / 2),
96 _nb_adjacent_neighbours_(_graph_->size()), _quasi_ratio_(theRatio),
97 _log_threshold_(std::log(1 + theThreshold)) {
98 if (graph !=
nullptr) { GUM_CONSTRUCTOR(SimplicialSet); }
106 SimplicialSet::SimplicialSet(
const SimplicialSet& from,
108 const NodeProperty< double >* log_domain_sizes,
109 NodeProperty< double >* log_weights,
111 _graph_(graph != nullptr
114 _log_weights_(log_weights != nullptr ? log_weights
117 "requires non-null log weights")),
118 _log_domain_sizes_(log_domain_sizes != nullptr
122 "requires non-null domain sizes")) {
126 if ((_graph_ == from._graph_) || (_log_weights_ == from._log_weights_)
127 || (*_graph_ != *from._graph_) || (*_log_domain_sizes_ != *from._log_domain_sizes_)) {
129 "SimplicialSet requires fresh copies of "
130 "graph, log weights and log domain sizes");
135 *_log_weights_ = *from._log_weights_;
136 _simplicial_nodes_ = from._simplicial_nodes_;
137 _almost_simplicial_nodes_ = from._almost_simplicial_nodes_;
138 _quasi_simplicial_nodes_ = from._quasi_simplicial_nodes_;
139 _containing_list_ = from._containing_list_;
140 _nb_triangles_ = from._nb_triangles_;
141 _nb_adjacent_neighbours_ = from._nb_adjacent_neighbours_;
142 _log_tree_width_ = from._log_tree_width_;
143 _quasi_ratio_ = from._quasi_ratio_;
144 _log_threshold_ = from._log_threshold_;
145 _changed_status_ = from._changed_status_;
146 _we_want_fill_ins_ = from._we_want_fill_ins_;
147 _fill_ins_list_ = from._fill_ins_list_;
149 GUM_CONS_CPY(SimplicialSet);
153 SimplicialSet::SimplicialSet(SimplicialSet&& from) :
154 _graph_(from._graph_), _log_weights_(from._log_weights_),
155 _log_domain_sizes_(from._log_domain_sizes_),
156 _simplicial_nodes_(
std::move(from._simplicial_nodes_)),
157 _almost_simplicial_nodes_(
std::move(from._almost_simplicial_nodes_)),
158 _quasi_simplicial_nodes_(
std::move(from._quasi_simplicial_nodes_)),
159 _containing_list_(
std::move(from._containing_list_)),
160 _nb_triangles_(
std::move(from._nb_triangles_)),
161 _nb_adjacent_neighbours_(
std::move(from._nb_adjacent_neighbours_)),
162 _log_tree_width_(from._log_tree_width_), _quasi_ratio_(from._quasi_ratio_),
163 _log_threshold_(from._log_threshold_), _changed_status_(
std::move(from._changed_status_)),
164 _we_want_fill_ins_(from._we_want_fill_ins_),
165 _fill_ins_list_(
std::move(from._fill_ins_list_)) {
166 GUM_CONS_MOV(SimplicialSet);
170 SimplicialSet::~SimplicialSet() { GUM_DESTRUCTOR(SimplicialSet); }
173 void SimplicialSet::makeClique(
const NodeId
id) {
175 if (_changed_status_.contains(
id)) _updateList_(
id);
188 if (_simplicial_nodes_.contains(
id)) {
190 }
else if (_almost_simplicial_nodes_.contains(
id)) {
195 const NodeSet& nei = _graph_->neighbours(
id);
196 Size nb_adj = nei.
size();
197 Size nb = _nb_adjacent_neighbours_[id];
204 Size nb_almost = ((nb_adj - 1) * (nb_adj - 2)) / 2;
207 for (
const auto current_node: nei) {
208 if (nb_almost == nb - _nb_triangles_[Edge(current_node,
id)]) {
218 node1 = current_node;
223 double log_domain_size_node1 = (*_log_domain_sizes_)[node1];
224 double& _log_weights_node1_ = (*_log_weights_)[node1];
232 unsigned int nb_n1 = 0;
236 for (
const auto node2: nei) {
237 if ((node2 != node1) && !_graph_->existsEdge(node1, node2)) {
239 const Edge e1_2(node1, node2);
240 _graph_->addEdge(node1, node2);
242 if (_we_want_fill_ins_) _fill_ins_list_.insert(e1_2);
244 if (!_changed_status_.contains(node2)) _changed_status_.insert(node2);
246 _log_weights_node1_ += (*_log_domain_sizes_)[node2];
247 (*_log_weights_)[node2] += log_domain_size_node1;
248 _nb_triangles_.insert(e1_2, 0);
253 unsigned int nb_n2 = 0;
257 if (_graph_->neighbours(node1).size() <= _graph_->neighbours(node2).size()) {
258 for (
const auto neighbor: _graph_->neighbours(node1)) {
259 if (_graph_->existsEdge(neighbor, node2)) {
265 ++_nb_adjacent_neighbours_[neighbor];
266 ++_nb_triangles_[Edge(node1, neighbor)];
267 ++_nb_triangles_[Edge(node2, neighbor)];
269 if (!_changed_status_.contains(neighbor)) _changed_status_.insert(neighbor);
273 for (
const auto neighbor: _graph_->neighbours(node2)) {
274 if (_graph_->existsEdge(neighbor, node1)) {
280 ++_nb_adjacent_neighbours_[neighbor];
281 ++_nb_triangles_[Edge(node2, neighbor)];
282 ++_nb_triangles_[Edge(node1, neighbor)];
284 if (!_changed_status_.contains(neighbor)) _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) {
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;
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() <= _graph_->neighbours(node2).size()) {
335 for (
const auto neighbor: _graph_->neighbours(node1))
336 if (_graph_->existsEdge(neighbor, node2)) {
341 ++_nb_adjacent_neighbours_[neighbor];
342 ++_nb_triangles_[Edge(node1, neighbor)];
343 ++_nb_triangles_[Edge(node2, neighbor)];
345 if (!_changed_status_.contains(neighbor)) _changed_status_.insert(neighbor);
348 for (
const auto neighbor: _graph_->neighbours(node2)) {
349 if (_graph_->existsEdge(neighbor, node1)) {
354 ++_nb_adjacent_neighbours_[neighbor];
355 ++_nb_triangles_[Edge(node2, neighbor)];
356 ++_nb_triangles_[Edge(node1, neighbor)];
358 if (!_changed_status_.contains(neighbor)) _changed_status_.insert(neighbor);
363 _nb_adjacent_neighbours_[node2] += nb_n2;
364 _nb_triangles_[e1_2] += nb_n2;
368 _nb_adjacent_neighbours_[node1] += nb_n1;
370 if (node1_status && !_changed_status_.contains(node1)) _changed_status_.insert(node1);
375 if (!_simplicial_nodes_.contains(
id)) {
376 if (_changed_status_.contains(
id)) _changed_status_.erase(
id);
378 switch (_containing_list_[
id]) {
379 case _Belong_::ALMOST_SIMPLICIAL : _almost_simplicial_nodes_.erase(
id);
break;
381 case _Belong_::QUASI_SIMPLICIAL : _quasi_simplicial_nodes_.erase(
id);
break;
386 _simplicial_nodes_.insert(
id, (*_log_weights_)[
id]);
387 _containing_list_[id] = _Belong_::SIMPLICIAL;
389 if (_changed_status_.contains(
id)) { _changed_status_.erase(
id); }
394 void SimplicialSet::eraseClique(
const NodeId
id) {
396 if (!_graph_->exists(
id)) {
400 const NodeSet nei = _graph_->neighbours(
id);
403 Size nb_adj = nei.size();
404 if (_nb_adjacent_neighbours_[
id] != (nb_adj * (nb_adj - 1)) / 2) {
409 double log_domain_size_id = (*_log_domain_sizes_)[id];
410 for (
auto iter1 = nei.begin(); iter1 != nei.end(); ++iter1) {
411 const NodeId node1 = *iter1;
412 _nb_adjacent_neighbours_[node1] -= nb_adj - 1;
413 (*_log_weights_)[node1] -= log_domain_size_id;
415 if (!_changed_status_.contains(node1)) _changed_status_.insert(node1);
417 _nb_triangles_.erase(Edge(node1,
id));
420 for (++iter2; iter2 != nei.end(); ++iter2)
421 --_nb_triangles_[Edge(node1, *iter2)];
424 _log_tree_width_ = std::max(_log_tree_width_, (*_log_weights_)[
id]);
426 switch (_containing_list_[
id]) {
427 case _Belong_::SIMPLICIAL : _simplicial_nodes_.erase(
id);
break;
429 case _Belong_::ALMOST_SIMPLICIAL : _almost_simplicial_nodes_.erase(
id);
break;
431 case _Belong_::QUASI_SIMPLICIAL : _quasi_simplicial_nodes_.erase(
id);
break;
436 _nb_adjacent_neighbours_.erase(
id);
437 _containing_list_.erase(
id);
438 _changed_status_.erase(
id);
439 _graph_->eraseNode(
id);
440 _log_weights_->erase(
id);
444 void SimplicialSet::eraseNode(
const NodeId
id) {
446 if (!_graph_->exists(
id)) {
451 const NodeSet& nei = _graph_->neighbours(
id);
453 for (
auto iter = nei.beginSafe(); iter != nei.endSafe();
455 eraseEdge(Edge(*iter,
id));
457 switch (_containing_list_[
id]) {
458 case _Belong_::SIMPLICIAL : _simplicial_nodes_.erase(
id);
break;
460 case _Belong_::ALMOST_SIMPLICIAL : _almost_simplicial_nodes_.erase(
id);
break;
462 case _Belong_::QUASI_SIMPLICIAL : _quasi_simplicial_nodes_.erase(
id);
break;
467 _nb_adjacent_neighbours_.erase(
id);
468 _containing_list_.erase(
id);
469 _changed_status_.erase(
id);
470 _graph_->eraseNode(
id);
471 _log_weights_->erase(
id);
475 void SimplicialSet::eraseEdge(
const Edge& edge) {
477 if (!_graph_->existsEdge(edge)) {
482 const NodeId node1 = edge.first();
483 const NodeId node2 = edge.second();
486 _graph_->eraseEdge(edge);
487 _nb_triangles_.erase(edge);
490 (*_log_weights_)[node1] -= (*_log_domain_sizes_)[node2];
491 (*_log_weights_)[node2] -= (*_log_domain_sizes_)[node1];
494 unsigned int nb_neigh_n1_n2 = 0;
495 for (
const auto othernode: _graph_->neighbours(node1)) {
496 if (_graph_->existsEdge(node2, othernode)) {
499 --_nb_triangles_[Edge(node1, othernode)];
500 --_nb_triangles_[Edge(node2, othernode)];
503 --_nb_adjacent_neighbours_[othernode];
505 if (!_changed_status_.contains(othernode)) _changed_status_.insert(othernode);
509 _nb_adjacent_neighbours_[node1] -= nb_neigh_n1_n2;
510 _nb_adjacent_neighbours_[node2] -= nb_neigh_n1_n2;
512 if (!_changed_status_.contains(node1)) _changed_status_.insert(node1);
513 if (!_changed_status_.contains(node2)) _changed_status_.insert(node2);
517 void SimplicialSet::addEdge(NodeId node1, NodeId node2) {
519 const Edge edge(node1, node2);
521 if (_graph_->existsEdge(edge))
return;
524 (*_log_weights_)[node1] += (*_log_domain_sizes_)[node2];
525 (*_log_weights_)[node2] += (*_log_domain_sizes_)[node1];
527 unsigned int nb_triangle_in_new_edge = 0;
528 unsigned int nb_neigh_n1_n2 = 0;
530 for (
const auto othernode: _graph_->neighbours(node1)) {
531 if (_graph_->existsEdge(node2, othernode)) {
534 ++_nb_triangles_[Edge(node1, othernode)];
535 ++_nb_triangles_[Edge(node2, othernode)];
536 ++nb_triangle_in_new_edge;
540 ++_nb_adjacent_neighbours_[othernode];
542 if (!_changed_status_.contains(othernode)) _changed_status_.insert(othernode);
546 _nb_adjacent_neighbours_[node1] += nb_neigh_n1_n2;
547 _nb_adjacent_neighbours_[node2] += nb_neigh_n1_n2;
550 _graph_->addEdge(node1, node2);
551 _nb_triangles_.insert(edge, nb_triangle_in_new_edge);
553 if (!_changed_status_.contains(node1)) _changed_status_.insert(node1);
554 if (!_changed_status_.contains(node2)) _changed_status_.insert(node2);
559 void SimplicialSet::_updateList_(
const NodeId
id) {
561 if (!_graph_->exists(
id)) {
GUM_ERROR(
NotFound,
"Node " <<
id <<
" could not be found") }
564 if (!_changed_status_.contains(
id))
return;
566 _changed_status_.erase(
id);
568 _Belong_& belong = _containing_list_[id];
569 const NodeSet& nei = _graph_->neighbours(
id);
570 Size nb_adj = nei.size();
573 if (_nb_adjacent_neighbours_[
id] == (nb_adj * (nb_adj - 1)) / 2) {
574 if (belong != _Belong_::SIMPLICIAL) {
575 if (belong == _Belong_::ALMOST_SIMPLICIAL) _almost_simplicial_nodes_.erase(
id);
576 else if (belong == _Belong_::QUASI_SIMPLICIAL) _quasi_simplicial_nodes_.erase(
id);
578 _simplicial_nodes_.insert(
id, (*_log_weights_)[
id]);
579 belong = _Belong_::SIMPLICIAL;
586 Size nb_almost = ((nb_adj - 1) * (nb_adj - 2)) / 2;
587 Size nb = _nb_adjacent_neighbours_[id];
589 for (
const auto cur: nei) {
590 if (nb_almost == nb - _nb_triangles_[Edge(cur,
id)]) {
592 if (belong != _Belong_::ALMOST_SIMPLICIAL) {
593 if (belong == _Belong_::SIMPLICIAL) _simplicial_nodes_.erase(
id);
594 else if (belong == _Belong_::QUASI_SIMPLICIAL) _quasi_simplicial_nodes_.erase(
id);
596 _almost_simplicial_nodes_.insert(
id, (*_log_weights_)[
id]);
597 belong = _Belong_::ALMOST_SIMPLICIAL;
598 }
else _almost_simplicial_nodes_.setPriority(
id, (*_log_weights_)[
id]);
605 if (_nb_adjacent_neighbours_[
id] / ((nb_adj * (nb_adj - 1)) / 2) >= _quasi_ratio_) {
606 if (belong != _Belong_::QUASI_SIMPLICIAL) {
607 if (belong == _Belong_::SIMPLICIAL) _simplicial_nodes_.erase(
id);
608 else if (belong == _Belong_::ALMOST_SIMPLICIAL) _almost_simplicial_nodes_.erase(
id);
610 _quasi_simplicial_nodes_.insert(
id, (*_log_weights_)[
id]);
611 belong = _Belong_::QUASI_SIMPLICIAL;
612 }
else _quasi_simplicial_nodes_.setPriority(
id, (*_log_weights_)[
id]);
619 if (belong == _Belong_::QUASI_SIMPLICIAL) _quasi_simplicial_nodes_.erase(
id);
620 else if (belong == _Belong_::ALMOST_SIMPLICIAL) _almost_simplicial_nodes_.erase(
id);
621 else if (belong == _Belong_::SIMPLICIAL) _simplicial_nodes_.erase(
id);
623 belong = _Belong_::NO_LIST;
627 bool SimplicialSet::hasAlmostSimplicialNode() {
629 double limit = _log_tree_width_ + _log_threshold_;
633 for (
auto iter = _changed_status_.beginSafe();
634 iter != _changed_status_.endSafe();
636 if (_almost_simplicial_nodes_.contains(*iter)) _updateList_(*iter);
640 if (!_almost_simplicial_nodes_.empty()
641 && ((*_log_weights_)[_almost_simplicial_nodes_.top()] <= limit))
646 for (
auto iter = _changed_status_.beginSafe();
647 iter != _changed_status_.endSafe();
651 if (!_almost_simplicial_nodes_.empty()
652 && ((*_log_weights_)[_almost_simplicial_nodes_.top()] <= limit))
660 bool SimplicialSet::hasSimplicialNode() {
663 for (
auto iter = _changed_status_.beginSafe();
664 iter != _changed_status_.endSafe();
666 if (_simplicial_nodes_.contains(*iter)) _updateList_(*iter);
670 if (!_simplicial_nodes_.empty())
return true;
674 for (
auto iter = _changed_status_.beginSafe();
675 iter != _changed_status_.endSafe();
679 if (!_simplicial_nodes_.empty())
return true;
686 bool SimplicialSet::hasQuasiSimplicialNode() {
688 double limit = _log_tree_width_ + _log_threshold_;
692 for (
auto iter = _changed_status_.beginSafe();
693 iter != _changed_status_.endSafe();
695 if (_quasi_simplicial_nodes_.contains(*iter)) _updateList_(*iter);
699 if (!_quasi_simplicial_nodes_.empty()
700 && ((*_log_weights_)[_quasi_simplicial_nodes_.top()] <= limit))
705 for (
auto iter = _changed_status_.beginSafe();
706 iter != _changed_status_.endSafe();
710 if (!_quasi_simplicial_nodes_.empty()
711 && ((*_log_weights_)[_quasi_simplicial_nodes_.top()] <= limit))
720 void SimplicialSet::_initialize_() {
722 if (_graph_->size() == 0)
return;
726 _log_tree_width_ = std::numeric_limits< double >::max();
727 _log_weights_->clear();
729 for (
const auto nodeX: *_graph_) {
730 double log_weight = (*_log_domain_sizes_)[nodeX];
731 for (
const auto& nei: _graph_->neighbours(nodeX))
732 log_weight += (*_log_domain_sizes_)[nei];
734 _log_weights_->insert(nodeX, log_weight);
735 if (_log_tree_width_ > log_weight) _log_tree_width_ = log_weight;
740 _nb_triangles_ = _graph_->edgesProperty(Size(0));
741 _nb_adjacent_neighbours_ = _graph_->nodesPropertyFromVal(Size(0));
742 _containing_list_ = _graph_->nodesPropertyFromVal(_Belong_::NO_LIST);
743 _changed_status_ = _graph_->asNodeSet();
749 for (
const auto nodeX: *_graph_) {
750 Size& nb_adjacent_neighbors_idX = _nb_adjacent_neighbours_[nodeX];
751 const NodeSet& nei = _graph_->neighbours(nodeX);
753 for (
auto iterY = nei.begin(); iterY != nei.end(); ++iterY)
754 if (*iterY > nodeX) {
755 const NodeId node_idY = *iterY;
756 Size& nb_adjacent_neighbors_idY = _nb_adjacent_neighbours_[node_idY];
759 for (++iterZ; iterZ != nei.end(); ++iterZ)
760 if ((*iterZ > nodeX) && _graph_->existsEdge(node_idY, *iterZ)) {
761 const NodeId node_idZ = *iterZ;
762 ++nb_adjacent_neighbors_idX;
763 ++nb_adjacent_neighbors_idY;
764 ++_nb_adjacent_neighbours_[node_idZ];
765 ++_nb_triangles_[Edge(nodeX, node_idY)];
766 ++_nb_triangles_[Edge(nodeX, node_idZ)];
767 ++_nb_triangles_[Edge(node_idZ, node_idY)];
774 void SimplicialSet::setGraph(UndiGraph* graph,
775 const NodeProperty< double >* log_domain_sizes,
776 NodeProperty< double >* log_weights,
778 double theThreshold) {
780 if ((graph ==
nullptr) || (log_domain_sizes ==
nullptr) || (log_weights ==
nullptr)) {
786 _log_weights_ = log_weights;
787 _log_domain_sizes_ = log_domain_sizes;
789 _simplicial_nodes_.clear();
790 _almost_simplicial_nodes_.clear();
791 _quasi_simplicial_nodes_.clear();
792 _simplicial_nodes_.resize(_graph_->size());
793 _almost_simplicial_nodes_.resize(_graph_->size());
794 _quasi_simplicial_nodes_.resize(_graph_->size());
796 _containing_list_.clear();
797 _containing_list_.resize(_graph_->size());
798 _nb_triangles_.clear();
799 _nb_triangles_.resize(_graph_->size() * _graph_->size() / 2);
800 _nb_adjacent_neighbours_.clear();
801 _nb_adjacent_neighbours_.resize(_graph_->size());
803 _log_tree_width_ = std::numeric_limits< double >::max();
804 _quasi_ratio_ = theRatio;
805 _log_threshold_ = std::log(1 + theThreshold);
806 _changed_status_.clear();
807 _fill_ins_list_.clear();
815 void SimplicialSet::replaceLogWeights(NodeProperty< double >* old_weights,
816 NodeProperty< double >* new_weights) {
818 if (old_weights != _log_weights_)
820 "the old set of weights shall be identical "
821 "to the current one");
823 _log_weights_ = new_weights;
Exception: at least one argument passed to a function is not what was expected.
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
Size size() const noexcept
Returns the number of elements in the set.
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
Base class for undirected graphs.
#define GUM_ERROR_IN_EXPR(type, msg)
#define GUM_ERROR(type, msg)
some utils for topology : NodeId, Edge, Arc and consorts ...
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
gum is the global namespace for all aGrUM entities
Class for fast retrieval of simplicial and quasi/almost simplicial nodes.
inline implementations of simplicial set