aGrUM 3.1.1
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-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
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::string result = std::format("digraph {} {{\n", name);
103
104 for (const auto& arc: arcs())
105 result += std::format("{} -> {};\n", label(arc.tail()).id, label(arc.head()).id);
106
107 result += "}\n";
108 return result;
109 }
110
113 Pattern p;
114 node_map.insert(u, p.addNodeWithLabel(label(u)));
115 node_map.insert(v, p.addNodeWithLabel(label(v)));
116
117 if (_arc_map_.exists(Arc(u, v))) {
118 p.addArc(1, 2, label(u, v));
119 } else {
120 p.addArc(1, 2, label(v, u));
121 }
122
123 for (const auto nei: children(u))
124 if (nei != v)
125 if (_rec_(p, node_map, u, nei)) return true;
126
127 for (const auto nei: parents(u))
128 if (nei != v)
129 if (_rec_(p, node_map, u, nei)) return true;
130
131 for (const auto nei: children(v))
132 if (nei != u)
133 if (_rec_(p, node_map, v, nei)) return true;
134
135 for (const auto nei: parents(v))
136 if (nei != u)
137 if (_rec_(p, node_map, v, nei)) return true;
138
139 return false;
140 }
141
143 if (node_map.existsFirst(v)) {
144 if (node_map.second(u) < node_map.second(v)) {
145 // Invalid forward edge
146 return false;
147 } else if ((p.existsArc(node_map.second(u), node_map.second(v)))
148 || (p.existsArc(node_map.second(v), node_map.second(u)))) {
149 // Duplicate arc !
150 return false;
151 }
152 } else {
153 node_map.insert(v, p.addNodeWithLabel(label(v)));
154 }
155
156 // Retrieving arc label data
157 LabelData* data = 0;
158
159 if (_arc_map_.exists(Arc(u, v))) {
160 data = &(label(u, v));
161 } else {
162 data = &(label(v, u));
163 }
164
165 // Adding arc
166 try {
167 p.addArc(node_map.second(u), node_map.second(v), *data);
168 } catch (OperationNotAllowed const&) {
169 // Invalid neighbor
170 if (node_map.second(u) < node_map.second(v)) {
171 p.remove(node_map.second(v));
172 node_map.eraseFirst(v);
173 }
174
175 return false;
176 }
177
178 // Check if this is minimal or if equal find another growth
179 if (size_t depth = p.code().codes.size() - 1;
180 *(p.code().codes.back()) < *(code().codes[depth])) {
181 return true;
182 } else if (*(p.code().codes.back()) == *(code().codes[depth])) {
183 std::list< NodeId > r_path;
184 p.rightmostPath(r_path);
185
186 for (const auto node: r_path) {
187 for (const auto nei: children(node_map.first(node)))
188 if (_rec_(p, node_map, node_map.first(node), nei)) return true;
189
190 for (const auto nei: parents(node_map.first(node)))
191 if (_rec_(p, node_map, node_map.first(node), nei)) return true;
192 }
193 }
194
195 if (p.code().codes.back()->isForward()) node_map.eraseFirst(v);
196
197 p.pop_back();
198 return false;
199 }
200
203 NodeId a_u,
204 NodeId a_v) {
205 std::vector< std::pair< NodeId, NodeId > > stack;
206 stack.emplace_back(a_u, a_v);
207 NodeId u = 0;
208 NodeId v = 0;
209
210 while (!stack.empty()) {
211 bool go = true;
212 u = stack.back().first;
213 v = stack.back().second;
214 stack.pop_back();
215
216 if ((u == 0) && (v == 0)) {
217 p.pop_back();
218 } else {
219 if (node_map.existsFirst(v)) {
220 if (node_map.second(u) < node_map.second(v)) {
221 // Invalid forward edge
222 go = false;
223 } else if ((p.existsArc(node_map.second(u), node_map.second(v)))
224 || (p.existsArc(node_map.second(v), node_map.second(u)))) {
225 // Duplicate arc !
226 go = false;
227 }
228 } else {
229 node_map.insert(v, p.addNodeWithLabel(label(v)));
230 }
231
232 if (go) {
233 // Retrieving arc label data
234 LabelData* data = 0;
235
236 if (_arc_map_.exists(Arc(u, v))) data = &(label(u, v));
237 else 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 void Pattern::rightmostPath(std::list< NodeId >& r_path) const {
283 r_path.push_back(NodeId(size()));
284
285 while (r_path.front() != 1) {
286 for (const auto par: parents(r_path.front())) {
287 if (par < r_path.front()) {
288 r_path.push_front(par);
289 break;
290 }
291 }
292 }
293 }
294} // 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 nodes which consists in the node and its parents returns the set of children of a ...
The base class for all directed edges.
const T2 & second(const T1 &first) const
Returns the second value of a pair given its first value.
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.
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.
Set of pairs of elements with fast search for both elements.
Definition bijection.h:1640
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:70
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
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:111
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:69
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:93
void pop_back()
Remove the last EdgeCode of this pattern.
DFSCode & code()
Returns the DFSCode of this Pattern.
ArcProperty< std::pair< LabelData *, EdgeCode * > > _arc_map_
Mapping between edges in this Pattern and their respective LabelData.
Definition pattern.h:230
Pattern()
Default constructor.
Definition pattern_inl.h:57
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.
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
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:142
LabelData & label(NodeId node)
Returns the LabelData assigned to node.
Definition pattern_inl.h:78
bool _not_rec_(Pattern &p, Bijection< NodeId, NodeId > &node_map, NodeId u, NodeId v)
A non recursive bugged version of rec.
Definition pattern.cpp:201
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.