diff options
Diffstat (limited to 'perllib/Graph/TransitiveClosure/Matrix.pm')
-rw-r--r-- | perllib/Graph/TransitiveClosure/Matrix.pm | 488 |
1 files changed, 0 insertions, 488 deletions
diff --git a/perllib/Graph/TransitiveClosure/Matrix.pm b/perllib/Graph/TransitiveClosure/Matrix.pm deleted file mode 100644 index be56f2a9..00000000 --- a/perllib/Graph/TransitiveClosure/Matrix.pm +++ /dev/null @@ -1,488 +0,0 @@ -package Graph::TransitiveClosure::Matrix; - -use strict; - -use Graph::AdjacencyMatrix; -use Graph::Matrix; - -sub _new { - my ($g, $class, $opt, $want_transitive, $want_reflexive, $want_path, $want_path_vertices) = @_; - my $m = Graph::AdjacencyMatrix->new($g, %$opt); - my @V = $g->vertices; - my $am = $m->adjacency_matrix; - my $dm; # The distance matrix. - my $pm; # The predecessor matrix. - my @di; - my %di; @di{ @V } = 0..$#V; - my @ai = @{ $am->[0] }; - my %ai = %{ $am->[1] }; - my @pi; - my %pi; - unless ($want_transitive) { - $dm = $m->distance_matrix; - @di = @{ $dm->[0] }; - %di = %{ $dm->[1] }; - $pm = Graph::Matrix->new($g); - @pi = @{ $pm->[0] }; - %pi = %{ $pm->[1] }; - for my $u (@V) { - my $diu = $di{$u}; - my $aiu = $ai{$u}; - for my $v (@V) { - my $div = $di{$v}; - my $aiv = $ai{$v}; - next unless - # $am->get($u, $v) - vec($ai[$aiu], $aiv, 1) - ; - # $dm->set($u, $v, $u eq $v ? 0 : 1) - $di[$diu]->[$div] = $u eq $v ? 0 : 1 - unless - defined - # $dm->get($u, $v) - $di[$diu]->[$div] - ; - $pi[$diu]->[$div] = $v unless $u eq $v; - } - } - } - # XXX (see the bits below): sometimes, being nice and clean is the - # wrong thing to do. In this case, using the public API for graph - # transitive matrices and bitmatrices makes things awfully slow. - # Instead, we go straight for the jugular of the data structures. - for my $u (@V) { - my $diu = $di{$u}; - my $aiu = $ai{$u}; - my $didiu = $di[$diu]; - my $aiaiu = $ai[$aiu]; - for my $v (@V) { - my $div = $di{$v}; - my $aiv = $ai{$v}; - my $didiv = $di[$div]; - my $aiaiv = $ai[$aiv]; - if ( - # $am->get($v, $u) - vec($aiaiv, $aiu, 1) - || ($want_reflexive && $u eq $v)) { - my $aivivo = $aiaiv; - if ($want_transitive) { - if ($want_reflexive) { - for my $w (@V) { - next if $w eq $u; - my $aiw = $ai{$w}; - return 0 - if vec($aiaiu, $aiw, 1) && - !vec($aiaiv, $aiw, 1); - } - # See XXX above. - # for my $w (@V) { - # my $aiw = $ai{$w}; - # if ( - # # $am->get($u, $w) - # vec($aiaiu, $aiw, 1) - # || ($u eq $w)) { - # return 0 - # if $u ne $w && - # # !$am->get($v, $w) - # !vec($aiaiv, $aiw, 1) - # ; - # # $am->set($v, $w) - # vec($aiaiv, $aiw, 1) = 1 - # ; - # } - # } - } else { - # See XXX above. - # for my $w (@V) { - # my $aiw = $ai{$w}; - # if ( - # # $am->get($u, $w) - # vec($aiaiu, $aiw, 1) - # ) { - # return 0 - # if $u ne $w && - # # !$am->get($v, $w) - # !vec($aiaiv, $aiw, 1) - # ; - # # $am->set($v, $w) - # vec($aiaiv, $aiw, 1) = 1 - # ; - # } - # } - $aiaiv |= $aiaiu; - } - } else { - if ($want_reflexive) { - $aiaiv |= $aiaiu; - vec($aiaiv, $aiu, 1) = 1; - # See XXX above. - # for my $w (@V) { - # my $aiw = $ai{$w}; - # if ( - # # $am->get($u, $w) - # vec($aiaiu, $aiw, 1) - # || ($u eq $w)) { - # # $am->set($v, $w) - # vec($aiaiv, $aiw, 1) = 1 - # ; - # } - # } - } else { - $aiaiv |= $aiaiu; - # See XXX above. - # for my $w (@V) { - # my $aiw = $ai{$w}; - # if ( - # # $am->get($u, $w) - # vec($aiaiu, $aiw, 1) - # ) { - # # $am->set($v, $w) - # vec($aiaiv, $aiw, 1) = 1 - # ; - # } - # } - } - } - if ($aiaiv ne $aivivo) { - $ai[$aiv] = $aiaiv; - $aiaiu = $aiaiv if $u eq $v; - } - } - if ($want_path && !$want_transitive) { - for my $w (@V) { - my $aiw = $ai{$w}; - next unless - # See XXX above. - # $am->get($v, $u) - vec($aiaiv, $aiu, 1) - && - # See XXX above. - # $am->get($u, $w) - vec($aiaiu, $aiw, 1) - ; - my $diw = $di{$w}; - my ($d0, $d1a, $d1b); - if (defined $dm) { - # See XXX above. - # $d0 = $dm->get($v, $w); - # $d1a = $dm->get($v, $u) || 1; - # $d1b = $dm->get($u, $w) || 1; - $d0 = $didiv->[$diw]; - $d1a = $didiv->[$diu] || 1; - $d1b = $didiu->[$diw] || 1; - } else { - $d1a = 1; - $d1b = 1; - } - my $d1 = $d1a + $d1b; - if (!defined $d0 || ($d1 < $d0)) { - # print "d1 = $d1a ($v, $u) + $d1b ($u, $w) = $d1 ($v, $w) (".(defined$d0?$d0:"-").")\n"; - # See XXX above. - # $dm->set($v, $w, $d1); - $didiv->[$diw] = $d1; - $pi[$div]->[$diw] = $pi[$div]->[$diu] - if $want_path_vertices; - } - } - # $dm->set($u, $v, 1) - $didiu->[$div] = 1 - if $u ne $v && - # $am->get($u, $v) - vec($aiaiu, $aiv, 1) - && - # !defined $dm->get($u, $v); - !defined $didiu->[$div]; - } - } - } - return 1 if $want_transitive; - my %V; @V{ @V } = @V; - $am->[0] = \@ai; - $am->[1] = \%ai; - if (defined $dm) { - $dm->[0] = \@di; - $dm->[1] = \%di; - } - if (defined $pm) { - $pm->[0] = \@pi; - $pm->[1] = \%pi; - } - bless [ $am, $dm, $pm, \%V ], $class; -} - -sub new { - my ($class, $g, %opt) = @_; - my %am_opt = (distance_matrix => 1); - if (exists $opt{attribute_name}) { - $am_opt{attribute_name} = $opt{attribute_name}; - delete $opt{attribute_name}; - } - if ($opt{distance_matrix}) { - $am_opt{distance_matrix} = $opt{distance_matrix}; - } - delete $opt{distance_matrix}; - if (exists $opt{path}) { - $opt{path_length} = $opt{path}; - $opt{path_vertices} = $opt{path}; - delete $opt{path}; - } - my $want_path_length; - if (exists $opt{path_length}) { - $want_path_length = $opt{path_length}; - delete $opt{path_length}; - } - my $want_path_vertices; - if (exists $opt{path_vertices}) { - $want_path_vertices = $opt{path_vertices}; - delete $opt{path_vertices}; - } - my $want_reflexive; - if (exists $opt{reflexive}) { - $want_reflexive = $opt{reflexive}; - delete $opt{reflexive}; - } - my $want_transitive; - if (exists $opt{is_transitive}) { - $want_transitive = $opt{is_transitive}; - $am_opt{is_transitive} = $want_transitive; - delete $opt{is_transitive}; - } - die "Graph::TransitiveClosure::Matrix::new: Unknown options: @{[map { qq['$_' => $opt{$_}]} keys %opt]}" - if keys %opt; - $want_reflexive = 1 unless defined $want_reflexive; - my $want_path = $want_path_length || $want_path_vertices; - # $g->expect_dag if $want_path; - _new($g, $class, - \%am_opt, - $want_transitive, $want_reflexive, - $want_path, $want_path_vertices); -} - -sub has_vertices { - my $tc = shift; - for my $v (@_) { - return 0 unless exists $tc->[3]->{ $v }; - } - return 1; -} - -sub is_reachable { - my ($tc, $u, $v) = @_; - return undef unless $tc->has_vertices($u, $v); - return 1 if $u eq $v; - $tc->[0]->get($u, $v); -} - -sub is_transitive { - if (@_ == 1) { # Any graph. - __PACKAGE__->new($_[0], is_transitive => 1); # Scary. - } else { # A TC graph. - my ($tc, $u, $v) = @_; - return undef unless $tc->has_vertices($u, $v); - $tc->[0]->get($u, $v); - } -} - -sub vertices { - my $tc = shift; - values %{ $tc->[3] }; -} - -sub path_length { - my ($tc, $u, $v) = @_; - return undef unless $tc->has_vertices($u, $v); - return 0 if $u eq $v; - $tc->[1]->get($u, $v); -} - -sub path_predecessor { - my ($tc, $u, $v) = @_; - return undef if $u eq $v; - return undef unless $tc->has_vertices($u, $v); - $tc->[2]->get($u, $v); -} - -sub path_vertices { - my ($tc, $u, $v) = @_; - return unless $tc->is_reachable($u, $v); - return wantarray ? () : 0 if $u eq $v; - my @v = ( $u ); - while ($u ne $v) { - last unless defined($u = $tc->path_predecessor($u, $v)); - push @v, $u; - } - $tc->[2]->set($u, $v, [ @v ]) if @v; - return @v; -} - -1; -__END__ -=pod - -=head1 NAME - -Graph::TransitiveClosure::Matrix - create and query transitive closure of graph - -=head1 SYNOPSIS - - use Graph::TransitiveClosure::Matrix; - use Graph::Directed; # or Undirected - - my $g = Graph::Directed->new; - $g->add_...(); # build $g - - # Compute the transitive closure matrix. - my $tcm = Graph::TransitiveClosure::Matrix->new($g); - - # Being reflexive is the default, - # meaning that null transitions are included. - my $tcm = Graph::TransitiveClosure::Matrix->new($g, reflexive => 1); - $tcm->is_reachable($u, $v) - - # is_reachable(u, v) is always reflexive. - $tcm->is_reachable($u, $v) - - # The reflexivity of is_transitive(u, v) depends of the reflexivity - # of the transitive closure. - $tcg->is_transitive($u, $v) - - my $tcm = Graph::TransitiveClosure::Matrix->new($g, path_length => 1); - $tcm->path_length($u, $v) - - my $tcm = Graph::TransitiveClosure::Matrix->new($g, path_vertices => 1); - $tcm->path_vertices($u, $v) - - my $tcm = Graph::TransitiveClosure::Matrix->new($g, attribute_name => 'length'); - $tcm->path_length($u, $v) - - $tcm->vertices - -=head1 DESCRIPTION - -You can use C<Graph::TransitiveClosure::Matrix> to compute the -transitive closure matrix of a graph and optionally also the minimum -paths (lengths and vertices) between vertices, and after that query -the transitiveness between vertices by using the C<is_reachable()> and -C<is_transitive()> methods, and the paths by using the -C<path_length()> and C<path_vertices()> methods. - -If you modify the graph after computing its transitive closure, -the transitive closure and minimum paths may become invalid. - -=head1 Methods - -=head2 Class Methods - -=over 4 - -=item new($g) - -Construct the transitive closure matrix of the graph $g. - -=item new($g, options) - -Construct the transitive closure matrix of the graph $g with options -as a hash. The known options are - -=over 8 - -=item C<attribute_name> => I<attribute_name> - -By default the edge attribute used for distance is C<w>. You can -change that by giving another attribute name with the C<attribute_name> -attribute to the new() constructor. - -=item reflexive => boolean - -By default the transitive closure matrix is not reflexive: that is, -the adjacency matrix has zeroes on the diagonal. To have ones on -the diagonal, use true for the C<reflexive> option. - -B<NOTE>: this behaviour has changed from Graph 0.2xxx: transitive -closure graphs were by default reflexive. - -=item path_length => boolean - -By default the path lengths are not computed, only the boolean transitivity. -By using true for C<path_length> also the path lengths will be computed, -they can be retrieved using the path_length() method. - -=item path_vertices => boolean - -By default the paths are not computed, only the boolean transitivity. -By using true for C<path_vertices> also the paths will be computed, -they can be retrieved using the path_vertices() method. - -=back - -=back - -=head2 Object Methods - -=over 4 - -=item is_reachable($u, $v) - -Return true if the vertex $v is reachable from the vertex $u, -or false if not. - -=item path_length($u, $v) - -Return the minimum path length from the vertex $u to the vertex $v, -or undef if there is no such path. - -=item path_vertices($u, $v) - -Return the minimum path (as a list of vertices) from the vertex $u to -the vertex $v, or an empty list if there is no such path, OR also return -an empty list if $u equals $v. - -=item has_vertices($u, $v, ...) - -Return true if the transitive closure matrix has all the listed vertices, -false if not. - -=item is_transitive($u, $v) - -Return true if the vertex $v is transitively reachable from the vertex $u, -false if not. - -=item vertices - -Return the list of vertices in the transitive closure matrix. - -=item path_predecessor - -Return the predecessor of vertex $v in the transitive closure path -going back to vertex $u. - -=back - -=head1 RETURN VALUES - -For path_length() the return value will be the sum of the appropriate -attributes on the edges of the path, C<weight> by default. If no -attribute has been set, one (1) will be assumed. - -If you try to ask about vertices not in the graph, undefs and empty -lists will be returned. - -=head1 ALGORITHM - -The transitive closure algorithm used is Warshall and Floyd-Warshall -for the minimum paths, which is O(V**3) in time, and the returned -matrices are O(V**2) in space. - -=head1 SEE ALSO - -L<Graph::AdjacencyMatrix> - -=head1 AUTHOR AND COPYRIGHT - -Jarkko Hietaniemi F<jhi@iki.fi> - -=head1 LICENSE - -This module is licensed under the same terms as Perl itself. - -=cut |