1 package Graph
::TransitiveClosure
::Matrix
;
5 use Graph
::AdjacencyMatrix
;
9 my ($g, $class, $opt, $want_transitive, $want_reflexive, $want_path, $want_path_vertices) = @_;
10 my $m = Graph
::AdjacencyMatrix
->new($g, %$opt);
12 my $am = $m->adjacency_matrix;
13 my $dm; # The distance matrix.
14 my $pm; # The predecessor matrix.
16 my %di; @di{ @V } = 0..$#V;
17 my @ai = @
{ $am->[0] };
18 my %ai = %{ $am->[1] };
21 unless ($want_transitive) {
22 $dm = $m->distance_matrix;
25 $pm = Graph
::Matrix
->new($g);
36 vec($ai[$aiu], $aiv, 1)
38 # $dm->set($u, $v, $u eq $v ? 0 : 1)
39 $di[$diu]->[$div] = $u eq $v ?
0 : 1
45 $pi[$diu]->[$div] = $v unless $u eq $v;
49 # XXX (see the bits below): sometimes, being nice and clean is the
50 # wrong thing to do. In this case, using the public API for graph
51 # transitive matrices and bitmatrices makes things awfully slow.
52 # Instead, we go straight for the jugular of the data structures.
56 my $didiu = $di[$diu];
57 my $aiaiu = $ai[$aiu];
61 my $didiv = $di[$div];
62 my $aiaiv = $ai[$aiv];
66 || ($want_reflexive && $u eq $v)) {
68 if ($want_transitive) {
69 if ($want_reflexive) {
74 if vec($aiaiu, $aiw, 1) &&
75 !vec($aiaiv, $aiw, 1);
82 # vec($aiaiu, $aiw, 1)
87 # !vec($aiaiv, $aiw, 1)
90 # vec($aiaiv, $aiw, 1) = 1
100 # vec($aiaiu, $aiw, 1)
104 # # !$am->get($v, $w)
105 # !vec($aiaiv, $aiw, 1)
108 # vec($aiaiv, $aiw, 1) = 1
115 if ($want_reflexive) {
117 vec($aiaiv, $aiu, 1) = 1;
123 # vec($aiaiu, $aiw, 1)
126 # vec($aiaiv, $aiw, 1) = 1
137 # vec($aiaiu, $aiw, 1)
140 # vec($aiaiv, $aiw, 1) = 1
146 if ($aiaiv ne $aivivo) {
148 $aiaiu = $aiaiv if $u eq $v;
151 if ($want_path && !$want_transitive) {
164 my ($d0, $d1a, $d1b);
167 # $d0 = $dm->get($v, $w);
168 # $d1a = $dm->get($v, $u) || 1;
169 # $d1b = $dm->get($u, $w) || 1;
170 $d0 = $didiv->[$diw];
171 $d1a = $didiv->[$diu] || 1;
172 $d1b = $didiu->[$diw] || 1;
177 my $d1 = $d1a + $d1b;
178 if (!defined $d0 || ($d1 < $d0)) {
179 # print "d1 = $d1a ($v, $u) + $d1b ($u, $w) = $d1 ($v, $w) (".(defined$d0?$d0:"-").")\n";
181 # $dm->set($v, $w, $d1);
182 $didiv->[$diw] = $d1;
183 $pi[$div]->[$diw] = $pi[$div]->[$diu]
184 if $want_path_vertices;
187 # $dm->set($u, $v, 1)
193 # !defined $dm->get($u, $v);
194 !defined $didiu->[$div];
198 return 1 if $want_transitive;
199 my %V; @V{ @V } = @V;
210 bless [ $am, $dm, $pm, \
%V ], $class;
214 my ($class, $g, %opt) = @_;
215 my %am_opt = (distance_matrix
=> 1);
216 if (exists $opt{attribute_name
}) {
217 $am_opt{attribute_name
} = $opt{attribute_name
};
218 delete $opt{attribute_name
};
220 if ($opt{distance_matrix
}) {
221 $am_opt{distance_matrix
} = $opt{distance_matrix
};
223 delete $opt{distance_matrix
};
224 if (exists $opt{path
}) {
225 $opt{path_length
} = $opt{path
};
226 $opt{path_vertices
} = $opt{path
};
229 my $want_path_length;
230 if (exists $opt{path_length
}) {
231 $want_path_length = $opt{path_length
};
232 delete $opt{path_length
};
234 my $want_path_vertices;
235 if (exists $opt{path_vertices
}) {
236 $want_path_vertices = $opt{path_vertices
};
237 delete $opt{path_vertices
};
240 if (exists $opt{reflexive
}) {
241 $want_reflexive = $opt{reflexive
};
242 delete $opt{reflexive
};
245 if (exists $opt{is_transitive
}) {
246 $want_transitive = $opt{is_transitive
};
247 $am_opt{is_transitive
} = $want_transitive;
248 delete $opt{is_transitive
};
250 die "Graph::TransitiveClosure::Matrix::new: Unknown options: @{[map { qq['$_' => $opt{$_}]} keys %opt]}"
252 $want_reflexive = 1 unless defined $want_reflexive;
253 my $want_path = $want_path_length || $want_path_vertices;
254 # $g->expect_dag if $want_path;
257 $want_transitive, $want_reflexive,
258 $want_path, $want_path_vertices);
264 return 0 unless exists $tc->[3]->{ $v };
270 my ($tc, $u, $v) = @_;
271 return undef unless $tc->has_vertices($u, $v);
272 return 1 if $u eq $v;
273 $tc->[0]->get($u, $v);
277 if (@_ == 1) { # Any graph.
278 __PACKAGE__
->new($_[0], is_transitive
=> 1); # Scary.
279 } else { # A TC graph.
280 my ($tc, $u, $v) = @_;
281 return undef unless $tc->has_vertices($u, $v);
282 $tc->[0]->get($u, $v);
288 values %{ $tc->[3] };
292 my ($tc, $u, $v) = @_;
293 return undef unless $tc->has_vertices($u, $v);
294 return 0 if $u eq $v;
295 $tc->[1]->get($u, $v);
298 sub path_predecessor
{
299 my ($tc, $u, $v) = @_;
300 return undef if $u eq $v;
301 return undef unless $tc->has_vertices($u, $v);
302 $tc->[2]->get($u, $v);
306 my ($tc, $u, $v) = @_;
307 return unless $tc->is_reachable($u, $v);
308 return wantarray ?
() : 0 if $u eq $v;
311 last unless defined($u = $tc->path_predecessor($u, $v));
314 $tc->[2]->set($u, $v, [ @v ]) if @v;
324 Graph::TransitiveClosure::Matrix - create and query transitive closure of graph
328 use Graph::TransitiveClosure::Matrix;
329 use Graph::Directed; # or Undirected
331 my $g = Graph::Directed->new;
332 $g->add_...(); # build $g
334 # Compute the transitive closure matrix.
335 my $tcm = Graph::TransitiveClosure::Matrix->new($g);
337 # Being reflexive is the default,
338 # meaning that null transitions are included.
339 my $tcm = Graph::TransitiveClosure::Matrix->new($g, reflexive => 1);
340 $tcm->is_reachable($u, $v)
342 # is_reachable(u, v) is always reflexive.
343 $tcm->is_reachable($u, $v)
345 # The reflexivity of is_transitive(u, v) depends of the reflexivity
346 # of the transitive closure.
347 $tcg->is_transitive($u, $v)
349 my $tcm = Graph::TransitiveClosure::Matrix->new($g, path_length => 1);
350 $tcm->path_length($u, $v)
352 my $tcm = Graph::TransitiveClosure::Matrix->new($g, path_vertices => 1);
353 $tcm->path_vertices($u, $v)
355 my $tcm = Graph::TransitiveClosure::Matrix->new($g, attribute_name => 'length');
356 $tcm->path_length($u, $v)
362 You can use C<Graph::TransitiveClosure::Matrix> to compute the
363 transitive closure matrix of a graph and optionally also the minimum
364 paths (lengths and vertices) between vertices, and after that query
365 the transitiveness between vertices by using the C<is_reachable()> and
366 C<is_transitive()> methods, and the paths by using the
367 C<path_length()> and C<path_vertices()> methods.
369 If you modify the graph after computing its transitive closure,
370 the transitive closure and minimum paths may become invalid.
380 Construct the transitive closure matrix of the graph $g.
382 =item new($g, options)
384 Construct the transitive closure matrix of the graph $g with options
385 as a hash. The known options are
389 =item C<attribute_name> => I<attribute_name>
391 By default the edge attribute used for distance is C<w>. You can
392 change that by giving another attribute name with the C<attribute_name>
393 attribute to the new() constructor.
395 =item reflexive => boolean
397 By default the transitive closure matrix is not reflexive: that is,
398 the adjacency matrix has zeroes on the diagonal. To have ones on
399 the diagonal, use true for the C<reflexive> option.
401 B<NOTE>: this behaviour has changed from Graph 0.2xxx: transitive
402 closure graphs were by default reflexive.
404 =item path_length => boolean
406 By default the path lengths are not computed, only the boolean transitivity.
407 By using true for C<path_length> also the path lengths will be computed,
408 they can be retrieved using the path_length() method.
410 =item path_vertices => boolean
412 By default the paths are not computed, only the boolean transitivity.
413 By using true for C<path_vertices> also the paths will be computed,
414 they can be retrieved using the path_vertices() method.
420 =head2 Object Methods
424 =item is_reachable($u, $v)
426 Return true if the vertex $v is reachable from the vertex $u,
429 =item path_length($u, $v)
431 Return the minimum path length from the vertex $u to the vertex $v,
432 or undef if there is no such path.
434 =item path_vertices($u, $v)
436 Return the minimum path (as a list of vertices) from the vertex $u to
437 the vertex $v, or an empty list if there is no such path, OR also return
438 an empty list if $u equals $v.
440 =item has_vertices($u, $v, ...)
442 Return true if the transitive closure matrix has all the listed vertices,
445 =item is_transitive($u, $v)
447 Return true if the vertex $v is transitively reachable from the vertex $u,
452 Return the list of vertices in the transitive closure matrix.
454 =item path_predecessor
456 Return the predecessor of vertex $v in the transitive closure path
457 going back to vertex $u.
463 For path_length() the return value will be the sum of the appropriate
464 attributes on the edges of the path, C<weight> by default. If no
465 attribute has been set, one (1) will be assumed.
467 If you try to ask about vertices not in the graph, undefs and empty
468 lists will be returned.
472 The transitive closure algorithm used is Warshall and Floyd-Warshall
473 for the minimum paths, which is O(V**3) in time, and the returned
474 matrices are O(V**2) in space.
478 L<Graph::AdjacencyMatrix>
480 =head1 AUTHOR AND COPYRIGHT
482 Jarkko Hietaniemi F<jhi@iki.fi>
486 This module is licensed under the same terms as Perl itself.