Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_cut_nodes.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
87# ifndef TPL_CUT_NODES_H
88# define TPL_CUT_NODES_H
89
90# include <tpl_graph_utils.H>
91
92namespace Aleph
93{
150 template <class GT, class SA = Dft_Show_Arc<GT>>
152 {
154 GT *gptr = nullptr;
156 long curr_df = 0;
157 long curr_color = 1;
158
160
161 void cut_nodes(typename GT::Node *p, typename GT::Arc *a)
162 {
163 NODE_BITS(p).set_bit(Depth_First, true); // mark p as visited
164 low<GT>(p) = df<GT>(p) = curr_df++; // assign df number
165
166 // traverse arcs of p
167 bool p_is_cut_node = false;
168 for (Node_Arc_Iterator<GT, SA> i(p, sa); i.has_curr(); i.next_ne())
169 {
170 auto arc = i.get_curr();
171 if (arc == a)
172 continue; // a is the parent arc ==> ignore it
173
174 auto tgt = i.get_tgt_node();
176 {
177 if (not IS_ARC_VISITED(arc, Depth_First)) // non-tree arc?
178 low<GT>(p) = std::min(df<GT>(tgt), low<GT>(p));
179 continue;
180 }
181
182 if (IS_ARC_VISITED(arc, Depth_First))
183 continue;
184
185 ARC_BITS(arc).set_bit(Depth_First, true); // mark arc
186
187 cut_nodes(tgt, arc);
188 low<GT>(p) = std::min(low<GT>(tgt), low<GT>(p));
189 if (low<GT>(tgt) >= df<GT>(p) and df<GT>(tgt) != 0) // cut node?
190 p_is_cut_node = true;
191 }
192
193 // at this point, p has already been explored recursively
194 if (p_is_cut_node)
195 {
196 NODE_BITS(p).set_bit(Cut, true);
197 list_ptr->append(p);
198 }
199 }
200
201 public:
214 void cut_nodes(typename GT::Node *start,
216 {
217 curr_df = 0; // global visit counter
218 list_ptr = &list;
219
220 list_ptr->empty();
221
222 gptr->for_each_node([](auto p) // initialize nodes
223 {
224 NODE_COUNTER(p) = 0;
225 NODE_BITS(p).reset();
226 low<GT>(p) = -1;
227 });
228 gptr->reset_arcs();
229
230 NODE_BITS(start).set_bit(Depth_First, true); // mark start
231 df<GT>(start) = curr_df++;
232
233 int call_counter = 0; // recursion call counter
234
235 // Traverse arcs from start while the graph is not fully spanned
236 for (Node_Arc_Iterator<GT, SA> it(start, sa);
237 it.has_curr() and curr_df < gptr->get_num_nodes(); it.next_ne())
238 {
239 auto tgt = it.get_tgt_node();
241 continue;
242
243 auto arc = it.get_curr();
244 if (IS_ARC_VISITED(arc, Depth_First))
245 continue;
246
247 ARC_BITS(arc).set_bit(Depth_First, true);
248 cut_nodes(tgt, arc);
249 ++call_counter;
250 }
251
252 if (call_counter > 1) // is the root an articulation point?
253 { // yes ==> append it to the list
254 NODE_BITS(start).set_bit(Cut, true);
255 list_ptr->append(start);
256 }
257
259 }
260
261 private:
262 void paint_subgraph(typename GT::Node *p)
263 {
265
266 if (is_node_painted<GT>(p))
267 return;
268
270
271 for (Node_Arc_Iterator<GT, SA> it(p, sa); it.has_curr(); it.next_ne())
272 {
273 auto arc = it.get_curr();
274 if (is_arc_painted<GT>(arc))
275 continue;
276
277 auto tgt = it.get_tgt_node();
278 if (is_a_cut_node<GT>(tgt))
279 continue;
280
282 paint_subgraph(tgt);
283 }
284 }
285
287 {
289
290 // Paint connected blocks adjacent to p with different colors
291 for (Node_Arc_Iterator<GT, SA> it(p, sa); it.has_curr(); it.next_ne())
292 {
293 auto arc = it.get_curr();
294
296
297 auto tgt_node = it.get_tgt_node();
298 if (is_a_cut_node<GT>(tgt_node)) // is it a cut arc?
299 {
300 ARC_BITS(arc).set_bit(Cut, true); // mark as cut arc
301 continue; // move to next arc
302 }
303 paint_arc<GT>(arc, Cross_Arc); // mark as cross arc
304 if (is_node_painted<GT>(tgt_node))
305 continue;
306
307 // paint the block reachable through this arc
308 paint_subgraph(tgt_node);
309
310 curr_color++; // next color (next arc belongs to a different block)
311
313 }
314 }
315
317 const long & color,
318 GT & sg)
319 {
320 (void) color;
323
324 std::unique_ptr<typename GT::Node> tp_auto(new typename GT::Node(gp));
325 sg.insert_node(tp_auto.get());
326 GT::map_nodes(gp, tp_auto.get());
327 NODE_BITS(gp).set_bit(Build_Subtree, true);
328
329 return tp_auto.release();
330 }
331
332 void map_subgraph(GT & sg, typename GT::Node *gsrc, const long & color)
333 {
335
336 auto tsrc = mapped_node<GT>(gsrc); // gsrc mapped into sg
337
338 // Traverse arcs of gsrc and insert into sg those with the requested color
339 for (Node_Arc_Iterator<GT, SA> i(gsrc, sa); i.has_curr(); i.next_ne())
340 {
341 auto garc = i.get_curr();
343 continue; // arc has a different color or was already visited
344
345 ARC_BITS(garc).set_bit(Build_Subtree, true);
346
347 auto gtgt = i.get_tgt_node();
348
350
351 typename GT::Node *ttgt = nullptr; // gtgt mapped into sg
352 if (IS_NODE_VISITED(gtgt, Build_Subtree)) // already in sg?
354 else
356
357 auto tarc = sg.insert_arc(tsrc, ttgt, garc->get_info());
359
360 map_subgraph(sg, gtgt, color);
361 }
362 }
363
364 public:
370 Compute_Cut_Nodes(const GT & g, SA __sa = SA())
371 : sa(__sa), gptr(&const_cast<GT &>(g)), state(Init)
372 {
373 /* empty */
374 }
375
386
394 void operator ()(typename GT::Node *start,
396 {
397 cut_nodes(start, list);
398 }
399
431 {
433 << "Cut nodes have not been computed or the class is in another phase";
434
437 curr_color = 1;
438
439 // Traverse each cut node and paint its adjacent blocks
441 i.has_curr(); i.next_ne())
442 paint_from_cut_node(i.get_curr());
443
444 state = Painted;
445
446 return curr_color;
447 }
448
459 void map_subgraph(GT & sg, const long & color)
460 {
461 ah_logic_error_if(state != Painted) << "Graph is not painted";
462
463 clear_graph(sg);
464
465 typename GT::Node *first = nullptr; // find the first node with the requested color
466
467 for (typename GT::Node_Iterator it(*gptr); it.has_curr(); it.next_ne())
468 if (get_color<GT>(it.get_curr()) == color)
469 first = it.get_curr();
470
471 if (first == nullptr) // did we find the color?
472 ah_domain_error_if(first == nullptr) << "Color does not exist in the graph";
473
474 // create first, insert it into sg, and map it
475 create_and_map_node(first, color, sg);
476 try
477 {
478 map_subgraph(sg, first, color); // map the component
479 }
480 catch (...)
481 {
482 clear_graph(sg);
483 }
484 }
485
506 {
507 ah_logic_error_if(state != Painted) << "Graph is not painted";
508
510
511 // Traverse the cut-node list and insert them into cut_graph
513 it.has_curr(); it.next_ne())
514 {
515 auto gp = it.get_curr();
516
518
519 std::unique_ptr<typename GT::Node> tp_auto(new typename GT::Node(gp));
520 cut_graph.insert_node(tp_auto.get());
521 GT::map_nodes(gp, tp_auto.release());
522 }
523
524 // Traverse arcs of g:
525 // - cut_graph will contain cut arcs (between cut nodes)
526 // - cross_arc_list will contain cross arcs (from cut nodes to blocks)
527 for (Arc_Iterator<GT, SA> it(*gptr, sa); it.has_curr(); it.next_ne())
528 {
529 auto garc = it.get_curr();
531 {
532 cross_arc_list.append(garc);
533 continue;
534 }
535
537 continue;
538
539 typename GT::Node *src = mapped_node<GT>(gptr->get_src_node(garc));
540 typename GT::Node *tgt = mapped_node<GT>(gptr->get_tgt_node(garc));
541
542 assert(src != nullptr and tgt != nullptr);
543
544 typename GT::Arc *arc =
545 cut_graph.insert_arc(src, tgt, garc->get_info());
546 GT::map_arcs(garc, arc);
547 }
548 }
549
568 GT & cut_graph,
570 {
571 ah_logic_error_if(state < Cut_Nodes_Computed) << "Cut nodes have not been computed";
572
575
576 // curr_color is the NEXT unused color, so valid colors are 1 .. curr_color-1.
577 // We allocate curr_color slots (indices 0 .. curr_color-1) so that
578 // block at color c lives at blocks[c-1]; slot [0] remains unused / empty.
579 const long & num_colors = curr_color;
580
581 DynArray<GT *> blocks; // blocks in an array for fast access
582 blocks.reserve(num_colors);
583
584 // Create an ordered list of empty components by color i
585 for (int i = 0; i < num_colors; ++i)
586 blocks.access(i) = &block_list.append(GT());
587
588 // Traverse nodes and copy/map according to color
589 for (typename GT::Node_Iterator it(*gptr); it.has_curr(); it.next_ne())
590 {
591 auto p = it.get_curr();
593 continue;
594
595 if (is_a_cut_node<GT>(p))
596 continue;
597
598 const long color = get_color<GT>(p);
599
600 GT & sg = *blocks.access(color - 1);
601
603
604 map_subgraph(sg, p, color);
605 }
606
608 }
609 };
610
611 // =========================================================================
612 // Bridge finding (Tarjan low-link algorithm)
613 // =========================================================================
614
651 template <class GT, class SA = Dft_Show_Arc<GT>>
653 {
656
657 void __dfs(typename GT::Node *p, typename GT::Arc *parent_arc,
658 long & curr_df, DynList<typename GT::Arc *> & bridges)
659 {
660 NODE_BITS(p).set_bit(Depth_First, true);
661 low<GT>(p) = df<GT>(p) = curr_df++;
662
663 for (Node_Arc_Iterator<GT, SA> it(p, sa); it.has_curr(); it.next_ne())
664 {
665 auto arc = it.get_curr();
666 if (arc == parent_arc)
667 continue; // skip the arc we came from
668
669 auto tgt = it.get_tgt_node();
670
672 {
673 // Back-edge: update low if this arc has not been traversed yet
675 low<GT>(p) = std::min(df<GT>(tgt), low<GT>(p));
676 continue;
677 }
678
679 if (IS_ARC_VISITED(arc, Depth_First))
680 continue;
681
682 ARC_BITS(arc).set_bit(Depth_First, true); // mark tree arc
683
684 __dfs(tgt, arc, curr_df, bridges);
685
686 low<GT>(p) = std::min(low<GT>(tgt), low<GT>(p));
687
688 if (low<GT>(tgt) > df<GT>(p)) // bridge condition
689 bridges.append(arc);
690 }
691 }
692
693 public:
699 Compute_Bridges(const GT & g, SA sa = SA())
700 : sa(sa), gptr(&const_cast<GT &>(g))
701 { /* empty */
702 }
703
720 {
721 ah_domain_error_if(gptr->is_digraph()) << "Compute_Bridges: does not work on digraphs";
722
724
725 if (gptr->get_num_nodes() == 0)
726 return bridges;
727
728 ah_invalid_argument_if(start == nullptr) << "Compute_Bridges::find_bridges(): start must be non-null";
729
730 gptr->for_each_node([](auto p)
731 {
732 NODE_COUNTER(p) = 0;
733 NODE_BITS(p).reset();
734 });
735 gptr->reset_arcs();
736
737 long curr_df = 0;
738 __dfs(start, nullptr, curr_df, bridges);
739
740 // Cover disconnected components: any node not reached from start.
741 gptr->for_each_node([&](auto p)
742 {
744 __dfs(p, nullptr, curr_df, bridges);
745 });
746
747 // NODE_COOKIE holds low-link values (stored as longs); clear them.
748 gptr->for_each_node([](auto p) { NODE_COOKIE(p) = nullptr; });
749
750 return bridges;
751 }
752
764
770 {
771 return find_bridges(start);
772 }
773
782 };
783
805 template <class GT, class SA = Dft_Show_Arc<GT>>
807 find_bridges(const GT & g, typename GT::Node *start, SA sa = SA())
808 {
809 return Compute_Bridges<GT, SA>(g, sa).find_bridges(start);
810 }
811
816 template <class GT>
818 {
819 return Compute_Bridges<GT>(g).find_bridges();
820 }
821} // end namespace Aleph
822
823# endif // TPL_CUT_NODES_H
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_logic_error_if(C)
Throws std::logic_error if condition holds.
Definition ah-errors.H:325
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
Find bridge edges (isthmuses) of a connected undirected graph.
DynList< typename GT::Arc * > operator()(typename GT::Node *start)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Compute_Bridges(const GT &g, SA sa=SA())
Constructor.
DynList< typename GT::Arc * > find_bridges()
This is an overloaded member function, provided for convenience. It differs from the above function o...
DynList< typename GT::Arc * > find_bridges(typename GT::Node *start)
Find all bridge edges in the graph, starting from start.
DynList< typename GT::Arc * > operator()()
This is an overloaded member function, provided for convenience. It differs from the above function o...
void __dfs(typename GT::Node *p, typename GT::Arc *parent_arc, long &curr_df, DynList< typename GT::Arc * > &bridges)
Computation of cut nodes (articulation points) of a graph.
void map_cut_graph(GT &cut_graph, DynDlist< typename GT::Arc * > &cross_arc_list)
Computes the mapped cut graph of a graph.
void map_subgraph(GT &sg, typename GT::Node *gsrc, const long &color)
GT::Node * create_and_map_node(typename GT::Node *gp, const long &color, GT &sg)
enum Aleph::Compute_Cut_Nodes::State state
void map_subgraph(GT &sg, const long &color)
Obtains a mapped copy of the component with the given color.
void operator()(DynDlist< typename GT::Node * > &list)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void paint_subgraph(typename GT::Node *p)
DynDlist< typename GT::Node * > * list_ptr
Compute_Cut_Nodes(const GT &g, SA __sa=SA())
Constructor for cut nodes calculator.
void compute_blocks(DynDlist< GT > &block_list, GT &cut_graph, DynDlist< typename GT::Arc * > &cross_arc_list)
Build mapped graphs around cut nodes.
void cut_nodes(typename GT::Node *start, DynDlist< typename GT::Node * > &list)
Computes the cut nodes.
long paint_subgraphs()
Paints the connected components around the cut nodes.
void cut_nodes(typename GT::Node *p, typename GT::Arc *a)
void paint_from_cut_node(typename GT::Node *p)
T & access(const size_t i) const noexcept
Fast access without checking allocation and bound_min_clock checking.
void reserve(const size_t l, const size_t r)
Allocate a range of entries.
Iterator dynamic list.
Dynamic doubly linked list with O(1) size and bidirectional access.
void empty() noexcept
@brief Empties the container.
T & append(const T &item)
Append a copied item at the end of the list.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Node * get_first_node() const
Return any node in the graph.
Definition tpl_graph.H:576
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
NodeInfo & get_info() noexcept
Return a modifiable reference to the data contained in the node.
Definition graph-dry.H:494
void reset_arcs() const
Reset all the arcs of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:933
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
bool is_digraph() const noexcept
Return true if the graph this is directed.
Definition graph-dry.H:657
static void map_arcs(A1 *p, A2 *q) noexcept
Map the arcs through their cookies.
Definition graph-dry.H:1032
void reset_counter_nodes() const noexcept
Reset all the counters to zero for all the nodes of graph.
Definition graph-dry.H:1070
void reset_counter_arcs() const noexcept
Reset all the counters to zero for all the arcs of graph.
Definition graph-dry.H:1076
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
static void map_nodes(N1 *p, N2 *q) noexcept
Map the nodes through their cookies.
Definition graph-dry.H:1001
void for_each_node(Operation &operation) const
Unconditionally traverse all the nodes of graph and on each one perform an operation.
Definition graph-dry.H:1482
#define NODE_COUNTER(p)
Get the counter of a node.
#define IS_NODE_VISITED(p, bit)
Determine whether the control bit is set or not to one.
#define ARC_BITS(p)
Return the control bits of arc p.
#define NODE_COOKIE(p)
Return the node cookie
void clear_graph(GT &g) noexcept
Clean a graph: all its nodes and arcs are removed and freed.
Definition tpl_graph.H:3556
#define IS_ARC_VISITED(p, bit)
Determine whether the bit field is or not set to one.
DynList< typename GT::Arc * > find_bridges(const GT &g, typename GT::Node *start, SA sa=SA())
Find all bridge edges in a connected undirected graph.
#define NODE_BITS(p)
Get the control bits of a node.
@ Depth_First
Definition aleph-graph.H:73
@ Build_Subtree
Definition aleph-graph.H:80
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
const long Cross_Arc
Special marker for arcs connecting a cut node to a non-cut block.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
static long & color(typename GT::Node *p)
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
Utility algorithms and operations for graphs.