Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
johnson_example.cc
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
128#include <iostream>
129#include <iomanip>
130#include <string>
131#include <vector>
132#include <limits>
133#include <chrono>
134#include <cmath>
135#include <random>
136#include <unordered_set>
137#include <cstring>
138
139#include <tpl_graph.H>
140#include <Johnson.H>
141#include <Floyd_Warshall.H>
142
143using namespace std;
144using namespace Aleph;
145
146// =============================================================================
147// Graph Type Definitions
148// =============================================================================
149
153
155struct Distance
156{
158
159 static void set_zero(Arc* a) { a->get_info() = 0.0; }
160 Distance_Type operator()(Arc* a) const { return a->get_info(); }
161};
162
163template <typename Func>
164double measure_ms(Func&& f);
165
166static void usage(const char* prog)
167{
168 cout << "Usage: " << prog << " [--compare] [-n <nodes>] [-e <edges>] [--help]\n";
169 cout << "\nIf no flags are given, all demos are executed.\n";
170 cout << "\nIf -n/-e are provided, a random non-negative weighted graph is generated.\n";
171 cout << "(If --compare is also given and the graph is small enough, Floyd-Warshall is run too.)\n";
172}
173
174static WeightedDigraph build_random_graph(int n, int e, unsigned seed)
175{
177
178 vector<Node*> nodes;
179 nodes.reserve(static_cast<size_t>(n));
180 for (int i = 0; i < n; ++i)
181 nodes.push_back(g.insert_node("V" + to_string(i)));
182
183 std::mt19937 rng(seed);
184 std::uniform_int_distribution<int> node_dist(0, n - 1);
185 std::uniform_real_distribution<double> weight_dist(1.0, 10.0);
186
187 std::unordered_set<long long> used;
188 used.reserve(static_cast<size_t>(e) * 2);
189
190 // Ensure basic connectivity from V0 to V(n-1)
191 int edges_added = 0;
192 for (int i = 0; i + 1 < n && edges_added < e; ++i)
193 {
194 g.insert_arc(nodes[i], nodes[i + 1], weight_dist(rng));
195 used.insert(static_cast<long long>(i) * n + (i + 1));
196 ++edges_added;
197 }
198
199 while (edges_added < e)
200 {
201 const int u = node_dist(rng);
202 const int v = node_dist(rng);
203 if (u == v)
204 continue;
205 const long long key = static_cast<long long>(u) * n + v;
206 if (used.find(key) != used.end())
207 continue;
208 used.insert(key);
209 g.insert_arc(nodes[u], nodes[v], weight_dist(rng));
210 ++edges_added;
211 }
212
213 return g;
214}
215
217{
218 vector<Node*> nodes;
219 nodes.reserve(static_cast<size_t>(g.get_num_nodes()));
220 for (auto it = g.get_node_it(); it.has_curr(); it.next())
221 nodes.push_back(it.get_curr());
222
223 double johnson_ms = 0.0;
224 vector<double> jdists;
225 jdists.reserve(nodes.size() * nodes.size());
226
227 johnson_ms = measure_ms([&]() {
229 for (auto* src : nodes)
230 for (auto* tgt : nodes)
231 jdists.push_back(johnson.get_distance(src, tgt));
232 });
233
234 double floyd_ms = 0.0;
236 floyd_ms = measure_ms([&]() {
238 });
239 auto& floyd = *floyd_ptr;
240
241 const auto& dist = floyd.get_dist_mat();
242 const double Inf = std::numeric_limits<double>::max();
243
244 size_t mismatches = 0;
245 size_t idx = 0;
246 for (auto* src : nodes)
247 {
248 const long isrc = floyd.index_node(src);
249 for (auto* tgt : nodes)
250 {
251 const long itgt = floyd.index_node(tgt);
252 const double fd = dist(isrc, itgt);
253 const double jd = jdists[idx++];
254
255 const bool f_inf = (fd == Inf);
256 const bool j_inf = (jd == std::numeric_limits<double>::infinity());
257 if (f_inf != j_inf)
258 {
259 ++mismatches;
260 continue;
261 }
262 if (!f_inf && std::abs(fd - jd) > 1e-9)
263 ++mismatches;
264 }
265 }
266
267 delete floyd_ptr;
268
269 cout << "\nComparison results:\n";
270 cout << " Johnson (multiple Dijkstra calls): " << fixed << setprecision(3) << johnson_ms << " ms\n";
271 cout << " Floyd-Warshall: " << fixed << setprecision(3) << floyd_ms << " ms\n";
272 cout << " Distance mismatches: " << mismatches << "\n";
273}
274
275// =============================================================================
276// Utility Functions
277// =============================================================================
278
279Node* find_node(WeightedDigraph& g, const string& name)
280{
281 for (auto it = g.get_node_it(); it.has_curr(); it.next())
282 if (it.get_curr()->get_info() == name)
283 return it.get_curr();
284 return nullptr;
285}
286
288{
289 cout << "Graph (" << g.get_num_nodes() << " nodes, "
290 << g.get_num_arcs() << " arcs):\n";
291 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
292 {
293 auto* node = nit.get_curr();
294 cout << " " << node->get_info() << " → ";
295 bool first = true;
296 for (auto ait = g.get_out_it(node); ait.has_curr(); ait.next())
297 {
298 auto* arc = ait.get_curr();
299 auto* tgt = g.get_tgt_node(arc);
300 if (!first) cout << ", ";
301 cout << tgt->get_info() << "(" << showpos << arc->get_info()
302 << noshowpos << ")";
303 first = false;
304 }
305 if (first) cout << "(none)";
306 cout << "\n";
307 }
308}
309
310template <typename Func>
311double measure_ms(Func&& f)
312{
313 auto start = chrono::high_resolution_clock::now();
314 f();
315 auto end = chrono::high_resolution_clock::now();
316 return chrono::duration<double, milli>(end - start).count();
317}
318
319// =============================================================================
320// Example 1: Basic All-Pairs Shortest Paths
321// =============================================================================
322
324{
325 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
326 cout << "Example 1: All-Pairs Shortest Paths with Negative Weights\n";
327 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
328
329 /*
330 * Graph with negative edges but no negative cycles:
331 *
332 * A ─(3)──→ B ─(1)─→ D
333 * │↖ ↑ │
334 * (8) (10) (-4) (-3)
335 * │ ↖ │ ↓
336 * └─────→ C ─(2)──→ E
337 *
338 * Negative edges: C→B (-4), D→E (-3)
339 * No negative cycle: A→C→B→D→E→A = 8+(-4)+1+(-3)+10 = 12 > 0
340 */
341
343
344 auto* A = g.insert_node("A");
345 auto* B = g.insert_node("B");
346 auto* C = g.insert_node("C");
347 auto* D = g.insert_node("D");
348 auto* E = g.insert_node("E");
349
350 g.insert_arc(A, B, 3.0);
351 g.insert_arc(A, C, 8.0);
352 g.insert_arc(B, D, 1.0);
353 g.insert_arc(C, B, -4.0); // Negative edge (but no cycle: A→C→B, not back)
354 g.insert_arc(C, E, 2.0);
355 g.insert_arc(D, E, -3.0); // Negative edge
356 g.insert_arc(E, A, 10.0); // Back edge but total cycle is positive
357
358 print_graph(g);
359
360 cout << "\n▶ Running Johnson's Algorithm:\n\n";
361
362 try
363 {
365
366 // Print node potentials (h values)
367 cout << " Node potentials (from Bellman-Ford):\n";
368 vector<Node*> nodes = {A, B, C, D, E};
369 for (auto* node : nodes)
370 {
371 double h = johnson.get_potential(node);
372 cout << " h(" << node->get_info() << ") = " << showpos << h
373 << noshowpos << "\n";
374 }
375
376 // Print all-pairs distances
377 cout << "\n Shortest distances (all pairs):\n\n";
378 cout << " ";
379 for (auto* tgt : nodes)
380 cout << setw(6) << tgt->get_info();
381 cout << "\n ";
382 for (size_t i = 0; i < nodes.size(); ++i)
383 cout << "──────";
384 cout << "\n";
385
386 for (auto* src : nodes)
387 {
388 cout << " " << src->get_info() << " │ ";
389 for (auto* tgt : nodes)
390 {
391 double dist = johnson.get_distance(src, tgt);
392 if (dist == numeric_limits<double>::infinity())
393 cout << setw(6) << "∞";
394 else
395 cout << setw(6) << fixed << setprecision(0) << dist;
396 }
397 cout << "\n";
398 }
399
400 // Show a specific path
401 cout << "\n Example path A → D:\n";
402 double dist_ad = johnson.get_distance(A, D);
403 cout << " Distance: " << dist_ad << "\n";
404 }
405 catch (const domain_error& e)
406 {
407 cout << " ERROR: " << e.what() << "\n";
408 }
409}
410
411// =============================================================================
412// Example 2: Negative Cycle Detection
413// =============================================================================
414
416{
417 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
418 cout << "Example 2: Negative Cycle Detection\n";
419 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
420
421 cout << "Johnson's algorithm uses Bellman-Ford internally, so it can\n";
422 cout << "detect negative cycles. If one exists, construction throws.\n\n";
423
424 /*
425 * Graph with a negative cycle: A → B → C → A has weight -1
426 *
427 * A ─(2)─→ B
428 * ↑ │
429 * (-5) (2)
430 * │ ↓
431 * └───── C
432 */
433
435
436 auto* A = g.insert_node("A");
437 auto* B = g.insert_node("B");
438 auto* C = g.insert_node("C");
439
440 g.insert_arc(A, B, 2.0);
441 g.insert_arc(B, C, 2.0);
442 g.insert_arc(C, A, -5.0); // Creates negative cycle: 2 + 2 - 5 = -1
443
444 print_graph(g);
445
446 cout << "\n▶ Attempting to run Johnson's Algorithm:\n\n";
447
448 try
449 {
451 cout << " Unexpected: No exception thrown!\n";
452 }
453 catch (const domain_error& e)
454 {
455 cout << " ⚠ EXCEPTION: " << e.what() << "\n";
456 cout << "\n Johnson cannot compute shortest paths when negative cycles exist\n";
457 cout << " because shortest paths become undefined (can always improve by\n";
458 cout << " going around the negative cycle one more time).\n";
459 }
460}
461
462// =============================================================================
463// Example 3: Understanding Reweighting
464// =============================================================================
465
467{
468 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
469 cout << "Example 3: Understanding Edge Reweighting\n";
470 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
471
472 cout << R"(
473 The key insight of Johnson's algorithm is REWEIGHTING:
474
475 w'(u,v) = w(u,v) + h(u) - h(v)
476
477 where h(v) is the shortest distance from a dummy source to v.
478
479 Why does this work?
480 ───────────────────
481 For ANY path p from s to t, the reweighted length is:
482
483 w'(p) = Σ w'(u,v)
484 = Σ [w(u,v) + h(u) - h(v)]
485 = Σ w(u,v) + h(s) - h(t) (telescoping sum!)
486 = w(p) + h(s) - h(t)
487
488 Since h(s) and h(t) are constants, minimizing w'(p) also minimizes w(p)!
489
490 Why are reweighted edges non-negative?
491 ─────────────────────────────────────
492 Since h is computed by Bellman-Ford:
493 h(v) ≤ h(u) + w(u,v) (triangle inequality)
494
495 Rearranging:
496 w(u,v) + h(u) - h(v) ≥ 0
497 w'(u,v) ≥ 0 ✓
498)";
499
500 // Demonstrate with a simple example
502
503 auto* A = g.insert_node("A");
504 auto* B = g.insert_node("B");
505 auto* C = g.insert_node("C");
506
507 g.insert_arc(A, B, 5.0);
508 g.insert_arc(B, C, -3.0); // Negative!
509 g.insert_arc(A, C, 4.0);
511 cout << "\n Original graph:\n";
512 print_graph(g);
513
514 try
515 {
517
518 cout << "\n Node potentials:\n";
519 cout << " h(A) = " << johnson.get_potential(A) << "\n";
520 cout << " h(B) = " << johnson.get_potential(B) << "\n";
521 cout << " h(C) = " << johnson.get_potential(C) << "\n";
522
523 cout << "\n After reweighting, all edges become non-negative,\n";
524 cout << " allowing Dijkstra to be used!\n";
525 }
526 catch (const domain_error& e)
527 {
528 cout << " Error: " << e.what() << "\n";
529 }
530}
531
532// =============================================================================
533// Example 4: Complexity Comparison
534// =============================================================================
535
537{
538 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
539 cout << "Example 4: When to Use Johnson vs Floyd-Warshall\n";
540 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
541
542 cout << R"(
543┌───────────────────────────────────────────────────────────────────────────┐
544│ All-Pairs Shortest Paths Algorithm Selection │
545├───────────────────────────────────────────────────────────────────────────┤
546│ │
547│ Algorithm │ Time Complexity │ Best For │
548│ ────────────────┼────────────────────┼──────────────────────────────────│
549│ Floyd-Warshall │ O(V³) │ Dense graphs (E ≈ V²) │
550│ │ │ Simple implementation │
551│ │ │ Works with negative edges │
552│ ────────────────┼────────────────────┼──────────────────────────────────│
553│ Johnson │ O(V² log V + VE) │ Sparse graphs (E ≈ V) │
554│ │ │ = O(V² log V) when sparse │
555│ │ │ Works with negative edges │
556│ ────────────────┼────────────────────┼──────────────────────────────────│
557│ V × Dijkstra │ O(V(V log V + E)) │ Non-negative edges only │
558│ │ │ Simpler than Johnson │
559│ │
560├───────────────────────────────────────────────────────────────────────────┤
561│ Sparsity Rule of Thumb: │
562│ ───────────────────────── │
563│ • E < V² / log V → Use Johnson │
564│ • E > V² / log V → Use Floyd-Warshall │
565│ │
566│ Example: V = 1000 │
567│ • E < 100,000 → Johnson is faster │
568│ • E > 100,000 → Floyd-Warshall may be faster │
569│ │
570└───────────────────────────────────────────────────────────────────────────┘
571)";
572}
573
574// =============================================================================
575// Example 5: Practical Application
576// =============================================================================
577
579{
580 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
581 cout << "Example 5: Currency Arbitrage Detection\n";
582 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
583
584 cout << R"(
585 Currency arbitrage occurs when you can profit by converting currencies
586 in a cycle, ending up with more than you started with.
587
588 To detect this using shortest paths:
589 - Create edge (A→B) with weight = -log(exchange_rate(A→B))
590 - A negative cycle means: product of rates > 1 → arbitrage opportunity!
591
592 Example rates:
593 USD → EUR: 0.85
594 EUR → GBP: 0.88
595 GBP → USD: 1.40
596
597 Product: 0.85 × 0.88 × 1.40 = 1.0472 > 1 → Arbitrage exists!
598
599 As -log values:
600 -log(0.85) = 0.163
601 -log(0.88) = 0.128
602 -log(1.40) = -0.336
603
604 Sum: 0.163 + 0.128 - 0.336 = -0.045 < 0 → Negative cycle!
605)";
606
608
609 auto* USD = g.insert_node("USD");
610 auto* EUR = g.insert_node("EUR");
611 auto* GBP = g.insert_node("GBP");
612
613 // Convert exchange rates to -log values
614 g.insert_arc(USD, EUR, -log(0.85));
615 g.insert_arc(EUR, GBP, -log(0.88));
616 g.insert_arc(GBP, USD, -log(1.40));
617
618 // Add some reverse edges
619 g.insert_arc(EUR, USD, -log(1.0 / 0.85));
620 g.insert_arc(GBP, EUR, -log(1.0 / 0.88));
621 g.insert_arc(USD, GBP, -log(1.0 / 1.40));
622
623 cout << "\n▶ Checking for arbitrage opportunity:\n\n";
624
625 try
626 {
628 cout << " No arbitrage opportunity found (no negative cycles).\n";
629 }
630 catch (const domain_error& e)
631 {
632 cout << " ⚠ ARBITRAGE OPPORTUNITY DETECTED!\n";
633 cout << " Negative cycle exists in the exchange rate graph.\n";
634 cout << "\n Profit path: USD → EUR → GBP → USD\n";
635 cout << " Starting with $1000: end with $" << fixed << setprecision(2)
636 << 1000 * 0.85 * 0.88 * 1.40 << "\n";
637 }
638}
639
640// =============================================================================
641// Main
642// =============================================================================
643
644int main(int argc, char* argv[])
645{
646 bool compare = false;
647 int n = -1;
648 int e = -1;
649
650 for (int i = 1; i < argc; ++i)
651 {
652 const string arg = argv[i];
653 if (arg == "--help" || arg == "-h")
654 {
655 usage(argv[0]);
656 return 0;
657 }
658 if (arg == "--compare")
659 {
660 compare = true;
661 continue;
662 }
663 if (arg == "-n")
664 {
665 if (i + 1 >= argc)
666 {
667 usage(argv[0]);
668 return 1;
669 }
670 n = std::stoi(argv[++i]);
671 continue;
672 }
673 if (arg == "-e")
674 {
675 if (i + 1 >= argc)
676 {
677 usage(argv[0]);
678 return 1;
679 }
680 e = std::stoi(argv[++i]);
681 continue;
682 }
683
684 cout << "Unknown argument: " << arg << "\n";
685 usage(argv[0]);
686 return 1;
687 }
688
689 if (n != -1 || e != -1)
690 {
691 if (n == -1)
692 n = 100;
693 if (e == -1)
694 e = 5 * n;
695 if (n <= 0 || e < 0)
696 {
697 usage(argv[0]);
698 return 1;
699 }
700
701 const int max_edges = n * (n - 1);
702 if (e > max_edges)
703 e = max_edges;
704
705 cout << "Random graph benchmark: n=" << n << ", e=" << e << "\n";
706 auto g = build_random_graph(n, e, 42);
707
708 // Run a single sample query to avoid O(n^2) repeated Dijkstra calls.
709 auto* src = find_node(g, "V0");
710 auto* tgt = find_node(g, "V" + to_string(n - 1));
711
712 try
713 {
715 double ms = measure_ms([&]() { (void)johnson.get_distance(src, tgt); });
716 cout << "Sample query V0 -> V" << (n - 1) << " computed in " << fixed << setprecision(3)
717 << ms << " ms\n";
718 }
719 catch (const std::exception& ex)
720 {
721 cout << "ERROR: " << ex.what() << "\n";
722 return 1;
723 }
724
725 if (compare && n <= 200)
727 else if (compare)
728 cout << "\nSkipping Floyd-Warshall comparison for n > 200.\n";
729
730 return 0;
731 }
732
733 if (compare)
734 {
736 auto* A = g.insert_node("A");
737 auto* B = g.insert_node("B");
738 auto* C = g.insert_node("C");
739 auto* D = g.insert_node("D");
740 auto* E = g.insert_node("E");
741 g.insert_arc(A, B, 3.0);
742 g.insert_arc(A, C, 8.0);
743 g.insert_arc(B, D, 1.0);
744 g.insert_arc(C, B, -4.0);
745 g.insert_arc(C, E, 2.0);
746 g.insert_arc(D, E, -3.0);
747 g.insert_arc(E, A, 10.0);
749 return 0;
750 }
751
752 cout << "╔══════════════════════════════════════════════════════════════════════╗\n";
753 cout << "║ Johnson's Algorithm for All-Pairs Shortest Paths ║\n";
754 cout << "╚══════════════════════════════════════════════════════════════════════╝\n\n";
755
761
762 cout << "\nDone.\n";
763 return 0;
764}
Floyd-Warshall algorithm for all-pairs shortest paths.
Johnson's algorithm for all-pairs shortest paths.
int main()
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
long double h
Definition btreepic.C:154
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Arc Arc
Definition graph-dry.H:3852
typename BaseGraph::Node Node
Definition graph-dry.H:3851
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
Compute the all-pairs shortest path distance matrix and the path reconstruction matrix for a graph g ...
Johnson's algorithm for all-pairs shortest paths.
Definition Johnson.H:125
iterator end() noexcept
Return an STL-compatible end iterator.
static mt19937 rng
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_log_function > > log(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4063
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
void example_complexity_comparison()
Node * find_node(WeightedDigraph &g, const string &name)
void example_reweighting()
void print_graph(WeightedDigraph &g)
void example_currency_arbitrage()
void example_basic_all_pairs()
static void compare_with_floyd(WeightedDigraph &g)
static void usage(const char *prog)
static WeightedDigraph build_random_graph(int n, int e, unsigned seed)
WeightedDigraph::Arc Arc
void example_negative_cycle()
double measure_ms(Func &&f)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::string to_string(const time_t t, const std::string &format)
Format a time_t value into a string using format.
Definition ah-date.H:140
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
Distance accessor.
static void set_zero(Arc *a)
Distance_Type operator()(Arc *a) const
Generic graph and digraph implementations.