aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
gspan_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
43
52
53namespace gum {
54 namespace prm {
55
56 template < GUM_Numeric GUM_SCALAR >
58 Timer t;
61
62 for (auto root = _tree_.roots().begin(); root != _tree_.roots().end(); ++root) {
63 if (_tree_.strategy().accept_root(&(_tree_.pattern(*root)))) {
64 gspan::Pattern& p = _tree_.pattern(*root);
66
67 for (const auto node: _tree_.iso_graph(p).nodes()) {
68 PRMInstance< GUM_SCALAR >* u = _tree_.iso_map(p, node).atPos(0);
69 PRMInstance< GUM_SCALAR >* v = _tree_.iso_map(p, node).atPos(1);
70 graph.internalGraph().eraseEdge(Edge(graph.id(u), graph.id(v)));
71 }
72 }
73 }
74
76 }
77
78 template < GUM_Numeric GUM_SCALAR >
80 for (auto iter = _graph_->labels().begin(); iter != _graph_->labels().end(); ++iter) {
81 try {
82 if (_graph_->nodes(iter.second()).size() >= 2) {
83 _cost_.insert(
84 iter.second(),
85 _cost_func_(iter.second()->tree_width, _graph_->nodes(iter.second()).size()));
86 _nodes_.push_back(const_cast< gspan::LabelData* >(iter.second()));
87 }
88 } catch (NotFound const&) {
89 // It's a label over edges
90 if (_isEdgeEligible_(*(_graph_->edges(iter.second()).begin()))) {
91 _cost_.insert(
92 iter.second(),
93 _cost_func_(iter.second()->tree_width, _graph_->edges(iter.second()).size()));
94 _edges_.push_back(iter.second());
95 }
96 }
97 }
98
100 GSpan< GUM_SCALAR >::LabelSort my_sort(this);
101 std::sort(_nodes_.begin(), _nodes_.end(), my_sort);
102 std::sort(_edges_.begin(), _edges_.end(), my_sort);
103 Size idx = 0;
104
105 for (auto iter = _nodes_.begin(); iter != _nodes_.end(); ++iter) {
106 (*iter)->id = ++idx;
107 new_labels->insert(idx, *iter);
108 }
109
110 for (auto iter = _edges_.begin(); iter != _edges_.end(); ++iter) {
111 (*iter)->id = ++idx;
112 new_labels->insert(idx, *iter);
113 _tree_.addRoot(**iter);
114 }
115
116 delete _graph_->_labels_;
117 _graph_->_labels_ = new_labels;
118 }
119
120 template < GUM_Numeric GUM_SCALAR >
122 gspan::Pattern& pat) {
123 std::vector< gspan::Pattern* > stack;
124 stack.push_back(&pat);
125 // Pointers used in the following while
126 gspan::Pattern* p = nullptr;
128 gspan::EdgeGrowth< GUM_SCALAR >* edge_growth = nullptr;
129 Sequence< PRMInstance< GUM_SCALAR >* >* seq = nullptr;
130 PRMInstance< GUM_SCALAR >* current = nullptr;
131 PRMInstance< GUM_SCALAR >* neighbor = nullptr;
132
133 // Neighbor_id is the neighbor's id in the interface graph and
134 // neighbor_node
135 // is its id in the rightmost path in the case of a backward edge growth
136 NodeId current_id = 0;
137 NodeId neighbor_node = 0;
138 gspan::LabelData* neighbor_label = 0;
139
140 typename gspan::EdgeData< GUM_SCALAR >* edge_data = nullptr;
141
142 size_t idx;
143 const std::list< NodeId >* children = 0;
144
145 while (!stack.empty()) {
146 // Getting next pattern
147 p = stack.back();
148 stack.pop_back();
149
150 if (p->code().codes.size() < _depth_stop_) {
151 // We need the rightmost path of p
152 std::list< NodeId > r_path;
153 p->rightmostPath(r_path);
154 // Mapping used to count each possible child of p, the position in the
155 // vector
156 // matches the one in the rightmost path
157 std::vector< HashTable< std::string, gspan::EdgeGrowth< GUM_SCALAR >* >* > count_vector;
158
159 for (size_t i = 0; i < r_path.size(); ++i)
160 count_vector.push_back(
161 new HashTable< std::string, gspan::EdgeGrowth< GUM_SCALAR >* >());
162
163 // For each subgraph represented by p, we look for a valid edge growth
164 // for
165 // each instance match of p in its isomorphism graph.
166 for (const auto iso_node: _tree_.iso_graph(*p).nodes()) {
167 seq = &(_tree_.iso_map(*p, iso_node));
168 idx = 0;
169
170 for (const auto node: r_path) {
171 edge_count = count_vector[idx];
172 // Retrieving the equivalent instance in the current match
173 current = seq->atPos((Idx)(node - 1));
174 current_id = ig.id(current);
175 // Checking for edges not in p
176
177 for (const auto neighbor_id: ig.internalGraph().neighbours(current_id)) {
178 neighbor = ig.node(neighbor_id).n;
179
180 // We want a forward edge in any case or a backward edge if
181 // current
182 // is the rightmost vertex
183 if ((!seq->exists(neighbor)) || (node == r_path.back())) {
184 // Things we need to know: the LabelData data of the neighbour
185 // and,
186 // if it's a backward edge, its node id in the rightmost path
187 edge_data = &(ig.edge(current_id, neighbor_id));
188 neighbor_label = (neighbor == edge_data->u) ? edge_data->l_u : edge_data->l_v;
189 neighbor_node = (seq->exists(neighbor)) ? seq->pos(neighbor) + 1 : 0;
190 // Adding the edge growth to the edge_growth hashtable
191 gspan::EdgeGrowth< GUM_SCALAR > temp_growth(node,
192 edge_data->l,
193 neighbor_label,
194 neighbor_node);
195
196 if (edge_count->exists(temp_growth.toString())) {
197 edge_growth = (*edge_count)[temp_growth.toString()];
198 edge_growth->insert(current, neighbor);
199 } else {
200 edge_growth = new gspan::EdgeGrowth< GUM_SCALAR >(node,
201 edge_data->l,
202 neighbor_label,
203 neighbor_node);
204 edge_growth->insert(current, neighbor);
205 edge_count->insert(edge_growth->toString(), edge_growth);
206 }
207 }
208 }
209 }
210 }
211
212 // Removing any infrequent child
213 for (size_t node = 0; node < count_vector.size(); ++node) {
214 edge_count = count_vector[node];
215
216 for (const auto& elt: *edge_count) {
217 try {
218 _tree_.growPattern(*p, *elt.second, 2);
219 } catch (OperationNotAllowed const&) {
220 // The child was not minimal or was not worth considering
221 }
222
223 delete elt.second;
224 }
225
226 delete edge_count;
227 }
228
229 // Calling _subgraph_mining_ over children of p
230 children = &(_tree_.children(*p));
231
232 for (std::list< NodeId >::const_reverse_iterator child = children->rbegin();
233 child != children->rend();
234 ++child)
235 stack.push_back(&(_tree_.pattern(*child)));
236 }
237 }
238 }
239
240 template < GUM_Numeric GUM_SCALAR >
242 // First we put all the patterns in _patterns_.
243 std::vector< NodeId > stack;
244
245 for (std::list< NodeId >::reverse_iterator root = tree().roots().rbegin();
246 root != tree().roots().rend();
247 ++root)
248 stack.push_back(*root);
249
250 NodeId id = 0;
251 std::list< NodeId >* children = nullptr;
252
253 while (!stack.empty()) {
254 id = stack.back();
255 stack.pop_back();
256 _patterns_.push_back(&(tree().pattern(id)));
257 children = &(tree().children(tree().pattern(id)));
258
259 for (std::list< NodeId >::reverse_iterator child = children->rbegin();
260 child != children->rend();
261 ++child)
262 stack.push_back(*child);
263 }
264
265 if (!_patterns_.empty()) {
266 // We sort _patterns_.
268 std::sort(_patterns_.begin(), _patterns_.end(), my_sort);
269 // Now we need to find all the matches we can, using _patterns_.
270 // We start by the best Pattern and add it's maximal independent set to
271 // _chosen_
274 Sequence< PRMInstance< GUM_SCALAR >* >* match = nullptr;
275
276 for (const auto node: tree().max_indep_set(*(_patterns_.front()))) {
277 match = &(tree().iso_map(*(_patterns_.front()), node));
278
279 for (const auto i: *match)
280 _chosen_.insert(i);
281
282 matches->insert(match);
283 }
284
285 _matched_instances_.insert(_patterns_.front(), matches);
286 // Now we see what kind of pattern we can still use
287 bool found;
288 UndiGraph* iso_graph = nullptr;
289
290 for (auto patt = _patterns_.begin() + 1; patt != _patterns_.end(); ++patt) {
291 UndiGraph reduced_iso_graph;
292 std::vector< NodeId > degree_list;
293 iso_graph = &(tree().iso_graph(**patt));
294
295 for (const auto node: iso_graph->nodes()) {
296 found = false;
297 match = &(tree().iso_map(**patt, node));
298
299 for (const auto i: *match)
300 if (_chosen_.exists(i)) {
301 found = true;
302 break;
303 }
304
305 if (!found) {
306 // We add the pattern to the reduced isomorphism graph to compute
307 // the
308 // max independent set
309 // over the remaining matches
310 reduced_iso_graph.addNodeWithId(node);
311
312 for (const auto iso: reduced_iso_graph.nodes())
313 if (iso_graph->existsEdge(node, iso)) reduced_iso_graph.addEdge(node, iso);
314
315 degree_list.push_back(node);
316 }
317 }
318
319 // We create a new set to hold all the chosen matches of patt
321 // We can compute the max independent set and the matches belonging to
322 // it
323 typename gspan::DFSTree< GUM_SCALAR >::NeighborDegreeSort my_sort(reduced_iso_graph);
324 std::sort(degree_list.begin(), degree_list.end(), my_sort);
325 Set< NodeId > removed;
326
327 for (const auto node: degree_list)
328 if (!removed.exists(node)) {
329 // First we update removed to follow the max independent set
330 // algorithm
331 removed.insert(node);
332
333 for (const auto neighbor: reduced_iso_graph.neighbours(node))
334 removed.insert(neighbor);
335
336 // Second we update match and matches to keep track of the current
337 // match
338 match = &(tree().iso_map(**patt, node));
339 matches->insert(match);
340
341 for (const auto elt: *match)
342 _chosen_.insert(elt);
343 }
344
345 _matched_instances_.insert(*patt, matches);
346 }
347
348 // // We remove patterns with 0 matches
349 std::vector< size_t > trash;
350
351 for (size_t idx = 0; idx < _patterns_.size(); ++idx)
352 if (_matched_instances_[_patterns_[idx]]->size() < 2) trash.push_back(idx);
353
354 while (trash.size()) {
355 delete _matched_instances_[_patterns_[trash.back()]];
356 _matched_instances_.erase(_patterns_[trash.back()]);
357 // delete _patterns_[trash.back()];
358 _patterns_[trash.back()] = _patterns_.back();
359 _patterns_.pop_back();
360 trash.pop_back();
361 }
362 }
363 }
364
365 template < GUM_Numeric GUM_SCALAR >
367 const PRMSystem< GUM_SCALAR >& sys,
369 _graph_(new gspan::InterfaceGraph< GUM_SCALAR >(sys)), _tree_(*_graph_, strategy),
370 _depth_stop_(INT_MAX) {
371 GUM_CONSTRUCTOR(GSpan);
372 }
373
374 template < GUM_Numeric GUM_SCALAR >
376 GUM_DESTRUCTOR(GSpan);
377
378 for (const auto& elt: _matched_instances_)
379 delete elt.second;
380
381 delete _graph_;
382 }
383
384 template < GUM_Numeric GUM_SCALAR >
388
389 template < GUM_Numeric GUM_SCALAR >
391 _depth_stop_ = depth;
392 }
393
394 template < GUM_Numeric GUM_SCALAR >
398
399 template < GUM_Numeric GUM_SCALAR >
403
404 template < GUM_Numeric GUM_SCALAR >
405 Idx GSpan< GUM_SCALAR >::_cost_func_(Size interface_size, Size frequency) {
406 return Idx(interface_size * frequency);
407 }
408
409 template < GUM_Numeric GUM_SCALAR >
410 std::vector< gspan::Pattern* >& GSpan< GUM_SCALAR >::patterns() {
411 return _patterns_;
412 }
413
414 template < GUM_Numeric GUM_SCALAR >
415 const std::vector< gspan::Pattern* >& GSpan< GUM_SCALAR >::patterns() const {
416 return _patterns_;
417 }
418
419 template < GUM_Numeric GUM_SCALAR >
422 return *(_matched_instances_[const_cast< gspan::Pattern* >(&p)]);
423 }
424
425 template < GUM_Numeric GUM_SCALAR >
428 return *(_matched_instances_[const_cast< gspan::Pattern* >(&p)]);
429 }
430
431 template < GUM_Numeric GUM_SCALAR >
435
436 template < GUM_Numeric GUM_SCALAR >
440
441 template < GUM_Numeric GUM_SCALAR >
443 return (_graph_->edges(e->l).size() >= 2) && (_graph_->nodes(e->l_u).size() >= 2)
444 && (_graph_->nodes(e->l_v).size() >= 2);
445 }
446
447 // LalbeSort
448
449 template < GUM_Numeric GUM_SCALAR >
451 GUM_CONSTRUCTOR(GSpan< GUM_SCALAR >::LabelSort);
452 }
453
454 template < GUM_Numeric GUM_SCALAR >
458
459 template < GUM_Numeric GUM_SCALAR >
463
464 template < GUM_Numeric GUM_SCALAR >
466 // We want a descending order
467 // return gspan-> _cost_[i] > gspan-> _cost_[j];
468 return gspan->_tree_.strategy()(i, j);
469 }
470
471 // PatternSort
472
473 template < GUM_Numeric GUM_SCALAR >
477
478 template < GUM_Numeric GUM_SCALAR >
482
483 template < GUM_Numeric GUM_SCALAR >
487
488 template < GUM_Numeric GUM_SCALAR >
490 // We want a descending order
491 return gspan->tree().strategy().operator()(i, j);
492 }
493
494 } /* namespace prm */
495} /* namespace gum */
void insert(const T1 &first, const T2 &second)
Inserts a new association in the gum::Bijection.
Set of pairs of elements with fast search for both elements.
Definition bijection.h:1640
bool existsEdge(const Edge &edge) const
indicates whether a given edge exists
const NodeSet & neighbours(NodeId id) const
returns the set of node neighbours to a given node
The base class for all undirected edges.
The class for generic Hash Tables.
Definition hashTable.h:640
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
const NodeGraphPart & nodes() const
return *this as a NodeGraphPart
virtual void addNodeWithId(const NodeId id)
try to insert a node with the given id
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
const Key & atPos(Idx i) const
Returns the object at the pos i.
Idx pos(const Key &key) const
Returns the position of the object passed in argument (if it exists).
bool exists(const Key &k) const
Check the existence of k in the sequence.
The generic class for storing (ordered) sequences of objects.
Definition sequence.h:994
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
Class used to compute response times for benchmark purposes.
Definition timer.h:69
Base class for undirected graphs.
Definition undiGraph.h:130
void addEdge(NodeId first, NodeId second) override
insert a new edge into the undirected graph
void discoverPatterns()
This will methods will discover repeated patterns in the PRMSystem<GUM_SCALAR> assigned to this class...
Definition gspan_tpl.h:57
HashTable< gspan::Pattern *, MatchedInstances * > _matched_instances_
Mapping between a pattern and the multiset of instances matched to it.
Definition gspan.h:254
std::vector< gspan::LabelData * > _nodes_
The vector of nodes in graph, in decreasing order of interest.
Definition gspan.h:244
std::vector< gspan::Pattern * > _patterns_
The vector of discovered patters, in decreasing order of interest.
Definition gspan.h:241
void setMaxDFSDepth(Size depth)
Defines the maximal depth of the DFSTree used by this class to discover new patterns.
Definition gspan_tpl.h:390
HashTable< gspan::LabelData *, Idx > _cost_
Mapping between labels and their cost.
Definition gspan.h:250
Size getMaxDFSDepth() const
Returns the maximal depth of the DFSTree used to discover new patterns.
Definition gspan_tpl.h:385
gspan::DFSTree< GUM_SCALAR > & tree()
Returns the DFSTree used to discover new patters.
Definition gspan_tpl.h:395
std::vector< gspan::Pattern * > & patterns()
Returns the Pattern mined by this class in a decreasing order of interest.
Definition gspan_tpl.h:410
MatchedInstances & matches(const gspan::Pattern &p)
Returns a mapping between patterns and the sequence of instance in the interface graph matching them.
Definition gspan_tpl.h:421
gspan::InterfaceGraph< GUM_SCALAR > * _graph_
The interface graph used by this class.
Definition gspan.h:232
gspan::DFSTree< GUM_SCALAR > _tree_
The DFSTree used to discover new patters.
Definition gspan.h:235
Set< PRMInstance< GUM_SCALAR > * > _chosen_
Contains all instance which belongs to a discovered and used pattern.
Definition gspan.h:257
Size _cost_func_(Size interface_size, Size frequency)
Returns the cost with respect to an interface size and its frequency.
Definition gspan_tpl.h:405
void _subgraph_mining_(gspan::InterfaceGraph< GUM_SCALAR > &graph, gspan::Pattern &p)
Discovers new patterns by developing p.
Definition gspan_tpl.h:121
std::vector< gspan::LabelData * > _edges_
The vector of edges in graph, in decreasing order of interest.
Definition gspan.h:247
gspan::InterfaceGraph< GUM_SCALAR > & interfaceGraph()
Returns the InterfaceGraph used by this.
Definition gspan_tpl.h:432
~GSpan()
Destructor.
Definition gspan_tpl.h:375
Set< Sequence< PRMInstance< GUM_SCALAR > * > * > MatchedInstances
Code alias.
Definition gspan.h:185
Size _depth_stop_
The max depth allowed for the DSF tree.
Definition gspan.h:238
void _sortNodesAndEdges_()
Sort the nodes and edges of graph.
Definition gspan_tpl.h:79
void _sortPatterns_()
Sort the patterns and compute their respective costs.
Definition gspan_tpl.h:241
bool _isEdgeEligible_(typename gspan::EdgeData< GUM_SCALAR > *e)
Returns true if e is an eligible root edge.
Definition gspan_tpl.h:442
GSpan(const PRM< GUM_SCALAR > &prm, const PRMSystem< GUM_SCALAR > &sys, gspan::SearchStrategy< GUM_SCALAR > *strategy=0)
Default constructor.
Definition gspan_tpl.h:366
An PRMInstance is a Bayesian network fragment defined by a Class and used in a PRMSystem.
Definition PRMInstance.h:79
A PRMSystem is a container of PRMInstance and describe a relational skeleton.
Definition PRMSystem.h:72
This class represents a Probabilistic Relational PRMSystem<GUM_SCALAR>.
Definition PRM.h:74
std::vector< EdgeCode * > codes
The vector containing the EdgeCode composing this DFSCode.
Definition DFSCode.h:109
A DFSTree is used by gspan to sort lexicographically patterns discovered in an interface graph.
Definition DFSTree.h:77
Inner class to handle data about edges in graph.
PRMInstance< GUM_SCALAR > * u
One of the two instance represented by this edge.
LabelData * l_u
The label data of u.
LabelData * l
The labal data of this edge.
LabelData * l_v
The label data of v.
This class is used to define an edge growth of a pattern in this DFSTree.
Definition edgeGrowth.h:73
std::string toString()
Return a string representation of this.
void insert(PRMInstance< GUM_SCALAR > *u, PRMInstance< GUM_SCALAR > *v)
Add the pair (u,v) as a match for the current growth.
This class represent the interface graph of a given gum::prm::PRMSystem<GUM_SCALAR>.
UndiGraph & internalGraph()
Returns the graph of this interface graph.
NodeData< GUM_SCALAR > & node(const PRMInstance< GUM_SCALAR > *i)
Returns data about a node.
EdgeData< GUM_SCALAR > & edge(NodeId u, NodeId v)
Returns data about an edge.
NodeId id(const PRMInstance< GUM_SCALAR > &i) const
Returns the id of i in this interface graph.
This contains all the information we want for a node in a DFSTree.
Definition pattern.h:90
DFSCode & code()
Returns the DFSCode of this Pattern.
void rightmostPath(std::list< NodeId > &r_path) const
Fill r_path with the rightmost path of this Pattern. The list is supposed empty.
Definition pattern.cpp:282
This is an abstract class used to tune search strategies in the gspan algorithm.
Headers of the DFSTree class.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Size NodeId
Type for node ids.
Headers of gspan.
namespace for all probabilistic relational models entities
Definition agrum.h:68
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
Private class used to sort LabelData using STL sort algorithms.
Definition gspan.h:300
bool operator()(gspan::LabelData *i, gspan::LabelData *j)
Returns true if i's cost is lesser than j's.
Definition gspan_tpl.h:465
LabelSort(GSpan *my_gspan)
Default constructor.
Definition gspan_tpl.h:450
GSpan * gspan
A pointer over an instance of the GSpan class using this class.
Definition gspan.h:327
Private class used to sort Pattern using STL sort algorithms.
Definition gspan.h:335
bool operator()(gspan::Pattern *i, gspan::Pattern *j)
Returns true if i's cost is lesser than j's.
Definition gspan_tpl.h:489
GSpan * gspan
A pointer over an instance of the GSpan class using this class.
Definition gspan.h:362
PatternSort(GSpan *my_gspan)
Default constructor.
Definition gspan_tpl.h:474
This is used to generate the max_indep_set of a Pattern.
Definition DFSTree.h:250
Inner class to handle data about labels in this interface graph.