Algorithm-DependencySolver
view release on metacpan or search on metacpan
lib/Algorithm/DependencySolver/Solver.pm view on Meta::CPAN
return \%r;
} else {
return;
}
}
# Safe to call on cyclic graphs. Will not fail early if cycle
# encountered
method _apply_orderings($G) {
for my $nodeB (@{$self->get_nodes}) {
for my $nodeA_id (@{$nodeB->prerequisites}) {
my %seen;
my $recurse;
$recurse = sub {
my $node_id = shift;
return if $seen{$node_id}++;
for my $to_id ($G->successors($node_id)) {
if ($to_id eq $nodeA_id) {
$G->delete_edge($node_id, $to_id);
}
else {
$recurse->($to_id);
}
}
};
$recurse->($nodeB->id);
}
}
}
=head2 _remove_redundancy
$self->_remove_redundancy($G); # Ignore the return value
Applied to a graph object, removes redundant edges. An edge is
redundant if it can be removed without invalidating the graph.
The fundamental law of the dependency graph is that a node can only be
traversed when all of its predecessors have been traversed.
Given some node, C<$n>, and a predecessor of C<$n>, C<$a>, then it is
safe to remove C<$a> if and only if another node exists, C<$b>, which
is a predecessor of C<$n>, and there is a path from C<$a> to C<$b>
(i.e., traversal of C<$b> requires that C<$a> has been visited).
Note that cycles may cause this algorithm to behave unexpectedly
(depending on what one expects). Consider what happens if C<$n> has
two successors, C<$a> and C<$b>, such that there is a cycle between
C<$a> and C<$b> (i.e., there is an edge from C<$a> to C<$b>, and
vice-versa). Suppose that the edge from C<$n> to C<$a> has been
removed. Can the edge from C<$n> to C<$b> safely be removed?
Using the algorithm described above, yes! This is because there is
another path from C<$n> to C<$b>: C<$n -> $b -> $a -> b>. We
can, of course, detect such occurrences; however, I choose not to,
because it's not clear to me what the most elegant result should be in
these situations. Semantically, it does not matter whether the edge
from C<$n> to the C<$a,$b>-cycle is from C<$n> to C<$a>, or C<$n> to
C<$b>. Which should it be? Both, or one-or-the-other (presumably
decided arbitrarily)?
Properties:
* This method can be safely called on cyclic graphs (i.e., it will not
enter a non-terminating loop)
* This method will not fail early if a cycle is encountered (i.e., it
will do as much work as it can, even though the graph is probably
invalid)
* If C<_apply_orderings> is to be called on the graph object, it
I<must> be done I<before> calling C<_remove_redundancy>
=cut
method _remove_redundancy($G) {
for my $node ($G->vertices) {
for my $pred ($G->predecessors($node)) {
next unless $G->has_edge($pred, $node);
my @other_predecessors =
grep { $_ ne $pred } $G->predecessors($node);
my $other_paths_to_pred = grep {
# Returns true only if the edge from $pred to $node can
# safely be removed
any { $_ eq $pred } $G->all_predecessors($_);
} @other_predecessors;
if ($other_paths_to_pred) {
$G->delete_edge($pred, $node);
}
}
}
}
method build_affects_index() {
my %index;
for my $node (@{$self->get_nodes}) {
for my $resource (@{$node->affects}) {
push @{$index{$resource}}, $node;
}
}
return \%index;
}
method to_s() {
return $self->get_GraphEasy->as_ascii();
}
=head2 to_png
$solver->to_png($file)
Outputs a dependency graph (showing only operations) to the given file
in PNG format
( run in 2.280 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )