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 <ah-graph-concepts.H>
91
92# include <tpl_graph_utils.H>
93
94namespace Aleph
95{
152 template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
154 {
155 SA sa;
156 GT *gptr = nullptr;
158 long curr_df = 0;
159 long curr_color = 1;
160
162
163 void cut_nodes(typename GT::Node *p, typename GT::Arc *a)
164 {
165 NODE_BITS(p).set_bit(Depth_First, true); // mark p as visited
166 low<GT>(p) = df<GT>(p) = curr_df++; // assign df number
167
168 // traverse arcs of p
169 bool p_is_cut_node = false;
170 for (Node_Arc_Iterator<GT, SA> i(p, sa); i.has_curr(); i.next_ne())
171 {
172 auto arc = i.get_curr();
173 if (arc == a)
174 continue; // a is the parent arc ==> ignore it
175
176 auto tgt = i.get_tgt_node();
178 {
179 if (not IS_ARC_VISITED(arc, Depth_First)) // non-tree arc?
180 low<GT>(p) = std::min(df<GT>(tgt), low<GT>(p));
181 continue;
182 }
183
184 if (IS_ARC_VISITED(arc, Depth_First))
185 continue;
186
187 ARC_BITS(arc).set_bit(Depth_First, true); // mark arc
188
189 cut_nodes(tgt, arc);
190 low<GT>(p) = std::min(low<GT>(tgt), low<GT>(p));
191 if (low<GT>(tgt) >= df<GT>(p) and df<GT>(tgt) != 0) // cut node?
192 p_is_cut_node = true;
193 }
194
195 // at this point, p has already been explored recursively
196 if (p_is_cut_node)
197 {
198 NODE_BITS(p).set_bit(Cut, true);
199 list_ptr->append(p);
200 }
201 }
202
203 public:
216 void cut_nodes(typename GT::Node *start,
218 {
219 curr_df = 0; // global visit counter
220 list_ptr = &list;
221
222 list_ptr->empty();
223
224 gptr->for_each_node([](auto p) // initialize nodes
225 {
226 NODE_COUNTER(p) = 0;
227 NODE_BITS(p).reset();
228 low<GT>(p) = -1;
229 });
230 gptr->reset_arcs();
231
232 NODE_BITS(start).set_bit(Depth_First, true); // mark start
233 df<GT>(start) = curr_df++;
234
235 int call_counter = 0; // recursion call counter
236
237 // Traverse arcs from start while the graph is not fully spanned
238 for (Node_Arc_Iterator<GT, SA> it(start, sa);
239 it.has_curr() and curr_df < gptr->get_num_nodes(); it.next_ne())
240 {
241 auto tgt = it.get_tgt_node();
243 continue;
244
245 auto arc = it.get_curr();
246 if (IS_ARC_VISITED(arc, Depth_First))
247 continue;
248
249 ARC_BITS(arc).set_bit(Depth_First, true);
250 cut_nodes(tgt, arc);
251 ++call_counter;
252 }
253
254 if (call_counter > 1) // is the root an articulation point?
255 { // yes ==> append it to the list
256 NODE_BITS(start).set_bit(Cut, true);
257 list_ptr->append(start);
258 }
259
261 }
262
263 private:
264 void paint_subgraph(typename GT::Node *p)
265 {
267
268 if (is_node_painted<GT>(p))
269 return;
270
272
273 for (Node_Arc_Iterator<GT, SA> it(p, sa); it.has_curr(); it.next_ne())
274 {
275 auto arc = it.get_curr();
276 if (is_arc_painted<GT>(arc))
277 continue;
278
279 auto tgt = it.get_tgt_node();
280 if (is_a_cut_node<GT>(tgt))
281 continue;
282
284 paint_subgraph(tgt);
285 }
286 }
287
289 {
291
292 // Paint connected blocks adjacent to p with different colors
293 for (Node_Arc_Iterator<GT, SA> it(p, sa); it.has_curr(); it.next_ne())
294 {
295 auto arc = it.get_curr();
296
298
299 auto tgt_node = it.get_tgt_node();
300 if (is_a_cut_node<GT>(tgt_node)) // is it a cut arc?
301 {
302 ARC_BITS(arc).set_bit(Cut, true); // mark as cut arc
303 continue; // move to next arc
304 }
305 paint_arc<GT>(arc, Cross_Arc); // mark as cross arc
306 if (is_node_painted<GT>(tgt_node))
307 continue;
308
309 // paint the block reachable through this arc
310 paint_subgraph(tgt_node);
311
312 curr_color++; // next color (next arc belongs to a different block)
313
315 }
316 }
317
319 const long & color,
320 GT & sg)
321 {
322 (void) color;
325
326 std::unique_ptr<typename GT::Node> tp_auto(new typename GT::Node(gp));
327 sg.insert_node(tp_auto.get());
328 GT::map_nodes(gp, tp_auto.get());
329 NODE_BITS(gp).set_bit(Build_Subtree, true);
330
331 return tp_auto.release();
332 }
333
334 void map_subgraph(GT & sg, typename GT::Node *gsrc, const long & color)
335 {
337
338 auto tsrc = mapped_node<GT>(gsrc); // gsrc mapped into sg
339
340 // Traverse arcs of gsrc and insert into sg those with the requested color
341 for (Node_Arc_Iterator<GT, SA> i(gsrc, sa); i.has_curr(); i.next_ne())
342 {
343 auto garc = i.get_curr();
345 continue; // arc has a different color or was already visited
346
347 ARC_BITS(garc).set_bit(Build_Subtree, true);
348
349 auto gtgt = i.get_tgt_node();
350
352
353 typename GT::Node *ttgt = nullptr; // gtgt mapped into sg
354 if (IS_NODE_VISITED(gtgt, Build_Subtree)) // already in sg?
356 else
358
359 auto tarc = sg.insert_arc(tsrc, ttgt, garc->get_info());
361
362 map_subgraph(sg, gtgt, color);
363 }
364 }
365
366 public:
372 Compute_Cut_Nodes(const GT & g, SA __sa = SA())
373 : sa(__sa), gptr(&const_cast<GT &>(g)), state(Init)
374 {
375 /* empty */
376 }
377
388
396 void operator ()(typename GT::Node *start,
398 {
399 cut_nodes(start, list);
400 }
401
433 {
435 << "Cut nodes have not been computed or the class is in another phase";
436
439 curr_color = 1;
440
441 // Traverse each cut node and paint its adjacent blocks
443 i.has_curr(); i.next_ne())
444 paint_from_cut_node(i.get_curr());
445
446 state = Painted;
447
448 return curr_color;
449 }
450
461 void map_subgraph(GT & sg, const long & color)
462 {
463 ah_logic_error_if(state != Painted) << "Graph is not painted";
464
465 clear_graph(sg);
466
467 typename GT::Node *first = nullptr; // find the first node with the requested color
468
469 for (typename GT::Node_Iterator it(*gptr); it.has_curr(); it.next_ne())
470 if (get_color<GT>(it.get_curr()) == color)
471 first = it.get_curr();
472
473 if (first == nullptr) // did we find the color?
474 ah_domain_error_if(first == nullptr) << "Color does not exist in the graph";
475
476 // create first, insert it into sg, and map it
477 create_and_map_node(first, color, sg);
478 try
479 {
480 map_subgraph(sg, first, color); // map the component
481 }
482 catch (...)
483 {
484 clear_graph(sg);
485 }
486 }
487
508 {
509 ah_logic_error_if(state != Painted) << "Graph is not painted";
510
512
513 // Traverse the cut-node list and insert them into cut_graph
515 it.has_curr(); it.next_ne())
516 {
517 auto gp = it.get_curr();
518
520
521 std::unique_ptr<typename GT::Node> tp_auto(new typename GT::Node(gp));
522 cut_graph.insert_node(tp_auto.get());
523 GT::map_nodes(gp, tp_auto.release());
524 }
525
526 // Traverse arcs of g:
527 // - cut_graph will contain cut arcs (between cut nodes)
528 // - cross_arc_list will contain cross arcs (from cut nodes to blocks)
529 for (Arc_Iterator<GT, SA> it(*gptr, sa); it.has_curr(); it.next_ne())
530 {
531 auto garc = it.get_curr();
533 {
534 cross_arc_list.append(garc);
535 continue;
536 }
537
539 continue;
540
541 typename GT::Node *src = mapped_node<GT>(gptr->get_src_node(garc));
542 typename GT::Node *tgt = mapped_node<GT>(gptr->get_tgt_node(garc));
543
544 assert(src != nullptr and tgt != nullptr);
545
546 typename GT::Arc *arc =
547 cut_graph.insert_arc(src, tgt, garc->get_info());
548 GT::map_arcs(garc, arc);
549 }
550 }
551
570 GT & cut_graph,
572 {
573 ah_logic_error_if(state < Cut_Nodes_Computed) << "Cut nodes have not been computed";
574
577
578 // curr_color is the NEXT unused color, so valid colors are 1 .. curr_color-1.
579 // We allocate curr_color slots (indices 0 .. curr_color-1) so that
580 // block at color c lives at blocks[c-1]; slot [0] remains unused / empty.
581 const long & num_colors = curr_color;
582
583 DynArray<GT *> blocks; // blocks in an array for fast access
584 blocks.reserve(num_colors);
585
586 // Create an ordered list of empty components by color i
587 for (int i = 0; i < num_colors; ++i)
588 blocks.access(i) = &block_list.append(GT());
589
590 // Traverse nodes and copy/map according to color
591 for (typename GT::Node_Iterator it(*gptr); it.has_curr(); it.next_ne())
592 {
593 auto p = it.get_curr();
595 continue;
596
597 if (is_a_cut_node<GT>(p))
598 continue;
599
600 const long color = get_color<GT>(p);
601
602 GT & sg = *blocks.access(color - 1);
603
605
606 map_subgraph(sg, p, color);
607 }
608
610 }
611 };
612
613 // =========================================================================
614 // Bridge finding (Tarjan low-link algorithm)
615 // =========================================================================
616
653 template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
655 {
656 SA sa;
658
659 void __dfs(typename GT::Node *p, typename GT::Arc *parent_arc,
660 long & curr_df, DynList<typename GT::Arc *> & bridges)
661 {
662 NODE_BITS(p).set_bit(Depth_First, true);
663 low<GT>(p) = df<GT>(p) = curr_df++;
664
665 for (Node_Arc_Iterator<GT, SA> it(p, sa); it.has_curr(); it.next_ne())
666 {
667 auto arc = it.get_curr();
668 if (arc == parent_arc)
669 continue; // skip the arc we came from
670
671 auto tgt = it.get_tgt_node();
672
674 {
675 // Back-edge: update low if this arc has not been traversed yet
677 low<GT>(p) = std::min(df<GT>(tgt), low<GT>(p));
678 continue;
679 }
680
681 if (IS_ARC_VISITED(arc, Depth_First))
682 continue;
683
684 ARC_BITS(arc).set_bit(Depth_First, true); // mark tree arc
685
686 __dfs(tgt, arc, curr_df, bridges);
687
688 low<GT>(p) = std::min(low<GT>(tgt), low<GT>(p));
689
690 if (low<GT>(tgt) > df<GT>(p)) // bridge condition
691 bridges.append(arc);
692 }
693 }
694
695 public:
701 Compute_Bridges(const GT & g, SA sa = SA())
702 : sa(sa), gptr(&const_cast<GT &>(g))
703 { /* empty */
704 }
705
722 {
723 ah_domain_error_if(gptr->is_digraph()) << "Compute_Bridges: does not work on digraphs";
724
726
727 if (gptr->get_num_nodes() == 0)
728 return bridges;
729
730 ah_invalid_argument_if(start == nullptr) << "Compute_Bridges::find_bridges(): start must be non-null";
731
732 gptr->for_each_node([](auto p)
733 {
734 NODE_COUNTER(p) = 0;
735 NODE_BITS(p).reset();
736 });
737 gptr->reset_arcs();
738
739 long curr_df = 0;
740 __dfs(start, nullptr, curr_df, bridges);
741
742 // Cover disconnected components: any node not reached from start.
743 gptr->for_each_node([&](auto p)
744 {
746 __dfs(p, nullptr, curr_df, bridges);
747 });
748
749 // NODE_COOKIE holds low-link values (stored as longs); clear them.
750 gptr->for_each_node([](auto p) { NODE_COOKIE(p) = nullptr; });
751
752 return bridges;
753 }
754
766
772 {
773 return find_bridges(start);
774 }
775
784 };
785
807 template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
809 find_bridges(const GT & g, typename GT::Node *start, SA sa = SA())
810 {
811 return Compute_Bridges<GT, SA>(g, sa).find_bridges(start);
812 }
813
818 template <AlephGraph GT>
820 {
821 return Compute_Bridges<GT>(g).find_bridges();
822 }
823} // end namespace Aleph
824
825# endif // TPL_CUT_NODES_H
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
#define ah_logic_error_if(C)
Throws std::logic_error if condition holds.
Definition ah-errors.H:330
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
C++20 concepts for the protocol shared by graph algorithms.
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.
Doubly-linked list (defined in tpl_dynList.H).
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:577
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:605
NodeInfo & get_info() noexcept
Return a modifiable reference to the data contained in the node.
Definition graph-dry.H:536
void reset_arcs() const
Reset all the arcs of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:975
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
bool is_digraph() const noexcept
Return true if the graph this is directed.
Definition graph-dry.H:699
static void map_arcs(A1 *p, A2 *q) noexcept
Map the arcs through their cookies.
Definition graph-dry.H:1074
void reset_counter_nodes() const noexcept
Reset all the counters to zero for all the nodes of graph.
Definition graph-dry.H:1112
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:1531
void reset_counter_arcs() const noexcept
Reset all the counters to zero for all the arcs of graph.
Definition graph-dry.H:1118
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
static void map_nodes(N1 *p, N2 *q) noexcept
Map the nodes through their cookies.
Definition graph-dry.H:1043
#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:3659
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
#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
const long Cross_Arc
Special marker for arcs connecting a cut node to a non-cut block.
and
Check uniqueness with explicit hash + equality functors.
static long & color(typename GT::Node *p)
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1120
Utility algorithms and operations for graphs.