comparison lib/Graph/PathsTraversal.pm @ 0:4816e4a8ae95 draft default tip

Uploaded
author deepakjadmin
date Wed, 20 Jan 2016 09:23:18 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4816e4a8ae95
1 package Graph::PathsTraversal;
2 #
3 # $RCSfile: PathsTraversal.pm,v $
4 # $Date: 2015/02/28 20:49:06 $
5 # $Revision: 1.29 $
6 #
7 # Author: Manish Sud <msud@san.rr.com>
8 #
9 # Copyright (C) 2015 Manish Sud. All rights reserved.
10 #
11 # This file is part of MayaChemTools.
12 #
13 # MayaChemTools is free software; you can redistribute it and/or modify it under
14 # the terms of the GNU Lesser General Public License as published by the Free
15 # Software Foundation; either version 3 of the License, or (at your option) any
16 # later version.
17 #
18 # MayaChemTools is distributed in the hope that it will be useful, but without
19 # any warranty; without even the implied warranty of merchantability of fitness
20 # for a particular purpose. See the GNU Lesser General Public License for more
21 # details.
22 #
23 # You should have received a copy of the GNU Lesser General Public License
24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
26 # Boston, MA, 02111-1307, USA.
27 #
28
29 use strict;
30 use Carp;
31 use Exporter;
32 use Graph;
33 use Graph::Path;
34
35 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
36
37 @ISA = qw(Exporter);
38 @EXPORT = qw();
39 @EXPORT_OK = qw();
40
41 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]);
42
43 # Setup class variables...
44 my($ClassName);
45 _InitializeClass();
46
47 # Overload Perl functions...
48 use overload '""' => 'StringifyPathsTraversal';
49
50 # Class constructor...
51 sub new {
52 my($Class, $Graph) = @_;
53
54 # Initialize object...
55 my $This = {};
56 bless $This, ref($Class) || $Class;
57 $This->_InitializePathsTraversal($Graph);
58
59 return $This;
60 }
61
62 # Initialize object data...
63 sub _InitializePathsTraversal {
64 my($This, $Graph) = @_;
65
66 # Graph object...
67 $This->{Graph} = $Graph;
68
69 # Traversal mode: Vertex or Path
70 $This->{TraversalMode} = '';
71
72 # Traversal type: DFS, DFSWithLimit, BFS, BFSWithLimit...
73 $This->{TraversalType} = '';
74
75 # For finding root vertex and controlling search...
76 my(@VertexIDs);
77 @VertexIDs = $This->{Graph}->GetVertices();
78 %{$This->{VerticesToVisit}} = ();
79 @{$This->{VerticesToVisit}}{ @VertexIDs } = @VertexIDs;
80
81 # Root vertex of all visited vertices...
82 %{$This->{VerticesRoots}} = ();
83
84 # Visited vertices...
85 %{$This->{VisitedVertices}} = ();
86
87 # Finished vertices...
88 %{$This->{FinishedVertices}} = ();
89
90 # List of active vertices during DFS/BFS search...
91 @{$This->{ActiveVertices}} = ();
92
93 # List of ordered vertices traversed during DFS/BFS search...
94 @{$This->{Vertices}} = ();
95
96 # Vertex neighbors during traversal...
97 %{$This->{VerticesNeighbors}} = ();
98
99 # Vertices depth from root...
100 %{$This->{VerticesDepth}} = ();
101
102 # Predecessor of each vertex during vertex traversal. For root vertex, it's root itself...
103 %{$This->{VerticesPredecessors}} = ();
104
105 # Successors of each vertex during vertex traversal...
106 %{$This->{VerticesSuccessors}} = ();
107
108 # Vertices at different neighborhood levels during vertex traversal...
109 @{$This->{VerticesNeighborhoods}} = ();
110
111 # Vertices, along with their successors, at different neighborhood levels during vertex traversal...
112 @{$This->{VerticesNeighborhoodsWithSuccessors}} = ();
113
114 # Visited edges during Path TraversalMode...
115 %{$This->{VisitedEdges}} = ();
116 %{$This->{VisitedEdges}->{From}} = ();
117 %{$This->{VisitedEdges}->{To}} = ();
118
119 # Vertex path during Path TraversalMode...
120 %{$This->{VerticesPaths}} = ();
121
122 # Allow cycles in paths during VertexNeighborhood TraversalMode. By default, cycles are not allowed
123 # during vertex traversal: a vertex is only visited once during BFS search for neighborhoods. For
124 # neighborhood vertices search during successors identification, vertex cycles are explicity allowed
125 # to indentify all successors.
126 $This->{AllowVertexCycles} = 0;
127
128 # Allow cycles in paths during Path TraversalMode...
129 $This->{AllowPathCycles} = 1;
130
131 # Cycle closure vertices during Path TraversalMode...
132 %{$This->{CycleClosureVertices}} = ();
133
134 # Paths traversed during search...
135 @{$This->{Paths}} = ();
136
137 return $This;
138 }
139
140 # Initialize class ...
141 sub _InitializeClass {
142 #Class name...
143 $ClassName = __PACKAGE__;
144 }
145
146 # Perform a depth first search (DFS)...
147 #
148 sub PerformDepthFirstSearch {
149 my($This, $RootVertexID) = @_;
150
151 if (defined $RootVertexID) {
152 if (!$This->{Graph}->HasVertex($RootVertexID)) {
153 carp "Warning: ${ClassName}->PerformDepthFirstSearch: No search performed: Vertex $RootVertexID doesn't exist...";
154 return undef;
155 }
156 }
157 return $This->_PerformVertexSearch("DFS", $RootVertexID);
158 }
159
160 # Perform a depth first search (DFS) with limit on depth...
161 #
162 sub PerformDepthFirstSearchWithLimit {
163 my($This, $DepthLimit, $RootVertexID) = @_;
164
165 if (!defined $DepthLimit) {
166 carp "Warning: ${ClassName}->PerformDepthFirstSearchWithLimit: No search performed: Depth limit must be specified...";
167 return undef;
168 }
169 if ($DepthLimit < 0) {
170 carp "Warning: ${ClassName}->PerformDepthFirstSearchWithLimit: No search performed: Specified depth limit, $DepthLimit, must be a positive integer...";
171 return undef;
172 }
173 if (defined $RootVertexID) {
174 if (!$This->{Graph}->HasVertex($RootVertexID)) {
175 carp "Warning: ${ClassName}->PerformDepthFirstSearchWithLimit: No search performed: Vertex $RootVertexID doesn't exist...";
176 return undef;
177 }
178 }
179 return $This->_PerformVertexSearch("DFSWithLimit", $RootVertexID, $DepthLimit);
180 }
181
182 # Perform a breadth first search (BFS)...
183 #
184 sub PerformBreadthFirstSearch {
185 my($This, $RootVertexID) = @_;
186
187 if (defined $RootVertexID) {
188 if (!$This->{Graph}->HasVertex($RootVertexID)) {
189 carp "Warning: ${ClassName}->PerformBreadthFirstSearch: No search performed: Vertex $RootVertexID doesn't exist...";
190 return undef;
191 }
192 }
193 return $This->_PerformVertexSearch("BFS", $RootVertexID);
194 }
195
196 # Perform a breadth first search (BFS) with limit...
197 #
198 sub PerformBreadthFirstSearchWithLimit {
199 my($This, $DepthLimit, $RootVertexID) = @_;
200
201 if (!defined $DepthLimit) {
202 carp "Warning: ${ClassName}->PerformBreadthFirstSearchWithLimit: No search performed: Depth limit must be specified...";
203 return undef;
204 }
205 if ($DepthLimit < 0) {
206 carp "Warning: ${ClassName}->PerformBreadthFirstSearchWithLimit: No search performed: Specified depth limit, $DepthLimit, must be a positive integer...";
207 return undef;
208 }
209 if (defined $RootVertexID) {
210 if (!$This->{Graph}->HasVertex($RootVertexID)) {
211 carp "Warning: ${ClassName}->PerformDepthFirstSearchWithLimit: No search performed: Vertex $RootVertexID doesn't exist...";
212 return undef;
213 }
214 }
215 return $This->_PerformVertexSearch("BFSWithLimit", $RootVertexID, $DepthLimit);
216 }
217
218 # Perform appropriate vertex search...
219 #
220 sub _PerformVertexSearch {
221 my($This, $SearchType, $RootVertexID, $DepthLimit, $TargetVertexID) = @_;
222
223 # Setup search...
224 $This->{TraversalMode} = 'Vertex';
225 $This->{TraversalType} = $SearchType;
226
227 if (defined $RootVertexID) {
228 $This->{RootVertex} = $RootVertexID;
229 }
230 if (defined $DepthLimit) {
231 $This->{DepthLimit} = $DepthLimit;
232 }
233 if (defined $TargetVertexID) {
234 $This->{TargetVertex} = $TargetVertexID;
235 }
236
237 # Perform search...
238 return $This->_TraverseGraph();
239 }
240
241 # Perform DFS or BFS traversal with or without any limits...
242 #
243 sub _TraverseGraph {
244 my($This) = @_;
245 my($ProcessingVertices, $CurrentVertexID, $NeighborVertexID, $VertexID);
246
247 if ($This->{TraversalMode} !~ /^(Vertex|Path|VertexNeighborhood)$/i) {
248 return $This;
249 }
250
251 $ProcessingVertices = 1;
252
253 VERTICES: while ($ProcessingVertices) {
254 # Set root vertex...
255 if (!@{$This->{ActiveVertices}}) {
256 my($RootVertexID);
257
258 $RootVertexID = $This->_GetRootVertex();
259 if (!defined $RootVertexID) {
260 $ProcessingVertices = 0; next VERTICES;
261 }
262 $This->_ProcessVisitedVertex($RootVertexID, $RootVertexID);
263 }
264
265 # Get current active vertex...
266 $CurrentVertexID = $This->_GetActiveVertex();
267 if (!defined $CurrentVertexID) {
268 $ProcessingVertices = 0; next VERTICES;
269 }
270
271 # Get next available neighbor of current vertex...
272 #
273 $NeighborVertexID = $This->_GetNeighborVertex($CurrentVertexID);
274
275 # Process neighbor or current vertex...
276 if (defined $NeighborVertexID) {
277 $This->_ProcessVisitedVertex($NeighborVertexID, $CurrentVertexID);
278 }
279 else {
280 # Finished with all neighbors for current vertex...
281 $This->_ProcessFinishedVertex($CurrentVertexID);
282 }
283 }
284 return $This;
285 }
286
287 # Get root vertex to start the search...
288 #
289 # Notes:
290 # . User specification of root vertex forces traversal in a specific connected component
291 # of graph; To traverse find all connected components, perform traversal without specification of
292 # a root vertex.
293 #
294 sub _GetRootVertex {
295 my($This) = @_;
296 my($RootVertexID);
297
298 # Check for specified root vertex and constrain traversal to specific connected
299 # component by setting root limit...
300 if (exists $This->{RootVertex}) {
301 $RootVertexID = $This->{RootVertex};
302 delete $This->{RootVertex};
303 $This->{RootVertexSpecified} = 1;
304
305 return $RootVertexID;
306 }
307 # Traversal limited to connected component containing specified root vertex...
308 if (exists $This->{RootVertexSpecified}) {
309 return undef;
310 }
311
312 # Use first vertex in sorted available vertices list to get root vertex. Vertex
313 # with largest degree could also be used as root vertex. However, for all
314 # practical purposes, any arbitrary vertex can be used as root vertex to
315 # start search for another disconnected component of the graph.
316 #
317 my(@VerticesToVisit);
318
319 $RootVertexID = undef; @VerticesToVisit = ();
320 @VerticesToVisit = sort { $a <=> $b } keys %{$This->{VerticesToVisit}};
321 if (@VerticesToVisit) {
322 $RootVertexID = $VerticesToVisit[0];
323 }
324 return $RootVertexID;
325 }
326
327 # Get current or new active vertex for DFS/BFS traversals...
328 #
329 sub _GetActiveVertex {
330 my($This) = @_;
331 my($ActiveVertexID);
332
333 $ActiveVertexID = undef;
334 if ($This->{TraversalType} =~ /^(DFS|DFSWithLimit)$/i) {
335 # For DFS, it's last vertex in LIFO queue...
336 $ActiveVertexID = $This->{ActiveVertices}[-1];
337 }
338 elsif ($This->{TraversalType} =~ /^(BFS|BFSWithLimit)$/i) {
339 # For BFS, it's first vertex in FIFO queue...
340 $ActiveVertexID = $This->{ActiveVertices}[0];
341 }
342 return $ActiveVertexID;
343 }
344
345 # Get available neigbor of specified vertex...
346 #
347 sub _GetNeighborVertex {
348 my($This, $VertexID) = @_;
349
350 # Retrieve neighbors for vertex...
351 if (!exists $This->{VerticesNeighbors}{$VertexID}) {
352 @{$This->{VerticesNeighbors}{$VertexID}} = ();
353
354 if (exists $This->{DepthLimit}) {
355 # Only collect neighbors to visit below specified depth limit...
356 if ($This->{VerticesDepth}{$VertexID} < $This->{DepthLimit}) {
357 push @{$This->{VerticesNeighbors}{$VertexID}}, $This->{Graph}->GetNeighbors($VertexID);
358 }
359 else {
360 if (!exists $This->{RootVertexSpecified}) {
361 # Mark all other downstream neighbor vertices to be ignored from any further
362 # processing and avoid selection of a new root...
363 $This->_IgnoreDownstreamNeighbors($VertexID);
364 }
365 }
366 }
367 elsif (exists $This->{TargetVertex}) {
368 if ($VertexID != $This->{TargetVertex}) {
369 push @{$This->{VerticesNeighbors}{$VertexID}}, $This->{Graph}->GetNeighbors($VertexID);
370 }
371 }
372 else {
373 push @{$This->{VerticesNeighbors}{$VertexID}}, $This->{Graph}->GetNeighbors($VertexID);
374 }
375 }
376
377 if ($This->{TraversalMode} =~ /^Path$/i) {
378 # Get available neighbor for path search...
379 return $This->_GetNeighborVertexDuringPathTraversal($VertexID);
380 }
381 elsif ($This->{TraversalMode} =~ /^Vertex$/i) {
382 # Get unvisited neighbor for vertex search...
383 return $This->_GetNeighborVertexDuringVertexTraversal($VertexID);
384 }
385 elsif ($This->{TraversalMode} =~ /^VertexNeighborhood$/i) {
386 # Get available neighbor during vertex neighborhood search...
387 return $This->_GetNeighborVertexDuringVertexNeighborhoodTraversal($VertexID);
388 }
389 return undef;
390 }
391
392 # Get unvisited neighbor of specified vertex during vertex traversal...
393 #
394 sub _GetNeighborVertexDuringVertexTraversal {
395 my($This, $VertexID) = @_;
396 my($NeighborVertexID, $UnvisitedNeighborVertexID);
397
398 # Get unvisited neighbor...
399 $UnvisitedNeighborVertexID = undef;
400 NEIGHBOR: for $NeighborVertexID (@{$This->{VerticesNeighbors}{$VertexID}}) {
401 if (!exists $This->{VisitedVertices}{$NeighborVertexID}) {
402 $UnvisitedNeighborVertexID = $NeighborVertexID;
403 last NEIGHBOR;
404 }
405 }
406 return $UnvisitedNeighborVertexID;
407 }
408
409 # Get available neighbor of specified vertex during vertex neighborhood traversal...
410 #
411 sub _GetNeighborVertexDuringVertexNeighborhoodTraversal {
412 my($This, $VertexID) = @_;
413 my($NeighborVertexID, $UnvisitedNeighborVertexID);
414
415 # Get available neighbor...
416 $UnvisitedNeighborVertexID = undef;
417 NEIGHBOR: for $NeighborVertexID (@{$This->{VerticesNeighbors}{$VertexID}}) {
418 if (!exists $This->{VisitedVertices}{$NeighborVertexID}) {
419 $UnvisitedNeighborVertexID = $NeighborVertexID;
420 last NEIGHBOR;
421 }
422 # Look for any unvisited edge back to visited vertex...
423 if ($This->_IsVisitedEdge($VertexID, $NeighborVertexID) || $This->_IsVisitedEdge($NeighborVertexID, $VertexID)) {
424 next NEIGHBOR;
425 }
426 # Check its depth...
427 if (exists $This->{DepthLimit}) {
428 if (($This->{VerticesDepth}{$VertexID} + 1) > $This->{DepthLimit}) {
429 next NEIGHBOR;
430 }
431 }
432 # Its an edge that makes a cycle during BFS search...
433 if ($This->{AllowVertexCycles}) {
434 $This->{CycleClosureVertices}{$NeighborVertexID} = 1;
435 $UnvisitedNeighborVertexID = $NeighborVertexID;
436 last NEIGHBOR;
437 }
438 }
439 return $UnvisitedNeighborVertexID;
440 }
441
442 # Get available neighbor of specified vertex during path traversal...
443 #
444 sub _GetNeighborVertexDuringPathTraversal {
445 my($This, $VertexID) = @_;
446 my($NeighborVertexID, $UnvisitedNeighborVertexID);
447
448 # Get unvisited neighbor...
449 $UnvisitedNeighborVertexID = undef;
450 NEIGHBOR: for $NeighborVertexID (@{$This->{VerticesNeighbors}{$VertexID}}) {
451 if (!exists $This->{VisitedVertices}{$NeighborVertexID}) {
452 # An unvisited vertex...
453 $UnvisitedNeighborVertexID = $NeighborVertexID;
454 last NEIGHBOR;
455 }
456 # Look for any unvisited edge back to visited vertex...
457 if ($This->_IsVisitedEdge($VertexID, $NeighborVertexID) || $This->_IsVisitedEdge($NeighborVertexID, $VertexID)) {
458 next NEIGHBOR;
459 }
460 # Check its depth...
461 if (exists $This->{DepthLimit}) {
462 if (($This->{VerticesDepth}{$VertexID} + 1) >= $This->{DepthLimit}) {
463 next NEIGHBOR;
464 }
465 }
466
467 # It's the edge final edge of a cycle in case $NeighborVertexID is already in the path; otherwise, it's
468 # part of the path from a different direction in a cycle or a left over vertex during Limit search.
469 #
470 if ($This->_IsCycleClosureEdge($VertexID, $NeighborVertexID)) {
471 if ($This->{AllowPathCycles}) {
472 $This->{CycleClosureVertices}{$NeighborVertexID} = 1;
473 $UnvisitedNeighborVertexID = $NeighborVertexID;
474 last NEIGHBOR;
475 }
476 }
477 else {
478 $UnvisitedNeighborVertexID = $NeighborVertexID;
479 last NEIGHBOR;
480 }
481 }
482 return $UnvisitedNeighborVertexID;
483 }
484
485 # Process visited vertex...
486 #
487 sub _ProcessVisitedVertex {
488 my($This, $VertexID, $PredecessorVertexID) = @_;
489
490 if (!exists $This->{VisitedVertices}{$VertexID}) {
491 # Add it to active vertices list...
492 push @{$This->{ActiveVertices}}, $VertexID;
493
494 # Mark vertex as visited vertex and take it out from the list of vertices to visit...
495 $This->{VisitedVertices}{$VertexID} = 1;
496 delete $This->{VerticesToVisit}{$VertexID};
497 }
498
499 # Set up root vertex, predecessor vertex and distance from root...
500 if ($VertexID == $PredecessorVertexID) {
501 $This->{VerticesRoots}{$VertexID} = $VertexID;
502
503 $This->{VerticesPredecessors}{$VertexID} = $VertexID;
504 if (!exists $This->{VerticesSuccessors}{$VertexID}) {
505 @{$This->{VerticesSuccessors}{$VertexID}} = ();
506 }
507
508 $This->{VerticesDepth}{$VertexID} = 0;
509
510 if ($This->{TraversalMode} =~ /^Path$/i) {
511 $This->_ProcessVisitedPath($VertexID, $PredecessorVertexID);
512 }
513 }
514 else {
515 $This->{VerticesRoots}{$VertexID} = $This->{VerticesRoots}{$PredecessorVertexID};
516
517 $This->{VerticesPredecessors}{$VertexID} = $PredecessorVertexID;
518 if (!exists $This->{VerticesSuccessors}{$PredecessorVertexID}) {
519 @{$This->{VerticesSuccessors}{$PredecessorVertexID}} = ();
520 }
521 push @{$This->{VerticesSuccessors}{$PredecessorVertexID}}, $VertexID;
522
523 if (!exists $This->{VerticesDepth}{$VertexID}) {
524 $This->{VerticesDepth}{$VertexID} = $This->{VerticesDepth}{$PredecessorVertexID} + 1;
525 }
526
527 if ($This->{TraversalMode} =~ /^Path$/i) {
528 $This->_ProcessVisitedPath($VertexID, $PredecessorVertexID);
529 $This->_ProcessVisitedEdge($PredecessorVertexID, $VertexID);
530 }
531 elsif ($This->{TraversalMode} =~ /^VertexNeighborhood$/i) {
532 $This->_ProcessVisitedEdge($PredecessorVertexID, $VertexID);
533 }
534 }
535 return $This;
536 }
537
538 # Process visited path...
539 #
540 sub _ProcessVisitedPath {
541 my($This, $VertexID, $PredecessorVertexID) = @_;
542
543 # Initialize VerticesPath...
544 if (!exists $This->{VerticesPaths}{$VertexID}) {
545 @{$This->{VerticesPaths}{$VertexID}} = ();
546 }
547
548 if ($VertexID == $PredecessorVertexID) {
549 # Starting of a path from root...
550 push @{$This->{VerticesPaths}{$VertexID}}, $VertexID;
551 }
552 else {
553 # Setup path for a vertex using path information from predecessor vertex...
554 if (exists $This->{CycleClosureVertices}{$PredecessorVertexID}) {
555 # Start of a new path from predecessor vertex...
556 push @{$This->{VerticesPaths}{$VertexID}}, "${PredecessorVertexID}-${VertexID}";
557 }
558 else {
559 my($PredecessorVertexPath);
560 for $PredecessorVertexPath (@{$This->{VerticesPaths}{$PredecessorVertexID}}) {
561 push @{$This->{VerticesPaths}{$VertexID}}, "${PredecessorVertexPath}-${VertexID}";
562 }
563 }
564 }
565 return $This;
566 }
567
568 # Process visited edge...
569 #
570 sub _ProcessVisitedEdge {
571 my($This, $VertexID1, $VertexID2) = @_;
572
573 if (!exists $This->{VisitedEdges}->{From}->{$VertexID1}) {
574 %{$This->{VisitedEdges}->{From}->{$VertexID1}} = ();
575 }
576 $This->{VisitedEdges}->{From}->{$VertexID1}->{$VertexID2} = $VertexID2;
577
578 if (!exists $This->{VisitedEdges}->{To}->{$VertexID2}) {
579 %{$This->{VisitedEdges}->{To}->{$VertexID2}} = ();
580 }
581 $This->{VisitedEdges}->{To}->{$VertexID2}->{$VertexID1} = $VertexID1;
582
583 return $This;
584 }
585
586 # Finished processing active vertex...
587 #
588 sub _ProcessFinishedVertex {
589 my($This, $VertexID) = @_;
590
591 if (!exists $This->{FinishedVertices}{$VertexID}) {
592 $This->{FinishedVertices}{$VertexID} = $VertexID;
593 # Add vertex to list of vertices found by traversal...
594 push @{$This->{Vertices}}, $VertexID;
595 }
596
597 # Any active vertices left...
598 if (!@{$This->{ActiveVertices}}) {
599 return $This;
600 }
601
602 # Take it off active vertices list...
603 if ($This->{TraversalType} =~ /^(DFS|DFSWithLimit)$/i) {
604 # For DFS, it's last vertex in LIFO queue...
605 pop @{$This->{ActiveVertices}};
606 }
607 elsif ($This->{TraversalType} =~ /^(BFS|BFSWithLimit)$/i) {
608 # For BFS, it's first vertex in FIFO queue...
609 shift @{$This->{ActiveVertices}};
610 }
611 return $This;
612 }
613
614 # Mark all other downstream neighbor vertices to be ignored from any further
615 # processing...
616 #
617 sub _IgnoreDownstreamNeighbors {
618 my($This, $VertexID, $PredecessorVertexID) = @_;
619
620 if (exists $This->{VerticesToVisit}{$VertexID}) {
621 # Mark vertex as visited vertex and take it out from the list of vertices to visit...
622 $This->{VisitedVertices}{$VertexID} = 1;
623 delete $This->{VerticesToVisit}{$VertexID};
624
625 if (defined($PredecessorVertexID) && $This->{TraversalMode} =~ /^(Path|VertexNeighborhood)$/i) {
626 $This->_ProcessVisitedEdge($VertexID, $PredecessorVertexID);
627 }
628 }
629 my($NeighborVertexID, @NeighborsVertexIDs);
630
631 @NeighborsVertexIDs = ();
632 @NeighborsVertexIDs = $This->{Graph}->GetNeighbors($VertexID);
633 NEIGHBOR: for $NeighborVertexID (@NeighborsVertexIDs) {
634 if (!exists $This->{VerticesToVisit}{$NeighborVertexID}) {
635 # Avoid going back to predecessor vertex which has already been ignored...
636 next NEIGHBOR;
637 }
638 $This->_IgnoreDownstreamNeighbors($NeighborVertexID, $VertexID);
639 }
640 return $This;
641 }
642
643 # Is it a visited edge?
644 #
645 sub _IsVisitedEdge {
646 my($This, $VertexID1, $VertexID2) = @_;
647
648 if (exists $This->{VisitedEdges}->{From}->{$VertexID1}) {
649 if (exists $This->{VisitedEdges}->{From}->{$VertexID1}->{$VertexID2}) {
650 return 1;
651 }
652 }
653 elsif (exists $This->{VisitedEdges}->{To}->{$VertexID2}) {
654 if (exists $This->{VisitedEdges}->{To}->{$VertexID2}->{$VertexID1}) {
655 return 1;
656 }
657 }
658 return 0;
659 }
660
661 # Is it a cycle closure edge?
662 #
663 # Notes:
664 # . Presence of VertexID2 in DFS path traversed for VertexID1 make it a cycle
665 # closure edge...
666 #
667 sub _IsCycleClosureEdge {
668 my($This, $VertexID1, $VertexID2) = @_;
669
670 if (!exists $This->{VerticesPaths}{$VertexID1}) {
671 return 0;
672 }
673 my($Path);
674 for $Path (@{$This->{VerticesPaths}{$VertexID1}}) {
675 if (($Path =~ /-$VertexID2-/ || $Path =~ /^$VertexID2-/ || $Path =~ /-$VertexID2$/)) {
676 return 1;
677 }
678 }
679 return 0;
680 }
681
682 # Search paths starting from a specified vertex with no sharing of edges in paths traversed.
683 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
684 # completing the cycle.
685 #
686 sub PerformPathsSearch {
687 my($This, $StartVertexID, $AllowCycles) = @_;
688
689 # Make sure start vertex is defined...
690 if (!defined $StartVertexID) {
691 carp "Warning: ${ClassName}->PerformPathsSearch: No paths search performed: Start vertex must be specified...";
692 return undef;
693 }
694
695 # Make sure start vertex is valid...
696 if (!$This->{Graph}->HasVertex($StartVertexID)) {
697 carp "Warning: ${ClassName}->PerformPathsSearch: No paths search performed: Vertex $StartVertexID doesn't exist...";
698 return undef;
699 }
700
701 if (!defined $AllowCycles) {
702 $AllowCycles = 1;
703 }
704
705 # Perform paths search...
706 return $This->_PerformPathsSearch("AllLengths", $StartVertexID, $AllowCycles);
707 }
708
709 # Search paths starting from a specified vertex with length upto a specified length
710 # with no sharing of edges in paths traversed...
711 #
712 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
713 # completing the cycle.
714 #
715 sub PerformPathsSearchWithLengthUpto {
716 my($This, $StartVertexID, $Length, $AllowCycles) = @_;
717
718 return $This->_PerformPathsSearchWithLength("LengthUpto", $StartVertexID, $Length, $AllowCycles);
719 }
720
721 # Search paths starting from a specified vertex with specified length
722 # with no sharing of edges in paths traversed...
723 #
724 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
725 # completing the cycle.
726 #
727 sub PerformPathsSearchWithLength {
728 my($This, $StartVertexID, $Length, $AllowCycles) = @_;
729
730 return $This->_PerformPathsSearchWithLength("Length", $StartVertexID, $Length, $AllowCycles);
731 }
732
733
734 # Search paths starting from a specified vertex with length upto a specified length
735 # with no sharing of edges in paths traversed...
736 #
737 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
738 # completing the cycle.
739 #
740 sub _PerformPathsSearchWithLength {
741 my($This, $Mode, $StartVertexID, $Length, $AllowCycles) = @_;
742
743 # Make sure both start vertex and length are defined...
744 if (!defined $StartVertexID) {
745 carp "Warning: ${ClassName}->_PerformPathsSearchWithLength: No paths search performed: Start vertex must be specified...";
746 return undef;
747 }
748 if (!defined $Length) {
749 carp "Warning: ${ClassName}->_PerformPathsSearchWithLength: No paths search performed: Length must be specified...";
750 return undef;
751 }
752
753 if (!defined $AllowCycles) {
754 $AllowCycles = 1;
755 }
756
757 # Make sure both start vertex and length are valid...
758 if (!$This->{Graph}->HasVertex($StartVertexID)) {
759 carp "Warning: ${ClassName}->_PerformPathsSearchWithLength: No paths search performed: Vertex $StartVertexID doesn't exist...";
760 return undef;
761 }
762
763 if ($Length < 1) {
764 carp "Warning: ${ClassName}->_PerformPathsSearchWithLength: No paths search performed: Specified length, $Length, must be a positive integer with value greater than 1...";
765 return undef;
766 }
767
768 # Perform paths search...
769 return $This->_PerformPathsSearch($Mode, $StartVertexID, $AllowCycles, $Length);
770 }
771
772 # Search all paths starting from a specified vertex with sharing of edges in paths traversed...
773 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
774 # completing the cycle.
775 #
776 sub PerformAllPathsSearch {
777 my($This, $StartVertexID, $AllowCycles) = @_;
778
779 # Make sure start vertex is defined...
780 if (!defined $StartVertexID) {
781 carp "Warning: ${ClassName}->PerformAllPathsSearch: No paths search performed: Start vertex must be specified...";
782 return undef;
783 }
784
785 # Make sure start vertex is valid...
786 if (!$This->{Graph}->HasVertex($StartVertexID)) {
787 carp "Warning: ${ClassName}->PerformAllPathsSearch: No paths search performed: Vertex $StartVertexID doesn't exist...";
788 return undef;
789 }
790
791 if (!defined $AllowCycles) {
792 $AllowCycles = 1;
793 }
794
795 # Perform paths search...
796 return $This->_PerformAllPathsSearch("AllLengths", $StartVertexID, $AllowCycles);
797 }
798
799 # Search all paths starting from a specified vertex with length upto a specified length with sharing of
800 # edges in paths traversed.
801 #
802 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
803 # completing the cycle.
804 #
805 sub PerformAllPathsSearchWithLengthUpto {
806 my($This, $StartVertexID, $Length, $AllowCycles) = @_;
807
808 return $This->_PerformAllPathsSearchWithLength("LengthUpto", $StartVertexID, $Length, $AllowCycles);
809 }
810
811 # Search all paths starting from a specified vertex with specified length with sharing of
812 # edges in paths traversed.
813 #
814 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
815 # completing the cycle.
816 #
817 sub PerformAllPathsSearchWithLength {
818 my($This, $StartVertexID, $Length, $AllowCycles) = @_;
819
820 return $This->_PerformAllPathsSearchWithLength("Length", $StartVertexID, $Length, $AllowCycles);
821 }
822
823 # Search all paths starting from a specified vertex with length upto a specified length with sharing of
824 # edges in paths traversed.
825 #
826 # By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
827 # completing the cycle.
828 #
829 sub _PerformAllPathsSearchWithLength {
830 my($This, $Mode, $StartVertexID, $Length, $AllowCycles) = @_;
831
832 # Make sure both start vertex and length are defined...
833 if (!defined $StartVertexID) {
834 carp "Warning: ${ClassName}->_PerformAllPathsSearchWithLength: No paths search performed: Start vertex must be specified...";
835 return undef;
836 }
837 if (!defined $Length) {
838 carp "Warning: ${ClassName}->_PerformAllPathsSearchWithLength: No paths search performed: Length must be specified...";
839 return undef;
840 }
841
842 if (!defined $AllowCycles) {
843 $AllowCycles = 1;
844 }
845
846 # Make sure both start vertex and length are valid...
847 if (!$This->{Graph}->HasVertex($StartVertexID)) {
848 carp "Warning: ${ClassName}->_PerformAllPathsSearchWithLength: No paths search performed: Vertex $StartVertexID doesn't exist...";
849 return undef;
850 }
851
852 if ($Length < 1) {
853 carp "Warning: ${ClassName}->_PerformAllPathsSearchWithLength: No paths search performed: Specified length, $Length, must be a positive integer with value greater than 1...";
854 return undef;
855 }
856
857 # Perform paths search...
858 return $This->_PerformAllPathsSearch($Mode, $StartVertexID, $AllowCycles, $Length);
859 }
860
861 # Search paths between two vertices...
862 #
863 sub PerformPathsSearchBetween {
864 my($This, $StartVertexID, $EndVertexID) = @_;
865
866 # Make sure start and end vertices are defined...
867 if (!defined $StartVertexID) {
868 carp "Warning: ${ClassName}->PerformPathsSearchBetweeb: No paths search performed: Start vertex must be specified...";
869 return undef;
870 }
871 if (!defined $EndVertexID) {
872 carp "Warning: ${ClassName}->PerformPathsSearchBetweeb: No paths search performed: EndVertex vertex must be specified...";
873 return undef;
874 }
875 # Make sure start and end vertices are valid...
876 if (!$This->{Graph}->HasVertex($StartVertexID)) {
877 carp "Warning: ${ClassName}->PerformPathsSearchBetween: No paths search performed: Vertex $StartVertexID doesn't exist...";
878 return undef;
879 }
880 if (!$This->{Graph}->HasVertex($EndVertexID)) {
881 carp "Warning: ${ClassName}->PerformPathsSearchBetween: No paths search performed: Vertex $EndVertexID doesn't exist...";
882 return undef;
883 }
884
885 # Perform paths search...
886 return $This->_PerformPathsSearchBetween($StartVertexID, $EndVertexID);
887 }
888
889 # Search paths starting from root vertex with no sharing of edges...
890 #
891 # Notes:
892 # . Possible paths searche modes are: DFSPathsWithLimit, DFSPaths. And each
893 # of these modes supports any combination of two options: CommonEdges, Cycles.
894 # Default for CommonEdges - No; Cycles - No.
895 #
896 sub _PerformPathsSearch {
897 my($This, $Mode, $RootVertexID, $AllowCycles, $Length) = @_;
898
899 # Perform DFS path search...
900
901 $This->{TraversalMode} = 'Path';
902
903 if ($Mode =~ /^(LengthUpto|Length)$/i) {
904 my($DepthLimit);
905
906 $DepthLimit = $Length - 1;
907 $This->{TraversalType} = 'DFSWithLimit';
908 $This->{DepthLimit} = $DepthLimit;
909 }
910 else {
911 $This->{TraversalType} = 'DFS';
912 }
913 if (defined $RootVertexID) {
914 $This->{RootVertex} = $RootVertexID;
915 }
916
917 $This->{AllowPathCycles} = $AllowCycles;
918
919 # Perform search...
920 $This->_TraverseGraph();
921
922 # Make sure traversal did get the root vertex...
923 if (!exists $This->{VerticesDepth}{$RootVertexID}) {
924 return $This;
925 }
926 if ($Mode =~ /^Length$/i) {
927 push @{$This->{Paths}}, $This->_CollectPathsTraversedDuringPathsSearchWithLength($Length);
928 }
929 else {
930 push @{$This->{Paths}}, $This->_CollectPathsTraversedDuringPathsSearch();
931 }
932
933 return $This;
934 }
935
936 # Search all paths starting from root vertex with sharing of edges...
937 #
938 sub _PerformAllPathsSearch {
939 my($This, $Mode, $RootVertexID, $AllowCycles, $Length) = @_;
940
941 # Perform DFS path search...
942
943 $This->{TraversalMode} = 'AllPaths';
944
945 if ($Mode =~ /^(LengthUpto|Length)$/i) {
946 my($DepthLimit);
947
948 $DepthLimit = $Length - 1;
949 $This->{TraversalType} = 'DFSWithLimit';
950 $This->{DepthLimit} = $DepthLimit;
951 }
952 else {
953 $This->{TraversalType} = 'DFS';
954 }
955 $This->{RootVertex} = $RootVertexID;
956 $This->{AllowPathCycles} = $AllowCycles;
957
958 # Traverse all paths search using DFS search...
959 $This->_TraverseAllPathsInGraph($Mode, $Length);
960
961 return $This;
962 }
963
964 # Travese all paths in graph starting from a specified root vertex...
965 #
966 sub _TraverseAllPathsInGraph {
967 my($This, $Mode, $Length) = @_;
968
969 if ($This->{TraversalMode} !~ /^AllPaths$/i) {
970 return $This;
971 }
972 my($CurrentVertexID, $PredecessorVertexID, $CurrentDepth, $CurrentPath);
973
974 $CurrentVertexID = $This->{RootVertex};
975 $PredecessorVertexID = $CurrentVertexID;
976 $CurrentDepth = 0;
977 $CurrentPath = "$CurrentVertexID";
978
979 $This->_TraverseAllPaths($CurrentVertexID, $PredecessorVertexID, $CurrentDepth, $CurrentPath);
980
981 if ($Mode =~ /^Length$/i) {
982 push @{$This->{Paths}}, $This->_CollectPathsTraversedDuringPathsSearchWithLength($Length);
983 }
984 else {
985 push @{$This->{Paths}}, $This->_CollectPathsTraversedDuringPathsSearch();
986 }
987
988 return $This;
989 }
990
991 # Traverse and collect all paths recuresively..
992 #
993 sub _TraverseAllPaths {
994 my($This, $CurrentVertexID, $PredecessorVertexID, $CurrentDepth, $CurrentPath) = @_;
995
996 # Save path traversed for current vertex..
997 if (!exists $This->{VerticesPaths}{$CurrentVertexID}) {
998 @{$This->{VerticesPaths}{$CurrentVertexID}} = ();
999 $This->{VerticesDepth}{$CurrentVertexID} = 0;
1000 }
1001 push @{$This->{VerticesPaths}{$CurrentVertexID}}, $CurrentPath;
1002 $This->{VerticesDepth}{$CurrentVertexID} = $CurrentDepth;
1003
1004 $CurrentDepth++;
1005 if (exists $This->{DepthLimit}) {
1006 if ($CurrentDepth > $This->{DepthLimit}) {
1007 # Nothing more to do...
1008 return $This;
1009 }
1010 }
1011 my($NeighborVertexID, $NewPath);
1012
1013 NEIGHBOR: for $NeighborVertexID ($This->{Graph}->GetNeighbors($CurrentVertexID)) {
1014 if ($NeighborVertexID == $PredecessorVertexID) {
1015 next NEIGHBOR;
1016 }
1017 if ($This->_IsVertexInTraversedPath($NeighborVertexID, $CurrentPath)) {
1018 # It's a cycle...
1019 if ($This->{AllowPathCycles}) {
1020 $NewPath = "${CurrentPath}-${NeighborVertexID}";
1021 if (!exists $This->{VerticesPaths}{$NeighborVertexID}) {
1022 @{$This->{VerticesPaths}{$NeighborVertexID}} = ();
1023 }
1024 push @{$This->{VerticesPaths}{$NeighborVertexID}}, $NewPath;
1025 }
1026 next NEIGHBOR;
1027 }
1028 $NewPath = "${CurrentPath}-${NeighborVertexID}";
1029 $This->_TraverseAllPaths($NeighborVertexID, $CurrentVertexID, $CurrentDepth, $NewPath);
1030 }
1031 return $This;
1032 }
1033
1034 # Is vertex already in traversed path?
1035 #
1036 sub _IsVertexInTraversedPath {
1037 my($This, $VertexID, $Path) = @_;
1038
1039 return ($Path =~ /-$VertexID-/ || $Path =~ /^$VertexID-/ || $Path =~ /-$VertexID$/) ? 1 : 0;
1040 }
1041
1042 # Collect all paths traversed during Path TraversalMode and sort 'em in
1043 # ascending order of lengths
1044 #
1045 sub _CollectPathsTraversedDuringPathsSearch {
1046 my($This) = @_;
1047 my($VertexID, @Paths, @SortedPaths);
1048
1049 @Paths = (); @SortedPaths = ();
1050
1051 # Create path objects from path vertex strings...
1052 for $VertexID (keys %{$This->{VerticesPaths}}) {
1053 push @Paths, map { new Graph::Path(split /-/, $_) } @{$This->{VerticesPaths}{$VertexID}};
1054 }
1055
1056 # Sort paths in ascending order of lengths...
1057 push @SortedPaths, sort { $a->GetLength() <=> $b->GetLength() } @Paths;
1058
1059 return @SortedPaths;
1060 }
1061
1062 # Collect paths traversed during Path TraversalMode with specific length...
1063 #
1064 sub _CollectPathsTraversedDuringPathsSearchWithLength {
1065 my($This, $Length) = @_;
1066 my($VertexID, $Depth, $PathString, @VertexIDs, @Paths);
1067
1068 @Paths = ();
1069 $Depth = $Length - 1;
1070
1071 # Create path objects from path vertex strings...
1072 VERTEXID: for $VertexID (keys %{$This->{VerticesPaths}}) {
1073 if ($This->{VerticesDepth}{$VertexID} != $Depth) {
1074 next VERTEXID;
1075 }
1076 # For vertices involved in cycles, the path might also contain some shorter paths. So check
1077 # the lengths before its collection...
1078 PATHSTRING: for $PathString (@{$This->{VerticesPaths}{$VertexID}}) {
1079 @VertexIDs = split /-/, $PathString;
1080 if ($Length != @VertexIDs) {
1081 next PATHSTRING;
1082 }
1083 push @Paths, new Graph::Path(@VertexIDs);
1084 }
1085 }
1086 return @Paths;
1087 }
1088
1089 # Collect paths traversed during Vertex TraversalMode...
1090 #
1091 sub _CollectPathsTraversedDuringVertexSearch {
1092 my($This, $RootVertexID) = @_;
1093 my($Depth, @Paths, @VerticesAtDepth);
1094 @Paths = ();
1095
1096 # Get vertices at specific depths...
1097 @VerticesAtDepth = ();
1098 @VerticesAtDepth = $This->_CollectVerticesAtSpecificDepths();
1099 if (!@VerticesAtDepth) {
1100 return @Paths;
1101 }
1102
1103 # Make sure search found only one root vertex and it corresponds to
1104 # what was specified...
1105 $Depth = 0;
1106 if ((@{$VerticesAtDepth[$Depth]} > 1) || ($VerticesAtDepth[$Depth][0] != $RootVertexID)) {
1107 carp "Warning: ${ClassName}->_PerformPathsSearch: No paths found: Root vertex, $VerticesAtDepth[$Depth][0], identified by paths traversal doen't match specified root vertex $RootVertexID...";
1108 return @Paths;
1109 }
1110
1111 # Setup root vertex at depth 0. And set its path...
1112 my($Path, $VertexID, $SuccessorVertexID, @VertexIDs, %PathAtVertex);
1113 %PathAtVertex = ();
1114 $PathAtVertex{$RootVertexID} = new Graph::Path($RootVertexID);
1115
1116 for $Depth (0 .. $#VerticesAtDepth) {
1117 # Go over all vertices at current depth...
1118 VERTEX: for $VertexID (@{$VerticesAtDepth[$Depth]}) {
1119 if (!exists $This->{VerticesSuccessors}{$VertexID}) {
1120 next VERTEX;
1121 }
1122 # Get vertices for current path...
1123 @VertexIDs = ();
1124 push @VertexIDs, $PathAtVertex{$VertexID}->GetVertices;
1125
1126 # Expand path to successor vertex found during traversal...
1127 for $SuccessorVertexID (@{$This->{VerticesSuccessors}{$VertexID}}) {
1128 $Path = new Graph::Path(@VertexIDs);
1129 $Path->AddVertex($SuccessorVertexID);
1130 $PathAtVertex{$SuccessorVertexID} = $Path;
1131 }
1132 }
1133 }
1134 # Sort paths in ascending order of lengths...
1135 push @Paths, sort { $a->GetLength() <=> $b->GetLength() } values %PathAtVertex;
1136
1137 return @Paths;
1138 }
1139
1140 # Collect vertices at specific depths. Depth values start from 0...
1141 #
1142 sub _CollectVerticesAtSpecificDepths {
1143 my($This) = @_;
1144 my($VertexID, $Depth, @VerticesAtDepth);
1145
1146 @VerticesAtDepth = ();
1147 while (($VertexID, $Depth) = each %{$This->{VerticesDepth}}) {
1148 push @{$VerticesAtDepth[$Depth]}, $VertexID;
1149 }
1150 return @VerticesAtDepth;
1151 }
1152
1153 # Collect vertices, along with their successors, at specific depths and return a list containing references to
1154 # lists with first value corresponding to vertex ID and second value a reference to a list containing
1155 # its successors.
1156 #
1157 # Depth values start from 0...
1158 #
1159 sub _CollectVerticesWithSuccessorsAtSpecificDepths {
1160 my($This) = @_;
1161 my($VertexID, $Depth, @VerticesWithSuccessorsAtDepth);
1162
1163 @VerticesWithSuccessorsAtDepth = ();
1164 while (($VertexID, $Depth) = each %{$This->{VerticesDepth}}) {
1165 my(@VertexWithSuccessors, @VertexSuccessors);
1166
1167 @VertexWithSuccessors = (); @VertexSuccessors = ();
1168 if (exists $This->{VerticesSuccessors}{$VertexID}) {
1169 push @VertexSuccessors, @{$This->{VerticesSuccessors}{$VertexID}};
1170 }
1171 push @VertexWithSuccessors, ($VertexID, \@VertexSuccessors);
1172 # Multiple entries for a vertex and its successors could be present at a specific depth...
1173 push @{$VerticesWithSuccessorsAtDepth[$Depth]}, \@VertexWithSuccessors;
1174 }
1175 return @VerticesWithSuccessorsAtDepth;
1176 }
1177
1178 # Search paths between two vertices...
1179 #
1180 sub _PerformPathsSearchBetween {
1181 my($This, $RootVertexID, $TargetVertexID) = @_;
1182 my($DepthLimit);
1183
1184 # Perform a targeted DFS search...
1185 $DepthLimit = undef;
1186 $This->_PerformVertexSearch("DFS", $RootVertexID, $DepthLimit, $TargetVertexID);
1187
1188 my($Path);
1189 $Path = $This->_CollectPathBetween($RootVertexID, $TargetVertexID);
1190
1191 if (defined $Path) {
1192 push @{$This->{Paths}}, $Path;
1193 }
1194 return $This;
1195 }
1196
1197 # Collect path between root and target vertex after the search...
1198 #
1199 sub _CollectPathBetween {
1200 my($This, $RootVertexID, $TargetVertexID) = @_;
1201
1202 # Does a path from root to target vertex exist?
1203 if (!(exists($This->{VerticesRoots}{$TargetVertexID}) && ($This->{VerticesRoots}{$TargetVertexID} == $RootVertexID))) {
1204 return undef;
1205 }
1206
1207 # Add target vertex ID path vertices...
1208 my($VertexID, $Path, @VertexIDs);
1209 @VertexIDs = ();
1210 $VertexID = $TargetVertexID;
1211 push @VertexIDs, $VertexID;
1212
1213 # Backtrack to root vertex ID...
1214 while ($This->{VerticesPredecessors}{$VertexID} != $VertexID) {
1215 $VertexID = $This->{VerticesPredecessors}{$VertexID};
1216 push @VertexIDs, $VertexID;
1217 }
1218
1219 # Create path from target to root and reverse it...
1220 $Path = new Graph::Path(@VertexIDs);
1221 $Path->Reverse();
1222
1223 return $Path;
1224 }
1225
1226 # Search vertices around specified root vertex with in specific neighborhood radius...
1227 #
1228 sub PerformNeighborhoodVerticesSearchWithRadiusUpto {
1229 my($This, $StartVertexID, $Radius) = @_;
1230
1231 # Make sure both start vertex and radius are defined...
1232 if (!defined $StartVertexID) {
1233 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithRadiusUpto: No vertices search performed: Start vertex must be specified...";
1234 return undef;
1235 }
1236 if (!defined $Radius) {
1237 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithRadiusUpto: No vertices search performed: Radius must be specified...";
1238 return undef;
1239 }
1240
1241 # Make sure both start vertex and length are valid...
1242 if (!$This->{Graph}->HasVertex($StartVertexID)) {
1243 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithRadiusUpto: No vertices search performed: Vertex $StartVertexID doesn't exist...";
1244 return undef;
1245 }
1246 if ($Radius < 0) {
1247 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithRadiusUpto: No vertices search performed: Specified radius, $Radius, must be a positive integer...";
1248 return undef;
1249 }
1250
1251 # Perform vertices search...
1252 return $This->_PerformNeighborhoodVerticesSearch("RadiusUpto", $StartVertexID, $Radius);
1253 }
1254
1255 # Search vertices around specified root vertex...
1256 #
1257 sub PerformNeighborhoodVerticesSearch {
1258 my($This, $StartVertexID) = @_;
1259
1260 # Make sure start vertex is defined...
1261 if (!defined $StartVertexID) {
1262 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearch: No vertices search performed: Start vertex must be specified...";
1263 return undef;
1264 }
1265
1266 # Make sure start vertex is valid...
1267 if (!$This->{Graph}->HasVertex($StartVertexID)) {
1268 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearch: No vertices search performed: Vertex $StartVertexID doesn't exist...";
1269 return undef;
1270 }
1271 # Perform vertices search...
1272 return $This->_PerformNeighborhoodVerticesSearch("AllRadii", $StartVertexID);
1273 }
1274
1275 # Search vertices around specified root vertex with in specific neighborhood radius along with
1276 # identification of successors of each vertex found during the search...
1277 #
1278 sub PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto {
1279 my($This, $StartVertexID, $Radius) = @_;
1280
1281 # Make sure both start vertex and radius are defined...
1282 if (!defined $StartVertexID) {
1283 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto: No vertices search performed: Start vertex must be specified...";
1284 return undef;
1285 }
1286 if (!defined $Radius) {
1287 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto: No vertices search performed: Radius must be specified...";
1288 return undef;
1289 }
1290
1291 # Make sure both start vertex and length are valid...
1292 if (!$This->{Graph}->HasVertex($StartVertexID)) {
1293 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto: No vertices search performed: Vertex $StartVertexID doesn't exist...";
1294 return undef;
1295 }
1296 if ($Radius < 0) {
1297 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto: No vertices search performed: Specified radius, $Radius, must be a positive integer...";
1298 return undef;
1299 }
1300
1301 # Perform vertices search...
1302 return $This->_PerformNeighborhoodVerticesSearch("WithSuccessorsAndRadiusUpto", $StartVertexID, $Radius);
1303 }
1304
1305 # Search vertices around specified root vertex along with identification of
1306 # successors of each vertex found during the search...
1307 #
1308 sub PerformNeighborhoodVerticesSearchWithSuccessors {
1309 my($This, $StartVertexID) = @_;
1310
1311 # Make sure start vertex is defined...
1312 if (!defined $StartVertexID) {
1313 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithSuccessors: No vertices search performed: Start vertex must be specified...";
1314 return undef;
1315 }
1316
1317 # Make sure start vertex is valid...
1318 if (!$This->{Graph}->HasVertex($StartVertexID)) {
1319 carp "Warning: ${ClassName}->PerformNeighborhoodVerticesSearchWithSuccessors: No vertices search performed: Vertex $StartVertexID doesn't exist...";
1320 return undef;
1321 }
1322 # Perform vertices search...
1323 return $This->_PerformNeighborhoodVerticesSearch("WithSuccessorsAndAllRadii", $StartVertexID);
1324 }
1325
1326 # Search vertices at successive neighborhood radii levels...
1327 #
1328 sub _PerformNeighborhoodVerticesSearch {
1329 my($This, $Mode, $RootVertexID, $Radius) = @_;
1330 my($DepthLimit, $AllowCycles);
1331
1332 $DepthLimit = defined $Radius ? $Radius : undef;
1333 $AllowCycles = undef;
1334
1335 # Perform BFS search...
1336 if ($Mode =~ /^RadiusUpto$/i) {
1337 $This->_PerformVertexNeighborhoodSearch("BFSWithLimit", $RootVertexID, $DepthLimit);
1338 }
1339 elsif ($Mode =~ /^(AllRadii)$/i) {
1340 $This->_PerformVertexNeighborhoodSearch("BFS", $RootVertexID);
1341 }
1342 elsif ($Mode =~ /^WithSuccessorsAndRadiusUpto$/i) {
1343 $AllowCycles = 1;
1344 $This->_PerformVertexNeighborhoodSearch("BFSWithLimit", $RootVertexID, $DepthLimit, $AllowCycles);
1345 }
1346 elsif ($Mode =~ /^WithSuccessorsAndAllRadii$/i) {
1347 $AllowCycles = 1;
1348 $This->_PerformVertexNeighborhoodSearch("BFSWithLimit", $RootVertexID, $DepthLimit, $AllowCycles);
1349 }
1350
1351 # Make sure traversal did get the root vertex...
1352 if (!exists $This->{VerticesDepth}{$RootVertexID}) {
1353 return $This;
1354 }
1355
1356 if ($Mode =~ /^(RadiusUpto|AllRadii)$/i) {
1357 push @{$This->{VerticesNeighborhoods}}, $This->_CollectVerticesAtSpecificDepths();
1358 }
1359 elsif ($Mode =~ /^(WithSuccessorsAndRadiusUpto|WithSuccessorsAndAllRadii)$/i) {
1360 push @{$This->{VerticesNeighborhoodsWithSuccessors}}, $This->_CollectVerticesWithSuccessorsAtSpecificDepths();
1361 }
1362
1363 return $This;
1364 }
1365
1366 # Perform appropriate vertex search...
1367 #
1368 sub _PerformVertexNeighborhoodSearch {
1369 my($This, $SearchType, $RootVertexID, $DepthLimit, $AllowCycles) = @_;
1370
1371 # Setup search...
1372 $This->{TraversalMode} = 'VertexNeighborhood';
1373 $This->{TraversalType} = $SearchType;
1374
1375 if (defined $RootVertexID) {
1376 $This->{RootVertex} = $RootVertexID;
1377 }
1378 if (defined $DepthLimit) {
1379 $This->{DepthLimit} = $DepthLimit;
1380 }
1381 if (defined $AllowCycles) {
1382 $This->{AllowVertexCycles} = $AllowCycles;
1383 }
1384
1385 # Perform search...
1386 return $This->_TraverseGraph();
1387 }
1388
1389 # Get orderded list of vertices after DFS/BFS search...
1390 #
1391 sub GetVertices {
1392 my($This) = @_;
1393
1394 return wantarray ? @{$This->{Vertices}} : scalar @{$This->{Vertices}};
1395 }
1396
1397 # Get a hash list containing vertex and root vertex as key/value pair for all vertices
1398 # ordered using DFS/BFS search available via GetVertices method...
1399 #
1400 sub GetVerticesRoots {
1401 my($This) = @_;
1402
1403 return %{$This->{VerticesRoots}};
1404 }
1405
1406 # Get a list containing lists of vertices in connected components of graph after DFS/BFS
1407 # search...
1408 #
1409 # Note:
1410 # . List is sorted in descending order of number of vertices in each connected component.
1411 #
1412 sub GetConnectedComponentsVertices {
1413 my($This) = @_;
1414 my($VertexID, $VertexRoot, @ConnectedVertices, %VerticesAtRoot);
1415
1416 @ConnectedVertices = ();
1417 %VerticesAtRoot = ();
1418 for $VertexID (@{$This->{Vertices}}) {
1419 $VertexRoot = $This->{VerticesRoots}{$VertexID};
1420 if (!exists $VerticesAtRoot{$VertexRoot}) {
1421 @{$VerticesAtRoot{$VertexRoot}} = ();
1422 }
1423 push @{$VerticesAtRoot{$VertexRoot}}, $VertexID;
1424 }
1425 push @ConnectedVertices, sort { @{$b} <=> @{$a} } values %VerticesAtRoot;
1426
1427 return wantarray ? @ConnectedVertices : scalar @ConnectedVertices;
1428 }
1429
1430 # Get predecessor vertices...
1431 #
1432 sub GetVerticesPredecessors {
1433 my($This) = @_;
1434
1435 return %{$This->{VerticesPredecessors}};
1436 }
1437
1438 # Get a hash list containing vertex and depth from root vertex as key/value pair for all vertices
1439 # ordered using DFS/BFS search available via GetVertices method...
1440 #
1441 sub GetVerticesDepth {
1442 my($This) = @_;
1443
1444 return %{$This->{VerticesDepth}};
1445 }
1446
1447 # Get paths found during paths search...
1448 #
1449 sub GetPaths {
1450 my($This) = @_;
1451
1452 return wantarray ? @{$This->{Paths}} : scalar @{$This->{Paths}};
1453 }
1454
1455 # Get vertices collected at various neighborhood radii...
1456 #
1457 sub GetVerticesNeighborhoods {
1458 my($This) = @_;
1459
1460 return wantarray ? @{$This->{VerticesNeighborhoods}} : scalar @{$This->{VerticesNeighborhoods}};
1461 }
1462
1463 # Get vertices, along with their successor vertices, collected at various neighborhood radii as
1464 # a list containing references to lists with first value corresponding to vertex ID and second value
1465 # a reference to a list containing its successors.
1466 #
1467 sub GetVerticesNeighborhoodsWithSuccessors {
1468 my($This) = @_;
1469
1470 return wantarray ? @{$This->{VerticesNeighborhoodsWithSuccessors}} : scalar @{$This->{VerticesNeighborhoodsWithSuccessors}};
1471 }
1472
1473 # Return a string containg data for PathsTraversal object...
1474 sub StringifyPathsTraversal {
1475 my($This) = @_;
1476 my($PathsTraversalString);
1477
1478 $PathsTraversalString = "PathsTraversalMode: " . $This->{TraversalMode};
1479 $PathsTraversalString .= "; PathsTraversalType: " . $This->{TraversalType};
1480
1481 # Vertices ordered by traversal...
1482 $PathsTraversalString .= "; Vertices: " . join(' ', @{$This->{Vertices}});
1483
1484 # Stringify depths of vertices...
1485 $PathsTraversalString .= "; " . $This->StringifyVerticesDepth();
1486
1487 # Stringify roots of vertices...
1488 $PathsTraversalString .= "; " . $This->StringifyVerticesRoots();
1489
1490 # Stringify predecessor of vertices...
1491 $PathsTraversalString .= "; " . $This->StringifyVerticesPredecessors();
1492
1493 # Stringify successor vertices...
1494 $PathsTraversalString .= "; " . $This->StringifyVerticesSuccessors();
1495
1496 # Stringify paths...
1497 $PathsTraversalString .= "; " . $This->StringifyPaths();
1498
1499 # Stringify vertices neighborhoods...
1500 $PathsTraversalString .= "; " . $This->StringifyVerticesNeighborhoods();
1501
1502 # Stringify vertices neighborhoods with successors...
1503 $PathsTraversalString .= "; " . $This->StringifyVerticesNeighborhoodsWithSuccessors();
1504
1505 return $PathsTraversalString;
1506 }
1507
1508 # Stringify vertices depth...
1509 #
1510 sub StringifyVerticesDepth {
1511 my($This) = @_;
1512 my($VertexID, $VertexDepth, $DepthString);
1513
1514 if (!@{$This->{Vertices}}) {
1515 $DepthString = "<Vertex-Depth>: None";
1516 return $DepthString;
1517 }
1518
1519 $DepthString = "<Vertex-Depth>: ";
1520 for $VertexID (@{$This->{Vertices}}) {
1521 $VertexDepth = $This->{VerticesDepth}{$VertexID};
1522 $DepthString .= " <$VertexID-$VertexDepth>";
1523 }
1524 return $DepthString;
1525 }
1526
1527 # Stringify roots of vertices...
1528 #
1529 sub StringifyVerticesRoots {
1530 my($This) = @_;
1531 my($VertexID, $RootVertexID, $RootsString);
1532
1533 if (!@{$This->{Vertices}}) {
1534 $RootsString = "<Vertex-RootVertex>: None";
1535 return $RootsString;
1536 }
1537
1538 $RootsString = "<Vertex-RootVertex>: ";
1539 for $VertexID (@{$This->{Vertices}}) {
1540 $RootVertexID = $This->{VerticesRoots}{$VertexID};
1541 $RootsString .= " <$VertexID-$RootVertexID>";
1542 }
1543 return $RootsString;
1544 }
1545
1546 # Stringify predecessor of vertices...
1547 #
1548 sub StringifyVerticesPredecessors {
1549 my($This) = @_;
1550 my($VertexID, $PredecessorVertexID, $PredecessorString);
1551
1552 if (!@{$This->{Vertices}}) {
1553 $PredecessorString = "<Vertex-PredecessorVertex>: None";
1554 return $PredecessorString;
1555 }
1556
1557 $PredecessorString = "<Vertex-PredecessorVertex>: ";
1558 for $VertexID (@{$This->{Vertices}}) {
1559 $PredecessorVertexID = $This->{VerticesPredecessors}{$VertexID};
1560 $PredecessorString .= " <$VertexID-$PredecessorVertexID>";
1561 }
1562 return $PredecessorString;
1563 }
1564
1565 # Stringify successor vertices...
1566 #
1567 sub StringifyVerticesSuccessors {
1568 my($This) = @_;
1569 my($VertexID, $SuccessorString, $VerticesSuccessorsString);
1570
1571 if (!@{$This->{Vertices}}) {
1572 $SuccessorString = "<Vertex-VerticesSuccessorsList>: None";
1573 return $SuccessorString;
1574 }
1575
1576 $SuccessorString = "<Vertex-VerticesSuccessorsList>: ";
1577 for $VertexID (@{$This->{Vertices}}) {
1578 if (exists($This->{VerticesSuccessors}{$VertexID}) && @{$This->{VerticesSuccessors}{$VertexID}}) {
1579 $VerticesSuccessorsString = join(',', @{$This->{VerticesSuccessors}{$VertexID}});
1580 }
1581 else {
1582 $VerticesSuccessorsString = "None";
1583 }
1584 $SuccessorString .= " <$VertexID-$VerticesSuccessorsString>";
1585 }
1586 return $SuccessorString;
1587 }
1588
1589 # Strinigify paths...
1590 #
1591 sub StringifyPaths {
1592 my($This) = @_;
1593 my($PathsString, $Path);
1594
1595 if (!@{$This->{Paths}}) {
1596 $PathsString = "Paths: None";
1597 return $PathsString;
1598 }
1599
1600 my($FirstPath);
1601 $PathsString = "Paths: ";
1602 $FirstPath = 1;
1603 for $Path (@{$This->{Paths}}) {
1604 if ($FirstPath) {
1605 $FirstPath = 0;
1606 }
1607 else {
1608 $PathsString .= " ";
1609 }
1610 $PathsString .= "<" . join('-', $Path->GetVertices()) . ">";
1611 }
1612 return $PathsString;
1613 }
1614
1615 # Strinigify vertices neighborhoods...
1616 #
1617 sub StringifyVerticesNeighborhoods {
1618 my($This) = @_;
1619 my($NeighborhoodsString, $NeighborhoodVerticesString, $Radius);
1620
1621 if (!@{$This->{VerticesNeighborhoods}}) {
1622 $NeighborhoodsString = "<NeighborhoodRadius-NeighborhoodVerticesList>: None";
1623 return $NeighborhoodsString;
1624 }
1625 $NeighborhoodsString = "<NeighborhoodRadius-NeighborhoodVerticesList>:";
1626 for $Radius (0 .. $#{$This->{VerticesNeighborhoods}}) {
1627 $NeighborhoodVerticesString = join(',', @{$This->{VerticesNeighborhoods}[$Radius]});
1628 $NeighborhoodsString .= " <$Radius-$NeighborhoodVerticesString>";
1629 }
1630
1631 return $NeighborhoodsString;
1632 }
1633
1634 # Strinigify vertices neighborhoods...
1635 #
1636 sub StringifyVerticesNeighborhoodsWithSuccessors {
1637 my($This) = @_;
1638 my($NeighborhoodsString, $NeighborhoodVertexSuccessorsString, $Radius, $NeighborhoodVertericesWithSuccessorsRef, $NeighborhoodVertexWithSuccessorsRef, $VertexID, $NeighborhoodVertexSuccessorsRef);
1639
1640 if (!@{$This->{VerticesNeighborhoodsWithSuccessors}}) {
1641 $NeighborhoodsString = "<NeighborhoodRadius-NeighborhoodVertex-NeighborhoodVerticeSuccessorsList>: None";
1642 return $NeighborhoodsString;
1643 }
1644 $NeighborhoodsString = "<NeighborhoodRadius-NeighborhoodVertex-NeighborhoodVerticeSuccessorsList>: None";
1645
1646 $Radius = 0;
1647 for $NeighborhoodVertericesWithSuccessorsRef (@{$This->{VerticesNeighborhoodsWithSuccessors}}) {
1648 for $NeighborhoodVertexWithSuccessorsRef (@{$NeighborhoodVertericesWithSuccessorsRef}) {
1649 ($VertexID, $NeighborhoodVertexSuccessorsRef) = @{$NeighborhoodVertexWithSuccessorsRef};
1650 $NeighborhoodVertexSuccessorsString = 'None';
1651 if (@{$NeighborhoodVertexSuccessorsRef}) {
1652 $NeighborhoodVertexSuccessorsString = join(',', @{$NeighborhoodVertexSuccessorsRef});
1653 }
1654 $NeighborhoodsString .= " <$Radius-$VertexID-$NeighborhoodVertexSuccessorsString>";
1655 }
1656 $Radius++;
1657 }
1658 return $NeighborhoodsString;
1659 }
1660
1661 # Return a reference to new paths traversal object...
1662 sub Copy {
1663 my($This) = @_;
1664 my($NewPathsTraversal);
1665
1666 $NewPathsTraversal = Storable::dclone($This);
1667
1668 return $NewPathsTraversal;
1669 }
1670
1671 1;
1672
1673 __END__
1674
1675 =head1 NAME
1676
1677 PathsTraversal
1678
1679 =head1 SYNOPSIS
1680
1681 use Graph::PathsTraversal;
1682
1683 use Graph::PathsTraversal qw(:all);
1684
1685 =head1 DESCRIPTION
1686
1687 B<PathsTraversal> class provides the following methods:
1688
1689 new, Copy, GetConnectedComponentsVertices, GetPaths, GetVertices,
1690 GetVerticesDepth, GetVerticesNeighborhoods,
1691 GetVerticesNeighborhoodsWithSuccessors, GetVerticesPredecessors, GetVerticesRoots,
1692 PerformAllPathsSearch, PerformAllPathsSearchWithLength,
1693 PerformAllPathsSearchWithLengthUpto, PerformBreadthFirstSearch,
1694 PerformBreadthFirstSearchWithLimit, PerformDepthFirstSearch,
1695 PerformDepthFirstSearchWithLimit, PerformNeighborhoodVerticesSearch,
1696 PerformNeighborhoodVerticesSearchWithRadiusUpto,
1697 PerformNeighborhoodVerticesSearchWithSuccessors,
1698 PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto, PerformPathsSearch,
1699 PerformPathsSearchBetween, PerformPathsSearchWithLength,
1700 PerformPathsSearchWithLengthUpto, StringifyPaths, StringifyPathsTraversal,
1701 StringifyVerticesDepth, StringifyVerticesNeighborhoods,
1702 StringifyVerticesNeighborhoodsWithSuccessors, StringifyVerticesPredecessors,
1703 StringifyVerticesRoots, StringifyVerticesSuccessors
1704
1705 =head2 METHODS
1706
1707 =over 4
1708
1709 =item B<new>
1710
1711 $PathsTraversal = new Graph::PathsTraversal($Graph);
1712
1713 Using specified I<Graph>, B<new> method creates a new B<PathsTraversal> object and returns
1714 newly created B<PathsTraversal> object.
1715
1716 =item B<Copy>
1717
1718 $PathsTraversal = $PathsTraversal->Copy();
1719
1720 Copies I<PathsTraversal> and its associated data using B<Storable::dclone> and returns a new
1721 B<PathsTraversal> object.
1722
1723 =item B<GetConnectedComponentsVertices>
1724
1725 @Components = $PathsTraversal->GetConnectedComponentsVertices();
1726 $NumOfComponents = $PathsTraversal->GetConnectedComponentsVertices();
1727
1728 Returns an array of B<Components> containing references to arrays of vertex IDs corresponding
1729 to connected components of graph after a search. In scalar context, the number of connected
1730 components is returned.
1731
1732 Connected B<Components> is sorted in descending order of number of vertices in each
1733 connected component.
1734
1735 =item B<GetPaths>
1736
1737 @Paths = $PathsTraversal->GetPaths();
1738 $NumOfPaths = $PathsTraversal->GetPaths();
1739
1740 Returns an array of B<Paths> containing references to arrays of vertex IDs corresponding to
1741 to paths traversed in a graph after a search. In scalar context, number of paths is returned.
1742
1743 B<Paths> array is sorted in ascending order of path lengths.
1744
1745 =item B<GetVertices>
1746
1747 @Vertices = $PathsTraversal->GetVertices();
1748 $NumOfVertices = $PathsTraversal->GetVertices();
1749
1750 Returns an array containing an ordered list of vertex IDs traversed during a search. In
1751 scalar context, the number of vertices is returned.
1752
1753 =item B<GetVerticesDepth>
1754
1755 %VerticesDepth = $PathsTraversal->GetVerticesDepth();
1756
1757 Returns a hash I<VerticesDepth> containing vertex ID and depth from root vertex as a key and
1758 value pair for all vertices traversed during a search.
1759
1760 =item B<GetVerticesNeighborhoods>
1761
1762 @VerticesNeighborhoods =
1763 $PathsTraversal->GetVerticesNeighborhoods();
1764 $NumOfVerticesNeighborhoods =
1765 $PathsTraversal->GetVerticesNeighborhoods();
1766
1767 Returns an array I<VerticesNeighborhoods> containing references to arrays corresponding
1768 to vertices collected at various neighborhood radii around a specified vertex during a vertex
1769 neighborhood search. In scalar context, the number of neighborhoods is returned.
1770
1771 =item B<GetVerticesNeighborhoodsWithSuccessors>
1772
1773 @VerticesNeighborhoodsWithSucceessors =
1774 $PathsTraversal->GetVerticesNeighborhoodsWithSuccessors();
1775 $NumOfVerticesNeighborhoodsWithSucceessors =
1776 $PathsTraversal->GetVerticesNeighborhoodsWithSuccessors();
1777
1778 Returns an array I<VerticesNeighborhoodsWithSucceessors> containing references to arrays
1779 with first value corresponding to vertex IDs corresponding to a vertex at a specific neighborhood
1780 radius level and second value a reference to an arraty containing its successors.
1781
1782 =item B<GetVerticesPredecessors>
1783
1784 %VerticesPredecessors = $PathsTraversal->GetVerticesPredecessors();
1785
1786 Returns a hash I<VerticesPredecessors> containing vertex ID and predecessor vertex ID as key
1787 and value pair for all vertices traversed during a search.
1788
1789 =item B<GetVerticesRoots>
1790
1791 %VerticesRoots = $PathsTraversal->GetVerticesRoots();
1792
1793 Returns a hash I<VerticesPredecessors> containing vertex ID and root vertex ID as a key
1794 and value pair for all vertices traversed during a search.
1795
1796 =item B<PerformAllPathsSearch>
1797
1798 $PathsTraversal->PerformAllPathsSearch($StartVertexID, [$AllowCycles]);
1799
1800 Searches all paths starting from a I<StartVertexID> with sharing of edges in paths traversed and
1801 returns I<PathsTraversal>.
1802
1803 By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
1804 completing the cycle.
1805
1806 =item B<PerformAllPathsSearchWithLength>
1807
1808 $PathsTraversal->PerformAllPathsSearchWithLength($StartVertexID,
1809 $Length, [$AllowCycles]);
1810
1811 Searches all paths starting from I<StartVertexID> of specific I<Length> with sharing of
1812 edges in paths traversed and returns I<PathsTraversal>.
1813
1814 By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
1815 completing the cycle.
1816
1817 =item B<PerformAllPathsSearchWithLengthUpto>
1818
1819 $PathsTraversal->PerformAllPathsSearchWithLengthUpto($StartVertexID,
1820 $Length, [$AllowCycles]);
1821
1822 Searches all paths starting from I<StartVertexID> of length upto a I<Length> with sharing of
1823 edges in paths traversed and returns I<PathsTraversal>.
1824
1825 By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
1826 completing the cycle.
1827
1828 =item B<PerformBreadthFirstSearch>
1829
1830 $PathsTraversal->PerformBreadthFirstSearch();
1831
1832 Performs Breadth First Search (BFS) and returns I<PathsTraversal>.
1833
1834 =item B<PerformBreadthFirstSearchWithLimit>
1835
1836 $PathsTraversal->PerformBreadthFirstSearchWithLimit($DepthLimit,
1837 [$RootVertexID]);
1838
1839 Performs BFS with depth up to I<DepthLimit> starting at I<RootVertexID> and returns
1840 I<PathsTraversal>. By default, root vertex ID corresponds to an arbitrary vertex.
1841
1842 =item B<PerformDepthFirstSearch>
1843
1844 $Return = $PathsTraversal->PerformDepthFirstSearch();
1845
1846 Performs Depth First Search (DFS) and returns I<PathsTraversal>.
1847
1848 =item B<PerformDepthFirstSearchWithLimit>
1849
1850 $PathsTraversal->PerformDepthFirstSearchWithLimit($DepthLimit,
1851 [$RootVertexID]);
1852
1853 Performs DFS with depth up to I<DepthLimit> starting at I<RootVertexID> and returns
1854 I<PathsTraversal>. By default, root vertex ID corresponds to an arbitrary vertex.
1855
1856 =item B<PerformNeighborhoodVerticesSearch>
1857
1858 $PathsTraversal->PerformNeighborhoodVerticesSearch($StartVertexID);
1859
1860 Searches vertices around I<StartVertexID> at all neighborhood radii and returns
1861 I<PathsTraversal> object.
1862
1863 =item B<PerformNeighborhoodVerticesSearchWithRadiusUpto>
1864
1865 $PathsTraversal->PerformNeighborhoodVerticesSearchWithRadiusUpto(
1866 $StartVertexID, $Radius);
1867
1868 Searches vertices around I<StartVertexID> with neighborhood radius up to I<Radius> and returns
1869 I<PathsTraversal> object.
1870
1871 =item B<PerformNeighborhoodVerticesSearchWithSuccessors>
1872
1873 $PathsTraversal->PerformNeighborhoodVerticesSearchWithSuccessors(
1874 $StartVertexID);
1875
1876 Searches vertices around I<StartVertexID> at all neighborhood radii along with identification of
1877 successor vertices for each vertex found during the traversal and returns I<PathsTraversal>.
1878
1879 =item B<PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto>
1880
1881 $PathsTraversal->
1882 PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto(
1883 $StartVertexID, $Radius);
1884
1885 Searches vertices around I<StartVertexID> with neighborhood radius upto I<Radius> along with
1886 identification of successor vertices for each vertex found during the traversal and returns
1887 I<PathsTraversal>.
1888
1889 =item B<PerformPathsSearch>
1890
1891 $PathsTraversal->PerformPathsSearch($StartVertexID, [$AllowCycles]);
1892
1893 Searches paths starting from I<StartVertexID> with no sharing of edges in paths traversed and
1894 returns I<PathsTraversal>.
1895
1896 By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
1897 completing the cycle.
1898
1899 =item B<PerformPathsSearchBetween>
1900
1901 $PathsTraversal->PerformPathsSearchBetween($StartVertexID, $EndVertexID);
1902
1903 Searches paths between I<StartVertexID> and I<EndVertexID> and returns I<PathsTraversal>
1904
1905 =item B<PerformPathsSearchWithLength>
1906
1907 $PathsTraversal->PerformPathsSearchWithLength($StartVertexID, $Length,
1908 [$AllowCycles]);
1909
1910 Searches paths starting from I<StartVertexID> with length I<Length> with no sharing of
1911 edges in paths traversed and returns I<PathsTraversal>.
1912
1913 By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
1914 completing the cycle.
1915
1916 =item B<PerformPathsSearchWithLengthUpto>
1917
1918 $PathsTraversal->PerformPathsSearchWithLengthUpto($StartVertexID, $Length,
1919 [$AllowCycles]);
1920
1921 Searches paths starting from I<StartVertexID> with length upto I<Length> with no sharing of
1922 edges in paths traversed and returns I<PathsTraversal>.
1923
1924 By default, cycles are included in paths. A path containing a cycle is terminated at a vertex
1925 completing the cycle.
1926
1927 =item B<StringifyPaths>
1928
1929 $String = $PathsTraversal->StringifyPaths();
1930
1931 Returns a string containing information about traversed paths in I<PathsTraversal> object
1932
1933 =item B<StringifyPathsTraversal>
1934
1935 $String = $PathsTraversal->StringifyPathsTraversal();
1936
1937 Returns a string containing information about I<PathsTraversal> object.
1938
1939 =item B<StringifyVerticesDepth>
1940
1941 $String = $PathsTraversal->StringifyVerticesDepth();
1942
1943 Returns a string containing information about depth of vertices found during search by
1944 I<PathsTraversal> object.
1945
1946 =item B<StringifyVerticesNeighborhoods>
1947
1948 $String = $PathsTraversal->StringifyVerticesNeighborhoods();
1949
1950 Returns a string containing information about neighborhoods of vertices found during search by
1951 I<PathsTraversal> object.
1952
1953 =item B<StringifyVerticesNeighborhoodsWithSuccessors>
1954
1955 $String = $PathsTraversal->StringifyVerticesNeighborhoodsWithSuccessors();
1956
1957 Returns a string containing information about neighborhoods of vertices along with their successors
1958 found during search by I<PathsTraversal> object.
1959
1960 =item B<StringifyVerticesPredecessors>
1961
1962 $String = $PathsTraversal->StringifyVerticesPredecessors();
1963
1964 Returns a string containing information about predecessors of vertices found during search by
1965 I<PathsTraversal> object.
1966
1967 =item B<StringifyVerticesRoots>
1968
1969 $String = $PathsTraversal->StringifyVerticesRoots();
1970
1971 Returns a string containing information about roots of vertices found during search by
1972 I<PathsTraversal> object.
1973
1974 =item B<StringifyVerticesSuccessors>
1975
1976 $String = $PathsTraversal->StringifyVerticesSuccessors();
1977
1978 Returns a string containing information about successors of vertices found during search by
1979 I<PathsTraversal> object.
1980
1981 =back
1982
1983 =head1 AUTHOR
1984
1985 Manish Sud <msud@san.rr.com>
1986
1987 =head1 SEE ALSO
1988
1989 Graph.pm, Path.pm
1990
1991 =head1 COPYRIGHT
1992
1993 Copyright (C) 2015 Manish Sud. All rights reserved.
1994
1995 This file is part of MayaChemTools.
1996
1997 MayaChemTools is free software; you can redistribute it and/or modify it under
1998 the terms of the GNU Lesser General Public License as published by the Free
1999 Software Foundation; either version 3 of the License, or (at your option)
2000 any later version.
2001
2002 =cut