LAL: Linear Arrangement Library 21.07.01
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
traversal.hpp
1/*********************************************************************
2 *
3 * Linear Arrangement Library - A library that implements a collection
4 * algorithms for linear arrangments of graphs.
5 *
6 * Copyright (C) 2019 - 2021
7 *
8 * This file is part of Linear Arrangement Library. To see the full code
9 * visit the webpage:
10 * https://github.com/lluisalemanypuig/linear-arrangement-library.git
11 *
12 * Linear Arrangement Library is free software: you can redistribute it
13 * and/or modify it under the terms of the GNU Affero General Public License
14 * as published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * Linear Arrangement Library is distributed in the hope that it will be
18 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with Linear Arrangement Library. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * Contact:
26 *
27 * LluĂ­s Alemany Puig (lalemany@cs.upc.edu)
28 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
29 * CQL (Complexity and Quantitative Linguistics Lab)
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://cqllab.upc.edu/people/lalemany/
32 *
33 * Ramon Ferrer i Cancho (rferrericancho@cs.upc.edu)
34 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Office S124, Omega building
37 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
38 * Webpage: https://cqllab.upc.edu/people/rferrericancho/
39 *
40 ********************************************************************/
41
42#pragma once
43
44// C++ includes
45#include <functional>
46#include <queue>
47
48// lal includes
49#include <lal/definitions.hpp>
50#include <lal/graphs/directed_graph.hpp>
51#include <lal/graphs/undirected_graph.hpp>
52#include <lal/internal/data_array.hpp>
53
54namespace lal {
55namespace internal {
56
57/* This class implements an abstract graph breadth-first traversal.
58 *
59 * Users of this class can control the traversal by setting custom control-flow
60 * functions. The user can set:
61 * - a function used for early termination of the traversal
62 * (see @ref set_terminate),
63 * - a function that processes the current node in the traversal
64 * (see @ref set_process_current),
65 * - a function that processes the current edge in the traversal
66 * (see @ref set_process_neighbour),
67 * - a function that can decide when to add another node to the queue of the
68 * traversal (see @ref set_node_add).
69 *
70 * This graph_traversal traversal can also use "reversed edges" when doing traversals on
71 * directed graphs. A back edge in a directed graph of a node u is a node
72 * that points to u, namely, the directed edge is of the form (v,u), for another
73 * node v of the graph. This can be set via the @ref set_use_back_edges method.
74 */
75template<
76 typename G,
77 bool is_directed = std::is_base_of_v<graphs::directed_graph, G>,
78 std::enable_if_t<
79 std::is_base_of_v<graphs::directed_graph, G> ||
80 std::is_base_of_v<graphs::undirected_graph, G>
81 , bool
82 > = true
83>
84class BFS {
85public:
86 typedef std::function<void (const BFS<G>&, node)> BFS_process_one;
87 typedef std::function<void (const BFS<G>&, node, node, bool)> BFS_process_two;
88 typedef std::function<bool (const BFS<G>&, node)> BFS_bool_one;
89 typedef std::function<bool (const BFS<G>&, node, node)> BFS_bool_two;
90
91public:
92 // Constructor
93 BFS(const G& g) : m_G(g), m_vis(m_G.get_num_nodes()) {
94 reset();
95 }
96 // Destructor
97 ~BFS() { }
98
99 // Set the graph_traversal to its default state.
100 void reset() {
101 reset_visited();
102 clear_structure();
103
104 set_use_rev_edges(false);
105 set_process_visited_neighbours(false);
106
107 set_terminate_default();
108 set_process_current_default();
109 set_process_neighbour_default();
110 set_node_add_default();
111 }
112
113 void start_at(node source) {
114 m_X.push(source);
115 m_vis[source] = 1;
116 do_traversal();
117 }
118
119 void start_at(const std::vector<node>& sources) {
120 for (const node& u : sources) {
121 m_X.push(u);
122 m_vis[u] = 1;
123 }
124 do_traversal();
125 }
126
127 /* SETTERS */
128
129 // set whether the traversal can use reversed edges
130 void set_use_rev_edges(bool use) { m_use_rev_edges = use; }
131
132 // see @ref m_term
133 void set_terminate_default()
134 { m_term = [](const BFS<G>&, const node) -> bool { return false; }; }
135 void set_terminate(const BFS_bool_one& f)
136 { m_term = f; }
137
138 // see @ref m_proc_cur
139 void set_process_current_default()
140 { m_proc_cur = [](const BFS<G>&, const node) -> void { }; }
141 void set_process_current(const BFS_process_one& f)
142 { m_proc_cur = f; }
143
144 // see @ref m_proc_neigh
145 void set_process_neighbour_default()
146 { m_proc_neigh = [](const BFS<G>&, const node, const node, bool) -> void { }; }
147 void set_process_neighbour(const BFS_process_two& f)
148 { m_proc_neigh = f; }
149
150 // see @ref m_add_node
151 void set_node_add_default()
152 { m_add_node = [](const BFS<G>&, const node, const node) -> bool { return true; }; }
153 void set_node_add(const BFS_bool_two& f)
154 { m_add_node = f; }
155
156 /*
157 * @brief Should the algorithm call the neighbour processing function
158 * for already visited neighbours?
159 * @param v Either true or false.
160 */
161 void set_process_visited_neighbours(bool v) { m_proc_vis_neighs = v; }
162
163 // Sets all nodes to not visited.
164 void reset_visited() { m_vis.fill(0); }
165
166 void clear_structure() { std::queue<node> q; m_X.swap(q); }
167
168 // Set node @e u as visited.
169 void set_visited(node u, char vis) { m_vis[u] = vis; }
170
171 /* GETTERS */
172
173 // Returns the set of visited nodes.
174 bool node_was_visited(node u) const { return m_vis[u]; }
175
176 // have all nodes been visited?
177 bool all_visited() const {
178 return std::find(m_vis.begin(), m_vis.end(), 0) == m_vis.end();
179 }
180
181 // returns the graph
182 const G& get_graph() const { return m_G; }
183
184 // Return visited nodes information
185 const data_array<char>& get_visited() const { return m_vis; }
186
187protected:
188 // ltr: is the 'natural' orientation of the vertices "s -> t"?
189 // If true, then the edge in the graph is (s,t)
190 // If false, the edge in the graph is (t,s)
191 void deal_with_neighbour(node s, node t, bool ltr) {
192 // Process the neighbour 't' of 's'.
193 if ((m_vis[t] and m_proc_vis_neighs) or not m_vis[t]) {
194 m_proc_neigh(*this, s, t, ltr);
195 }
196
197 if (not m_vis[t] and m_add_node(*this, s, t)) {
198 m_X.push(t);
199 // set node as visited
200 m_vis[t] = true;
201 }
202 }
203
204 // process neighbours
205 // when the graph is an undirected graph
206 template<
207 class GG = G,
208 std::enable_if_t<
209 std::is_base_of_v<graphs::undirected_graph, GG>, bool
210 > = true
211 >
212 void process_neighbours(node s) {
213 for (const node& t : m_G.get_neighbours(s)) {
214 // Edges are processed in the direction "s -> t".
215 // This is also the 'natural' orientation of the edge,
216 // so this explains the 'true'.
217 deal_with_neighbour(s, t, true);
218 }
219 }
220
221 // when the graph is a directed graph
222 template<
223 class GG = G,
224 std::enable_if_t<
225 std::is_base_of_v<graphs::directed_graph, GG>, bool
226 > = true
227 >
228 void process_neighbours(node s) {
229 for (const node& t : m_G.get_out_neighbours(s)) {
230 // Edges are processed in the direction "s -> t".
231 // This is also the 'natural' orientation of the edge,
232 // hence the 'true'.
233 deal_with_neighbour(s, t, true);
234 }
235 // process in-neighbours whenever appropriate
236 if (m_use_rev_edges) {
237 for (const node& t : m_G.get_in_neighbours(s)) {
238 // Edges are processed in the direction "s -> t".
239 // However, the 'natural' orientation of the edge
240 // is "t -> s", hence the 'false'.
241 deal_with_neighbour(s, t, false);
242 }
243 }
244 }
245
246 node next_node() const { return m_X.front(); }
247
248 /* The graph_traversal traversal is implemented as follows:
249 *
250 * <pre>
251 * ProcessNeighbourhood(graph, u, Nv):
252 * 1. for each w in Nv do
253 * 2. if w has not been visited before, or it has been but
254 * 3. already-visited nodes have to be processed
255 * 4. then
256 * 5. proc_neigh(u,w)
257 * 6. endif
258 * 7.
259 * 8. if w not visited before and node_add(w) then
260 * 9. push w into X
261 * 10. mark w as visited in vis
262 * 11. endif
263 * 12. endfor
264 * </pre>
265 *
266 * <pre>
267 * graph_traversal(graph, source):
268 * 1. vis = {false} // set of |V(graph)| bits set to false
269 * 2. X = {source} // structure of the traversal,
270 * // initialised with the source,
271 * // either a stack or a queue.
272 * 3. while X is not empty do
273 * 4. v = X.front or X.top
274 * 5. remove X's top
275 * 6. proc_curr(v)
276 * 7. if terminate(v) then Finish traversal
277 * 8. else
278 * 9. Nv = out-neighbourhood of v
279 * 10. ProcessNeighbourhood(graph, v, Nv)
280 * 11. If graph is directed and process reverse edges then
281 * 12. Nv = in-neighbourhood of v
282 * 13. ProcessNeighbourhood(graph, v, Nv)
283 * 14. endif
284 * 15. endif
285 * 16. endwhile
286 * </pre>
287 *
288 * Note that lines (...) extend the neighbourhood of a node "Nv"
289 * depending of the type of graph. If the graph is directed and
290 * the user wants to process "reversed edges", then the neighbourhood
291 * is not limited to "out-neighbours", but also to "in-neighbours".
292 */
293 void do_traversal() {
294 while (not m_X.empty()) {
295 const node s = next_node();
296 m_X.pop();
297
298 // process current node
299 m_proc_cur(*this, s);
300
301 // check user-defined early termination condition
302 if (m_term(*this, s)) { break; }
303
304 process_neighbours(s);
305 }
306 }
307
308protected:
309 // Constant reference to the graph.
310 const G& m_G;
311 // The structure of the traversal (either a queue or a stack).
312 std::queue<node> m_X;
313 // The set of visited nodes.
314 data_array<char> m_vis;
315 // Should we process already visitied neighbours?
316 bool m_proc_vis_neighs = false;
317 // Use back edges in directed graphs.
318 bool m_use_rev_edges = false;
319
320protected:
321 /*
322 * @brief graph_traversal early terminating function.
323 *
324 * Returns true if the @ref graph_traversal algorithm should terminate.
325 *
326 * For more details on when this function is called see @ref do_traversal.
327 * @param graph_traversal The object containing the traversal. This also contains
328 * several attributes that might be useful to guide the traversal.
329 * @param s The node at the front of the queue of the algorithm.
330 */
331 BFS_bool_one m_term;
332
333 /*
334 * @brief graph_traversal node processing function.
335 *
336 * Processes the current node visited.
337 *
338 * For more details on when this function is called see @ref do_traversal.
339 * @param graph_traversal The object containing the traversal. This also contains
340 * several attributes that might be useful to guide the traversal.
341 * @param s The node at the front of the queue of the algorithm.
342 */
343 BFS_process_one m_proc_cur;
344
345 /*
346 * @brief graph_traversal neighbour node processing function.
347 *
348 * Processes the next visited node. The direction of the nodes
349 * visited passed as parameters is given by the boolean parameter. If
350 * it is true then the edge is s -> t. If it is false then the edge is
351 * t -> s.
352 *
353 * For more details on when this function is called see @ref do_traversal.
354 * @param graph_traversal The object containing the traversal. This also contains
355 * several attributes that might be useful to guide the traversal.
356 * @param s The node at the front of the queue of the algorithm.
357 * @param t The node neighbour of @e u visited by the algorithm.
358 */
359 BFS_process_two m_proc_neigh;
360
361 /*
362 * @brief graph_traversal node addition function.
363 *
364 * Determines whether a node @e s should be added to the queue or not.
365 *
366 * For more details on when this function is called see @ref do_traversal.
367 * @param graph_traversal The object containing the traversal. This also contains
368 * several attributes that might be useful to guide the traversal.
369 * @param s The node candidate for addition.
370 */
371 BFS_bool_two m_add_node;
372};
373
374} // -- namespace internal
375} // -- namespace lal
Main namespace of the library.
Definition definitions.hpp:48
uint32_t node
Node type.
Definition definitions.hpp:51