Graph
view release on metacpan or search on metacpan
lib/Graph/TransitiveClosure/Matrix.pm view on Meta::CPAN
package Graph::TransitiveClosure::Matrix;
use strict;
use warnings;
use Graph::AdjacencyMatrix;
use Graph::Matrix;
use Scalar::Util qw(weaken);
use List::Util qw(min);
sub _A() { 0 } # adjacency
sub _D() { 1 } # distance
sub _S() { 2 } # successors
sub _V() { 3 } # vertices
sub _G() { 4 } # the original graph (OG)
sub _new {
my ($g, $class, $am_opt, $want_transitive, $want_reflexive, $want_path, $want_path_vertices, $want_path_count) = @_;
my $m = Graph::AdjacencyMatrix->new($g, %$am_opt);
my @V = $g->vertices;
my %v2i; @v2i{ @V } = 0..$#V; # paths are in array -> stable ordering
my $am = $m->adjacency_matrix;
$am->[1] = \%v2i;
my ($dm, @di); # The distance matrix.
my ($sm, @si); # The successor matrix.
# directly use (not via API) arrays of bit-vectors etc for speed.
# the API is so low-level it adds no clarity anyway
my @ai = @{ $am->[0] };
my $multi = $g->multiedged;
unless ($want_transitive) {
$dm = $m->distance_matrix || Graph::Matrix->new($g); # if no distance_matrix in AM, we make our own
if ($want_path_count) {
# force defined
@di = map [ (0) x @V ], 0..$#V;
} else {
@di = @{ $dm->[0] };
}
$sm = Graph::Matrix->new($g);
$dm->[1] = $sm->[1] = \%v2i;
@si = @{ $sm->[0] };
for (my $iu = $#V; $iu >= 0; $iu--) {
vec($ai[$iu], $iu, 1) = 1 if $want_reflexive;
for (my $iv = $#V; $iv >= 0; $iv--) {
next unless vec($ai[$iu], $iv, 1);
if ($want_path_count or !defined $di[$iu][$iv]) {
$di[$iu][$iv] = $iu == $iv ? 0 : 1;
} elsif ($multi and ref($di[$iu][$iv]) eq 'HASH') {
$di[$iu][$iv] = min values %{ $di[$iu][$iv] };
}
$si[$iu]->[$iv] = $V[$iv] unless $iu == $iv;
}
}
}
# naming here is u = start, v = midpoint, w = endpoint
for (my $iv = $#V; $iv >= 0; $iv--) {
my $div = $di[$iv];
my $aiv = $ai[$iv];
for (my $iu = $#V; $iu >= 0; $iu--) {
my $aiu = $ai[$iu];
next if !vec($aiu, $iv, 1);
if ($want_transitive) {
for (my $iw = $#V; $iw >= 0; $iw--) {
return 0
if $iw != $iv &&
vec($aiv, $iw, 1) &&
!vec($aiu, $iw, 1);
}
next;
}
my $aiuo = $aiu;
$aiu |= $aiv;
if ($aiu ne $aiuo) {
$ai[$iu] = $aiu;
$aiv = $aiu if $iv == $iu;
}
( run in 1.242 second using v1.01-cache-2.11-cpan-5b529ec07f3 )