my $widists= Graph::Undirected->new();
my $wiarchs= Graph::Undirected->new();
+my $wispr;
+my $dbspr;
my @wiarchlabels;
my %wiisland2node;
my %winode2island;
print sort @$m or die $!;
}
}
+sub progress ($) { print "($_[0])\n"; }
if (@ARGV && $ARGV[0] eq '--debug') {
shift @ARGV;
}
}
+sub database_graph_spr () {
+ $dbspr= shortest_path_reduction('db',$dbdists);
+}
+
sub yppedia_graphs_add_shortcuts () {
# We add edges between LPs we know about, as you can chart
# between them. Yppedia often lacks these edges.
}
}
-sub yppedia_graphs_shortest_paths () {
+sub yppedia_graph_shortest_paths () {
$wialldists= $widists->APSP_Floyd_Warshall();
}
}
my $dbarch= $dbisland2arch{$island};
if ($wiarch ne $dbarch) {
- change("change archipelago from $dbarch to $wiarch".
+ change("archipelago change from $dbarch to $wiarch".
" for island $island");
}
}
next;
# We check arches of non-new islands above
}
- change("new island in $wiarch: $island");
+ change("island new in $wiarch: $island");
+ }
+ }
+}
+
+sub shortest_path_reduction ($$) {
+ my ($what,$g) = @_;
+ #
+ # Takes a graph $g (and a string for messages $what) and returns
+ # a new graph which is the miminal shortest path transient reduction
+ # of $g.
+ #
+ # We also check that the shortest path closure of the intended result
+ # is the same graph as the input. Thus the input must itself be
+ # a shortest path closure; if it isn't, we die.
+
+ my $proof=<<'END'; # way to make a big comment
+
+ Premises and definitions:
+
+ 1. F is an undirected weighted graph with positive edge weights.
+
+ 2. All graphs we will consider have the same vertices as F.
+
+ 3. G = Closure(F) is the graph of cliques whose edge weights
+ are the shortest paths in F, one clique for each connected
+ component in F.
+
+ 3a. |XY| for vertices X, Y is the weight of the edge XY in G.
+ If XY is not in G, |XY| is infinite.
+
+ 4. A `reduction' of G is a subgraph K of G such that Closure(K) = G.
+ The reduction is `minimal' if there is no strict subgraph K'
+ of K such that Closure(K') = G.
+
+ 5. Now each edge of G may be:
+ - `unnecessary': included in no minimal reductions of G.
+ - `essential': included in all minimal reductions of G.
+ - `contingent': included in some but not all.
+
+ 6. Consider for any edge AC between the vertices A and C,
+ whether there is any B such that |AB|+|BC| = |AC| ?
+ (There can be no B such that the sum < |AC| since that would
+ mean that |AC| wasn't equal to the shortest path length.)
+
+ 6a. No such B: AC is therefore the only shortest path from A to C
+ (since G is not a multigraph). AC is thus an essential edge.
+
+ 6b. Some such B: Call all such edges AC `questionable'.
+
+ 6c. Thus all edges are essential or questionable.
+
+ 7. Suppose AC is a shortest contingent edge. AC must be
+ questionable since it is not essential. Suppose it is
+ made questionable by the existence of B such that |AB|+|BC| =
+ |AC|. Consider AB and BC. Since |AB| and |BC| are positive,
+ |BC| and |AB| must be < |AC| ie AB and BC are shorter than AC.
+ Since AC is a shortest contingent edge, there must be shortest
+ paths in G for AB and BC consisting entirely of essential edges.
+
+ 8. Therefore it is always safe to remove AC since the paths
+ A..B and B..C will definitely still remain and provide a path
+ A..B..C with length |AB|+|BC| = |AC|.
+
+ 9. Thus AC is unnecessary, contradicting the assumption in 7.
+ There are therefore no shortest contingent edges, and
+ thus no contingent edges.
+
+ 10. We can construct a minimal reduction directly: for each edge
+ AC in G, search for a vertex B such that |AB|+|BC| = |AC|.
+ If we find none, AC is essential. If we find one then AC is
+ not essential and is therefore unnecessary.
+
+END
+
+ printf DEBUG "spr %s before %d\n", $what, scalar($g->edges());
+
+ my $result= Graph::Undirected->new();
+ foreach my $edge_ac ($g->edges()) {
+ my $edgename_ac= join ' .. ', @$edge_ac;
+ printf DEBUG "spr %s edge %s\n", $what, $edgename_ac;
+ my $w_ac= $g->get_edge_weight(@$edge_ac);
+ my $needed= 1;
+ foreach my $vertex_b ($g->vertices()) {
+ next if grep { $_ eq $vertex_b } @$edge_ac;
+ my $w_ab= $g->get_edge_weight($edge_ac->[0], $vertex_b);
+ next unless defined $w_ab;
+ next if $w_ab >= $w_ac;
+ my $w_bc= $g->get_edge_weight($vertex_b, $edge_ac->[1]);
+ next unless defined $w_ac;
+ next if $w_ab + $w_bc > $w_ac;
+ # found path
+ printf DEBUG "spr %s edge %s unnecessary %s\n",
+ $what, $edgename_ac, $vertex_b;
+ $needed= 0;
+ last;
+ }
+ if ($needed) {
+ printf DEBUG "spr %s edge %s essential\n", $what, $edgename_ac;
+ $result->add_weighted_edge(@$edge_ac,$w_ac);
+ }
+ }
+ printf DEBUG "spr %s result %d\n", $what, scalar($result->edges());
+
+ my $apsp= $result->APSP_Floyd_Warshall();
+ foreach my $ia (sort $g->vertices()) {
+ foreach my $ib (sort $g->vertices()) {
+ my $din= $g->get_edge_weight($ia,$ib);
+ my $dout= $apsp->path_length($ia,$ib);
+ $din= defined($din) ? $din : 'infinity';
+ $dout= defined($dout) ? $dout : 'infinity';
+ error("$what spr apsp discrepancy in=$din out=$dout".
+ " for $ia .. $ib")
+ if $din != $dout;
+ }
+ }
+ return $result;
+}
+
+sub yppedia_graph_spr () {
+ my $base= Graph::Undirected->new();
+ foreach my $na (sort keys %winode2island) {
+ my $ia= $winode2island{$na};
+ foreach my $nb (sort keys %winode2island) {
+ my $ib= $winode2island{$nb};
+ $base->add_weighted_edge($ia,$ib, widist($na,$nb));
}
}
+ $wispr= shortest_path_reduction('wi',$base);
}
sub compare_distances () {
next unless defined $na;
foreach my $ib (sort keys %dbisland2arch) {
next unless $ia le $ib; # do every pair only once
- my $nb= $wiisland2node{$ib};
- next unless defined $nb;
- my $dbdist= $dbdists->get_edge_weight($ia,$ib);
- my $widist= widist($na,$nb);
- if (!defined $dbdist) {
- change(sprintf "define distance %2d for %s..%s",
+ my $dbdist= $dbspr->get_edge_weight($ia,$ib);
+ my $widist= $wispr->get_edge_weight($ia,$ib);
+ next unless defined $dbdist || defined $widist;
+
+ if (!defined $widist) {
+ warning(sprintf "route delete %2d for %s .. %s",
+ $dbdist, $ia,$ib);
+ } elsif (!defined $dbdist) {
+ change(sprintf "route new %2d for %s .. %s",
$widist, $ia,$ib);
} elsif ($dbdist != $widist) {
- change(sprintf "change distance %2d to %2d for %s..%s",
+ change(sprintf "route change %2d to %2d for %s .. %s",
$dbdist, $widist, $ia,$ib);
}
}
parse_info_serverside();
-print "reading database\n";
+progress("reading database");
db_setocean($ocean);
db_connect();
database_fetch_ocean();
-print "reading yppedia chart\n"; yppedia_chart_parse();
-print "adding shortcuts\n"; yppedia_graphs_add_shortcuts();
-print "pruning bording vertices\n"; yppedia_graphs_prune_boring();
-print "checking yppedia graphs\n"; yppedia_graphs_check();
-print "setting archs from source-info\n"; yppedia_archs_sourceinfo();
-print "computing shortest paths\n"; yppedia_graphs_shortest_paths();
-print "setting archs from labels\n"; yppedia_archs_chart_labels();
-print "setting archs from nearby\n"; yppedia_archs_fillbynearest();
+progress("computing database spr"); database_graph_spr();
-print "comparing\n";
+progress("reading yppedia chart"); yppedia_chart_parse();
+progress("adding shortcuts"); yppedia_graphs_add_shortcuts();
+progress("pruning boring vertices"); yppedia_graphs_prune_boring();
+progress("checking yppedia graphs"); yppedia_graphs_check();
+progress("setting archs from source-info"); yppedia_archs_sourceinfo();
+progress("computing shortest paths"); yppedia_graph_shortest_paths();
+progress("setting archs from labels"); yppedia_archs_chart_labels();
+progress("setting archs from nearby"); yppedia_archs_fillbynearest();
+progress("computing yppedia spr"); yppedia_graph_spr();
-compare_island_lists();
-compare_distances();
+progress("comparing islands"); compare_island_lists();
+progress("comparing distances"); compare_distances();
print_messages();