aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
pattern.cpp
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
41
48
50
51#ifdef GUM_NO_INLINE
53#endif // GUM_NO_INLINE
54
55namespace gum::prm::gspan {
56
57 Pattern::Pattern(const Pattern& source) : DiGraph() {
58 GUM_CONS_CPY(Pattern);
60
61 for (NodeId node = 1; node <= source.size(); ++node) {
62 node_map.insert(node, addNodeWithLabel(const_cast< LabelData& >(source.label(node))));
63 }
64
65 for (const auto& edge: source.code().codes)
66 addArc(node_map[edge->i],
67 node_map[edge->j],
68 const_cast< LabelData& >(source.label(node_map[edge->i], node_map[edge->j])));
69 }
70
72 for (const auto node: nodes()) {
73 for (const auto next: parents(node)) {
74 Size u = label(node).id;
75 Size v = label(next).id;
76 EdgeCode edge_code(1, 2, u, label(next, node).id, v);
77
78 if (edge_code < *(code().codes.front())) {
79 return false;
80 } else if (edge_code == (*code().codes.front())) {
81 if (_expandCodeIsMinimal_(node, next)) { return false; }
82 }
83 }
84
85 for (const auto next: children(node)) {
86 Size u = label(node).id;
87 Size v = label(next).id;
88 EdgeCode edge_code(1, 2, u, label(node, next).id, v);
89
90 if (edge_code < *(code().codes.front())) {
91 return false;
92 } else if (edge_code == (*code().codes.front())) {
93 if (_expandCodeIsMinimal_(node, next)) { return false; }
94 }
95 }
96 }
97
98 return true;
99 }
100
101 std::string Pattern::toDot(size_t name) const {
102 std::stringstream sBuff;
103 sBuff << "digraph " << name << " {\n";
104
105 for (const auto& arc: arcs()) {
106 sBuff << label(arc.tail()).id << " -> ";
107 sBuff << label(arc.head()).id << ";\n";
108 }
109
110 sBuff << "}\n";
111 return sBuff.str();
112 }
113
116 Pattern p;
117 node_map.insert(u, p.addNodeWithLabel(label(u)));
118 node_map.insert(v, p.addNodeWithLabel(label(v)));
119
120 try {
121 p.addArc(1, 2, label(u, v));
122 } catch (NotFound const&) { p.addArc(1, 2, label(v, u)); }
123
124 for (const auto nei: children(u))
125 if (nei != v)
126 if (_rec_(p, node_map, u, nei)) return true;
127
128 for (const auto nei: parents(u))
129 if (nei != v)
130 if (_rec_(p, node_map, u, nei)) return true;
131
132 for (const auto nei: children(v))
133 if (nei != u)
134 if (_rec_(p, node_map, v, nei)) return true;
135
136 for (const auto nei: parents(v))
137 if (nei != u)
138 if (_rec_(p, node_map, v, nei)) return true;
139
140 return false;
141 }
142
144 if (node_map.existsFirst(v)) {
145 if (node_map.second(u) < node_map.second(v)) {
146 // Invalid forward edge
147 return false;
148 } else if ((p.existsArc(node_map.second(u), node_map.second(v)))
149 || (p.existsArc(node_map.second(v), node_map.second(u)))) {
150 // Duplicate arc !
151 return false;
152 }
153 } else {
154 node_map.insert(v, p.addNodeWithLabel(label(v)));
155 }
156
157 // Retrieving arc label data
158 LabelData* data = 0;
159
160 try {
161 data = &(label(u, v));
162 } catch (NotFound const&) { data = &(label(v, u)); }
163
164 // Adding arc
165 try {
166 p.addArc(node_map.second(u), node_map.second(v), *data);
167 } catch (OperationNotAllowed const&) {
168 // Invalid neighbor
169 if (node_map.second(u) < node_map.second(v)) {
170 p.remove(node_map.second(v));
171 node_map.eraseFirst(v);
172 }
173
174 return false;
175 }
176
177 // Check if this is minimal or if equal find another growth
178 if (size_t depth = p.code().codes.size() - 1;
179 *(p.code().codes.back()) < *(code().codes[depth])) {
180 return true;
181 } else if (*(p.code().codes.back()) == *(code().codes[depth])) {
182 std::list< NodeId > r_path;
183 p.rightmostPath(r_path);
184
185 for (const auto node: r_path) {
186 for (const auto nei: children(node_map.first(node)))
187 if (_rec_(p, node_map, node_map.first(node), nei)) return true;
188
189 for (const auto nei: parents(node_map.first(node)))
190 if (_rec_(p, node_map, node_map.first(node), nei)) return true;
191 }
192 }
193
194 if (p.code().codes.back()->isForward()) node_map.eraseFirst(v);
195
196 p.pop_back();
197 return false;
198 }
199
202 NodeId a_u,
203 NodeId a_v) {
204 std::vector< std::pair< NodeId, NodeId > > stack;
205 stack.emplace_back(a_u, a_v);
206 NodeId u = 0;
207 NodeId v = 0;
208
209 while (!stack.empty()) {
210 bool go = true;
211 u = stack.back().first;
212 v = stack.back().second;
213 stack.pop_back();
214
215 if ((u == 0) && (v == 0)) {
216 p.pop_back();
217 } else {
218 if (node_map.existsFirst(v)) {
219 if (node_map.second(u) < node_map.second(v)) {
220 // Invalid forward edge
221 go = false;
222 } else if ((p.existsArc(node_map.second(u), node_map.second(v)))
223 || (p.existsArc(node_map.second(v), node_map.second(u)))) {
224 // Duplicate arc !
225 go = false;
226 }
227 } else {
228 node_map.insert(v, p.addNodeWithLabel(label(v)));
229 }
230
231 if (go) {
232 // Retrieving arc label data
233 LabelData* data = 0;
234
235 try {
236 data = &(label(u, v));
237 } catch (NotFound const&) { data = &(label(v, u)); }
238
239 // Adding arc
240 try {
241 p.addArc(node_map.second(u), node_map.second(v), *data);
242 } catch (OperationNotAllowed const&) {
243 // Invalid neighbor
244 if (node_map.second(u) < node_map.second(v)) {
245 p.remove(node_map.second(v));
246 node_map.eraseFirst(v);
247 }
248
249 go = false;
250 }
251
252 if (go) {
253 // Check if this is minimal or if equal find another growth
254 if (size_t depth = p.code().codes.size() - 1;
255 *(p.code().codes.back()) < *(code().codes[depth])) {
256 return true;
257 } else if (*(p.code().codes.back()) == *(code().codes[depth])) {
258 std::list< NodeId > r_path;
259 p.rightmostPath(r_path);
260 stack.emplace_back((NodeId)0, (NodeId)0);
261
262 for (const auto node: r_path) {
263 for (const auto nei: children(node)) {
264 stack.emplace_back(node_map.first(node), nei);
265 }
266
267 for (const auto nei: parents(node)) {
268 stack.emplace_back(node_map.first(node), nei);
269 }
270 }
271 }
272
273 if (p.code().codes.back()->isForward()) node_map.eraseFirst(v);
274 }
275 }
276 }
277 }
278
279 return false;
280 }
281
282} // namespace gum::prm::gspan
bool existsArc(const Arc &arc) const
indicates whether a given arc exists
const NodeSet & parents(NodeId id) const
returns the set of nodes with arc ingoing to a given node
NodeSet children(const NodeSet &ids) const
returns the set of children of a set of nodes
void eraseFirst(const T1 &first)
Erases an association containing the given first element.
bool existsFirst(const T1 &first) const
Returns true if first is the first element in a pair in the gum::Bijection.
void insert(const T1 &first, const T2 &second)
Inserts a new association in the gum::Bijection.
const T1 & first(const T2 &second) const
Returns the first value of a pair given its second value.
const T2 & second(const T1 &first) const
Returns the second value of a pair given its first value.
Set of pairs of elements with fast search for both elements.
Definition bijection.h:1594
DiGraph(Size nodes_size=HashTableConst::default_size, bool nodes_resize_policy=true, Size arcs_size=HashTableConst::default_size, bool arcs_resize_policy=true)
default constructor
Definition diGraph.cpp:69
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
std::vector< EdgeCode * > codes
The vector containing the EdgeCode composing this DFSCode.
Definition DFSCode.h:109
bool _expandCodeIsMinimal_(NodeId u, NodeId v)
Returns true if the expand code by adding and edge betwenne u and v is minimal with respect to code.
Definition pattern.cpp:114
void rightmostPath(std::list< NodeId > &r_path) const
Fill r_path with the rightmost path of this Pattern. The list is supposed empty.
const ArcSet & arcs() const
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
Size size() const
Returns the number of nodes in this Pattern.
virtual std::string toDot() const
to friendly display the content of the graph in the DOT syntax
Definition diGraph.cpp:88
void pop_back()
Remove the last EdgeCode of this pattern.
DFSCode & code()
Returns the DFSCode of this Pattern.
Pattern()
Default constructor.
Definition pattern_inl.h:54
void remove(NodeId node)
Remove a node if it has no neighbors, raise an OperationNotAllowed otherwise.
void addArc(NodeId i, NodeId j, LabelData &l)
Add an arc to this Pattern.
const NodeGraphPart & nodes() const
bool _rec_(Pattern &p, Bijection< NodeId, NodeId > &node_map, NodeId u, NodeId v)
Recurisve method used by expandCodeIsMinimal.
Definition pattern.cpp:143
LabelData & label(NodeId node)
Returns the LabelData assigned to node.
Definition pattern_inl.h:75
bool _not_rec_(Pattern &p, Bijection< NodeId, NodeId > &node_map, NodeId u, NodeId v)
A non recursive bugged version of rec.
Definition pattern.cpp:200
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size NodeId
Type for node ids.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Headers of the Pattern class.
Inline implementation of the Pattern class.
represent a DFS code used by gspan.
Definition edgeCode.h:72
Inner class to handle data about labels in this interface graph.
Idx id
An unique identifier for this label.