Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
cut_nodes_example.C
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
94#include <iostream>
95#include <iomanip>
96#include <string>
97#include <vector>
98#include <tpl_graph.H>
99#include <tpl_cut_nodes.H>
100#include <tclap/CmdLine.h>
101
102using namespace std;
103using namespace Aleph;
104
105// Graph types
109
122{
123 Graph g;
124
125 auto a = g.insert_node("A");
126 auto b = g.insert_node("B");
127 auto c = g.insert_node("C");
128 auto d = g.insert_node("D");
129 auto e = g.insert_node("E");
130 auto f = g.insert_node("F");
131 auto gg = g.insert_node("G");
132 auto h = g.insert_node("H");
133
134 g.insert_arc(a, b);
135 g.insert_arc(b, c);
136 g.insert_arc(a, d);
137 g.insert_arc(b, e);
138 g.insert_arc(d, e);
139 g.insert_arc(e, f);
140 g.insert_arc(f, gg);
141 g.insert_arc(f, h);
142 g.insert_arc(gg, h);
143
144 return g;
145}
146
161{
162 Graph g;
163
164 auto a = g.insert_node("A");
165 auto b = g.insert_node("B");
166 auto c = g.insert_node("C");
167 auto d = g.insert_node("D");
168 auto e = g.insert_node("E");
169 auto f = g.insert_node("F");
170 auto gg = g.insert_node("G");
171
172 // Main cycle
173 g.insert_arc(a, b);
174 g.insert_arc(b, c);
175 g.insert_arc(c, f);
176 g.insert_arc(f, d);
177 g.insert_arc(d, e);
178 g.insert_arc(e, a);
179
180 // Cross connections
181 g.insert_arc(a, d);
182 g.insert_arc(b, f);
183
184 // Pendant node
185 g.insert_arc(f, gg);
186
187 return g;
188}
189
202{
203 Graph g;
204
205 auto server1 = g.insert_node("Server1");
206 auto server2 = g.insert_node("Server2");
207 auto router1 = g.insert_node("Router1");
208 auto router2 = g.insert_node("Router2");
209 auto switch1 = g.insert_node("Switch1");
210 auto switch2 = g.insert_node("Switch2");
211 auto switch3 = g.insert_node("Switch3");
212 auto pc1 = g.insert_node("PC1");
213 auto pc2 = g.insert_node("PC2");
214 auto pc3 = g.insert_node("PC3");
215
226
227 return g;
228}
229
233Graph::Node* find_node(Graph& g, const string& name)
234{
235 for (auto it = g.get_node_it(); it.has_curr(); it.next())
236 if (it.get_curr()->get_info() == name)
237 return it.get_curr();
238 return nullptr;
239}
240
244void print_graph(Graph& g, const string& title)
245{
246 cout << "\n=== " << title << " ===" << endl;
247 cout << "Nodes: " << g.get_num_nodes() << endl;
248 cout << "Edges: " << g.get_num_arcs() << endl;
249
250 cout << "\nConnections:" << endl;
251 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
252 {
253 auto* node = nit.get_curr();
254 cout << " " << node->get_info() << " -- ";
255
256 bool first = true;
257 for (Node_Arc_Iterator<Graph> ait(node); ait.has_curr(); ait.next())
258 {
259 auto* arc = ait.get_curr();
260 auto* neighbor = g.get_connected_node(arc, node);
261 if (not first) cout << ", ";
262 cout << neighbor->get_info();
263 first = false;
264 }
265 cout << endl;
266 }
267}
268
272void demo_cut_nodes(Graph& g, const string& description)
273{
274 cout << "\n--- Finding Cut Nodes (Articulation Points) ---" << endl;
275 cout << "Graph: " << description << endl;
276
277 Compute_Cut_Nodes<Graph> compute(g);
278 DynDlist<Graph::Node*> cut_nodes;
279
280 compute(g.get_first_node(), cut_nodes);
281
282 if (cut_nodes.is_empty())
283 {
284 cout << "\nNo cut nodes found - graph is biconnected!" << endl;
285 cout << "Removing any single node won't disconnect the graph." << endl;
286 }
287 else
288 {
289 cout << "\nCut nodes found: " << cut_nodes.size() << endl;
290 cout << "Cut nodes: ";
291 bool first = true;
292 for (auto it = cut_nodes.get_it(); it.has_curr(); it.next())
293 {
294 if (not first) cout << ", ";
295 cout << it.get_curr()->get_info();
296 first = false;
297 }
298 cout << endl;
299
300 cout << "\nImpact: Removing any of these nodes disconnects the graph." << endl;
301 }
302}
303
308{
309 cout << "\n" << string(60, '=') << endl;
310 cout << "Practical Example: Network Vulnerability Analysis" << endl;
311 cout << string(60, '=') << endl;
312
314 print_graph(g, "Computer Network");
315
316 Compute_Cut_Nodes<Graph> compute(g);
317 DynDlist<Graph::Node*> cut_nodes;
318
319 compute(g.get_first_node(), cut_nodes);
320
321 cout << "\n--- Vulnerability Analysis ---" << endl;
322
323 if (cut_nodes.is_empty())
324 {
325 cout << "Network is fully redundant - no single point of failure!" << endl;
326 }
327 else
328 {
329 cout << "Single points of failure identified:" << endl;
330 for (auto it = cut_nodes.get_it(); it.has_curr(); it.next())
331 {
332 auto* node = it.get_curr();
333 cout << "\n * " << node->get_info() << endl;
334
335 // Count connections
336 int connections = 0;
337 for (Node_Arc_Iterator<Graph> ait(node); ait.has_curr(); ait.next())
338 ++connections;
339
340 cout << " Connections: " << connections << endl;
341 cout << " Risk: CRITICAL - failure would partition the network" << endl;
342 }
343 }
344
345 cout << "\n--- Recommendations ---" << endl;
346 cout << "1. Add redundant links to eliminate cut nodes" << endl;
347 cout << "2. Prioritize backup for critical equipment" << endl;
348 cout << "3. Monitor cut nodes for failures" << endl;
349}
350
355{
356 cout << "\n" << string(60, '=') << endl;
357 cout << "Biconnected Components" << endl;
358 cout << string(60, '=') << endl;
359
361 print_graph(g, "Network Graph");
362
363 Compute_Cut_Nodes<Graph> compute(g);
364 DynDlist<Graph::Node*> cut_nodes;
365
366 compute(g.get_first_node(), cut_nodes);
367
368 cout << "\nCut nodes: ";
369 for (auto it = cut_nodes.get_it(); it.has_curr(); it.next())
370 cout << it.get_curr()->get_info() << " ";
371 cout << endl;
372
373 // Paint subgraphs (components)
374 long num_colors = compute.paint_subgraphs();
375
376 // paint_subgraphs() returns the next unused color (N+1 for N components).
377 cout << "\n--- Biconnected Components ---" << endl;
378 cout << "Number of components: " << (num_colors - 1) << endl;
379
380 cout << "\nNodes by component (color):" << endl;
381 for (long color = 1; color < num_colors; ++color)
382 {
383 cout << " Component " << color << ": ";
384 bool first = true;
385 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
386 {
387 auto* node = nit.get_curr();
388 if (g.get_counter(node) == color)
389 {
390 if (not first) cout << ", ";
391 cout << node->get_info();
392 first = false;
393 }
394 }
395 cout << endl;
396 }
397
398 cout << "\n--- Analysis ---" << endl;
399 cout << "A biconnected component has no cut nodes within it." << endl;
400 cout << "Components are connected through cut nodes." << endl;
401}
402
407{
408 cout << "\n" << string(60, '=') << endl;
409 cout << "Network Resilience Comparison" << endl;
410 cout << string(60, '=') << endl;
411
412 // Fragile network (tree-like)
413 cout << "\n--- Fragile Network (Tree-like) ---" << endl;
415 print_graph(fragile, "Fragile Network");
416
417 {
419 DynDlist<Graph::Node*> cut_nodes;
420 compute(fragile.get_first_node(), cut_nodes);
421
422 cout << "Cut nodes: " << cut_nodes.size() << " out of "
423 << fragile.get_num_nodes() << " nodes" << endl;
424 double fragility = 100.0 * cut_nodes.size() / fragile.get_num_nodes();
425 cout << "Fragility score: " << fixed << setprecision(1) << fragility << "%" << endl;
426 }
427
428 // Resilient network (with cycles)
429 cout << "\n--- Resilient Network (With Cycles) ---" << endl;
431 print_graph(resilient, "Resilient Network");
432
433 {
435 DynDlist<Graph::Node*> cut_nodes;
436 compute(resilient.get_first_node(), cut_nodes);
437
438 cout << "Cut nodes: " << cut_nodes.size() << " out of "
439 << resilient.get_num_nodes() << " nodes" << endl;
440 double fragility = 100.0 * cut_nodes.size() / resilient.get_num_nodes();
441 cout << "Fragility score: " << fixed << setprecision(1) << fragility << "%" << endl;
442 }
443
444 cout << "\n--- Key Insight ---" << endl;
445 cout << "Adding redundant connections (creating cycles) reduces fragility" << endl;
446 cout << "by eliminating articulation points." << endl;
447}
448
458{
459 cout << "\n" << string(60, '=') << endl;
460 cout << "Bridge Edges (Isthmuses / Cut Edges)" << endl;
461 cout << string(60, '=') << endl;
462 cout << "A bridge is an edge whose removal disconnects the graph." << endl;
463 cout << "Algorithm: Tarjan low-link DFS — O(V + E)." << endl;
464
465 // ── Scenario 1: computer network (has bridges) ──────────────────────────
466 {
467 cout << "\n--- Scenario 1: Computer Network ---" << endl;
468
470 print_graph(g, "Computer Network");
471
472 // Free-function form
473 auto bridges = find_bridges(g);
474
475 cout << "\nBridge edges (" << bridges.size() << " found):" << endl;
476 if (bridges.is_empty())
477 cout << " (none — graph is 2-edge-connected)" << endl;
478 else
479 bridges.for_each([&g](Graph::Arc * a)
480 {
481 cout << " " << g.get_src_node(a)->get_info()
482 << " ══ " << g.get_tgt_node(a)->get_info()
483 << " ← bridge" << endl;
484 });
485
486 cout << "\nImpact: severing a bridge immediately splits the network." << endl;
487 }
488
489 // ── Scenario 2: pure cycle (no bridges) ─────────────────────────────────
490 {
491 cout << "\n--- Scenario 2: Ring Topology (no bridges) ---" << endl;
492
493 Graph g;
494 auto a = g.insert_node("A");
495 auto b = g.insert_node("B");
496 auto c = g.insert_node("C");
497 auto d = g.insert_node("D");
498 auto e = g.insert_node("E");
499 g.insert_arc(a, b); g.insert_arc(b, c);
500 g.insert_arc(c, d); g.insert_arc(d, e);
501 g.insert_arc(e, a);
502
503 // Class form — useful when calling find_bridges() multiple times
505 auto bridges = cb.find_bridges();
506
507 cout << "Ring A-B-C-D-E-A: ";
508 if (bridges.is_empty())
509 cout << "no bridges (fully 2-edge-connected)." << endl;
510 else
511 cout << bridges.size() << " bridge(s) found." << endl;
512 }
513
514 // ── Scenario 3: two triangles joined by a bridge ────────────────────────
515 {
516 cout << "\n--- Scenario 3: Two Triangles + Bridge ---" << endl;
517 cout << " Triangle1: A-B-C-A Bridge: C--D Triangle2: D-E-F-D" << endl;
518
519 Graph g;
520 auto a = g.insert_node("A"), b = g.insert_node("B"),
521 c = g.insert_node("C"), d = g.insert_node("D"),
522 e = g.insert_node("E"), f = g.insert_node("F");
523 g.insert_arc(a, b); g.insert_arc(b, c); g.insert_arc(c, a);
524 auto br = g.insert_arc(c, d); // the bridge
525 g.insert_arc(d, e); g.insert_arc(e, f); g.insert_arc(f, d);
526
527 auto bridges = find_bridges(g, a);
528
529 cout << "\nBridges found: " << bridges.size() << endl;
530 bridges.for_each([&g](Graph::Arc * arc)
531 {
532 cout << " " << g.get_src_node(arc)->get_info()
533 << " ══ " << g.get_tgt_node(arc)->get_info() << endl;
534 });
535 (void)br; // used above
536
537 // Show relationship: bridge endpoints are also articulation points
538 auto cut_nodes = compute_cut_nodes(g);
539 cout << "\nArticulation points (cut nodes): ";
540 cut_nodes.for_each([](Graph::Node * p)
541 { cout << p->get_info() << " "; });
542 cout << endl;
543 cout << "Note: bridge endpoints C and D are also articulation points." << endl;
544 }
545}
546
551{
552 cout << "\n" << string(60, '=') << endl;
553 cout << "Fixing Network Vulnerabilities" << endl;
554 cout << string(60, '=') << endl;
555
557
558 cout << "\n--- Before: Original Network ---" << endl;
559
560 {
561 Compute_Cut_Nodes<Graph> compute(g);
562 DynDlist<Graph::Node*> cut_nodes;
563 compute(g.get_first_node(), cut_nodes);
564
565 cout << "Cut nodes: ";
566 for (auto it = cut_nodes.get_it(); it.has_curr(); it.next())
567 cout << it.get_curr()->get_info() << " ";
568 cout << endl;
569 }
570
571 cout << "\n--- Adding Redundant Links ---" << endl;
572
573 // Add redundant links to eliminate cut nodes
574 auto* c = find_node(g, "C");
575 auto* d = find_node(g, "D");
576 auto* a = find_node(g, "A");
577 auto* f = find_node(g, "F");
578
579 cout << "Adding link: C -- D" << endl;
580 g.insert_arc(c, d);
581
582 cout << "Adding link: A -- F" << endl;
583 g.insert_arc(a, f);
584
585 cout << "\n--- After: Reinforced Network ---" << endl;
586
587 {
588 Compute_Cut_Nodes<Graph> compute(g);
589 DynDlist<Graph::Node*> cut_nodes;
590 compute(g.get_first_node(), cut_nodes);
591
592 if (cut_nodes.is_empty())
593 cout << "No cut nodes! Network is now more resilient." << endl;
594 else
595 {
596 cout << "Remaining cut nodes: ";
597 for (auto it = cut_nodes.get_it(); it.has_curr(); it.next())
598 cout << it.get_curr()->get_info() << " ";
599 cout << endl;
600 }
601 }
602
603 cout << "\n--- Lesson ---" << endl;
604 cout << "Strategic addition of edges can eliminate articulation points" << endl;
605 cout << "and improve network reliability." << endl;
606}
607
608int main(int argc, char* argv[])
609{
610 try
611 {
612 TCLAP::CmdLine cmd("Cut Nodes (Articulation Points) Example", ' ', "1.0");
613
614 TCLAP::SwitchArg basicArg("b", "basic",
615 "Show basic cut nodes demo", false);
616 TCLAP::SwitchArg netArg("n", "network",
617 "Show network vulnerability analysis", false);
618 TCLAP::SwitchArg biconArg("c", "biconnected",
619 "Show biconnected components", false);
620 TCLAP::SwitchArg compareArg("r", "resilience",
621 "Compare network resilience", false);
622 TCLAP::SwitchArg fixArg("f", "fix",
623 "Show fixing vulnerabilities", false);
624 TCLAP::SwitchArg bridgesArg("d", "bridges",
625 "Show bridge edges demo", false);
626 TCLAP::SwitchArg allArg("a", "all",
627 "Run all demos", false);
628
629 cmd.add(basicArg);
630 cmd.add(netArg);
631 cmd.add(biconArg);
632 cmd.add(compareArg);
633 cmd.add(fixArg);
634 cmd.add(bridgesArg);
635 cmd.add(allArg);
636
637 cmd.parse(argc, argv);
638
639 bool runBasic = basicArg.getValue();
640 bool runNet = netArg.getValue();
641 bool runBicon = biconArg.getValue();
642 bool runCompare = compareArg.getValue();
643 bool runFix = fixArg.getValue();
644 bool runBridges = bridgesArg.getValue();
645 bool runAll = allArg.getValue();
646
649 runAll = true;
650
651 cout << "=== Cut Nodes, Bridges, and Biconnected Components ===" << endl;
652 cout << "Cut node removal / bridge removal disconnects the graph." << endl;
653
654 if (runAll or runBasic)
655 {
657 print_graph(g, "Sample Network");
658 demo_cut_nodes(g, "Sample network");
659 }
660
661 if (runAll or runNet)
663
664 if (runAll or runBicon)
666
667 if (runAll or runCompare)
669
670 if (runAll or runFix)
672
673 if (runAll or runBridges)
674 demo_bridges();
675
676 cout << "\n=== Summary ===" << endl;
677 cout << "Cut nodes and bridges are dual notions of graph vulnerability:" << endl;
678 cout << " cut node = vertex whose removal disconnects the graph" << endl;
679 cout << " bridge = edge whose removal disconnects the graph" << endl;
680 cout << "Both detected by Tarjan low-link DFS in O(V + E)." << endl;
681 cout << "Headers: tpl_cut_nodes.H (Compute_Cut_Nodes, Compute_Bridges," << endl;
682 cout << " compute_cut_nodes(), find_bridges())" << endl;
683
684 return 0;
685 }
686 catch (TCLAP::ArgException& e)
687 {
688 cerr << "Error: " << e.error() << " for arg " << e.argId() << endl;
689 return 1;
690 }
691}
692
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
long double h
Definition btreepic.C:154
Find bridge edges (isthmuses) of a connected undirected graph.
Computation of cut nodes (articulation points) of a graph.
long paint_subgraphs()
Paints the connected components around the cut nodes.
Dynamic doubly linked list with O(1) size and bidirectional access.
const size_t & size() const noexcept
Return the number of elements (constant time)
void next()
Advances the iterator to the next filtered element.
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
Node Node
The graph type.
Definition tpl_graph.H:432
Arc Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
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
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:778
long & get_counter(Node *node) const noexcept
Get a modifiable reference to the counter of node
Definition graph-dry.H:873
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:784
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2786
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:222
Graph build_computer_network()
Build a graph representing a computer network.
Graph build_network_graph()
Build a network with clear cut nodes.
void demo_biconnected_components()
Demonstrate biconnected components.
void print_graph(Graph &g, const string &title)
Print graph structure.
Graph build_cyclic_graph()
Build a cyclic graph with fewer cut nodes.
void demo_fixing_vulnerabilities()
Demonstrate fixing network vulnerabilities.
void demo_network_vulnerability()
Practical example: Network vulnerability analysis.
void demo_resilience_comparison()
Compare resilient vs fragile networks.
Graph::Node * find_node(Graph &g, const string &name)
Find a node by name.
void demo_bridges()
Demonstrate bridge finding with Compute_Bridges / find_bridges().
void demo_cut_nodes(Graph &g, const string &description)
Demonstrate finding cut nodes.
DynList< typename GT::Node * > compute_cut_nodes(const GT &g, typename GT::Node *start)
Compute articulation points (cut vertices) of an undirected graph.
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.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
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)
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:121
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
CmdLine cmd
Definition testHash.C:43
Articulation points (cut nodes), bridges, and biconnected components.
Generic graph and digraph implementations.