Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
topological_sort_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
196#include <iostream>
197#include <iomanip>
198#include <string>
199#include <vector>
200#include <unordered_map>
201#include <unordered_set>
202#include <tpl_graph.H>
203#include <topological_sort.H>
204#include <tclap/CmdLine.h>
205
206using namespace std;
207using namespace Aleph;
208
209// Task node
211using DependencyArc = Graph_Arc<int>; // Weight could be time/cost
213
214namespace
215{
218 std::vector<TaskGraph::Node*> * missing_nodes = nullptr)
219{
220 std::unordered_map<TaskGraph::Node*, size_t> pos;
221 pos.reserve(order.size());
222
223 size_t i = 0;
224 for (auto it = order.get_it(); it.has_curr(); it.next(), ++i)
225 pos.emplace(it.get_curr(), i);
226
227 if (missing_nodes != nullptr)
228 {
229 missing_nodes->clear();
230 missing_nodes->reserve(g.get_num_nodes());
231 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next_ne())
232 {
233 auto * n = nit.get_curr();
234 if (pos.find(n) == pos.end())
235 missing_nodes->push_back(n);
236 }
237 }
238
239 for (auto ait = g.get_arc_it(); ait.has_curr(); ait.next_ne())
240 {
241 auto * a = ait.get_curr();
242 auto * u = g.get_src_node(a);
243 auto * v = g.get_tgt_node(a);
244
245 const auto it_u = pos.find(u);
246 const auto it_v = pos.find(v);
247 if (it_u == pos.end() || it_v == pos.end())
248 return false; // ordering incomplete
249
250 if (it_u->second >= it_v->second)
251 return false; // violates u -> v
252 }
253
254 return true;
255}
256
257void print_missing_nodes(const std::vector<TaskGraph::Node*> & missing)
258{
259 if (missing.empty())
260 return;
261
262 cout << "Missing nodes (not produced by the algorithm): ";
263 bool first = true;
264 for (auto * n : missing)
265 {
266 if (!first) cout << ", ";
267 cout << n->get_info();
268 first = false;
269 }
270 cout << endl;
271}
272} // namespace
273
293{
294 TaskGraph g;
295
296 // Header files
297 auto config_h = g.insert_node("config.h");
298 auto utils_h = g.insert_node("utils.h");
299
300 // Object files
301 auto utils_o = g.insert_node("utils.o");
302 auto parser_o = g.insert_node("parser.o");
303 auto lexer_o = g.insert_node("lexer.o");
304 auto main_o = g.insert_node("main.o");
305 auto test_o = g.insert_node("test.o");
306
307 // Executables
308 auto program = g.insert_node("program");
309 auto test_suite = g.insert_node("test_suite");
310
311 // Dependencies (edges go from dependency to dependent)
312 g.insert_arc(config_h, utils_h);
313 g.insert_arc(config_h, parser_o);
314 g.insert_arc(utils_h, utils_o);
315 g.insert_arc(utils_h, lexer_o);
316 g.insert_arc(utils_o, main_o);
317 g.insert_arc(utils_o, test_o);
318 g.insert_arc(parser_o, main_o);
319 g.insert_arc(lexer_o, parser_o);
320 g.insert_arc(lexer_o, test_o);
321 g.insert_arc(main_o, program);
322 g.insert_arc(test_o, test_suite);
323
324 return g;
325}
326
341{
342 TaskGraph g;
343
344 // Math courses
345 auto math101 = g.insert_node("Math101");
346 auto math201 = g.insert_node("Math201");
347 auto math301 = g.insert_node("Math301");
348
349 // CS courses
350 auto cs101 = g.insert_node("CS101");
351 auto cs102 = g.insert_node("CS102");
352 auto cs201 = g.insert_node("CS201");
353 auto cs301 = g.insert_node("CS301");
354 auto cs302 = g.insert_node("CS302");
355
356 // Prerequisites
357 g.insert_arc(math101, math201);
358 g.insert_arc(math201, math301);
359 g.insert_arc(math101, cs101);
360 g.insert_arc(cs101, cs102);
361 g.insert_arc(cs101, cs201);
362 g.insert_arc(math201, cs201);
363 g.insert_arc(cs201, cs301);
364 g.insert_arc(cs301, cs302);
365 g.insert_arc(cs102, cs302);
366
367 return g;
368}
369
373TaskGraph::Node* find_task(TaskGraph& g, const string& name)
374{
375 for (auto it = g.get_node_it(); it.has_curr(); it.next())
376 if (it.get_curr()->get_info() == name)
377 return it.get_curr();
378 return nullptr;
379}
380
384void print_graph(TaskGraph& g, const string& title)
385{
386 cout << "\n=== " << title << " ===" << endl;
387 cout << "Tasks: " << g.get_num_nodes() << endl;
388 cout << "Dependencies: " << g.get_num_arcs() << endl;
389
390 cout << "\nDependency structure:" << endl;
391 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
392 {
393 auto* node = nit.get_curr();
394 cout << " " << node->get_info() << " depends on: ";
395
396 // Count incoming edges (dependencies)
397 bool first = true;
398 for (auto ait = g.get_arc_it(); ait.has_curr(); ait.next())
399 {
400 auto* arc = ait.get_curr();
401 if (g.get_tgt_node(arc) == node)
402 {
403 if (not first) cout << ", ";
404 cout << g.get_src_node(arc)->get_info();
405 first = false;
406 }
407 }
408 if (first) cout << "(none - root task)";
409 cout << endl;
410 }
411}
412
417{
418 cout << "\n--- DFS-based Topological Sort ---" << endl;
419 cout << "Algorithm: Post-order DFS traversal" << endl;
420
423
424 sorter(g, sorted);
425
426 cout << "\nExecution order:" << endl;
427 int step = 1;
428 for (auto it = sorted.get_it(); it.has_curr(); it.next(), ++step)
429 {
430 auto* node = it.get_curr();
431 cout << " " << setw(2) << step << ". " << node->get_info() << endl;
432 }
433
434 std::vector<TaskGraph::Node*> missing;
435 const bool ok = verify_topological_order(g, sorted, &missing);
436 if (!ok)
437 {
438 cout << "\n[Warning] The produced order is not a valid topological ordering.\n";
439 cout << "This usually means the input graph is not a DAG (has a cycle) or the order is incomplete.\n";
441 }
442
443 if (verbose)
444 {
445 if (ok)
446 cout << "\nVerification: Each task appears after all its dependencies." << endl;
447 }
448}
449
454{
455 cout << "\n--- BFS-based Topological Sort (Kahn's Algorithm) ---" << endl;
456 cout << "Algorithm: Iteratively remove source nodes (in-degree 0)" << endl;
457
460
461 sorter(g, sorted);
462
463 cout << "\nExecution order:" << endl;
464 int step = 1;
465 for (auto it = sorted.get_it(); it.has_curr(); it.next(), ++step)
466 {
467 auto* node = it.get_curr();
468 cout << " " << setw(2) << step << ". " << node->get_info() << endl;
469 }
470
471 std::vector<TaskGraph::Node*> missing;
472 const bool ok = verify_topological_order(g, sorted, &missing);
473 if (!ok)
474 {
475 cout << "\n[Warning] Kahn's algorithm did not produce a complete valid ordering.\n";
476 cout << "If output size < V, the graph contains a cycle (not a DAG).\n";
478 }
479
480 if (verbose)
481 {
482 // Show ranks (parallel execution levels)
483 cout << "\nParallel execution ranks:" << endl;
485 auto ranks = rank_sorter.ranks(g);
486
487 // Validate rank output by flattening ranks into a single ordering
489 std::unordered_set<TaskGraph::Node*> seen;
490 seen.reserve(g.get_num_nodes());
491 for (auto rit = ranks.get_it(); rit.has_curr(); rit.next_ne())
492 for (auto nit = rit.get_curr().get_it(); nit.has_curr(); nit.next_ne())
493 {
494 auto * n = nit.get_curr();
495 if (seen.insert(n).second)
496 flat.append(n);
497 }
498 std::vector<TaskGraph::Node*> missing_ranks;
500 if (!ok_ranks)
501 {
502 cout << "\n[Warning] Rank-based output is incomplete/invalid (cycle or missing nodes).\n";
504 }
505
506 int rank_num = 0;
507 for (auto rit = ranks.get_it(); rit.has_curr(); rit.next(), ++rank_num)
508 {
509 cout << " Level " << rank_num << " (can run in parallel): ";
510 auto& rank = rit.get_curr();
511 bool first = true;
512 for (auto nit = rank.get_it(); nit.has_curr(); nit.next())
513 {
514 if (not first) cout << ", ";
515 cout << nit.get_curr()->get_info();
516 first = false;
517 }
518 cout << endl;
519 }
520 }
521}
522
527{
528 cout << "\n" << string(60, '=') << endl;
529 cout << "Example: Build System Dependencies" << endl;
530 cout << string(60, '=') << endl;
531
533 print_graph(g, "Project Build Graph");
534
535 cout << "\n--- Computing Build Order ---" << endl;
536
539
541
542 cout << "\nBuild order (satisfies all dependencies):" << endl;
543 cout << " make ";
544 bool first = true;
545 for (auto it = build_order.get_it(); it.has_curr(); it.next())
546 {
547 if (not first) cout << " && make ";
548 cout << it.get_curr()->get_info();
549 first = false;
550 }
551 cout << endl;
552}
553
558{
559 cout << "\n" << string(60, '=') << endl;
560 cout << "Example: University Course Prerequisites" << endl;
561 cout << string(60, '=') << endl;
562
564 print_graph(g, "Course Prerequisites Graph");
565
566 cout << "\n--- Computing Course Order ---" << endl;
567
569 auto semesters = sorter.ranks(g);
570
571 cout << "\nSuggested course schedule:" << endl;
572 int semester = 1;
573 for (auto sit = semesters.get_it(); sit.has_curr(); sit.next(), ++semester)
574 {
575 cout << " Semester " << semester << ": ";
576 auto& courses = sit.get_curr();
577 bool first = true;
578 for (auto cit = courses.get_it(); cit.has_curr(); cit.next())
579 {
580 if (not first) cout << ", ";
581 cout << cit.get_curr()->get_info();
582 first = false;
583 }
584 cout << endl;
585 }
586
587 cout << "\nTotal semesters needed: " << (semester - 1) << endl;
588}
589
590int main(int argc, char* argv[])
591{
592 try
593 {
594 TCLAP::CmdLine cmd("Topological Sort Example", ' ', "1.0");
595
596 TCLAP::SwitchArg buildArg("b", "build",
597 "Show build system example", false);
598 TCLAP::SwitchArg courseArg("c", "courses",
599 "Show course scheduling example", false);
600 TCLAP::SwitchArg allArg("a", "all",
601 "Run all demos", false);
602 TCLAP::SwitchArg verboseArg("v", "verbose",
603 "Show detailed output", false);
604
605 cmd.add(buildArg);
606 cmd.add(courseArg);
607 cmd.add(allArg);
608 cmd.add(verboseArg);
609
610 cmd.parse(argc, argv);
611
612 bool runBuild = buildArg.getValue();
613 bool runCourse = courseArg.getValue();
614 bool runAll = allArg.getValue();
615 bool verbose = verboseArg.getValue();
616
618 runAll = true;
619
620 cout << "=== Topological Sort: Task Ordering with Dependencies ===" << endl;
621
622 if (runAll or runBuild)
623 {
625
626 // Also show both algorithms
630 }
631
632 if (runAll or runCourse)
634
635 cout << "\n=== Algorithm Summary ===" << endl;
636 cout << "DFS-based: O(V + E), post-order traversal" << endl;
637 cout << "BFS-based: O(V + E), Kahn's algorithm (removes sources)" << endl;
638 cout << "Requirement: Input must be a DAG (no cycles)" << endl;
639
640 return 0;
641 }
642 catch (TCLAP::ArgException& e)
643 {
644 cerr << "Error: " << e.error() << " for arg " << e.argId() << endl;
645 return 1;
646 }
647}
648
int main()
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Node Node
Definition graph-dry.H:3851
Dynamic doubly linked list with O(1) size and bidirectional access.
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
void empty() noexcept
empty the list
Definition htlist.H:1689
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Computes topological ordering using breadth-first search (Kahn's algorithm).
Computes topological ordering using depth-first search.
void emplace(Args &&... args)
Appends a new element into the container by constructing it in-place with the given args.
Definition ah-dry.H:582
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
iterator end() noexcept
Return an STL-compatible end iterator.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Array< size_t > ranks(const Array< T > &array)
Computes the rank of each element in an Array.
Definition ahSort.H:524
bool verbose
Flag enabling verbose output.
Definition ahDefs.C:45
DynList< T > maps(const C &c, Op op)
Classic map operation.
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
Topological sorting algorithms for directed acyclic graphs (DAGs).
void print_graph(TaskGraph &g, const string &title)
Print the graph structure.
TaskGraph build_courses_graph()
Build a course prerequisites graph.
void demo_build_order()
Demonstrate practical application: build order.
TaskGraph::Node * find_task(TaskGraph &g, const string &name)
Find a node by name.
TaskGraph build_project_graph()
Build a sample build system dependency graph.
void demo_course_scheduling()
Demonstrate practical application: course scheduling.
void demo_bfs_topological_sort(TaskGraph &g, bool verbose)
Demonstrate BFS-based topological sort (Kahn's algorithm)
void demo_dfs_topological_sort(TaskGraph &g, bool verbose)
Demonstrate DFS-based topological sort.
Generic graph and digraph implementations.