aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
DFSTree_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-2025 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-2025 *
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#pragma once
41
42
49
52
53namespace gum {
54 namespace prm {
55 namespace gspan {
56 template < typename GUM_SCALAR >
58 GUM_DESTRUCTOR(DFSTree);
59
60 for (const auto& elt: _data_) {
61 delete elt.first;
62 delete elt.second;
63 }
64
65 delete _strategy_;
66 }
67
68 template < typename GUM_SCALAR >
72
73 for (const auto& edge: _graph_->edges(&label)) {
74 bool u_first = (edge->l_u->id < edge->l_v->id);
75 Idx u_idx = (u_first) ? edge->l_u->id : edge->l_v->id;
76 Idx v_idx = (!u_first) ? edge->l_u->id : edge->l_v->id;
77
78 bool found = false;
79
80 for (const auto& elt: roots)
81 if ((elt.second.first == u_idx) && (elt.second.second == v_idx)) {
82 roots_edges[elt.first]->insert(edge);
83 found = true;
84 break;
85 }
86
88 if (!found) {
89 Pattern* p = new Pattern();
90 roots.insert(p, std::make_pair(u_idx, v_idx));
91 roots_edges.insert(p, new Sequence< EdgeData< GUM_SCALAR >* >());
92 roots_edges[p]->insert(edge);
94 NodeId u = p->addNodeWithLabel((u_first) ? *edge->l_u : *edge->l_v);
95 NodeId v = p->addNodeWithLabel((!u_first) ? *edge->l_u : *edge->l_v);
96 p->addArc(u, v, label);
97 _node_map_.insert(DiGraph::addNode(), p);
98 _data_.insert(p, data);
99 _roots_.push_back(_node_map_.first(p));
100 }
101 }
102
103 // This is used to compute the max independent set of p->max_indep_set
104 for (const auto& elt: roots_edges) {
105 _initialiaze_root_(elt.first, *elt.second);
106 strategy().accept_root(elt.first);
107 delete elt.second;
108 }
109 }
110
111 template < typename GUM_SCALAR >
112 void
114 Sequence< EdgeData< GUM_SCALAR >* >& edge_seq) {
116 std::vector< NodeId > degree_list;
117
118 for (auto iter = edge_seq.begin(); iter != edge_seq.end(); ++iter) {
119 const auto& edge = *iter;
122
123 // Creating the multiset of instances matching p
124 bool u_first = (edge->l_u->id < edge->l_v->id);
125 seq->insert((u_first) ? edge->u : edge->v);
126 seq->insert((!u_first) ? edge->u : edge->v);
127
128 NodeId an_id = data->iso_graph.addNode();
129 data->iso_map.insert(an_id, seq);
130 degree_list.push_back(an_id);
131
132 // Adding edges between two isomorphisms of p sharing at least one
133 // instance
134 for (const auto& elt: data->iso_map)
135 if (elt.first != an_id)
136 for (auto iter = elt.second->begin(); iter != elt.second->end(); ++iter)
137 if (seq->exists(*iter)) {
138 data->iso_graph.addEdge(an_id, elt.first);
139 break;
140 }
141 }
142
143 // Computing p->max_indep_set using a greedy algorithm
144 DFSTree< GUM_SCALAR >::NeighborDegreeSort my_operator(data->iso_graph);
145 std::sort(degree_list.begin(), degree_list.end(), my_operator);
146 Set< NodeId > removed;
147
148 for (const auto node: degree_list) {
149 if (!removed.exists(node)) {
150 removed.insert(node);
151
152 for (const auto neighbor: data->iso_graph.neighbours(node))
153 removed.insert(neighbor);
154
155 data->max_indep_set.insert(node);
156 }
157 }
158 }
159
160 template < typename GUM_SCALAR >
164 for (const auto& elt: iso_map) {
165 bool found = false;
166
167 for (const auto& inst: seq)
168 if (!(elt.second->exists(inst))) {
169 found = true;
170 break;
171 }
172
173 if (!found) { return false; }
174 }
175
176 return true;
177 }
178
179 template < typename GUM_SCALAR >
181 Pattern* child,
182 EdgeGrowth< GUM_SCALAR >& edge_growth) {
183 // Adding child to the tree
184 NodeId node = DiGraph::addNode();
185 _node_map_.insert(node, child);
186 // Adding child in p's children list
187 std::list< NodeId >& children = _data_[&p]->children;
188
189 if (children.empty()) {
190 children.push_back(node);
191 } else {
192 size_t size = children.size();
193
194 for (std::list< NodeId >::iterator iter = children.begin(); iter != children.end();
195 ++iter) {
196 if (child->code() < pattern(*iter).code()) {
197 children.insert(iter, node);
198 break;
199 }
200 }
201
202 if (size == children.size()) { children.push_back(node); }
203 }
204 }
205
206 template < typename GUM_SCALAR >
208 Pattern* child,
209 EdgeGrowth< GUM_SCALAR >& edge_growth) {
210 NodeId v = edge_growth.v;
211
212 // First we check if the edge is legal
213 if (v == 0) { v = child->addNodeWithLabel(*(edge_growth.l_v)); }
214
215 child->addArc(edge_growth.u, v, *(edge_growth.edge));
216 // Neighborhood restriction is checked by the Pattern class
217 const EdgeCode& edge = child->edgeCode(edge_growth.u, v);
218
219 // Then we check if the edge we added is valid
220 if (edge < *(child->code().codes.front())) {
222 "added edge code is lesser than the first "
223 "one in the pattern's DFSCode");
224 }
225
226 if (edge.isBackward()) {
227 for (auto iter = child->code().codes.begin(); (iter + 1) != child->code().codes.end();
228 ++iter) {
229 if ((((**iter).i == v) || ((**iter).j == v)) && edge < (**iter)) {
231 "added backward edge is lesser than an existing edge on v");
232 }
233 }
234 }
235
236 // Finally, we check if child is minimal.
237 if (!child->isMinimal()) {
238 GUM_ERROR(OperationNotAllowed, "the DFSCode for this growth is not minimal")
239 }
240 }
241
242 template < typename GUM_SCALAR >
244 EdgeGrowth< GUM_SCALAR >& edge_growth,
245 Size min_freq) {
246 auto* child = new Pattern(p);
247
248 try {
249 _checkGrowth_(p, child, edge_growth);
250 } catch (OperationNotAllowed const&) {
251 delete child;
252 throw;
253 }
254
255 // Now we need to build the pattern data about child
256 auto* data = new DFSTree< GUM_SCALAR >::PatternData(child);
257 std::vector< NodeId > degree_list;
258 NodeProperty< Sequence< PRMInstance< GUM_SCALAR >* >* >& p_iso_map = _data_[&p]->iso_map;
259 // typename NodeProperty< std::pair< PRMInstance< GUM_SCALAR >*,
260 // PRMInstance< GUM_SCALAR >* > >::iterator_safe match;
261 // Using p information to build child's isomorphism graph
262 NodeId id = 0;
263
264 for (const auto& elt: p_iso_map) {
265 auto match = edge_growth.matches.begin();
266
267 for (; match != edge_growth.matches.end(); ++match) {
268 // Adding the isomorphism in the iso_graph and building the iso_map.
269 if (child->code().codes.back()->isForward()) {
270 if (elt.second->exists(match.val().first)
271 && !(elt.second->exists(match.val().second))) {
272 // Let's see if the new match is already matched
273 auto* new_seq = new Sequence< PRMInstance< GUM_SCALAR >* >(*elt.second);
274 new_seq->insert(match.val().second);
275
276 if (_is_new_seq_(*new_seq, data->iso_map)) {
277 id = data->iso_graph.addNode();
278 data->iso_map.insert(id, new_seq);
279 } else {
280 delete new_seq;
281 }
282
283 break;
284 }
285 } else {
286 if (elt.second->exists(match.val().first) && elt.second->exists(match.val().second)) {
288 = new Sequence< PRMInstance< GUM_SCALAR >* >(*elt.second);
289
290 if (_is_new_seq_(*new_seq, data->iso_map)) {
291 id = data->iso_graph.addNode();
292 data->iso_map.insert(id, new_seq);
293 } else {
294 delete new_seq;
295 }
296
297 break;
298 }
299 }
300 }
301
302 if (match != edge_growth.matches.end()) {
303 // Adding edges in the iso_graph
304 for (const auto node: data->iso_graph.nodes())
305 if (node != id)
306 for (const auto m: *data->iso_map[id])
307 if (data->iso_map[node]->exists(m)) {
308 data->iso_graph.addEdge(node, id);
309 break;
310 }
311
312 degree_list.push_back(id);
313 edge_growth.matches.erase(match.key());
314 }
315 }
316
317 if (data->iso_graph.size() < min_freq) {
318 delete data;
319 delete child;
320 GUM_ERROR(OperationNotAllowed, "child is not frequent enough")
321 }
322
323 // Now we can compute the maximal independent set of child
324 DFSTree< GUM_SCALAR >::NeighborDegreeSort my_operator(data->iso_graph);
325 std::sort(degree_list.begin(), degree_list.end(), my_operator);
326 Set< NodeId > removed;
327
328 for (const auto node: degree_list) {
329 if (!removed.exists(node)) {
330 removed.insert(node);
331
332 for (const auto neighbor: data->iso_graph.neighbours(node))
333 removed.insert(neighbor);
334
335 data->max_indep_set.insert(node);
336 }
337 }
338
339 _data_.insert(child, data);
340
341 if (!_strategy_->accept_growth(&p, child, edge_growth)) {
342 _data_.erase(child);
343 delete data;
344 delete child;
345 GUM_ERROR(OperationNotAllowed, "child is not frequent enough")
346 }
347
348 _addChild_(p, child, edge_growth);
349 return *child;
350 }
351
352 template < typename GUM_SCALAR >
356 try {
357 for (const auto& elt: x)
358 if (y[elt.first] != elt.second) return false;
359 } catch (NotFound const&) { return false; }
360
361 return true;
362 }
363
364 // PatternData
365 template < typename GUM_SCALAR >
368 max_indep_set(from.max_indep_set), cost(from.cost), gain(from.gain) {
370
371 for (const auto& elt: from.iso_map)
372 iso_map.insert(elt.first, new Sequence< PRMInstance< GUM_SCALAR >* >(*elt.second));
373 }
374
375 template < typename GUM_SCALAR >
378
379 for (const auto& elt: iso_map)
380 delete elt.second;
381 }
382
383 template < typename GUM_SCALAR >
393
394 template < typename GUM_SCALAR >
395 INLINE std::list< NodeId >& DFSTree< GUM_SCALAR >::roots() {
396 return _roots_;
397 }
398
399 template < typename GUM_SCALAR >
400 INLINE const std::list< NodeId >& DFSTree< GUM_SCALAR >::roots() const {
401 return _roots_;
402 }
403
404 template < typename GUM_SCALAR >
406 try {
407 return *(_node_map_.second(
408 *(DiGraph::parents(_node_map_.first(const_cast< Pattern* >(&p))).begin())));
409 } catch (NotFound const&) {
410 if (_node_map_.existsSecond(const_cast< Pattern* >(&p))) {
411 GUM_ERROR(NotFound, "the given pattern is a root node")
412 } else {
413 GUM_ERROR(NotFound, "pattern not found in this DFSTree")
414 }
415 }
416 }
417
418 template < typename GUM_SCALAR >
419 INLINE const Pattern& DFSTree< GUM_SCALAR >::parent(const Pattern& p) const {
420 try {
421 return *(_node_map_.second(
422 *(DiGraph::parents(_node_map_.first(const_cast< Pattern* >(&p))).begin())));
423 } catch (NotFound const&) {
424 if (_node_map_.existsSecond(const_cast< Pattern* >(&p))) {
425 GUM_ERROR(NotFound, "the given pattern is a root node")
426 } else {
427 GUM_ERROR(NotFound, "pattern not found in this DFSTree")
428 }
429 }
430 }
431
432 template < typename GUM_SCALAR >
433 INLINE std::list< NodeId >& DFSTree< GUM_SCALAR >::children(const Pattern& p) {
434 try {
435 return _data_[const_cast< Pattern* >(&p)]->children;
436 } catch (NotFound const&) { GUM_ERROR(NotFound, "pattern not found in this DFSTree") }
437 }
438
439 template < typename GUM_SCALAR >
440 INLINE const std::list< NodeId >& DFSTree< GUM_SCALAR >::children(const Pattern& p) const {
441 try {
442 return _data_[const_cast< Pattern* >(&p)]->children;
443 } catch (NotFound const&) { GUM_ERROR(NotFound, "pattern not found in this DFSTree") }
444 }
445
446 template < typename GUM_SCALAR >
448 try {
449 return *(_node_map_.second(id));
450 } catch (NotFound const&) { GUM_ERROR(NotFound, "no pattern matching the given id") }
451 }
452
453 template < typename GUM_SCALAR >
455 try {
456 return *(_node_map_.second(id));
457 } catch (NotFound const&) { GUM_ERROR(NotFound, "no pattern matching the given id") }
458 }
459
460 template < typename GUM_SCALAR >
462 try {
463 return _data_[const_cast< Pattern* >(&p)]->iso_graph;
464 } catch (NotFound const&) { GUM_ERROR(NotFound, "pattern not found in this DFSTree") }
465 }
466
467 template < typename GUM_SCALAR >
470 try {
471 return *(_data_[const_cast< Pattern* >(&p)]->iso_map[node]);
472 } catch (NotFound const&) {
473 if (_data_.exists(const_cast< Pattern* >(&p))) {
474 GUM_ERROR(NotFound, "node not found in Pattern's isomorphism graph")
475 } else {
476 GUM_ERROR(NotFound, "pattern not found in this DFSTree")
477 }
478 }
479 }
480
481 template < typename GUM_SCALAR >
483 try {
484 return _data_[const_cast< Pattern* >(&p)]->max_indep_set;
485 } catch (NotFound const&) { GUM_ERROR(NotFound, "pattern not found in this DFSTree") }
486 }
487
488 template < typename GUM_SCALAR >
490 return *_graph_;
491 }
492
493 template < typename GUM_SCALAR >
494 INLINE std::ostream& operator<<(std::ostream& out, const EdgeGrowth< GUM_SCALAR >& edge) {
495 out << edge.u << ", " << *(edge.edge) << ", " << *(edge.l_v) << ", " << edge.v;
496 return out;
497 }
498
499 template < typename GUM_SCALAR >
500 INLINE double DFSTree< GUM_SCALAR >::frequency(const Pattern& p) const {
501 return (double)_data_[const_cast< Pattern* >(&p)]->max_indep_set.size();
502 }
503
504 template < typename GUM_SCALAR >
507 return *(_data_[const_cast< Pattern* >(&p)]);
508 }
509
510 template < typename GUM_SCALAR >
511 INLINE const typename DFSTree< GUM_SCALAR >::PatternData&
513 return *(_data_[const_cast< Pattern* >(&p)]);
514 }
515
516 template < typename GUM_SCALAR >
520
521 template < typename GUM_SCALAR >
525
526 // NeighborDegreeSort
527
528 template < typename GUM_SCALAR >
533
534 template < typename GUM_SCALAR >
539
540 template < typename GUM_SCALAR >
544
545 template < typename GUM_SCALAR >
547 return g.neighbours(i).size() < g.neighbours(j).size();
548 }
549
550 // PatternData
551
552 template < typename GUM_SCALAR >
557
558 } /* namespace gspan */
559 } /* namespace prm */
560} /* namespace gum */
Headers of the DFSTree class.
const NodeSet & parents(NodeId id) const
returns the set of nodes with arc ingoing to a given node
The class for generic Hash Tables.
Definition hashTable.h:637
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
Size size() const
alias for sizeNodes
virtual NodeId addNode()
insert a new node and return its id
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
bool exists(const Key &k) const
Check the existence of k in the sequence.
void insert(const Key &k)
Insert an element at the end of the sequence.
The generic class for storing (ordered) sequences of objects.
Definition sequence.h:972
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:533
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
Base class for undirected graphs.
Definition undiGraph.h:128
Abstract class representing an element of PRM class.
An PRMInstance is a Bayesian network fragment defined by a Class and used in a PRMSystem.
Definition PRMInstance.h:79
std::vector< EdgeCode * > codes
The vector containing the EdgeCode composing this DFSCode.
Definition DFSCode.h:109
Set< NodeId > & max_indep_set(const Pattern &p)
Returns the maximal independent set of p isomorphism graph.
Pattern & pattern(NodeId id)
Returns the pattern represented by id in this DFSTree.
std::list< NodeId > & children(const Pattern &p)
Returns the list of p children in this DFSTree.
const InterfaceGraph< GUM_SCALAR > & graph() const
Returns the list of root patterns in this DFSTree.
SearchStrategy< GUM_SCALAR > * _strategy_
The strategy used to prune the search tree.
Definition DFSTree.h:280
void _checkGrowth_(Pattern &p, Pattern *child, EdgeGrowth< GUM_SCALAR > &edge_growth)
Raise different exceptions if child is invalid or illegal.
std::list< NodeId > & roots()
Returns the list of root patterns in this DFSTree.
bool _test_equality_(HashTable< PRMClassElement< GUM_SCALAR > *, Size > &x, HashTable< PRMClassElement< GUM_SCALAR > *, Size > &y)
Pattern & parent(const Pattern &p)
Returns the parent of p in this DFSTree.
PatternData & data(const Pattern &p)
UndiGraph & iso_graph(const Pattern &p)
Returns the isomorphism graph of p in the interface graph.
void addRoot(LabelData &data)
Add a one edge Pattern in this DFSTree.
Definition DFSTree_tpl.h:69
Sequence< PRMInstance< GUM_SCALAR > * > & iso_map(const Pattern &p, NodeId node)
Given a pattern and a node in its isomorphism graph, this methods returns the sequence of instance ma...
Pattern & growPattern(Pattern &p, EdgeGrowth< GUM_SCALAR > &edge_growth, Size min_freq)
Add a one edge growth of p as one of its child.
void _addChild_(Pattern &p, Pattern *child, EdgeGrowth< GUM_SCALAR > &edge_growth)
Add a child to this DFSTree.
void _initialiaze_root_(Pattern *p, Sequence< EdgeData< GUM_SCALAR > * > &seq)
This initialize the DSFTree with a new root.
HashTable< Pattern *, PatternData * > _data_
Data about patterns in this DFSTree.
Definition DFSTree.h:277
bool _is_new_seq_(Sequence< PRMInstance< GUM_SCALAR > * > &seq, NodeProperty< Sequence< PRMInstance< GUM_SCALAR > * > * > &iso_map)
Check if an instance match is redundant.
double frequency(const Pattern &p) const
Returns the frequency of p respecting it's maximal independent set.
SearchStrategy< GUM_SCALAR > & strategy()
strategy getter
Bijection< NodeId, Pattern * > _node_map_
The mapping between nodes in this DFSTree and the patterns they represents.
Definition DFSTree.h:274
const InterfaceGraph< GUM_SCALAR > * _graph_
The interface graph on which this DFSTree applies.
Definition DFSTree.h:267
std::list< NodeId > _roots_
The list of root patterns in this DFSTree.
Definition DFSTree.h:270
DFSTree(const InterfaceGraph< GUM_SCALAR > &graph, SearchStrategy< GUM_SCALAR > *strategy=0)
Default constructor.
Inner class to handle data about edges in graph.
This class is used to define an edge growth of a pattern in this DFSTree.
Definition edgeGrowth.h:73
NodeId u
The id of the node from which we grow an edge.
Definition edgeGrowth.h:83
NodeProperty< std::pair< PRMInstance< GUM_SCALAR > *, PRMInstance< GUM_SCALAR > * > > matches
The mapping between the u and v for each match in the interface graph.
Definition edgeGrowth.h:95
NodeId v
If the growth is backward you must assigned the subscript of v, otherwise 0 is assigned (recall that ...
Definition edgeGrowth.h:90
LabelData * edge
The LabelData over the edge of this edge growth.
Definition edgeGrowth.h:85
LabelData * l_v
The LabelData over the node of this edge growth.
Definition edgeGrowth.h:87
This is class is an implementation of a simple serach strategy for the gspan algorithm: it accept a g...
This class represent the interface graph of a given gum::prm::PRMSystem<GUM_SCALAR>.
This contains all the information we want for a node in a DFSTree.
Definition pattern.h:90
bool isMinimal()
Returns the DFSCode of this Pattern.
Definition pattern.cpp:71
NodeId addNodeWithLabel(LabelData &l)
Insert a node with the given LabelData.
Definition pattern_inl.h:66
DFSCode & code()
Returns the DFSCode of this Pattern.
void addArc(NodeId i, NodeId j, LabelData &l)
Add an arc to this Pattern.
EdgeCode & edgeCode(NodeId tail, NodeId head)
Returns the EdgeCode of an edge of this Pattern.
This is an abstract class used to tune search strategies in the gspan algorithm.
Headers of the DFSTree class.
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
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.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
std::ostream & operator<<(std::ostream &out, const DFSCode &code)
Print code in out.
Definition DFSCode.cpp:59
namespace for all probabilistic relational models entities
Definition agrum.h:68
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
This is used to generate the max_indep_set of a Pattern.
Definition DFSTree.h:250
NeighborDegreeSort(UndiGraph &graph)
Constructor.
bool operator()(NodeId i, NodeId j)
The operator used to sort stuff.
UndiGraph & g
The isomorphism graph.
Definition DFSTree.h:260
Pattern * pattern
The pattern.
Definition DFSTree.h:108
Size cost
The cost of this Pattern.
Definition DFSTree.h:118
Size gain
The gain of this Pattern.
Definition DFSTree.h:120
NodeProperty< Sequence< PRMInstance< GUM_SCALAR > * > * > iso_map
The instances matching p in the interface graph.
Definition DFSTree.h:114
UndiGraph iso_graph
The isomorphism graph of the pattern.
Definition DFSTree.h:112
PatternData(Pattern *p)
Constructor.
Set< NodeId > max_indep_set
The maximal independent set of p.
Definition DFSTree.h:116
std::list< NodeId > children
The list of the pattern's children, sorted lexicographically.
Definition DFSTree.h:110
represent a DFS code used by gspan.
Definition edgeCode.h:72
bool isBackward() const
Returns true if this EdgeCode is a backward edge.
Inner class to handle data about labels in this interface graph.