diff options
Diffstat (limited to 'perllib/Graph/Traversal.pm')
-rw-r--r-- | perllib/Graph/Traversal.pm | 714 |
1 files changed, 0 insertions, 714 deletions
diff --git a/perllib/Graph/Traversal.pm b/perllib/Graph/Traversal.pm deleted file mode 100644 index edfc5b1..0000000 --- a/perllib/Graph/Traversal.pm +++ /dev/null @@ -1,714 +0,0 @@ -package Graph::Traversal; - -use strict; - -# $SIG{__DIE__ } = sub { use Carp; confess }; -# $SIG{__WARN__} = sub { use Carp; confess }; - -sub DEBUG () { 0 } - -sub reset { - my $self = shift; - $self->{ unseen } = { map { $_ => $_ } $self->{ graph }->vertices }; - $self->{ seen } = { }; - $self->{ order } = [ ]; - $self->{ preorder } = [ ]; - $self->{ postorder } = [ ]; - $self->{ roots } = [ ]; - $self->{ tree } = - Graph->new( directed => $self->{ graph }->directed ); - delete $self->{ terminate }; -} - -my $see = sub { - my $self = shift; - $self->see; -}; - -my $see_active = sub { - my $self = shift; - delete @{ $self->{ active } }{ $self->see }; -}; - -sub has_a_cycle { - my ($u, $v, $t, $s) = @_; - $s->{ has_a_cycle } = 1; - $t->terminate; -} - -sub find_a_cycle { - my ($u, $v, $t, $s) = @_; - my @cycle = ( $u ); - push @cycle, $v unless $u eq $v; - my $path = $t->{ order }; - if (@$path) { - my $i = $#$path; - while ($i >= 0 && $path->[ $i ] ne $v) { $i-- } - if ($i >= 0) { - unshift @cycle, @{ $path }[ $i+1 .. $#$path ]; - } - } - $s->{ a_cycle } = \@cycle; - $t->terminate; -} - -sub configure { - my ($self, %attr) = @_; - $self->{ pre } = $attr{ pre } if exists $attr{ pre }; - $self->{ post } = $attr{ post } if exists $attr{ post }; - $self->{ pre_vertex } = $attr{ pre_vertex } if exists $attr{ pre_vertex }; - $self->{ post_vertex } = $attr{ post_vertex } if exists $attr{ post_vertex }; - $self->{ pre_edge } = $attr{ pre_edge } if exists $attr{ pre_edge }; - $self->{ post_edge } = $attr{ post_edge } if exists $attr{ post_edge }; - if (exists $attr{ successor }) { # Graph 0.201 compatibility. - $self->{ tree_edge } = $self->{ non_tree_edge } = $attr{ successor }; - } - if (exists $attr{ unseen_successor }) { - if (exists $self->{ tree_edge }) { # Graph 0.201 compatibility. - my $old_tree_edge = $self->{ tree_edge }; - $self->{ tree_edge } = sub { - $old_tree_edge->( @_ ); - $attr{ unseen_successor }->( @_ ); - }; - } else { - $self->{ tree_edge } = $attr{ unseen_successor }; - } - } - if ($self->graph->multiedged || $self->graph->countedged) { - $self->{ seen_edge } = $attr{ seen_edge } if exists $attr{ seen_edge }; - if (exists $attr{ seen_successor }) { # Graph 0.201 compatibility. - $self->{ seen_edge } = $attr{ seen_edge }; - } - } - $self->{ non_tree_edge } = $attr{ non_tree_edge } if exists $attr{ non_tree_edge }; - $self->{ pre_edge } = $attr{ tree_edge } if exists $attr{ tree_edge }; - $self->{ back_edge } = $attr{ back_edge } if exists $attr{ back_edge }; - $self->{ down_edge } = $attr{ down_edge } if exists $attr{ down_edge }; - $self->{ cross_edge } = $attr{ cross_edge } if exists $attr{ cross_edge }; - if (exists $attr{ start }) { - $attr{ first_root } = $attr{ start }; - $attr{ next_root } = undef; - } - if (exists $attr{ get_next_root }) { - $attr{ next_root } = $attr{ get_next_root }; # Graph 0.201 compat. - } - $self->{ next_root } = - exists $attr{ next_root } ? - $attr{ next_root } : - $attr{ next_alphabetic } ? - \&Graph::_next_alphabetic : - $attr{ next_numeric } ? - \&Graph::_next_numeric : - \&Graph::_next_random; - $self->{ first_root } = - exists $attr{ first_root } ? - $attr{ first_root } : - exists $attr{ next_root } ? - $attr{ next_root } : - $attr{ next_alphabetic } ? - \&Graph::_next_alphabetic : - $attr{ next_numeric } ? - \&Graph::_next_numeric : - \&Graph::_next_random; - $self->{ next_successor } = - exists $attr{ next_successor } ? - $attr{ next_successor } : - $attr{ next_alphabetic } ? - \&Graph::_next_alphabetic : - $attr{ next_numeric } ? - \&Graph::_next_numeric : - \&Graph::_next_random; - if (exists $attr{ has_a_cycle }) { - my $has_a_cycle = - ref $attr{ has_a_cycle } eq 'CODE' ? - $attr{ has_a_cycle } : \&has_a_cycle; - $self->{ back_edge } = $has_a_cycle; - if ($self->{ graph }->is_undirected) { - $self->{ down_edge } = $has_a_cycle; - } - } - if (exists $attr{ find_a_cycle }) { - my $find_a_cycle = - ref $attr{ find_a_cycle } eq 'CODE' ? - $attr{ find_a_cycle } : \&find_a_cycle; - $self->{ back_edge } = $find_a_cycle; - if ($self->{ graph }->is_undirected) { - $self->{ down_edge } = $find_a_cycle; - } - } - $self->{ add } = \&add_order; - $self->{ see } = $see; - delete @attr{ qw( - pre post pre_edge post_edge - successor unseen_successor seen_successor - tree_edge non_tree_edge - back_edge down_edge cross_edge seen_edge - start get_next_root - next_root next_alphabetic next_numeric next_random next_successor - first_root - has_a_cycle find_a_cycle - ) }; - if (keys %attr) { - require Carp; - my @attr = sort keys %attr; - Carp::croak(sprintf "Graph::Traversal: unknown attribute%s @{[map { qq['$_'] } @attr]}\n", @attr == 1 ? '' : 's'); - } -} - -sub new { - my $class = shift; - my $g = shift; - unless (ref $g && $g->isa('Graph')) { - require Carp; - Carp::croak("Graph::Traversal: first argument is not a Graph"); - } - my $self = { graph => $g, state => { } }; - bless $self, $class; - $self->reset; - $self->configure( @_ ); - return $self; -} - -sub terminate { - my $self = shift; - $self->{ terminate } = 1; -} - -sub add_order { - my ($self, @next) = @_; - push @{ $self->{ order } }, @next; -} - -sub visit { - my ($self, @next) = @_; - delete @{ $self->{ unseen } }{ @next }; - print "unseen = @{[sort keys %{$self->{unseen}}]}\n" if DEBUG; - @{ $self->{ seen } }{ @next } = @next; - print "seen = @{[sort keys %{$self->{seen}}]}\n" if DEBUG; - $self->{ add }->( $self, @next ); - print "order = @{$self->{order}}\n" if DEBUG; - if (exists $self->{ pre }) { - my $p = $self->{ pre }; - for my $v (@next) { - $p->( $v, $self ); - } - } -} - -sub visit_preorder { - my ($self, @next) = @_; - push @{ $self->{ preorder } }, @next; - for my $v (@next) { - $self->{ preordern }->{ $v } = $self->{ preorderi }++; - } - print "preorder = @{$self->{preorder}}\n" if DEBUG; - $self->visit( @next ); -} - -sub visit_postorder { - my ($self) = @_; - my @post = reverse $self->{ see }->( $self ); - push @{ $self->{ postorder } }, @post; - for my $v (@post) { - $self->{ postordern }->{ $v } = $self->{ postorderi }++; - } - print "postorder = @{$self->{postorder}}\n" if DEBUG; - if (exists $self->{ post }) { - my $p = $self->{ post }; - for my $v (@post) { - $p->( $v, $self ) ; - } - } - if (exists $self->{ post_edge }) { - my $p = $self->{ post_edge }; - my $u = $self->current; - if (defined $u) { - for my $v (@post) { - $p->( $u, $v, $self, $self->{ state }); - } - } - } -} - -sub _callbacks { - my ($self, $current, @all) = @_; - return unless @all; - my $nontree = $self->{ non_tree_edge }; - my $back = $self->{ back_edge }; - my $down = $self->{ down_edge }; - my $cross = $self->{ cross_edge }; - my $seen = $self->{ seen_edge }; - my $bdc = defined $back || defined $down || defined $cross; - if (defined $nontree || $bdc || defined $seen) { - my $u = $current; - my $preu = $self->{ preordern }->{ $u }; - my $postu = $self->{ postordern }->{ $u }; - for my $v ( @all ) { - my $e = $self->{ tree }->has_edge( $u, $v ); - if ( !$e && (defined $nontree || $bdc) ) { - if ( exists $self->{ seen }->{ $v }) { - $nontree->( $u, $v, $self, $self->{ state }) - if $nontree; - if ($bdc) { - my $postv = $self->{ postordern }->{ $v }; - if ($back && - (!defined $postv || $postv >= $postu)) { - $back ->( $u, $v, $self, $self->{ state }); - } else { - my $prev = $self->{ preordern }->{ $v }; - if ($down && $prev > $preu) { - $down ->( $u, $v, $self, $self->{ state }); - } elsif ($cross && $prev < $preu) { - $cross->( $u, $v, $self, $self->{ state }); - } - } - } - } - } - if ($seen) { - my $c = $self->graph->get_edge_count($u, $v); - while ($c-- > 1) { - $seen->( $u, $v, $self, $self->{ state } ); - } - } - } - } -} - -sub next { - my $self = shift; - return undef if $self->{ terminate }; - my @next; - while ($self->seeing) { - my $current = $self->current; - print "current = $current\n" if DEBUG; - @next = $self->{ graph }->successors( $current ); - print "next.0 - @next\n" if DEBUG; - my %next; @next{ @next } = @next; -# delete $next{ $current }; - print "next.1 - @next\n" if DEBUG; - @next = keys %next; - my @all = @next; - print "all = @all\n" if DEBUG; - delete @next{ $self->seen }; - @next = keys %next; - print "next.2 - @next\n" if DEBUG; - if (@next) { - @next = $self->{ next_successor }->( $self, \%next ); - print "next.3 - @next\n" if DEBUG; - for my $v (@next) { - $self->{ tree }->add_edge( $current, $v ); - } - if (exists $self->{ pre_edge }) { - my $p = $self->{ pre_edge }; - my $u = $self->current; - for my $v (@next) { - $p->( $u, $v, $self, $self->{ state }); - } - } - last; - } else { - $self->visit_postorder; - } - return undef if $self->{ terminate }; - $self->_callbacks($current, @all); -# delete $next{ $current }; - } - print "next.4 - @next\n" if DEBUG; - unless (@next) { - unless ( @{ $self->{ roots } } ) { - my $first = $self->{ first_root }; - if (defined $first) { - @next = - ref $first eq 'CODE' ? - $self->{ first_root }->( $self, $self->{ unseen } ) : - $first; - return unless @next; - } - } - unless (@next) { - return unless defined $self->{ next_root }; - return unless @next = - $self->{ next_root }->( $self, $self->{ unseen } ); - } - return if exists $self->{ seen }->{ $next[0] }; # Sanity check. - print "next.5 - @next\n" if DEBUG; - push @{ $self->{ roots } }, $next[0]; - } - print "next.6 - @next\n" if DEBUG; - if (@next) { - $self->visit_preorder( @next ); - } - return $next[0]; -} - -sub _order { - my ($self, $order) = @_; - 1 while defined $self->next; - my $wantarray = wantarray; - if ($wantarray) { - @{ $self->{ $order } }; - } elsif (defined $wantarray) { - shift @{ $self->{ $order } }; - } -} - -sub preorder { - my $self = shift; - $self->_order( 'preorder' ); -} - -sub postorder { - my $self = shift; - $self->_order( 'postorder' ); -} - -sub unseen { - my $self = shift; - values %{ $self->{ unseen } }; -} - -sub seen { - my $self = shift; - values %{ $self->{ seen } }; -} - -sub seeing { - my $self = shift; - @{ $self->{ order } }; -} - -sub roots { - my $self = shift; - @{ $self->{ roots } }; -} - -sub is_root { - my ($self, $v) = @_; - for my $u (@{ $self->{ roots } }) { - return 1 if $u eq $v; - } - return 0; -} - -sub tree { - my $self = shift; - $self->{ tree }; -} - -sub graph { - my $self = shift; - $self->{ graph }; -} - -sub vertex_by_postorder { - my ($self, $i) = @_; - exists $self->{ postorder } && $self->{ postorder }->[ $i ]; -} - -sub postorder_by_vertex { - my ($self, $v) = @_; - exists $self->{ postordern } && $self->{ postordern }->{ $v }; -} - -sub postorder_vertices { - my ($self, $v) = @_; - exists $self->{ postordern } ? %{ $self->{ postordern } } : (); -} - -sub vertex_by_preorder { - my ($self, $i) = @_; - exists $self->{ preorder } && $self->{ preorder }->[ $i ]; -} - -sub preorder_by_vertex { - my ($self, $v) = @_; - exists $self->{ preordern } && $self->{ preordern }->{ $v }; -} - -sub preorder_vertices { - my ($self, $v) = @_; - exists $self->{ preordern } ? %{ $self->{ preordern } } : (); -} - -sub has_state { - my ($self, $var) = @_; - exists $self->{ state } && exists $self->{ state }->{ $var }; -} - -sub get_state { - my ($self, $var) = @_; - exists $self->{ state } ? $self->{ state }->{ $var } : undef; -} - -sub set_state { - my ($self, $var, $val) = @_; - $self->{ state }->{ $var } = $val; - return 1; -} - -sub delete_state { - my ($self, $var) = @_; - delete $self->{ state }->{ $var }; - delete $self->{ state } unless keys %{ $self->{ state } }; - return 1; -} - -1; -__END__ -=pod - -=head1 NAME - -Graph::Traversal - traverse graphs - -=head1 SYNOPSIS - -Don't use Graph::Traversal directly, use Graph::Traversal::DFS -or Graph::Traversal::BFS instead. - - use Graph; - my $g = Graph->new; - $g->add_edge(...); - use Graph::Traversal::...; - my $t = Graph::Traversal::...->new(%opt); - $t->... - -=head1 DESCRIPTION - -You can control how the graph is traversed by the various callback -parameters in the C<%opt>. In the parameters descriptions below the -$u and $v are vertices, and the $self is the traversal object itself. - -=head2 Callback parameters - -The following callback parameters are available: - -=over 4 - -=item tree_edge - -Called when traversing an edge that belongs to the traversal tree. -Called with arguments ($u, $v, $self). - -=item non_tree_edge - -Called when an edge is met which either leads back to the traversal tree -(either a C<back_edge>, a C<down_edge>, or a C<cross_edge>). -Called with arguments ($u, $v, $self). - -=item pre_edge - -Called for edges in preorder. -Called with arguments ($u, $v, $self). - -=item post_edge - -Called for edges in postorder. -Called with arguments ($u, $v, $self). - -=item back_edge - -Called for back edges. -Called with arguments ($u, $v, $self). - -=item down_edge - -Called for down edges. -Called with arguments ($u, $v, $self). - -=item cross_edge - -Called for cross edges. -Called with arguments ($u, $v, $self). - -=item pre - -=item pre_vertex - -Called for vertices in preorder. -Called with arguments ($v, $self). - -=item post - -=item post_vertex - -Called for vertices in postorder. -Called with arguments ($v, $self). - -=item first_root - -Called when choosing the first root (start) vertex for traversal. -Called with arguments ($self, $unseen) where $unseen is a hash -reference with the unseen vertices as keys. - -=item next_root - -Called when choosing the next root (after the first one) vertex for -traversal (useful when the graph is not connected). Called with -arguments ($self, $unseen) where $unseen is a hash reference with -the unseen vertices as keys. If you want only the first reachable -subgraph to be processed, set the next_root to C<undef>. - -=item start - -Identical to defining C<first_root> and undefining C<next_root>. - -=item next_alphabetic - -Set this to true if you want the vertices to be processed in -alphabetic order (and leave first_root/next_root undefined). - -=item next_numeric - -Set this to true if you want the vertices to be processed in -numeric order (and leave first_root/next_root undefined). - -=item next_successor - -Called when choosing the next vertex to visit. Called with arguments -($self, $next) where $next is a hash reference with the possible -next vertices as keys. Use this to provide a custom ordering for -choosing vertices, as opposed to C<next_numeric> or C<next_alphabetic>. - -=back - -The parameters C<first_root> and C<next_successor> have a 'hierarchy' -of how they are determined: if they have been explicitly defined, use -that value. If not, use the value of C<next_alphabetic>, if that has -been defined. If not, use the value of C<next_numeric>, if that has -been defined. If not, the next vertex to be visited is chose randomly. - -=head2 Methods - -The following methods are available: - -=over 4 - -=item unseen - -Return the unseen vertices in random order. - -=item seen - -Return the seen vertices in random order. - -=item seeing - -Return the active fringe vertices in random order. - -=item preorder - -Return the vertices in preorder traversal order. - -=item postorder - -Return the vertices in postorder traversal order. - -=item vertex_by_preorder - - $v = $t->vertex_by_preorder($i) - -Return the ith (0..$V-1) vertex by preorder. - -=item preorder_by_vertex - - $i = $t->preorder_by_vertex($v) - -Return the preorder index (0..$V-1) by vertex. - -=item vertex_by_postorder - - $v = $t->vertex_by_postorder($i) - -Return the ith (0..$V-1) vertex by postorder. - -=item postorder_by_vertex - - $i = $t->postorder_by_vertex($v) - -Return the postorder index (0..$V-1) by vertex. - -=item preorder_vertices - -Return a hash with the vertices as the keys and their preorder indices -as the values. - -=item postorder_vertices - -Return a hash with the vertices as the keys and their postorder -indices as the values. - -=item tree - -Return the traversal tree as a graph. - -=item has_state - - $t->has_state('s') - -Test whether the traversal has state 's' attached to it. - -=item get_state - - $t->get_state('s') - -Get the state 's' attached to the traversal (C<undef> if none). - -=item set_state - - $t->set_state('s', $s) - -Set the state 's' attached to the traversal. - -=item delete_state - - $t->delete_state('s') - -Delete the state 's' from the traversal. - -=back - -=head2 Backward compatibility - -The following parameters are for backward compatibility to Graph 0.2xx: - -=over 4 - -=item get_next_root - -Like C<next_root>. - -=item successor - -Identical to having C<tree_edge> both C<non_tree_edge> defined -to be the same. - -=item unseen_successor - -Like C<tree_edge>. - -=item seen_successor - -Like C<seed_edge>. - -=back - -=head2 Special callbacks - -If in a callback you call the special C<terminate> method, -the traversal is terminated, no more vertices are traversed. - -=head1 SEE ALSO - -L<Graph::Traversal::DFS>, L<Graph::Traversal::BFS> - -=head1 AUTHOR AND COPYRIGHT - -Jarkko Hietaniemi F<jhi@iki.fi> - -=head1 LICENSE - -This module is licensed under the same terms as Perl itself. - -=cut |