Mercurial > repos > deepakjadmin > mayatool3_test2
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 |