1 package Graph; 2 # 3 # File: Graph.pm 4 # Author: Manish Sud <msud@san.rr.com> 5 # 6 # Copyright (C) 2024 Manish Sud. All rights reserved. 7 # 8 # This file is part of MayaChemTools. 9 # 10 # MayaChemTools is free software; you can redistribute it and/or modify it under 11 # the terms of the GNU Lesser General Public License as published by the Free 12 # Software Foundation; either version 3 of the License, or (at your option) any 13 # later version. 14 # 15 # MayaChemTools is distributed in the hope that it will be useful, but without 16 # any warranty; without even the implied warranty of merchantability of fitness 17 # for a particular purpose. See the GNU Lesser General Public License for more 18 # details. 19 # 20 # You should have received a copy of the GNU Lesser General Public License 21 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 22 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 23 # Boston, MA, 02111-1307, USA. 24 # 25 26 use strict; 27 use Carp; 28 use Exporter; 29 use Storable (); 30 use Scalar::Util (); 31 use Graph::CyclesDetection; 32 use Graph::PathsTraversal; 33 use Graph::GraphMatrix; 34 35 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 36 37 @ISA = qw(Exporter); 38 @EXPORT = qw(IsGraph); 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 '""' => 'StringifyGraph'; 49 50 # Class constructor... 51 sub new { 52 my($Class, @VertexIDs) = @_; 53 54 # Initialize object... 55 my $This = {}; 56 bless $This, ref($Class) || $Class; 57 $This->_InitializeGraph(); 58 59 if (@VertexIDs) { $This->AddVertices(@VertexIDs); } 60 61 return $This; 62 } 63 64 # Initialize object data... 65 sub _InitializeGraph { 66 my($This) = @_; 67 68 %{$This->{Vertices}} = (); 69 70 %{$This->{Edges}} = (); 71 %{$This->{Edges}->{From}} = (); 72 %{$This->{Edges}->{To}} = (); 73 74 %{$This->{Properties}} = (); 75 %{$This->{Properties}->{Graph}} = (); 76 %{$This->{Properties}->{Vertices}} = (); 77 %{$This->{Properties}->{Edges}} = (); 78 } 79 80 # Initialize class ... 81 sub _InitializeClass { 82 #Class name... 83 $ClassName = __PACKAGE__; 84 } 85 86 # Add a vertex... 87 sub AddVertex { 88 my($This, $VertexID) = @_; 89 90 if (!defined $VertexID ) { 91 carp "Warning: ${ClassName}->AddVertex: No vertex added: Vertex ID must be specified..."; 92 return undef; 93 } 94 if (exists $This->{Vertices}->{$VertexID}) { 95 carp "Warning: ${ClassName}->AddVertex: Didn't add vertex $VertexID: Already exists in the graph..."; 96 return undef; 97 } 98 99 $This->{Vertices}->{$VertexID} = $VertexID; 100 101 return $This; 102 } 103 104 # Add vertices to the graph and return graph... 105 sub AddVertices { 106 my($This, @VertexIDs) = @_; 107 108 if (!@VertexIDs) { 109 carp "Warning: ${ClassName}->AddVertices: No vertices added: Vertices list is empty..."; 110 return undef; 111 } 112 113 my($VertexID); 114 for $VertexID (@VertexIDs) { 115 $This->AddVertex($VertexID); 116 } 117 118 return $This; 119 } 120 121 # Delete a vertex... 122 sub DeleteVertex { 123 my($This, $VertexID) = @_; 124 125 if (!defined $VertexID ) { 126 carp "Warning: ${ClassName}->DeleteVertex: No vertex deleted: Vertex ID must be specified..."; 127 return undef; 128 } 129 if (!$This->HasVertex($VertexID)) { 130 carp "Warning: ${ClassName}->DeleteVertex: Didn't delete vertex $VertexID: Vertex $VertexID doesn't exist..."; 131 return undef; 132 } 133 $This->_DeleteVertex($VertexID); 134 135 return $This; 136 } 137 138 # Delete vertex... 139 sub _DeleteVertex { 140 my($This, $VertexID) = @_; 141 142 # Delete corresponding edges; the corresponding edge properties are deleted during 143 # edges deletetion... 144 my(@VertexIDs); 145 @VertexIDs = $This->GetEdges($VertexID); 146 if (@VertexIDs) { 147 $This->DeleteEdges(@VertexIDs); 148 } 149 150 # Delete the vertex and any properties associated with vertex... 151 $This->DeleteVertexProperties($VertexID); 152 delete $This->{Vertices}->{$VertexID}; 153 } 154 155 # Delete vertices... 156 sub DeleteVertices { 157 my($This, @VertexIDs) = @_; 158 159 if (!@VertexIDs) { 160 carp "Warning: ${ClassName}->DeleteVertices: No vertices deleted: Vertices list is empty..."; 161 return undef; 162 } 163 my($VertexID); 164 for $VertexID (@VertexIDs) { 165 $This->DeleteVertex($VertexID); 166 } 167 168 return $This; 169 } 170 171 # Get vertex data... 172 sub GetVertex { 173 my($This, $VertexID) = @_; 174 175 if (!defined $VertexID) { 176 return undef; 177 } 178 179 return (exists $This->{Vertices}->{$VertexID}) ? $This->{Vertices}->{$VertexID} : undef; 180 } 181 182 # Get data for all vertices or those specifed in the list. In scalar context, returned 183 # the number of vertices found. 184 # 185 sub GetVertices { 186 my($This, @VertexIDs) = @_; 187 my($ValuesCount, @VertexValues); 188 189 @VertexValues = (); 190 if (@VertexIDs) { 191 @VertexValues = map { $This->GetVertex($_) } @VertexIDs; 192 $ValuesCount = grep { 1 } @VertexValues; 193 } 194 else { 195 @VertexValues = sort { $a <=> $b } keys %{$This->{Vertices}}; 196 $ValuesCount = @VertexValues; 197 } 198 199 return wantarray ? @VertexValues : $ValuesCount; 200 } 201 202 # Is this vertex present? 203 sub HasVertex { 204 my($This, $VertexID) = @_; 205 206 if (!defined $VertexID) { 207 return 0; 208 } 209 return (exists $This->{Vertices}->{$VertexID}) ? 1 : 0; 210 } 211 212 # Are these vertices present? Return an array containing 1 or 0 for each vertex. 213 # In scalar context, return number of vertices found. 214 sub HasVertices { 215 my($This, @VertexIDs) = @_; 216 217 if (!@VertexIDs) { 218 return undef; 219 } 220 my($VerticesCount, @VerticesStatus); 221 222 @VerticesStatus = map { $This->HasVertex($_) } @VertexIDs; 223 $VerticesCount = grep { 1 } @VerticesStatus; 224 225 return wantarray ? @VerticesStatus : $VerticesCount; 226 } 227 228 # Add an edge... 229 sub AddEdge { 230 my($This, $VertexID1, $VertexID2) = @_; 231 232 if (!(defined($VertexID1) && defined($VertexID2))) { 233 carp "Warning: ${ClassName}->AddEdge: No edge added: Both vertices must be defined..."; 234 return undef; 235 } 236 if (!$This->HasVertex($VertexID1)) { 237 carp "Warning: ${ClassName}->AddEdge: Didn't add edge between vertices $VertexID1 and $VertexID2: Vertex $VertexID1 doesn's exist..."; 238 return undef; 239 } 240 if (!$This->HasVertex($VertexID2)) { 241 carp "Warning: ${ClassName}->AddEdge: Didn't add edge between vertices $VertexID1 and $VertexID2: Vertex $VertexID2 doesn's exist..."; 242 return undef; 243 } 244 if ($VertexID1 == $VertexID2) { 245 carp "Warning: ${ClassName}->AddEdge: Didn't add edge between vertices $VertexID1 and $VertexID2: Vertices must be different..."; 246 return undef; 247 } 248 if ($This->HasEdge($VertexID1, $VertexID2)) { 249 carp "Warning: ${ClassName}->AddEdge: Didn't add edge between vertices $VertexID1 and $VertexID2: Edge already exists..."; 250 return undef; 251 } 252 253 if (!exists $This->{Edges}->{From}->{$VertexID1}) { 254 %{$This->{Edges}->{From}->{$VertexID1}} = (); 255 } 256 $This->{Edges}->{From}->{$VertexID1}->{$VertexID2} = $VertexID2; 257 258 if (!exists $This->{Edges}->{To}->{$VertexID2}) { 259 %{$This->{Edges}->{To}->{$VertexID2}} = (); 260 } 261 $This->{Edges}->{To}->{$VertexID2}->{$VertexID1} = $VertexID1; 262 263 return $This; 264 } 265 266 # Add edges... 267 sub AddEdges { 268 my($This, @VertexIDs) = @_; 269 270 if (!@VertexIDs) { 271 carp "Warning: ${ClassName}->AddEdges: No edges added: Vertices list is empty..."; 272 return undef; 273 } 274 if (@VertexIDs % 2) { 275 carp "Warning: ${ClassName}->AddEdges: No edges added: Invalid vertices data: Input list must contain even number of vertex IDs..."; 276 return undef; 277 } 278 my($VertexID1, $VertexID2, $Index); 279 for ($Index = 0; $Index < $#VertexIDs; $Index += 2) { 280 $VertexID1 = $VertexIDs[$Index]; $VertexID2 = $VertexIDs[$Index + 1]; 281 $This->AddEdge($VertexID1, $VertexID2); 282 } 283 284 return $This; 285 } 286 287 # Delete an edge... 288 sub DeleteEdge { 289 my($This, $VertexID1, $VertexID2) = @_; 290 291 if (!(defined($VertexID1) && defined($VertexID2))) { 292 carp "Warning: ${ClassName}->Delete: No edge deleted: Both vertices must be defined..."; 293 return undef; 294 } 295 if (!$This->HasVertex($VertexID1)) { 296 carp "Warning: ${ClassName}->DeleteEdge: Didn't delete edge between vertices $VertexID1 and $VertexID2: Vertex $VertexID1 doesn's exist..."; 297 return undef; 298 } 299 if (!$This->HasVertex($VertexID2)) { 300 carp "Warning: ${ClassName}->DeleteEdge: Didn't delete edge between vertices $VertexID1 and $VertexID2: Vertex $VertexID2 doesn's exist..."; 301 return undef; 302 } 303 if (!$This->HasEdge($VertexID1, $VertexID2)) { 304 carp "Warning: ${ClassName}->DeleteEdge: Didn't delete edge between vertices $VertexID1 and $VertexID2: Edge doesn't exist..."; 305 return undef; 306 } 307 $This->_DeleteEdge($VertexID1, $VertexID2); 308 $This->_DeleteEdge($VertexID2, $VertexID1); 309 } 310 311 # Delete edge... 312 sub _DeleteEdge { 313 my($This, $VertexID1, $VertexID2) = @_; 314 315 # Delete the edge... 316 if (exists $This->{Edges}->{From}->{$VertexID1}) { 317 if (exists $This->{Edges}->{From}->{$VertexID1}->{$VertexID2}) { 318 delete $This->{Edges}->{From}->{$VertexID1}->{$VertexID2}; 319 } 320 if (! keys %{$This->{Edges}->{From}->{$VertexID1}}) { 321 delete $This->{Edges}->{From}->{$VertexID1}; 322 } 323 } 324 325 if (exists $This->{Edges}->{To}->{$VertexID2}) { 326 if (exists $This->{Edges}->{To}->{$VertexID2}->{$VertexID1}) { 327 delete $This->{Edges}->{To}->{$VertexID2}->{$VertexID1}; 328 } 329 if (! keys %{$This->{Edges}->{To}->{$VertexID2}}) { 330 delete $This->{Edges}->{To}->{$VertexID2}; 331 } 332 } 333 334 # Delete properties associated with the edge... 335 $This->DeleteEdgeProperties($VertexID1, $VertexID2); 336 } 337 338 # Delete edges... 339 sub DeleteEdges { 340 my($This, @VertexIDs) = @_; 341 342 if (!@VertexIDs) { 343 carp "Warning: ${ClassName}->DeleteEdges: No edges deleted: Vertices list is empty..."; 344 return undef; 345 } 346 if (@VertexIDs % 2) { 347 carp "Warning: ${ClassName}->DeleteEdges: No edges deleted: Invalid vertices data: Input list must contain even number of vertex IDs..."; 348 return undef; 349 } 350 my($VertexID1, $VertexID2, $Index); 351 for ($Index = 0; $Index < $#VertexIDs; $Index += 2) { 352 $VertexID1 = $VertexIDs[$Index]; $VertexID2 = $VertexIDs[$Index + 1]; 353 $This->DeleteEdge($VertexID1, $VertexID2); 354 } 355 356 return $This; 357 } 358 359 # Does the edge defiend by a vertex pair exists? Edges defined from VertexID1 to VertecID2 360 # and VertexID2 to VertexID1 are considered equivalent... 361 sub HasEdge { 362 my($This, $VertexID1, $VertexID2) = @_; 363 364 if (!(defined($VertexID1) && defined($VertexID2))) { 365 return 0; 366 } 367 368 return ($This->_HasEdge($VertexID1, $VertexID2) || $This->_HasEdge($VertexID2, $VertexID1)) ? 1 : 0; 369 } 370 371 # Does edge exists? 372 sub _HasEdge { 373 my($This, $VertexID1, $VertexID2) = @_; 374 375 if (exists $This->{Edges}->{From}->{$VertexID1}) { 376 if (exists $This->{Edges}->{From}->{$VertexID1}->{$VertexID2}) { 377 return 1; 378 } 379 } 380 elsif (exists $This->{Edges}->{To}->{$VertexID2}) { 381 if (exists $This->{Edges}->{To}->{$VertexID2}->{$VertexID1}) { 382 return 1; 383 } 384 } 385 return 0; 386 } 387 388 # Do the edges defiend by vertex pairs exist? In scalar context, return the number 389 # of edges found... 390 sub HasEdges { 391 my($This, @VertexIDs) = @_; 392 393 if (!@VertexIDs) { 394 return 0; 395 } 396 if (@VertexIDs % 2) { 397 return 0; 398 } 399 my($VertexID1, $VertexID2, $Index, $Status, $EdgesCount, @EdgesStatus); 400 @EdgesStatus = (); 401 $EdgesCount = 0; 402 for ($Index = 0; $Index < $#VertexIDs; $Index += 2) { 403 $VertexID1 = $VertexIDs[$Index]; $VertexID2 = $VertexIDs[$Index + 1]; 404 $Status = $This->HasEdge($VertexID1, $VertexID2); 405 push @EdgesStatus, ($Status); 406 if (defined($Status) && $Status) { 407 $EdgesCount++; 408 } 409 } 410 return wantarray ? @EdgesStatus : $EdgesCount; 411 } 412 413 # Get edges for a vertex ID or retrieve all the edges. In scalar context, 414 # return the number of edges. 415 # 416 sub GetEdges { 417 my($This, $VertexID) = @_; 418 my(@VertexIDs); 419 420 @VertexIDs = (); 421 if (defined $VertexID) { 422 push @VertexIDs, ($This->_GetEdgesFrom($VertexID), $This->_GetEdgesTo($VertexID)) 423 } 424 else { 425 push @VertexIDs, $This->_GetEdges(); 426 } 427 return (wantarray ? @VertexIDs : @VertexIDs/2); 428 } 429 430 # Get edge starting from the vertex to its successor vertices... 431 sub _GetEdgesFrom { 432 my($This, $VertexID1) = @_; 433 my($VertexID2) = undef; 434 435 return $This->_GetEdges($VertexID1, $VertexID2); 436 } 437 438 # Get edge starting from predecessors to the vertex... 439 sub _GetEdgesTo { 440 my($This, $VertexID2) = @_; 441 my($VertexID1) = undef; 442 443 return $This->_GetEdges($VertexID1, $VertexID2); 444 } 445 446 # Get edges as pair of vertex IDs. Edges data can be retrieved in three 447 # different ways: 448 # 449 # Both vertex IDs are defined: Returns existing edge between the vertices 450 # Only first vertex ID defined: Returns all edges at the vertex 451 # Only second vertex defined: Returns all edges at the vertex 452 # No vertex IDs defined: Returns all edges 453 # 454 sub _GetEdges { 455 my($This, $VertexID1, $VertexID2) = @_; 456 my($VertexID, @VertexIDs); 457 458 @VertexIDs = (); 459 460 if (defined($VertexID1) && defined($VertexID2)) { 461 if ($This->HasEdge($VertexID1, $VertexID2)) { 462 push @VertexIDs, ($VertexID1, $VertexID2); 463 } 464 } 465 elsif (defined($VertexID1)) { 466 for $VertexID ($This->_GetNeighborsFrom($VertexID1)) { 467 push @VertexIDs, $This->_GetEdges($VertexID1, $VertexID); 468 } 469 } 470 elsif (defined($VertexID2)) { 471 for $VertexID ($This->_GetNeighborsTo($VertexID2)) { 472 push @VertexIDs, $This->_GetEdges($VertexID, $VertexID2); 473 } 474 } 475 else { 476 for $VertexID ($This->GetVertices()) { 477 push @VertexIDs, $This->_GetEdges($VertexID); 478 } 479 } 480 481 return @VertexIDs; 482 } 483 484 # Add edges between successive pair of vertex IDs...... 485 sub AddPath { 486 my($This, @VertexIDs) = @_; 487 488 if (!@VertexIDs) { 489 carp "Warning: ${ClassName}->AddPath: No path added: Vertices list is empty..."; 490 return undef; 491 } 492 if (@VertexIDs == 1) { 493 carp "Warning: ${ClassName}->AddPath: No path added: Invalid vertices data: Input list must contain more than on vertex ID..."; 494 return undef; 495 } 496 if (!$This->HasVertices(@VertexIDs)) { 497 carp "Warning: ${ClassName}->AddPath: No path added: Some of the vertex IDs don't exist in the graph..."; 498 return undef; 499 } 500 if ($This->HasPath(@VertexIDs)) { 501 carp "Warning: ${ClassName}->AddPath: No path added: Path already exist in the graph..."; 502 return undef; 503 } 504 my(@PathVertexIDs); 505 @PathVertexIDs =$This-> _SetupPathVertices(@VertexIDs); 506 507 return $This->AddEdges(@PathVertexIDs); 508 } 509 510 511 # Delete edges between successive pair of vertex IDs...... 512 sub DeletePath { 513 my($This, @VertexIDs) = @_; 514 515 if (!@VertexIDs) { 516 carp "Warning: ${ClassName}->DeletePath: No path deleted: Vertices list is empty..."; 517 return undef; 518 } 519 if (@VertexIDs == 1) { 520 carp "Warning: ${ClassName}->DeletePath: No path deleted: Invalid vertices data: Input list must contain more than on vertex ID..."; 521 return undef; 522 } 523 if (!$This->HasVertices(@VertexIDs)) { 524 carp "Warning: ${ClassName}->DeletePath: No path deleted: Some of the vertex IDs don't exist in the graph..."; 525 return undef; 526 } 527 if (!$This->HasPath(@VertexIDs)) { 528 carp "Warning: ${ClassName}->DeletePath: No path deleted: Path doesn't exist in the graph..."; 529 return undef; 530 } 531 my(@PathVertexIDs); 532 @PathVertexIDs = $This->_SetupPathVertices(@VertexIDs); 533 534 return $This->DeleteEdges(@PathVertexIDs); 535 } 536 537 # Does the path defiend by edges between successive pairs of vertex IDs exist? 538 sub HasPath { 539 my($This, @VertexIDs) = @_; 540 541 if (!@VertexIDs) { 542 return 0; 543 } 544 if (@VertexIDs == 1) { 545 return 0; 546 } 547 if (!$This->HasVertices(@VertexIDs)) { 548 return 0; 549 } 550 my($Status, @PathVertexIDs); 551 @PathVertexIDs = $This->_SetupPathVertices(@VertexIDs); 552 $Status = ($This->HasEdges(@PathVertexIDs) == (@PathVertexIDs/2)) ? 1 : 0; 553 554 return $Status; 555 } 556 557 # Setup vertices for the path to define edges between successive pair of vertex IDs... 558 sub _SetupPathVertices { 559 my($This, @VertexIDs) = @_; 560 my($VertexID1, $VertexID2, $Index, @PathVertexIDs); 561 562 @PathVertexIDs = (); 563 for $Index (0 .. ($#VertexIDs - 1)) { 564 $VertexID1 = $VertexIDs[$Index]; 565 $VertexID2 = $VertexIDs[$Index + 1]; 566 push @PathVertexIDs, ($VertexID1, $VertexID2); 567 } 568 569 return @PathVertexIDs; 570 } 571 572 # Add edges between successive pair of vertex IDs and an additional edge from the last to 573 # the first ID to complete the cycle...... 574 sub AddCycle { 575 my($This, @VertexIDs) = @_; 576 577 if (!@VertexIDs) { 578 carp "Warning: ${ClassName}->AddCycle: No cycle added: Vertices list is empty..."; 579 return undef; 580 } 581 if (@VertexIDs == 1) { 582 carp "Warning: ${ClassName}->AddCycle: No cycle added: Invalid vertices data: Input list must contain more than on vertex ID..."; 583 return undef; 584 } 585 if (!$This->HasVertices(@VertexIDs)) { 586 carp "Warning: ${ClassName}->AddCycle: No cycle added: Some of the vertex IDs don't exist in the graph..."; 587 return undef; 588 } 589 my($FirstVertextID) = $VertexIDs[0]; 590 push @VertexIDs, ($FirstVertextID); 591 592 if ($This->HasCycle(@VertexIDs)) { 593 carp "Warning: ${ClassName}->AddCycle: No cycle added: Cycle already exist in the graph..."; 594 return undef; 595 } 596 597 return $This->AddPath(@VertexIDs); 598 } 599 600 # Delete edges between successive pair of vertex IDs and an additional edge from the last to 601 # the first ID to complete the cycle...... 602 sub DeleteCycle { 603 my($This, @VertexIDs) = @_; 604 605 if (!@VertexIDs) { 606 carp "Warning: ${ClassName}->DeleteCycle: No cycle deleted: Vertices list is empty..."; 607 return undef; 608 } 609 if (@VertexIDs == 1) { 610 carp "Warning: ${ClassName}->DeleteCycle: No cycle deleted: Invalid vertices data: Input list must contain more than on vertex ID..."; 611 return undef; 612 } 613 if (!$This->HasVertices(@VertexIDs)) { 614 carp "Warning: ${ClassName}->DeleteCycle: No cycle deleted: Some of the vertex IDs don't exist in the graph..."; 615 return undef; 616 } 617 if (!$This->HasCycle(@VertexIDs)) { 618 carp "Warning: ${ClassName}->DeleteCycle: No cycle deleted: Cycle doesn't exist in the graph..."; 619 return undef; 620 } 621 622 my($FirstVertextID) = $VertexIDs[0]; 623 push @VertexIDs, ($FirstVertextID); 624 625 return $This->DeletePath(@VertexIDs); 626 } 627 628 # Does the cycle defiend by edges between successive pairs of vertex IDs along with an additional 629 # edge from last to first vertex ID exist? 630 sub HasCycle { 631 my($This, @VertexIDs) = @_; 632 633 if (!@VertexIDs) { 634 return 0; 635 } 636 if (@VertexIDs == 1) { 637 return 0; 638 } 639 my($FirstVertextID) = $VertexIDs[0]; 640 push @VertexIDs, ($FirstVertextID); 641 642 return $This->HasPath(@VertexIDs); 643 } 644 645 # Get neighbors... 646 sub GetNeighbors { 647 my($This, $VertexID) = @_; 648 649 if (!defined $VertexID) { 650 return undef; 651 } 652 if (! exists $This->{Vertices}->{$VertexID}) { 653 return undef; 654 } 655 656 # Get a list of unsorted vertices and sort 'em once before returning... 657 # 658 my($VerticesCount, $SortVertices, @VertexIDs); 659 660 $SortVertices = 0; 661 @VertexIDs = (); 662 663 push @VertexIDs, $This->_GetNeighborsFrom($VertexID, $SortVertices); 664 push @VertexIDs, $This->_GetNeighborsTo($VertexID, $SortVertices); 665 $VerticesCount = @VertexIDs; 666 667 return wantarray ? sort {$a <=> $b} @VertexIDs : $VerticesCount; 668 } 669 670 # Get neighbors added by defining edges from the vertex. For undirected graph, it has no 671 # strict meaning... 672 sub _GetNeighborsFrom { 673 my($This, $VertexID, $SortVertices) = @_; 674 my(@VertexIDs); 675 676 $SortVertices = defined $SortVertices ? $SortVertices : 1; 677 @VertexIDs = (); 678 679 if (exists $This->{Edges}->{From}->{$VertexID}) { 680 push @VertexIDs, map { $This->{Edges}->{From}->{$VertexID}->{$_} } keys %{$This->{Edges}->{From}->{$VertexID}}; 681 } 682 return $SortVertices ? sort {$a <=> $b} @VertexIDs : @VertexIDs; 683 } 684 685 # Get neighbors added by defining edges to the vertex. For undirected graphs, it has no 686 # strict meaning. 687 sub _GetNeighborsTo { 688 my($This, $VertexID, $SortVertices) = @_; 689 my(@VertexIDs); 690 691 $SortVertices = defined $SortVertices ? $SortVertices : 1; 692 @VertexIDs = (); 693 694 if (exists $This->{Edges}->{To}->{$VertexID}) { 695 push @VertexIDs, map { $This->{Edges}->{To}->{$VertexID}->{$_} } keys %{$This->{Edges}->{To}->{$VertexID}}; 696 } 697 return $SortVertices ? sort {$a <=> $b} @VertexIDs : @VertexIDs; 698 } 699 700 # Get vertex degree... 701 # 702 sub GetDegree { 703 my($This, $VertexID) = @_; 704 705 if (!defined $VertexID) { 706 return undef; 707 } 708 if (! exists $This->{Vertices}->{$VertexID}) { 709 return undef; 710 } 711 my($Degree); 712 $Degree = $This->_GetInDegree($VertexID) + $This->_GetOutDegree($VertexID); 713 714 return $Degree; 715 } 716 717 # Get in degree added by defining edges to the vertex. For undirected graphs, it has no 718 # strict meaning. 719 # 720 sub _GetInDegree { 721 my($This, $VertexID) = @_; 722 my($Degree); 723 724 $Degree = 0; 725 if (exists $This->{Edges}->{To}->{$VertexID}) { 726 $Degree = keys %{$This->{Edges}->{To}->{$VertexID}}; 727 } 728 return $Degree; 729 } 730 731 # Get out degree added by defining edges from the vertex. For undirected graphs, it has no 732 # strict meaning. 733 # 734 sub _GetOutDegree { 735 my($This, $VertexID) = @_; 736 my($Degree); 737 738 $Degree = 0; 739 if (exists $This->{Edges}->{From}->{$VertexID}) { 740 $Degree = keys %{$This->{Edges}->{From}->{$VertexID}}; 741 } 742 return $Degree; 743 } 744 745 # Get vertex with smallest degree... 746 # 747 sub GetVertexWithSmallestDegree { 748 my($This) = @_; 749 my($Degree, $SmallestDegree, $SmallestDegreeVertexID, $VertexID, @VertexIDs); 750 751 @VertexIDs = (); 752 @VertexIDs = $This->GetVertices(); 753 if (! @VertexIDs) { 754 return undef; 755 } 756 $SmallestDegree = 99999; $SmallestDegreeVertexID = undef; 757 758 for $VertexID (@VertexIDs) { 759 $Degree = $This->_GetInDegree($VertexID) + $This->_GetOutDegree($VertexID); 760 if ($Degree < $SmallestDegree) { 761 $SmallestDegree = $Degree; 762 $SmallestDegreeVertexID = $VertexID; 763 } 764 } 765 return $SmallestDegreeVertexID; 766 } 767 768 # Get vertex with largest degree... 769 # 770 sub GetVertexWithLargestDegree { 771 my($This) = @_; 772 my($Degree, $LargestDegree, $LargestDegreeVertexID, $VertexID, @VertexIDs); 773 774 @VertexIDs = (); 775 @VertexIDs = $This->GetVertices(); 776 if (! @VertexIDs) { 777 return undef; 778 } 779 $LargestDegree = 0; $LargestDegreeVertexID = undef; 780 781 for $VertexID (@VertexIDs) { 782 $Degree = $This->_GetInDegree($VertexID) + $This->_GetOutDegree($VertexID); 783 if ($Degree > $LargestDegree) { 784 $LargestDegree = $Degree; 785 $LargestDegreeVertexID = $VertexID; 786 } 787 } 788 return $LargestDegreeVertexID; 789 } 790 791 # Get maximum degree in the graph... 792 # 793 sub GetMaximumDegree { 794 my($This) = @_; 795 my($Degree, $MaximumDegree, $VertexID, @VertexIDs); 796 797 @VertexIDs = (); 798 @VertexIDs = $This->GetVertices(); 799 if (! @VertexIDs) { 800 return undef; 801 } 802 $MaximumDegree = 0; 803 804 for $VertexID (@VertexIDs) { 805 $Degree = $This->GetDegree($VertexID); 806 if ($Degree > $MaximumDegree) { 807 $MaximumDegree = $Degree; 808 } 809 } 810 return $MaximumDegree; 811 } 812 813 # Get minimum degree in the graph... 814 # 815 sub GetMininumDegree { 816 my($This) = @_; 817 my($Degree, $MininumDegree, $VertexID, @VertexIDs); 818 819 @VertexIDs = (); 820 @VertexIDs = $This->GetVertices(); 821 if (! @VertexIDs) { 822 return undef; 823 } 824 $MininumDegree = 99999; 825 826 for $VertexID (@VertexIDs) { 827 $Degree = $This->GetDegree($VertexID); 828 if ($Degree < $MininumDegree) { 829 $MininumDegree = $Degree; 830 } 831 } 832 return $MininumDegree; 833 } 834 835 # Is it a isolated vertex? 836 # 837 sub IsIsolatedVertex { 838 my($This, $VertexID) = @_; 839 840 if (!defined $VertexID) { 841 return undef; 842 } 843 if (! exists $This->{Vertices}->{$VertexID}) { 844 return undef; 845 } 846 return ($This->GetDegree() == 0) ? 1 : 0; 847 } 848 849 # Get all isolated vertices... 850 # 851 sub GetIsolatedVertices { 852 my($This) = @_; 853 854 return $This->GetVerticesWithDegreeLessThan(1); 855 } 856 857 # Is it a leaf vertex? 858 # 859 sub IsLeafVertex { 860 my($This, $VertexID) = @_; 861 862 if (!defined $VertexID) { 863 return undef; 864 } 865 if (! exists $This->{Vertices}->{$VertexID}) { 866 return undef; 867 } 868 return ($This->GetDegree() == 1) ? 1 : 0; 869 } 870 871 # Get all leaf vertices... 872 # 873 sub GetLeafVertices { 874 my($This) = @_; 875 876 return $This->GetVerticesWithDegreeLessThan(2); 877 } 878 879 # Get vertices with degree less than a specified value... 880 # 881 sub GetVerticesWithDegreeLessThan { 882 my($This, $SpecifiedDegree) = @_; 883 my($Degree, $VertexID, @VertexIDs, @FilteredVertexIDs); 884 885 @VertexIDs = (); @FilteredVertexIDs = (); 886 887 @VertexIDs = $This->GetVertices(); 888 if (! @VertexIDs) { 889 return @FilteredVertexIDs; 890 } 891 892 for $VertexID (@VertexIDs) { 893 $Degree = $This->_GetInDegree($VertexID) + $This->_GetOutDegree($VertexID); 894 if ($Degree < $SpecifiedDegree) { 895 push @FilteredVertexIDs, $VertexID; 896 } 897 } 898 return @FilteredVertexIDs; 899 } 900 901 # Set a property for graph... 902 sub SetGraphProperty { 903 my($This, $Name, $Value) = @_; 904 905 if (!(defined($Name) && defined($Value))) { 906 carp "Warning: ${ClassName}->SetGraphProperty: Didn't set property: Both property name and value should be specified..."; 907 return undef; 908 } 909 if (exists $This->{Properties}->{Graph}->{$Name}) { 910 carp "Warning: ${ClassName}->SetGraphProperty: Didn't set property $Name: Already exists in the graph..."; 911 return undef; 912 } 913 914 $This->{Properties}->{Graph}->{$Name} = $Value; 915 916 return $This; 917 } 918 919 # Set a properties for graph... 920 sub SetGraphProperties { 921 my($This, %NamesAndValues) = @_; 922 923 if (!(keys %NamesAndValues)) { 924 carp "Warning: ${ClassName}->SetGraphProperties: Didn't set properties: Names and values list is empty..."; 925 return undef; 926 } 927 928 my($Name, $Value); 929 while (($Name, $Value) = each %NamesAndValues) { 930 $This->SetGraphProperty($Name, $Value); 931 } 932 933 return $This; 934 } 935 936 # Get a property value for graph... 937 sub GetGraphProperty { 938 my($This, $Name) = @_; 939 940 if (!$This->HasGraphProperty($Name)) { 941 return undef; 942 } 943 944 return $This->{Properties}->{Graph}->{$Name}; 945 } 946 947 # Get all poperty name/value pairs for graph... 948 sub GetGraphProperties { 949 my($This) = @_; 950 951 return %{$This->{Properties}->{Graph}}; 952 } 953 954 # Delete a property for graph... 955 sub DeleteGraphProperty { 956 my($This, $Name) = @_; 957 958 if (!defined $Name) { 959 carp "Warning: ${ClassName}->DeleteGraphProperty: Didn't delete graph property: Name must be specified..."; 960 return undef; 961 } 962 if (!$This->HasGraphProperty($Name)) { 963 carp "Warning: ${ClassName}-> DeleteGraphProperty: Didn't delete graph property $Name: Property doesn't exist..."; 964 return undef; 965 } 966 delete $This->{Properties}->{Graph}->{$Name}; 967 968 return $This; 969 } 970 971 # Delete graph properites... 972 sub DeleteGraphProperties { 973 my($This) = @_; 974 975 %{$This->{Properties}->{Graph}} = (); 976 977 return $This; 978 } 979 980 981 # Is this property associated with graph? 982 sub HasGraphProperty { 983 my($This, $Name) = @_; 984 985 if (!defined $Name) { 986 return 0; 987 } 988 return (exists $This->{Properties}->{Graph}->{$Name}) ? 1 : 0; 989 } 990 991 # Set a property for vertex... 992 sub SetVertexProperty { 993 my($This, $Name, $Value, $VertexID) = @_; 994 995 if (!(defined($Name) && defined($Value) && defined($VertexID))) { 996 carp "Warning: ${ClassName}->SetVertexProperty: Didn't set property: Property name, value and vertexID should be specified..."; 997 return undef; 998 } 999 if (!$This->HasVertex($VertexID)) { 1000 carp "Warning: ${ClassName}->SetVertexProperty: Didn't set property $Name for vertex $VertexID: Vertex doesn't exist..."; 1001 return undef; 1002 } 1003 if ($This->HasVertexProperty($Name, $VertexID)) { 1004 carp "Warning: ${ClassName}->SetVertexProperty: Didn't set property $Name for vertex $VertexID: Property already exists..."; 1005 return undef; 1006 } 1007 1008 $This->_SetVertexProperty($Name, $Value, $VertexID); 1009 1010 return $This; 1011 } 1012 1013 # Update a property for vertex... 1014 sub UpdateVertexProperty { 1015 my($This, $Name, $Value, $VertexID) = @_; 1016 1017 if (!(defined($Name) && defined($Value) && defined($VertexID))) { 1018 carp "Warning: ${ClassName}->UpdateVextexProperty: Didn't update property: Property name, value and vertexID should be specified..."; 1019 return undef; 1020 } 1021 if (!$This->HasVertex($VertexID)) { 1022 carp "Warning: ${ClassName}->UpdateVextexProperty: Didn't update property $Name for vertex $VertexID: Vertex doesn't exist..."; 1023 return undef; 1024 } 1025 if (!$This->HasVertexProperty($Name, $VertexID)) { 1026 carp "Warning: ${ClassName}->UpdateVextexProperty: Didn't update property $Name for vertex $VertexID: Property doesn't exists..."; 1027 return undef; 1028 } 1029 $This->_SetVertexProperty($Name, $Value, $VertexID); 1030 1031 return $This; 1032 } 1033 1034 # Set a vextex property... 1035 sub _SetVertexProperty { 1036 my($This, $Name, $Value, $VertexID) = @_; 1037 1038 if (!exists $This->{Properties}->{Vertices}->{$VertexID}) { 1039 %{$This->{Properties}->{Vertices}->{$VertexID}} = (); 1040 } 1041 $This->{Properties}->{Vertices}->{$VertexID}->{$Name} = $Value; 1042 1043 return $This; 1044 } 1045 1046 # Set a properties for vertex.. 1047 sub SetVertexProperties { 1048 my($This, $VertexID, @NamesAndValues) = @_; 1049 1050 if (!defined $VertexID) { 1051 carp "Warning: ${ClassName}->SetVertexProperties: Didn't set property: VertexID should be specified..."; 1052 return undef; 1053 } 1054 if (!@NamesAndValues) { 1055 carp "Warning: ${ClassName}->SetVertexProperties: Didn't set property: Names and values list is empty..."; 1056 return undef; 1057 } 1058 if (@NamesAndValues % 2) { 1059 carp "Warning: ${ClassName}->SetVertexProperties: Didn't set property: Invalid property name and values IDs data: Input list must contain even number of values..."; 1060 return undef; 1061 } 1062 1063 my($Name, $Value, $Index); 1064 for ($Index = 0; $Index < $#NamesAndValues; $Index += 2) { 1065 $Name = $NamesAndValues[$Index]; $Value = $NamesAndValues[$Index + 1]; 1066 $This->SetVertexProperty($Name, $Value, $VertexID); 1067 } 1068 1069 return $This; 1070 } 1071 1072 1073 # Set a property for vertices... 1074 sub SetVerticesProperty { 1075 my($This, $Name, @ValuesAndVertexIDs) = @_; 1076 1077 if (!defined $Name) { 1078 carp "Warning: ${ClassName}->SetVerticesProperty: Didn't set property: Property name should be specified..."; 1079 return undef; 1080 } 1081 if (!@ValuesAndVertexIDs) { 1082 carp "Warning: ${ClassName}->SetVerticesProperty: Didn't set property: Values and vertex IDs list is empty..."; 1083 return undef; 1084 } 1085 if (@ValuesAndVertexIDs % 2) { 1086 carp "Warning: ${ClassName}->SetVerticesProperty: Didn't set property: Invalid property values and vertex IDs data: Input list must contain even number of values..."; 1087 return undef; 1088 } 1089 1090 my($Value, $VertexID, $Index); 1091 for ($Index = 0; $Index < $#ValuesAndVertexIDs; $Index += 2) { 1092 $Value = $ValuesAndVertexIDs[$Index]; $VertexID = $ValuesAndVertexIDs[$Index + 1]; 1093 $This->SetVertexProperty($Name, $Value, $VertexID); 1094 } 1095 1096 return $This; 1097 } 1098 1099 # Get a property value for vertex... 1100 sub GetVertexProperty { 1101 my($This, $Name, $VertexID) = @_; 1102 1103 if (!$This->HasVertexProperty($Name, $VertexID)) { 1104 return undef; 1105 } 1106 1107 return $This->{Properties}->{Vertices}->{$VertexID}->{$Name}; 1108 } 1109 1110 # Get a property values for vertices... 1111 sub GetVerticesProperty { 1112 my($This, $Name, @VertexIDs) = @_; 1113 my($ValuesCount, @Values); 1114 1115 if (!@VertexIDs) { 1116 @VertexIDs = (); 1117 @VertexIDs = $This->GetVertices(); 1118 } 1119 @Values = (); 1120 @Values = map { $This->GetVertexProperty($Name, $_ ) } @VertexIDs; 1121 $ValuesCount = grep { defined($_) } @Values; 1122 1123 return wantarray ? @Values : $ValuesCount; 1124 } 1125 1126 # Get all property name/values pairs for vertex... 1127 sub GetVertexProperties { 1128 my($This, $VertexID) = @_; 1129 1130 if (!$This->HasVertex($VertexID)) { 1131 return (); 1132 } 1133 1134 if (exists $This->{Properties}->{Vertices}->{$VertexID}) { 1135 return %{$This->{Properties}->{Vertices}->{$VertexID}}; 1136 } 1137 else { 1138 return (); 1139 } 1140 } 1141 1142 1143 # Delete a property for vertex... 1144 sub DeleteVertexProperty { 1145 my($This, $Name, $VertexID) = @_; 1146 1147 if (!(defined($Name) && defined($VertexID))) { 1148 carp "Warning: ${ClassName}->DeleteVertexProperty: Didn't delete property: Property name and vertex ID must be defined..."; 1149 return undef; 1150 } 1151 if (!$This->HasVertexProperty($Name, $VertexID)) { 1152 carp "Warning: ${ClassName}->DeleteVertexProperty: Didn't delete property $Name for vertex $VertexID: Vertex property doesn't exist..."; 1153 return undef; 1154 } 1155 $This->_DeleteVertexProperty($VertexID, $Name); 1156 1157 return $This; 1158 } 1159 1160 # Delete vertex property or all properties... 1161 sub _DeleteVertexProperty { 1162 my($This, $VertexID, $Name) = @_; 1163 1164 if (exists $This->{Properties}->{Vertices}->{$VertexID}) { 1165 if (defined $Name) { 1166 if (exists $This->{Properties}->{Vertices}->{$VertexID}->{$Name}) { 1167 delete $This->{Properties}->{Vertices}->{$VertexID}->{$Name}; 1168 } 1169 } 1170 else { 1171 %{$This->{Properties}->{Vertices}->{$VertexID}} = (); 1172 } 1173 if (! keys %{$This->{Properties}->{Vertices}->{$VertexID}}) { 1174 delete $This->{Properties}->{Vertices}->{$VertexID}; 1175 } 1176 } 1177 return $This; 1178 } 1179 1180 # Delete a property for specified vertices or all the vertices... 1181 sub DeleteVerticesProperty { 1182 my($This, $Name, @VertexIDs) = @_; 1183 1184 if (!defined $Name) { 1185 carp "Warning: ${ClassName}->DeleteVerticesProperty: Didn't delete property: Property name should be specified..."; 1186 return undef; 1187 } 1188 if (!@VertexIDs) { 1189 @VertexIDs = (); 1190 @VertexIDs = $This->GetVertices(); 1191 } 1192 my($VertexID); 1193 for $VertexID (@VertexIDs) { 1194 $This->DeleteVertexProperty($Name, $VertexID); 1195 } 1196 1197 return $This; 1198 } 1199 1200 # Delete all properities for vertex... 1201 sub DeleteVertexProperties { 1202 my($This, $VertexID) = @_; 1203 1204 if (!defined $VertexID) { 1205 carp "Warning: ${ClassName}->DeleteVertexProperties: Didn't delete properties: Vertex ID must be defined..."; 1206 return undef; 1207 } 1208 $This->_DeleteVertexProperty($VertexID); 1209 1210 return $This; 1211 } 1212 1213 # Is this property associated with vertex? 1214 sub HasVertexProperty { 1215 my($This, $Name, $VertexID) = @_; 1216 1217 if (!(defined($Name) && defined($VertexID))) { 1218 return 0; 1219 } 1220 1221 if (exists $This->{Properties}->{Vertices}->{$VertexID}) { 1222 if (exists $This->{Properties}->{Vertices}->{$VertexID}->{$Name}) { 1223 return 1; 1224 } 1225 } 1226 return 0; 1227 } 1228 1229 # Set a property for edge... 1230 sub SetEdgeProperty { 1231 my($This, $Name, $Value, $VertexID1, $VertexID2) = @_; 1232 1233 if (!(defined($Name) && defined($Value) && defined($VertexID1) && defined($VertexID2))) { 1234 carp "Warning: ${ClassName}->SetEdgeProperty: Didn't set property: Property name, value, vertexID1 and vertexID2 should be specified..."; 1235 return undef; 1236 } 1237 if (!$This->HasEdge($VertexID1, $VertexID2)) { 1238 carp "Warning: ${ClassName}->SetEdgeProperty: Didn't set property $Name for edge between vertices $VertexID1 and $VertexID2: Edge doesn't exist..."; 1239 return undef; 1240 } 1241 if ($This->HasEdgeProperty($Name, $VertexID1, $VertexID2)) { 1242 carp "Warning: ${ClassName}->SetEdgeProperty: Didn't set property $Name for edge between vertices $VertexID1 and $VertexID2: Edge property already exists..."; 1243 return undef; 1244 } 1245 $This->_SetEdgeProperty($Name, $Value, $VertexID1, $VertexID2); 1246 $This->_SetEdgeProperty($Name, $Value, $VertexID2, $VertexID1); 1247 1248 return $This; 1249 } 1250 1251 # Update a property for edge... 1252 sub UpdateEdgeProperty { 1253 my($This, $Name, $Value, $VertexID1, $VertexID2) = @_; 1254 1255 if (!(defined($Name) && defined($Value) && defined($VertexID1) && defined($VertexID2))) { 1256 carp "Warning: ${ClassName}->UpdateEdgeProperty: Didn't update property: Property name, value, vertexID1 and vertexID2 should be specified..."; 1257 return undef; 1258 } 1259 if (!$This->HasEdge($VertexID1, $VertexID2)) { 1260 carp "Warning: ${ClassName}->UpdateEdgeProperty: Didn't update property $Name for edge between vertices $VertexID1 and $VertexID2: Edge doesn't exist..."; 1261 return undef; 1262 } 1263 if (!$This->HasEdgeProperty($Name, $VertexID1, $VertexID2)) { 1264 carp "Warning: ${ClassName}->UpdateEdgeProperty: Didn't update property $Name for edge between vertices $VertexID1 and $VertexID2: Edge property doesn't exists..."; 1265 return undef; 1266 } 1267 $This->_SetEdgeProperty($Name, $Value, $VertexID1, $VertexID2); 1268 $This->_SetEdgeProperty($Name, $Value, $VertexID2, $VertexID1); 1269 1270 return $This; 1271 } 1272 # Set a property for edges... 1273 sub SetEdgesProperty { 1274 my($This, $Name, @ValuesAndVertexIDs) = @_; 1275 1276 if (!defined $Name) { 1277 carp "Warning: ${ClassName}->SetEdgesProperty: Didn't set property: Property name should be specified..."; 1278 return undef; 1279 } 1280 if (!@ValuesAndVertexIDs) { 1281 carp "Warning: ${ClassName}->SetEdgesProperty: Didn't set property: Values and vertex IDs list is empty..."; 1282 return undef; 1283 } 1284 if (@ValuesAndVertexIDs % 3) { 1285 carp "Warning: ${ClassName}->SetEdgesProperty: Didn't set property: Invalid property values and vertex IDs data: Input list must contain triplets of values..."; 1286 return undef; 1287 } 1288 1289 my($Value, $VertexID1, $VertexID2, $Index); 1290 for ($Index = 0; $Index < $#ValuesAndVertexIDs; $Index += 3) { 1291 $Value = $ValuesAndVertexIDs[$Index]; 1292 $VertexID1 = $ValuesAndVertexIDs[$Index + 1]; $VertexID2 = $ValuesAndVertexIDs[$Index + 2]; 1293 $This->SetEdgeProperty($Name, $Value, $VertexID1, $VertexID2); 1294 } 1295 1296 return $This; 1297 } 1298 1299 # Set a properties for a edge... 1300 sub SetEdgeProperties { 1301 my($This, $VertexID1, $VertexID2, @NamesAndValues) = @_; 1302 1303 if (!(defined($VertexID1) && defined($VertexID2))) { 1304 carp "Warning: ${ClassName}->SetEdgeProperties: Didn't set property: Both vertexID1 and vertexID2 should be specified..."; 1305 return undef; 1306 } 1307 if (!@NamesAndValues) { 1308 carp "Warning: ${ClassName}->SetEdgeProperties: Didn't set property: Property name and values ist is empty..."; 1309 return undef; 1310 } 1311 if (@NamesAndValues % 2) { 1312 carp "Warning: ${ClassName}->SetEdgeProperties: Didn't set property: Invalid property name and values data: Input list must contain triplets of values..."; 1313 return undef; 1314 } 1315 1316 my($Name, $Value, $Index); 1317 for ($Index = 0; $Index < $#NamesAndValues; $Index += 2) { 1318 $Name = $NamesAndValues[$Index]; 1319 $Value = $NamesAndValues[$Index + 1]; 1320 $This->SetEdgeProperty($Name, $Value, $VertexID1, $VertexID2); 1321 } 1322 1323 return $This; 1324 } 1325 1326 # Set edge property... 1327 sub _SetEdgeProperty { 1328 my($This, $Name, $Value, $VertexID1, $VertexID2) = @_; 1329 1330 if (!exists $This->{Properties}->{Edges}->{$VertexID1}) { 1331 %{$This->{Properties}->{Edges}->{$VertexID1}} = (); 1332 } 1333 if (!exists $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}) { 1334 %{$This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}} = (); 1335 } 1336 $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}->{$Name} = $Value; 1337 1338 return $This; 1339 } 1340 1341 # Get a property value for edge... 1342 sub GetEdgeProperty { 1343 my($This, $Name, $VertexID1, $VertexID2) = @_; 1344 1345 if (!$This->HasEdgeProperty($Name, $VertexID1, $VertexID2)) { 1346 return undef; 1347 } 1348 return $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}->{$Name}; 1349 } 1350 1351 # Get a property values for edges... 1352 sub GetEdgesProperty { 1353 my($This, $Name, @VertexIDs) = @_; 1354 1355 if (!@VertexIDs) { 1356 @VertexIDs = (); 1357 @VertexIDs = $This->GetEdges(); 1358 } 1359 if (@VertexIDs % 2) { 1360 return undef; 1361 } 1362 1363 my($VertexID1, $VertexID2, $Index, $ValuesCount, @Values); 1364 @Values = (); 1365 for ($Index = 0; $Index < $#VertexIDs; $Index += 2) { 1366 $VertexID1 = $VertexIDs[$Index]; $VertexID2 = $VertexIDs[$Index + 1]; 1367 push @Values, $This->GetEdgeProperty($Name, $VertexID1, $VertexID2); 1368 } 1369 $ValuesCount = grep { defined($_) } @Values; 1370 1371 return wantarray ? @Values : $ValuesCount; 1372 } 1373 1374 # Get all property name/value paries for edge... 1375 sub GetEdgeProperties { 1376 my($This, $VertexID1, $VertexID2) = @_; 1377 1378 if (!(defined($VertexID1) && defined($VertexID2))) { 1379 return (); 1380 } 1381 if (!exists $This->{Properties}->{Edges}->{$VertexID1}) { 1382 return (); 1383 } 1384 if (!exists $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}) { 1385 return (); 1386 } 1387 return %{$This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}}; 1388 } 1389 1390 # Delete a property for edge... 1391 sub DeleteEdgeProperty { 1392 my($This, $Name, $VertexID1, $VertexID2) = @_; 1393 1394 if (!(defined($Name) && defined($VertexID1) && defined($VertexID2))) { 1395 carp "Warning: ${ClassName}->DeleteEdgeProperty: Didn't delete property: Property name, vertexID1 and vertexID2 should be specified..."; 1396 return undef; 1397 } 1398 if (!$This->HasEdgeProperty($Name, $VertexID1, $VertexID2)) { 1399 carp "Warning: ${ClassName}->DeleteEdgeProperty: Didn't delete property $Name for edge between vertices $VertexID1 and $VertexID2: Edge property doesn't exist..."; 1400 return undef; 1401 } 1402 $This->_DeleteEdgeProperty($VertexID1, $VertexID2, $Name); 1403 $This->_DeleteEdgeProperty($VertexID2, $VertexID1, $Name); 1404 1405 return $This; 1406 } 1407 1408 # Delete a property for edges... 1409 sub DeleteEdgesProperty { 1410 my($This, $Name, @VertexIDs) = @_; 1411 1412 if (!defined $Name) { 1413 carp "Warning: ${ClassName}->DeleteEdgesProperty: Didn't delete property: Property name should be specified..."; 1414 return undef; 1415 } 1416 if (!@VertexIDs) { 1417 @VertexIDs = (); 1418 @VertexIDs = $This->GetEdges(); 1419 } 1420 if (@VertexIDs % 2) { 1421 carp "Warning: ${ClassName}->DeleteEdgesProperty: Didn't set property: Invalid property values and vertex IDs data: Input list must contain even number of values..."; 1422 return undef; 1423 } 1424 my($Index, $VertexID1, $VertexID2); 1425 for ($Index = 0; $Index < $#VertexIDs; $Index += 2) { 1426 $VertexID1 = $VertexIDs[$Index]; $VertexID2 = $VertexIDs[$Index + 1]; 1427 $This->DeleteEdgeProperty($Name, $VertexID1, $VertexID2); 1428 } 1429 1430 return $This; 1431 } 1432 1433 # Delete all properties for edge... 1434 sub DeleteEdgeProperties { 1435 my($This, $VertexID1, $VertexID2) = @_; 1436 1437 if (!(defined($VertexID1) && defined($VertexID2))) { 1438 carp "Warning: ${ClassName}->DeleteEdgeProperties: Didn't delete property: VertexID1 and vertexID2 should be specified..."; 1439 return undef; 1440 } 1441 $This->_DeleteEdgeProperty($VertexID1, $VertexID2); 1442 $This->_DeleteEdgeProperty($VertexID2, $VertexID1); 1443 1444 return $This; 1445 } 1446 1447 # Delete properties for edges... 1448 sub DeleteEdgesProperties { 1449 my($This, @VertexIDs) = @_; 1450 1451 if (!@VertexIDs) { 1452 @VertexIDs = (); 1453 @VertexIDs = $This->GetEdges(); 1454 } 1455 if (@VertexIDs % 2) { 1456 carp "Warning: ${ClassName}->DeleteEdgesProperty: Didn't set property: Invalid property values and vertex IDs data: Input list must contain even number of values..."; 1457 return undef; 1458 } 1459 my($Index, $VertexID1, $VertexID2); 1460 for ($Index = 0; $Index < $#VertexIDs; $Index += 2) { 1461 $VertexID1 = $VertexIDs[$Index]; $VertexID2 = $VertexIDs[$Index + 1]; 1462 $This->DeleteEdgeProperties($VertexID1, $VertexID2); 1463 } 1464 return $This; 1465 } 1466 1467 1468 # Delete a specific edge property or all edge properties... 1469 sub _DeleteEdgeProperty { 1470 my($This, $VertexID1, $VertexID2, $Name) = @_; 1471 1472 if (exists $This->{Properties}->{Edges}->{$VertexID1}) { 1473 if (exists $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}) { 1474 if (defined $Name) { 1475 if (exists $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}->{$Name}) { 1476 delete $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}->{$Name}; 1477 } 1478 } 1479 else { 1480 %{$This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}} = (); 1481 } 1482 if (! keys %{$This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}}) { 1483 delete $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}; 1484 } 1485 } 1486 if (! keys %{$This->{Properties}->{Edges}->{$VertexID1}}) { 1487 delete $This->{Properties}->{Edges}->{$VertexID1}; 1488 } 1489 } 1490 1491 return $This; 1492 } 1493 1494 # Is this property associated with edge? 1495 sub HasEdgeProperty { 1496 my($This, $Name, $VertexID1, $VertexID2) = @_; 1497 1498 if (!(defined($Name) && defined($VertexID1) && defined($VertexID2))) { 1499 return 0; 1500 } 1501 my($Status); 1502 1503 $Status = ($This->_HasEdgeProperty($Name, $VertexID1, $VertexID2) || $This->_HasEdgeProperty($Name, $VertexID2, $VertexID1)) ? 1 : 0; 1504 1505 return $Status; 1506 } 1507 1508 # Does edge proprty exists? 1509 sub _HasEdgeProperty { 1510 my($This, $Name, $VertexID1, $VertexID2) = @_; 1511 1512 if (exists $This->{Properties}->{Edges}->{$VertexID1}) { 1513 if (exists $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}) { 1514 if (exists $This->{Properties}->{Edges}->{$VertexID1}->{$VertexID2}->{$Name}) { 1515 return 1; 1516 } 1517 } 1518 } 1519 return 0; 1520 } 1521 1522 # Is it a graph object? 1523 sub IsGraph ($) { 1524 my($Object) = @_; 1525 1526 return _IsGraph($Object); 1527 } 1528 1529 # Return a string containg vertices, edges and other properties... 1530 sub StringifyGraph { 1531 my($This) = @_; 1532 my($GraphString); 1533 1534 $GraphString = 'Graph:' . $This->StringifyVerticesAndEdges() . '; ' . $This->StringifyProperties(); 1535 1536 return $GraphString; 1537 } 1538 1539 # Return a string containg vertices, edges and other properties... 1540 sub StringifyProperties { 1541 my($This) = @_; 1542 my($PropertiesString); 1543 1544 $PropertiesString = $This->StringifyGraphProperties() . '; ' . $This->StringifyVerticesProperties(). '; ' . $This->StringifyEdgesProperties(); 1545 1546 return $PropertiesString; 1547 } 1548 1549 # Return a string containg vertices and edges... 1550 sub StringifyVerticesAndEdges { 1551 my($This) = @_; 1552 my($GraphString, $Index, $VertexID, $VertexID1, $VertexID2, $Count, @EdgeVertexIDs, @VertexIDs); 1553 1554 # Get vertices and edges... 1555 $GraphString = ''; 1556 @VertexIDs = (); 1557 @VertexIDs = $This->GetVertices(); 1558 $Count = 0; 1559 for $VertexID (@VertexIDs) { 1560 $Count++; 1561 @EdgeVertexIDs = (); 1562 @EdgeVertexIDs = $This->_GetEdgesFrom($VertexID); 1563 if (@EdgeVertexIDs) { 1564 for ($Index = 0; $Index < $#EdgeVertexIDs; $Index += 2) { 1565 $VertexID1 = $EdgeVertexIDs[$Index]; $VertexID2 = $EdgeVertexIDs[$Index + 1]; 1566 $GraphString .= " ${VertexID1}-${VertexID2}"; 1567 } 1568 } 1569 else { 1570 $GraphString .= " ${VertexID}"; 1571 } 1572 } 1573 if (!$Count) { 1574 $GraphString = 'Graph: None'; 1575 } 1576 return $GraphString; 1577 } 1578 1579 # Return a string containg graph properties... 1580 sub StringifyGraphProperties { 1581 my($This) = @_; 1582 my($Name, $Value, $Count, $GraphPropertyString, %GraphProperties); 1583 1584 $GraphPropertyString = "GraphProperties: "; 1585 %GraphProperties = (); 1586 %GraphProperties = $This->GetGraphProperties(); 1587 if (keys %GraphProperties) { 1588 for $Name (sort keys %GraphProperties) { 1589 $Value = $GraphProperties{$Name}; 1590 if (ref($Value) =~ /^ARRAY/) { 1591 if (@{$Value}) { 1592 $GraphPropertyString .= " ${Name}=(Count: " . scalar @{$Value} . "; " . join(', ', @{$Value}) . ")"; 1593 } 1594 else { 1595 $GraphPropertyString .= " ${Name}=None"; 1596 } 1597 } 1598 else { 1599 $GraphPropertyString .= " ${Name}=${Value}"; 1600 } 1601 } 1602 } 1603 else { 1604 $GraphPropertyString .= " None"; 1605 } 1606 return $GraphPropertyString; 1607 } 1608 1609 # Return a string containg vertices properties... 1610 sub StringifyVerticesProperties { 1611 my($This) = @_; 1612 my($Name, $Value, $Count, $VertexPropertyString, $VertexID, @VertexIDs, %VertexProperties); 1613 1614 @VertexIDs = (); 1615 @VertexIDs = $This->GetVertices(); 1616 $Count = 0; 1617 $VertexPropertyString = "VertexProperties:"; 1618 for $VertexID (@VertexIDs) { 1619 %VertexProperties = (); 1620 %VertexProperties = $This->GetVertexProperties($VertexID); 1621 if (keys %VertexProperties) { 1622 $Count++; 1623 $VertexPropertyString .= " <Vertex ${VertexID}: "; 1624 for $Name (sort keys %VertexProperties) { 1625 $Value = $VertexProperties{$Name}; 1626 if (ref($Value) =~ /^ARRAY/) { 1627 if (@{$Value}) { 1628 $VertexPropertyString .= " ${Name}=(" . join(', ', @{$Value}) . ")"; 1629 } 1630 else { 1631 $VertexPropertyString .= " ${Name}=None"; 1632 } 1633 } 1634 else { 1635 $VertexPropertyString .= " ${Name}=${Value}"; 1636 } 1637 } 1638 $VertexPropertyString .= ">"; 1639 } 1640 } 1641 if (!$Count) { 1642 $VertexPropertyString = "VertexProperties: None"; 1643 } 1644 return $VertexPropertyString; 1645 } 1646 1647 # Return a string containg edges properties... 1648 sub StringifyEdgesProperties { 1649 my($This) = @_; 1650 my($Name, $Value, $Index, $EdgePropertyString, $Count, $VertexID, $VertexID1, $VertexID2, @EdgesVertexIDs, %EdgeProperties); 1651 1652 @EdgesVertexIDs = (); 1653 @EdgesVertexIDs = $This->GetEdges(); 1654 $Count = 0; 1655 $EdgePropertyString = "EdgeProperties:"; 1656 for ($Index = 0; $Index < $#EdgesVertexIDs; $Index += 2) { 1657 $VertexID1 = $EdgesVertexIDs[$Index]; $VertexID2 = $EdgesVertexIDs[$Index + 1]; 1658 %EdgeProperties = (); 1659 %EdgeProperties = $This->GetEdgeProperties($VertexID1, $VertexID2); 1660 if (keys %EdgeProperties) { 1661 $Count++; 1662 $EdgePropertyString .= " <Edge ${VertexID1}-${VertexID2}:"; 1663 for $Name (sort keys %EdgeProperties) { 1664 $Value = $EdgeProperties{$Name}; 1665 if (ref($Value) =~ /^ARRAY/) { 1666 if (@{$Value}) { 1667 $EdgePropertyString .= " ${Name}=(" . join(', ', @{$Value}) . ")"; 1668 } 1669 else { 1670 $EdgePropertyString .= " ${Name}=None"; 1671 } 1672 } 1673 else { 1674 $EdgePropertyString .= " ${Name}=${Value}"; 1675 } 1676 } 1677 $EdgePropertyString .= ">"; 1678 } 1679 } 1680 if (!$Count) { 1681 $EdgePropertyString = "EdgeProperties: None"; 1682 } 1683 1684 return $EdgePropertyString; 1685 } 1686 1687 # Is it a graph object? 1688 sub _IsGraph { 1689 my($Object) = @_; 1690 1691 return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0; 1692 } 1693 1694 # Copy graph and its associated data using Storable::dclone and return a new graph... 1695 # 1696 sub Copy { 1697 my($This) = @_; 1698 my($NewGraph); 1699 1700 $NewGraph = Storable::dclone($This); 1701 1702 return $NewGraph; 1703 } 1704 1705 # Copy vertrices and edges from This graph to NewGraph specified... 1706 # 1707 sub CopyVerticesAndEdges { 1708 my($This, $NewGraph) = @_; 1709 1710 # Copy vertices and edges... 1711 my(@Vertices, @Edges); 1712 @Vertices = $This->GetVertices(); 1713 if (@Vertices) { 1714 $NewGraph->AddVertices(@Vertices); 1715 } 1716 @Edges = $This->GetEdges(); 1717 if (@Edges) { 1718 $NewGraph->AddEdges(@Edges); 1719 } 1720 1721 return $NewGraph; 1722 } 1723 1724 # Copy properties of vertices from This graph to NewGraph specified... 1725 # 1726 sub CopyVerticesProperties { 1727 my($This, $NewGraph) = @_; 1728 1729 my($VertexID, @VertexIDs, %VertexProperties); 1730 @VertexIDs = (); 1731 @VertexIDs = $This->GetVertices(); 1732 for $VertexID (@VertexIDs) { 1733 %VertexProperties = (); %VertexProperties = $This->GetVertexProperties($VertexID); 1734 if (keys %VertexProperties) { 1735 $NewGraph->SetVertexProperties($VertexID, %VertexProperties); 1736 } 1737 } 1738 return $NewGraph; 1739 } 1740 1741 # Copy properties of edges from This graph to NewGraph specified... 1742 # 1743 sub CopyEdgesProperties { 1744 my($This, $NewGraph) = @_; 1745 1746 my($Index, $VertexID1, $VertexID2, @EdgesVertexIDs, %EdgeProperties); 1747 @EdgesVertexIDs = (); 1748 @EdgesVertexIDs = $This->GetEdges(); 1749 for ($Index = 0; $Index < $#EdgesVertexIDs; $Index += 2) { 1750 $VertexID1 = $EdgesVertexIDs[$Index]; $VertexID2 = $EdgesVertexIDs[$Index + 1]; 1751 %EdgeProperties = (); %EdgeProperties = $This->GetEdgeProperties($VertexID1, $VertexID2); 1752 if (keys %EdgeProperties) { 1753 $NewGraph->SetEdgeProperties($VertexID1, $VertexID2, %EdgeProperties); 1754 } 1755 } 1756 return $NewGraph; 1757 } 1758 1759 # Detect cycles and associate 'em to graph as graph property... 1760 # 1761 # Note: 1762 # . CyclesDetection class detects all cycles in the graph and filters 'em to find 1763 # independent cycles. 1764 # . All cycles related methods in the graph operate on active cyclic paths. By default, 1765 # active cyclic paths correspond to independent cycles. This behavior can be changed 1766 # using SetActiveCyclicPaths method. 1767 # . For topologically complex graphs containing large number of cycles, DetectCycles method 1768 # implemented in CyclesDetection can time out in which case no cycles are detected or 1769 # assigned. 1770 # 1771 sub DetectCycles { 1772 my($This) = @_; 1773 my($CyclesDetection); 1774 1775 # Delete existing graph cycles... 1776 $This->_DeleteCyclesAssociatedWithGraph(); 1777 1778 # Detect cycles... 1779 $CyclesDetection = new Graph::CyclesDetection($This); 1780 if (!$CyclesDetection->DetectCycles()) { 1781 # Cycles detection didn't finish... 1782 return undef; 1783 } 1784 1785 # Get cycles and associate 'em to graph as properties... 1786 my(@AllCyclicPaths, @IndependentCyclicPaths); 1787 @AllCyclicPaths = $CyclesDetection->GetAllCyclicPaths(); 1788 @IndependentCyclicPaths = $CyclesDetection->GetIndependentCyclicPaths(); 1789 1790 $This->SetGraphProperty('ActiveCyclicPaths', \@IndependentCyclicPaths); 1791 $This->SetGraphProperty('AllCyclicPaths', \@AllCyclicPaths); 1792 $This->SetGraphProperty('IndependentCyclicPaths', \@IndependentCyclicPaths); 1793 1794 # Map cycles information to vertices and edges; identify fused cycles as well... 1795 return $This->_ProcessDetectedCycles(); 1796 } 1797 1798 # Delete any cycle properties assigned to graph, vertices and edges during detect cycles operation... 1799 # 1800 sub ClearCycles { 1801 my($This) = @_; 1802 1803 # Delete cycle properties associated with graph... 1804 $This->_DeleteCyclesAssociatedWithGraph(); 1805 $This->_DeleteFusedCyclesAssociatedWithGraph(); 1806 1807 # Delete cycle properties associated with vertices and edges... 1808 $This->_DeleteCyclesAssociatedWithVertices(); 1809 $This->_DeleteCyclesAssociatedWithEdges(); 1810 1811 return $This; 1812 } 1813 1814 # Setup cyclic paths to use during all cycle related methods. Possible values: 1815 # Independent or All. Default is to use Independent cyclic paths. 1816 # 1817 sub SetActiveCyclicPaths { 1818 my($This, $CyclicPathsType) = @_; 1819 1820 if (!defined $CyclicPathsType) { 1821 carp "Warning: ${ClassName}->SetActiveCyclicPaths: Didn't set active cyclic path: Cyclic path must be specified..."; 1822 return undef; 1823 } 1824 if ($CyclicPathsType !~ /^(Independent|All)$/i) { 1825 carp "Warning: ${ClassName}->SetActiveCyclicPaths: Didn't set active cyclic path: Specified path type, $CyclicPathsType, is not valid. Supported valeus: Independent or All..."; 1826 return undef; 1827 } 1828 if (!$This->HasGraphProperty('ActiveCyclicPaths')) { 1829 carp "Warning: ${ClassName}->SetActiveCyclicPaths: Didn't set active cyclic path: Cycles haven't been detected yet..."; 1830 return undef; 1831 } 1832 $This->DeleteGraphProperty('ActiveCyclicPaths'); 1833 1834 my($ActiveCyclicPathsRef); 1835 if ($CyclicPathsType =~ /^Independent$/i) { 1836 $ActiveCyclicPathsRef = $This->GetGraphProperty('IndependentCyclicPaths'); 1837 } 1838 elsif ($CyclicPathsType =~ /^All$/i) { 1839 $ActiveCyclicPathsRef = $This->GetGraphProperty('AllCyclicPaths'); 1840 } 1841 $This->SetGraphProperty('ActiveCyclicPaths', $ActiveCyclicPathsRef); 1842 1843 # Map cycles information to vertices and edges; identify fused cycles as well... 1844 $This->_ProcessDetectedCycles(); 1845 1846 return $This; 1847 } 1848 1849 # Assign cycles information on to vertices and edges as vertex edge properties properties; 1850 # identify fused cycles as well... 1851 # 1852 sub _ProcessDetectedCycles { 1853 my($This) = @_; 1854 1855 $This->_AssociateCyclesWithVertices(); 1856 $This->_AssociateCyclesWithEdgesAndIdentifyFusedCycles(); 1857 1858 return $This; 1859 } 1860 1861 # Associate cycles information to vertices as vertex properties... 1862 # 1863 sub _AssociateCyclesWithVertices { 1864 my($This) = @_; 1865 1866 # Clear up any exisiting properties... 1867 $This->_DeleteCyclesAssociatedWithVertices(); 1868 1869 # Collects CyclicPaths for each vertex... 1870 my($VertexID, $ActiveCyclicPath, $ActiveCyclicPathsRef, @CyclicPathVertexIDs, %VertexIDToCylicPaths); 1871 1872 %VertexIDToCylicPaths = (); 1873 $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); 1874 1875 if (!@{$ActiveCyclicPathsRef}) { 1876 # No cycles out there... 1877 return $This; 1878 } 1879 1880 for $ActiveCyclicPath (@{$ActiveCyclicPathsRef}) { 1881 @CyclicPathVertexIDs = (); 1882 @CyclicPathVertexIDs = $ActiveCyclicPath->GetVertices(); 1883 # Take out end vertex: It's same as start vertex for cyclic path... 1884 pop @CyclicPathVertexIDs; 1885 for $VertexID (@CyclicPathVertexIDs) { 1886 if (!exists $VertexIDToCylicPaths{$VertexID}) { 1887 @{$VertexIDToCylicPaths{$VertexID}} = (); 1888 } 1889 push @{$VertexIDToCylicPaths{$VertexID}}, $ActiveCyclicPath; 1890 } 1891 } 1892 1893 # Associate CyclicPaths to vertices... 1894 for $VertexID (keys %VertexIDToCylicPaths) { 1895 $This->SetVertexProperty('ActiveCyclicPaths', \@{$VertexIDToCylicPaths{$VertexID}}, $VertexID); 1896 } 1897 return $This; 1898 } 1899 1900 # Associate cycles information to edges as edge properties and identify fused 1901 # cycles... 1902 # 1903 sub _AssociateCyclesWithEdgesAndIdentifyFusedCycles { 1904 my($This) = @_; 1905 1906 # Delete existing cycles... 1907 $This->_DeleteCyclesAssociatedWithEdges(); 1908 $This->_DeleteFusedCyclesAssociatedWithGraph(); 1909 1910 # Collect cyclic paths for each edge... 1911 my($Index, $VertexID1, $VertexID2, $ActiveCyclicPath, $ActiveCyclicPathsRef, $EdgeID, $EdgeIDDelimiter, $CyclesWithCommonEdgesPresent, @CyclicPathEdgeVertexIDs, %EdgeIDToCylicPaths); 1912 1913 %EdgeIDToCylicPaths = (); 1914 $EdgeIDDelimiter = "~"; 1915 $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); 1916 1917 if (!@{$ActiveCyclicPathsRef}) { 1918 # No cycles out there... 1919 return $This; 1920 } 1921 1922 $CyclesWithCommonEdgesPresent = 0; 1923 for $ActiveCyclicPath (@{$ActiveCyclicPathsRef}) { 1924 @CyclicPathEdgeVertexIDs = (); 1925 @CyclicPathEdgeVertexIDs = $ActiveCyclicPath->GetEdges(); 1926 for ($Index = 0; $Index < $#CyclicPathEdgeVertexIDs; $Index += 2) { 1927 $VertexID1 = $CyclicPathEdgeVertexIDs[$Index]; $VertexID2 = $CyclicPathEdgeVertexIDs[$Index + 1]; 1928 $EdgeID = ($VertexID1 < $VertexID2) ? "${VertexID1}${EdgeIDDelimiter}${VertexID2}" : "${VertexID2}${EdgeIDDelimiter}${VertexID1}"; 1929 if (exists $EdgeIDToCylicPaths{$EdgeID}) { 1930 # A common edge between two cycles indicates a potential fused cycle... 1931 $CyclesWithCommonEdgesPresent = 1; 1932 } 1933 else { 1934 @{$EdgeIDToCylicPaths{$EdgeID}} = (); 1935 } 1936 push @{$EdgeIDToCylicPaths{$EdgeID}}, $ActiveCyclicPath; 1937 } 1938 } 1939 1940 # Associate CyclicPaths with edges... 1941 for $EdgeID (keys %EdgeIDToCylicPaths) { 1942 ($VertexID1, $VertexID2) = split($EdgeIDDelimiter, $EdgeID); 1943 $This->SetEdgeProperty('ActiveCyclicPaths', \@{$EdgeIDToCylicPaths{$EdgeID}}, $VertexID1, $VertexID2); 1944 } 1945 1946 if ($CyclesWithCommonEdgesPresent) { 1947 # Identify fused cycles... 1948 $This->_IdentifyAndAssociateFusedCyclesWithGraph(); 1949 } 1950 1951 return $This; 1952 } 1953 1954 # Identify fused cycles and associate them to graph as graph property after cycles 1955 # have been associated with edges... 1956 # 1957 # Note: 1958 # . During aromaticity detection, fused cycles are treated as one set for counting 1959 # number of available pi electrons to check against Huckel's rule. 1960 # . Fused cylce sets contain cycles with at least one common edge between pair 1961 # of cycles. A specific pair of cycles might not have a direct common edge, but 1962 # ends up in the same set due to a common edge with another cycle. 1963 # . Fused cycles are attached to graph as 'FusedActiveCyclicPaths' property with 1964 # its value as a reference to list of reference where each refernece corresponds 1965 # to a list of cyclic path objects in a fused set. 1966 # . For graphs containing fused cycles, non-fused cycles are separeted from fused 1967 # cycles and attached to the graph as 'NonFusedActiveCyclicPaths'. It's a reference 1968 # to list containing cylic path objects. 1969 # 1970 sub _IdentifyAndAssociateFusedCyclesWithGraph { 1971 my($This) = @_; 1972 1973 # Delete exisiting fused and non-fused cycles... 1974 $This->_DeleteFusedCyclesAssociatedWithGraph(); 1975 1976 my($ActiveCyclicPathsRef); 1977 $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); 1978 if (!@{$ActiveCyclicPathsRef}) { 1979 # No cycles out there... 1980 return $This; 1981 } 1982 1983 # Get fused cycle pairs... 1984 my($FusedCyclePairsRef, $FusedCyclesRef, $InValidFusedCycleRef); 1985 ($FusedCyclePairsRef, $FusedCyclesRef, $InValidFusedCycleRef) = $This->_GetFusedCyclePairs($ActiveCyclicPathsRef); 1986 1987 # Get fused cycle set indices... 1988 my($FusedCycleSetsIndicesRef, $FusedCycleSetsCommonEdgesRef); 1989 $FusedCycleSetsIndicesRef = $This->_GetFusedCycleSetsIndices($FusedCyclePairsRef, $FusedCyclesRef); 1990 if (!@{$FusedCycleSetsIndicesRef}) { 1991 # No fused cycles out there... 1992 return $This; 1993 } 1994 1995 # Get fused and non-fused cycles... 1996 my($FusedCycleSetsRef, $NonFusedCyclesRef); 1997 ($FusedCycleSetsRef, $NonFusedCyclesRef) = $This->_GetFusedAndNonFusedCycles($ActiveCyclicPathsRef, $FusedCycleSetsIndicesRef, $InValidFusedCycleRef); 1998 if (!@{$FusedCycleSetsRef}) { 1999 # No fused cycles out there... 2000 return $This; 2001 } 2002 2003 # Associate fused and non fused cycles to graph.... 2004 $This->SetGraphProperty('FusedActiveCyclicPaths', $FusedCycleSetsRef); 2005 $This->SetGraphProperty('NonFusedActiveCyclicPaths', $NonFusedCyclesRef); 2006 2007 return $This; 2008 } 2009 2010 # Collect fused cycle pairs... 2011 # 2012 sub _GetFusedCyclePairs { 2013 my($This, $ActiveCyclicPathsRef) = @_; 2014 2015 # Setup a CyclicPathID to CyclicPathIndex map... 2016 my($CyclicPathIndex, $CyclicPathID, $ActiveCyclicPath, %CyclicPathIDToIndex); 2017 2018 %CyclicPathIDToIndex = (); 2019 for $CyclicPathIndex (0 .. $#{$ActiveCyclicPathsRef}) { 2020 $ActiveCyclicPath = $ActiveCyclicPathsRef->[$CyclicPathIndex]; 2021 $CyclicPathID = "$ActiveCyclicPath"; 2022 $CyclicPathIDToIndex{$CyclicPathID} = $CyclicPathIndex; 2023 } 2024 # Go over cycle edges and collect fused cycle pairs... 2025 my($Index, $VertexID1, $VertexID2, $EdgeCyclicPathsRef, $EdgeID, $CyclicPath1, $CyclicPath2, $CyclicPathID1, $CyclicPathID2, $FusedCyclePairID, $FusedCyclicPath1, $FusedCyclicPath2, $FusedCyclicPathID1, $FusedCyclicPathID2, $FusedCyclicPathIndex1, $FusedCyclicPathIndex2, $FusedCyclePairEdgeCount, @CyclicPathEdgeVertexIDs, %FusedCyclePairs, %CommonEdgeVisited, %CommonEdgesCount, %FusedCycles, %InValidFusedCycles); 2026 2027 %FusedCyclePairs = (); %CommonEdgeVisited = (); 2028 %CommonEdgesCount = (); 2029 %InValidFusedCycles = (); %FusedCycles = (); 2030 2031 for $ActiveCyclicPath (@{$ActiveCyclicPathsRef}) { 2032 @CyclicPathEdgeVertexIDs = (); 2033 @CyclicPathEdgeVertexIDs = $ActiveCyclicPath->GetEdges(); 2034 EDGE: for ($Index = 0; $Index < $#CyclicPathEdgeVertexIDs; $Index += 2) { 2035 $VertexID1 = $CyclicPathEdgeVertexIDs[$Index]; $VertexID2 = $CyclicPathEdgeVertexIDs[$Index + 1]; 2036 $EdgeCyclicPathsRef = $This->GetEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2); 2037 if (@{$EdgeCyclicPathsRef} != 2) { 2038 # Not considered a fused edge... 2039 next EDGE; 2040 } 2041 # Set up a fused cycle pair... 2042 ($FusedCyclicPath1, $FusedCyclicPath2) = @{$EdgeCyclicPathsRef}; 2043 ($FusedCyclicPathID1, $FusedCyclicPathID2) = ("${FusedCyclicPath1}", "${FusedCyclicPath2}"); 2044 ($FusedCyclicPathIndex1, $FusedCyclicPathIndex2) = ($CyclicPathIDToIndex{$FusedCyclicPathID1}, $CyclicPathIDToIndex{$FusedCyclicPathID2}); 2045 $FusedCyclePairID = ($FusedCyclicPathIndex1 < $FusedCyclicPathIndex2) ? "${FusedCyclicPathIndex1}-${FusedCyclicPathIndex2}" : "${FusedCyclicPathIndex2}-${FusedCyclicPathIndex1}"; 2046 $EdgeID = ($VertexID1 < $VertexID2) ? "${VertexID1}-${VertexID2}" : "${VertexID2}-${VertexID1}"; 2047 2048 if (exists $FusedCyclePairs{$FusedCyclePairID}) { 2049 if (exists $CommonEdgeVisited{$FusedCyclePairID}{$EdgeID}) { 2050 # Edge already processed... 2051 next EDGE; 2052 } 2053 $CommonEdgeVisited{$FusedCyclePairID}{$EdgeID} = $EdgeID; 2054 2055 $CommonEdgesCount{$FusedCyclePairID} += 1; 2056 push @{$FusedCyclePairs{$FusedCyclePairID}}, $EdgeID; 2057 } 2058 else { 2059 @{$FusedCyclePairs{$FusedCyclePairID}} = (); 2060 push @{$FusedCyclePairs{$FusedCyclePairID}}, $EdgeID; 2061 2062 %{$CommonEdgeVisited{$FusedCyclePairID}} = (); 2063 $CommonEdgeVisited{$FusedCyclePairID}{$EdgeID} = $EdgeID; 2064 2065 $CommonEdgesCount{$FusedCyclePairID} = 1; 2066 } 2067 } 2068 } 2069 # Valid fused cyle in fused cycle pairs must have only one common egde... 2070 for $FusedCyclePairID (keys %FusedCyclePairs) { 2071 ($FusedCyclicPathIndex1, $FusedCyclicPathIndex2) = split /-/, $FusedCyclePairID; 2072 $FusedCycles{$FusedCyclicPathIndex1} = $FusedCyclicPathIndex1; 2073 $FusedCycles{$FusedCyclicPathIndex2} = $FusedCyclicPathIndex2; 2074 if (@{$FusedCyclePairs{$FusedCyclePairID}} != 1) { 2075 # Mark the cycles involved as invalid fused cycles... 2076 $InValidFusedCycles{$FusedCyclicPathIndex1} = $FusedCyclicPathIndex1; 2077 $InValidFusedCycles{$FusedCyclicPathIndex2} = $FusedCyclicPathIndex2; 2078 } 2079 } 2080 return (\%FusedCyclePairs, \%FusedCycles, \%InValidFusedCycles); 2081 } 2082 2083 # Go over fused cycles and set up a graph to collect fused cycle sets. Graph vertices 2084 # correspond to cylce indices; edges correspond to pair of fused cylcles; fused cycle 2085 # sets correspond to connected components. Addionally set up common edges for 2086 # fused cycle sets. 2087 # 2088 sub _GetFusedCycleSetsIndices { 2089 my($This, $FusedCyclePairsRef, $FusedCyclesRef) = @_; 2090 my($FusedCyclesGraph, @FusedCycleIndices, @FusedCyclePairIndices, @FusedCycleSetsIndices); 2091 2092 @FusedCycleIndices = (); @FusedCyclePairIndices = (); 2093 @FusedCycleSetsIndices = (); 2094 2095 @FusedCycleIndices = sort { $a <=> $b } keys %{$FusedCyclesRef}; 2096 @FusedCyclePairIndices = map { split /-/, $_; } keys %{$FusedCyclePairsRef}; 2097 if (!@FusedCycleIndices) { 2098 # No fused cycles out there... 2099 return \@FusedCycleSetsIndices; 2100 } 2101 $FusedCyclesGraph = new Graph(@FusedCycleIndices); 2102 $FusedCyclesGraph->AddEdges(@FusedCyclePairIndices); 2103 2104 @FusedCycleSetsIndices = $FusedCyclesGraph->GetConnectedComponentsVertices(); 2105 2106 return \@FusedCycleSetsIndices; 2107 } 2108 2109 # Go over indices of fused cycle sets and map cyclic path indices to cyclic path objects. 2110 # For fused sets containing a cycle with more than one common edge, the whole set is treated 2111 # as non-fused set... 2112 # 2113 sub _GetFusedAndNonFusedCycles { 2114 my($This, $ActiveCyclicPathsRef, $FusedCycleSetsIndicesRef, $InValidFusedCycleRef) = @_; 2115 my($CycleSetIndicesRef, $CyclicPathIndex, $ValidFusedCycleSet, @FusedCycleSets, @UnsortedNonFusedCycles, @NonFusedCycles, %CycleIndexVisited); 2116 2117 @FusedCycleSets = (); @NonFusedCycles = (); @UnsortedNonFusedCycles = (); 2118 %CycleIndexVisited = (); 2119 for $CycleSetIndicesRef (@{$FusedCycleSetsIndicesRef}) { 2120 # Is it a valid fused cycle set? Fused cycle set containing any cycle with more than one common 2121 # edge is considered invalid and all its cycles are treated as non-fused cycles. 2122 $ValidFusedCycleSet = 1; 2123 for $CyclicPathIndex (@{$CycleSetIndicesRef}) { 2124 $CycleIndexVisited{$CyclicPathIndex} = $CyclicPathIndex; 2125 if (exists $InValidFusedCycleRef->{$CyclicPathIndex}) { 2126 $ValidFusedCycleSet = 0; 2127 } 2128 } 2129 if ($ValidFusedCycleSet) { 2130 my(@FusedCycleSet); 2131 @FusedCycleSet = (); 2132 push @FusedCycleSet, sort { $a->GetLength() <=> $b->GetLength() } map { $ActiveCyclicPathsRef->[$_] } @{$CycleSetIndicesRef}; 2133 push @FusedCycleSets, \@FusedCycleSet; 2134 } 2135 else { 2136 push @UnsortedNonFusedCycles, map { $ActiveCyclicPathsRef->[$_] } @{$CycleSetIndicesRef}; 2137 } 2138 } 2139 2140 # Add any leftover cycles to non-fused cycles list... 2141 CYCLICPATH: for $CyclicPathIndex (0 .. $#{$ActiveCyclicPathsRef}) { 2142 if (exists $CycleIndexVisited{$CyclicPathIndex}) { 2143 next CYCLICPATH; 2144 } 2145 push @UnsortedNonFusedCycles, $ActiveCyclicPathsRef->[$CyclicPathIndex]; 2146 } 2147 @NonFusedCycles = sort { $a->GetLength() <=> $b->GetLength() } @UnsortedNonFusedCycles; 2148 2149 return (\@FusedCycleSets, \@NonFusedCycles); 2150 } 2151 2152 # Delete cycles associated with graph... 2153 # 2154 sub _DeleteCyclesAssociatedWithGraph { 2155 my($This) = @_; 2156 2157 if ($This->HasGraphProperty('ActiveCyclicPaths')) { 2158 $This->DeleteGraphProperty('ActiveCyclicPaths'); 2159 $This->DeleteGraphProperty('AllCyclicPaths'); 2160 $This->DeleteGraphProperty('IndependentCyclicPaths'); 2161 } 2162 return $This; 2163 } 2164 2165 # Delete cycles associated with vertices... 2166 # 2167 sub _DeleteCyclesAssociatedWithVertices { 2168 my($This) = @_; 2169 my($VertexID, @VertexIDs); 2170 2171 @VertexIDs = (); 2172 @VertexIDs = $This->GetVertices(); 2173 for $VertexID (@VertexIDs) { 2174 if ($This->HasVertexProperty('ActiveCyclicPaths', $VertexID)) { 2175 $This->DeleteVertexProperty('ActiveCyclicPaths', $VertexID); 2176 } 2177 } 2178 return $This; 2179 } 2180 2181 # Delete cycles associated with edges... 2182 # 2183 sub _DeleteCyclesAssociatedWithEdges { 2184 my($This) = @_; 2185 my($Index, $VertexID1, $VertexID2, @EdgeVertexIDs); 2186 2187 @EdgeVertexIDs = (); 2188 @EdgeVertexIDs = $This->GetEdges(); 2189 for ($Index = 0; $Index < $#EdgeVertexIDs; $Index += 2) { 2190 $VertexID1 = $EdgeVertexIDs[$Index]; $VertexID2 = $EdgeVertexIDs[$Index + 1]; 2191 if ($This->HasEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2)) { 2192 $This->DeleteEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2); 2193 } 2194 } 2195 return $This; 2196 } 2197 2198 # Delete fused cycles associated with edges... 2199 # 2200 sub _DeleteFusedCyclesAssociatedWithGraph { 2201 my($This) = @_; 2202 2203 # Delete exisiting cycles... 2204 if ($This->HasGraphProperty('FusedActiveCyclicPaths')) { 2205 $This->DeleteGraphProperty('FusedActiveCyclicPaths'); 2206 $This->DeleteGraphProperty('NonFusedActiveCyclicPaths'); 2207 } 2208 return $This; 2209 } 2210 2211 # Does graph contains any cycles? 2212 # 2213 sub IsAcyclic { 2214 my($This) = @_; 2215 2216 return $This->GetNumOfCycles() ? 0 : 1; 2217 } 2218 2219 # Does graph contains cycles? 2220 # 2221 sub IsCyclic { 2222 my($This) = @_; 2223 2224 return $This->GetNumOfCycles() ? 1 : 0; 2225 } 2226 2227 # Does graph contains only any cycle? 2228 # 2229 sub IsUnicyclic { 2230 my($This) = @_; 2231 2232 return ($This->GetNumOfCycles() == 1) ? 1 : 0; 2233 } 2234 2235 # Get size of smallest cycle in graph... 2236 # 2237 sub GetGirth { 2238 my($This) = @_; 2239 2240 return $This->GetSizeOfSmallestCycle(); 2241 } 2242 2243 # Get size of smallest cycle in graph... 2244 # 2245 sub GetSizeOfSmallestCycle { 2246 my($This) = @_; 2247 2248 return $This->_GetCycleSize('GraphCycle', 'SmallestCycle'); 2249 } 2250 2251 # Get size of largest cycle in graph... 2252 # 2253 sub GetCircumference { 2254 my($This) = @_; 2255 2256 return $This->GetSizeOfLargestCycle(); 2257 } 2258 2259 # Get size of largest cycle in graph... 2260 # 2261 sub GetSizeOfLargestCycle { 2262 my($This) = @_; 2263 2264 return $This->_GetCycleSize('GraphCycle', 'LargestCycle'); 2265 } 2266 2267 # Get number of cycles in graph... 2268 # 2269 sub GetNumOfCycles { 2270 my($This) = @_; 2271 2272 return $This->_GetNumOfCyclesWithSize('GraphCycle', 'AllSizes'); 2273 } 2274 2275 # Get number of cycles with odd size in graph... 2276 # 2277 sub GetNumOfCyclesWithOddSize { 2278 my($This) = @_; 2279 2280 return $This->_GetNumOfCyclesWithSize('GraphCycle', 'OddSize'); 2281 } 2282 2283 # Get number of cycles with even size in graph... 2284 # 2285 sub GetNumOfCyclesWithEvenSize { 2286 my($This) = @_; 2287 2288 return $This->_GetNumOfCyclesWithSize('GraphCycle', 'EvenSize'); 2289 } 2290 2291 # Get number of cycles with specific size in graph... 2292 # 2293 sub GetNumOfCyclesWithSize { 2294 my($This, $CycleSize) = @_; 2295 2296 return $This->_GetNumOfCyclesWithSize('GraphCycle', 'SpecifiedSize', $CycleSize); 2297 } 2298 2299 # Get number of cycles with size less than a specific size in graph... 2300 # 2301 sub GetNumOfCyclesWithSizeLessThan { 2302 my($This, $CycleSize) = @_; 2303 2304 return $This->_GetNumOfCyclesWithSize('GraphCycle', 'SizeLessThan', $CycleSize); 2305 } 2306 2307 # Get number of cycles with size greater than a specific size in graph... 2308 # 2309 sub GetNumOfCyclesWithSizeGreaterThan { 2310 my($This, $CycleSize) = @_; 2311 2312 return $This->_GetNumOfCyclesWithSize('GraphCycle', 'SizeGreaterThan', $CycleSize); 2313 } 2314 2315 # Get largest cyclic path in graph... 2316 # 2317 sub GetLargestCycle { 2318 my($This) = @_; 2319 2320 return $This->_GetCycle('GraphCycle', 'LargestCycle'); 2321 } 2322 2323 # Get smallest cyclic path in graph... 2324 # 2325 sub GetSmallestCycle { 2326 my($This) = @_; 2327 2328 return $This->_GetCycle('GraphCycle', 'SmallestCycle'); 2329 } 2330 2331 # Get all cycles in graph... 2332 # 2333 sub GetCycles { 2334 my($This) = @_; 2335 2336 return $This->_GetCyclesWithSize('GraphCycle', 'AllSizes'); 2337 } 2338 2339 # Get cycles with odd size in graph... 2340 # 2341 sub GetCyclesWithOddSize { 2342 my($This) = @_; 2343 2344 return $This->_GetCyclesWithSize('GraphCycle', 'OddSize'); 2345 } 2346 2347 # Get cycles with even size in graph... 2348 # 2349 sub GetCyclesWithEvenSize { 2350 my($This) = @_; 2351 2352 return $This->_GetCyclesWithSize('GraphCycle', 'EvenSize'); 2353 } 2354 2355 # Get cycles with specific size in graph... 2356 # 2357 sub GetCyclesWithSize { 2358 my($This, $CycleSize) = @_; 2359 2360 return $This->_GetCyclesWithSize('GraphCycle', 'SpecifiedSize', $CycleSize); 2361 } 2362 2363 # Get cycles with size less than a specific size in graph... 2364 # 2365 sub GetCyclesWithSizeLessThan { 2366 my($This, $CycleSize) = @_; 2367 2368 return $This->_GetCyclesWithSize('GraphCycle', 'SizeLessThan', $CycleSize); 2369 } 2370 2371 # Get cycles with size greater than a specific size in graph... 2372 # 2373 sub GetCyclesWithSizeGreaterThan { 2374 my($This, $CycleSize) = @_; 2375 2376 return $This->_GetCyclesWithSize('GraphCycle', 'SizeGreaterThan', $CycleSize); 2377 } 2378 2379 # Is vertex in a cycle? 2380 # 2381 sub IsCyclicVertex { 2382 my($This, $VertexID) = @_; 2383 2384 return $This->GetNumOfVertexCycles($VertexID) ? 1 : 0; 2385 } 2386 2387 # Is vertex in a only one cycle? 2388 # 2389 sub IsUnicyclicVertex { 2390 my($This, $VertexID) = @_; 2391 2392 return ($This->GetNumOfVertexCycles($VertexID) == 1) ? 1 : 0; 2393 } 2394 2395 # Is vertex not in a cycle? 2396 # 2397 sub IsAcyclicVertex { 2398 my($This, $VertexID) = @_; 2399 2400 return $This->GetNumOfVertexCycles($VertexID) ? 0 : 1; 2401 } 2402 2403 # Get size of smallest cycle containing specified vertex... 2404 # 2405 sub GetSizeOfSmallestVertexCycle { 2406 my($This, $VertexID) = @_; 2407 2408 return $This->_GetCycleSize('VertexCycle', 'SmallestCycle', $VertexID); 2409 } 2410 2411 # Get size of largest cycle containing specified vertex... 2412 # 2413 sub GetSizeOfLargestVertexCycle { 2414 my($This, $VertexID) = @_; 2415 2416 return $This->_GetCycleSize('VertexCycle', 'LargestCycle', $VertexID); 2417 } 2418 2419 # Get number of cycles containing specified vertex... 2420 # 2421 sub GetNumOfVertexCycles { 2422 my($This, $VertexID) = @_; 2423 2424 return $This->_GetNumOfCyclesWithSize('VertexCycle', 'AllSizes', 0, $VertexID); 2425 } 2426 2427 # Get number of cycles with odd size containing specified vertex... 2428 # 2429 sub GetNumOfVertexCyclesWithOddSize { 2430 my($This, $VertexID) = @_; 2431 2432 return $This->_GetNumOfCyclesWithSize('VertexCycle', 'OddSize', 0, $VertexID); 2433 } 2434 2435 # Get number of cycles with even size containing specified vertex... 2436 # 2437 sub GetNumOfVertexCyclesWithEvenSize { 2438 my($This, $VertexID) = @_; 2439 2440 return $This->_GetNumOfCyclesWithSize('VertexCycle', 'EvenSize', 0, $VertexID); 2441 } 2442 2443 # Get number of cycles with specified size containing specified vertex... 2444 # 2445 sub GetNumOfVertexCyclesWithSize { 2446 my($This, $VertexID, $CycleSize) = @_; 2447 2448 return $This->_GetNumOfCyclesWithSize('VertexCycle', 'SpecifiedSize', $CycleSize, $VertexID); 2449 } 2450 2451 # Get number of cycles with size less than specified size containing specified vertex... 2452 # 2453 sub GetNumOfVertexCyclesWithSizeLessThan { 2454 my($This, $VertexID, $CycleSize) = @_; 2455 2456 return $This->_GetNumOfCyclesWithSize('VertexCycle', 'SizeLessThan', $CycleSize, $VertexID); 2457 } 2458 2459 # Get number of cycles with size greater than specified size containing specified vertex... 2460 # 2461 sub GetNumOfVertexCyclesWithSizeGreaterThan { 2462 my($This, $VertexID, $CycleSize) = @_; 2463 2464 return $This->_GetNumOfCyclesWithSize('VertexCycle', 'SizeGreaterThan', $CycleSize, $VertexID); 2465 } 2466 2467 # Get smallest cycle containing specified vertex... 2468 # 2469 sub GetSmallestVertexCycle { 2470 my($This, $VertexID) = @_; 2471 2472 return $This->_GetCycle('VertexCycle', 'SmallestCycle', $VertexID); 2473 } 2474 2475 # Get largest cycle containing specified vertex... 2476 # 2477 sub GetLargestVertexCycle { 2478 my($This, $VertexID) = @_; 2479 2480 return $This->_GetCycle('VertexCycle', 'LargestCycle', $VertexID); 2481 } 2482 2483 # Get cycles containing specified vertex... 2484 # 2485 sub GetVertexCycles { 2486 my($This, $VertexID) = @_; 2487 2488 return $This->_GetCyclesWithSize('VertexCycle', 'AllSizes', 0, $VertexID); 2489 } 2490 2491 # Get cycles with odd size containing specified vertex... 2492 # 2493 sub GetVertexCyclesWithOddSize { 2494 my($This, $VertexID) = @_; 2495 2496 return $This->_GetCyclesWithSize('VertexCycle', 'OddSize', 0, $VertexID); 2497 } 2498 2499 # Get cycles with even size containing specified vertex... 2500 # 2501 sub GetVertexCyclesWithEvenSize { 2502 my($This, $VertexID) = @_; 2503 2504 return $This->_GetCyclesWithSize('VertexCycle', 'EvenSize', 0, $VertexID); 2505 } 2506 2507 # Get cycles with specified size containing specified vertex... 2508 # 2509 sub GetVertexCyclesWithSize { 2510 my($This, $VertexID, $CycleSize) = @_; 2511 2512 return $This->_GetCyclesWithSize('VertexCycle', 'SpecifiedSize', $CycleSize, $VertexID); 2513 } 2514 2515 # Get cycles with size less than specified size containing specified vertex... 2516 # 2517 sub GetVertexCyclesWithSizeLessThan { 2518 my($This, $VertexID, $CycleSize) = @_; 2519 2520 return $This->_GetCyclesWithSize('VertexCycle', 'SizeLessThan', $CycleSize, $VertexID); 2521 } 2522 2523 # Get cycles with size greater than specified size containing specified vertex... 2524 # 2525 sub GetVertexCyclesWithSizeGreaterThan { 2526 my($This, $VertexID, $CycleSize) = @_; 2527 2528 return $This->_GetCyclesWithSize('VertexCycle', 'SizeGreaterThan', $CycleSize, $VertexID); 2529 } 2530 2531 # Is edge in a cycle? 2532 # 2533 sub IsCyclicEdge { 2534 my($This, $VertexID1, $VertexID2) = @_; 2535 2536 return $This->GetNumOfEdgeCycles($VertexID1, $VertexID2) ? 1 : 0; 2537 } 2538 2539 # Is edge in a only one cycle? 2540 # 2541 sub IsUnicyclicEdge { 2542 my($This, $VertexID1, $VertexID2) = @_; 2543 2544 return ($This->GetNumOfEdgeCycles($VertexID1, $VertexID2) == 1) ? 1 : 0; 2545 } 2546 2547 # Is Edge not in a cycle? 2548 # 2549 sub IsAcyclicEdge { 2550 my($This, $VertexID1, $VertexID2) = @_; 2551 2552 return $This->GetNumOfEdgeCycles($VertexID1, $VertexID2) ? 0 : 1; 2553 } 2554 2555 # Get size of smallest cycle containing specified edge... 2556 # 2557 sub GetSizeOfSmallestEdgeCycle { 2558 my($This, $VertexID1, $VertexID2) = @_; 2559 2560 return $This->_GetCycleSize('EdgeCycle', 'SmallestCycle', $VertexID1, $VertexID2); 2561 } 2562 2563 # Get size of largest cycle containing specified edge... 2564 # 2565 sub GetSizeOfLargestEdgeCycle { 2566 my($This, $VertexID1, $VertexID2) = @_; 2567 2568 return $This->_GetCycleSize('EdgeCycle', 'LargestCycle', $VertexID1, $VertexID2); 2569 } 2570 2571 # Get number of cycles containing specified edge... 2572 # 2573 sub GetNumOfEdgeCycles { 2574 my($This, $VertexID1, $VertexID2) = @_; 2575 2576 return $This->_GetNumOfCyclesWithSize('EdgeCycle', 'AllSizes', 0, $VertexID1, $VertexID2); 2577 } 2578 2579 # Get number of cycles with odd size containing specified edge... 2580 # 2581 sub GetNumOfEdgeCyclesWithOddSize { 2582 my($This, $VertexID1, $VertexID2) = @_; 2583 2584 return $This->_GetNumOfCyclesWithSize('EdgeCycle', 'OddSize', 0, $VertexID1, $VertexID2); 2585 } 2586 2587 # Get number of cycles with even size containing specified edge... 2588 # 2589 sub GetNumOfEdgeCyclesWithEvenSize { 2590 my($This, $VertexID1, $VertexID2) = @_; 2591 2592 return $This->_GetNumOfCyclesWithSize('EdgeCycle', 'EvenSize', 0, $VertexID1, $VertexID2); 2593 } 2594 2595 # Get number of cycles with specified size containing specified edge... 2596 # 2597 sub GetNumOfEdgeCyclesWithSize { 2598 my($This, $VertexID1, $VertexID2, $CycleSize) = @_; 2599 2600 return $This->_GetNumOfCyclesWithSize('EdgeCycle', 'SpecifiedSize', $CycleSize, $VertexID1, $VertexID2); 2601 } 2602 2603 # Get number of cycles with size less than specified size containing specified edge... 2604 # 2605 sub GetNumOfEdgeCyclesWithSizeLessThan { 2606 my($This, $VertexID1, $VertexID2, $CycleSize) = @_; 2607 2608 return $This->_GetNumOfCyclesWithSize('EdgeCycle', 'SizeLessThan', $CycleSize, $VertexID1, $VertexID2); 2609 } 2610 2611 # Get number of cycles with size greater than specified size containing specified edge... 2612 # 2613 sub GetNumOfEdgeCyclesWithSizeGreaterThan { 2614 my($This, $VertexID1, $VertexID2, $CycleSize) = @_; 2615 2616 return $This->_GetNumOfCyclesWithSize('EdgeCycle', 'SizeGreaterThan', $CycleSize, $VertexID1, $VertexID2); 2617 } 2618 2619 # Get smallest cycle containing specified edge... 2620 # 2621 sub GetSmallestEdgeCycle { 2622 my($This, $VertexID1, $VertexID2) = @_; 2623 2624 return $This->_GetCycle('EdgeCycle', 'SmallestCycle', $VertexID1, $VertexID2); 2625 } 2626 2627 # Get largest cycle containing specified edge... 2628 # 2629 sub GetLargestEdgeCycle { 2630 my($This, $VertexID1, $VertexID2) = @_; 2631 2632 return $This->_GetCycle('EdgeCycle', 'LargestCycle', $VertexID1, $VertexID2); 2633 } 2634 2635 # Get cycles containing specified edge... 2636 # 2637 sub GetEdgeCycles { 2638 my($This, $VertexID1, $VertexID2) = @_; 2639 2640 return $This->_GetCyclesWithSize('EdgeCycle', 'AllSizes', 0, $VertexID1, $VertexID2); 2641 } 2642 2643 # Get cycles with odd size containing specified edge... 2644 # 2645 sub GetEdgeCyclesWithOddSize { 2646 my($This, $VertexID1, $VertexID2) = @_; 2647 2648 return $This->_GetCyclesWithSize('EdgeCycle', 'OddSize', 0, $VertexID1, $VertexID2); 2649 } 2650 2651 # Get cycles with even size containing specified edge... 2652 # 2653 sub GetEdgeCyclesWithEvenSize { 2654 my($This, $VertexID1, $VertexID2) = @_; 2655 2656 return $This->_GetCyclesWithSize('EdgeCycle', 'EvenSize', 0, $VertexID1, $VertexID2); 2657 } 2658 2659 # Get cycles with specified size containing specified edge... 2660 # 2661 sub GetEdgeCyclesWithSize { 2662 my($This, $VertexID1, $VertexID2, $CycleSize) = @_; 2663 2664 return $This->_GetCyclesWithSize('EdgeCycle', 'SpecifiedSize', $CycleSize, $VertexID1, $VertexID2); 2665 } 2666 2667 # Get cycles with size less than specified size containing specified edge... 2668 # 2669 sub GetEdgeCyclesWithSizeLessThan { 2670 my($This, $VertexID1, $VertexID2, $CycleSize) = @_; 2671 2672 return $This->_GetCyclesWithSize('EdgeCycle', 'SizeLessThan', $CycleSize, $VertexID1, $VertexID2); 2673 } 2674 2675 # Get cycles with size greater than specified size containing specified edge... 2676 # 2677 sub GetEdgeCyclesWithSizeGreaterThan { 2678 my($This, $VertexID1, $VertexID2, $CycleSize) = @_; 2679 2680 return $This->_GetCyclesWithSize('EdgeCycle', 'SizeGreaterThan', $CycleSize, $VertexID1, $VertexID2); 2681 } 2682 2683 # Get size of specified cycle type... 2684 # 2685 sub _GetCycleSize { 2686 my($This, $Mode, $CycleSize, $VertexID1, $VertexID2) = @_; 2687 my($ActiveCyclicPathsRef, $CyclicPath, $Size); 2688 2689 if (!$This->HasGraphProperty('ActiveCyclicPaths')) { 2690 return 0; 2691 } 2692 if ($Mode =~ /^VertexCycle$/i) { 2693 if (!$This->HasVertexProperty('ActiveCyclicPaths', $VertexID1)) { 2694 return 0; 2695 } 2696 } 2697 elsif ($Mode =~ /^EdgeCycle$/i) { 2698 if (!$This->HasEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2)) { 2699 return 0; 2700 } 2701 } 2702 2703 MODE: { 2704 if ($Mode =~ /^GraphCycle$/i) { $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); last MODE; } 2705 if ($Mode =~ /^VertexCycle$/i) { $ActiveCyclicPathsRef = $This->GetVertexProperty('ActiveCyclicPaths', $VertexID1); last MODE; } 2706 if ($Mode =~ /^EdgeCycle$/i) { $ActiveCyclicPathsRef = $This->GetEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2); last MODE; } 2707 return 0; 2708 } 2709 2710 if (!@{$ActiveCyclicPathsRef}) { 2711 return 0; 2712 } 2713 2714 CYCLESIZE: { 2715 if ($CycleSize =~ /^SmallestCycle$/i) { $CyclicPath = $ActiveCyclicPathsRef->[0]; last CYCLESIZE; } 2716 if ($CycleSize =~ /^LargestCycle$/i) { $CyclicPath = $ActiveCyclicPathsRef->[$#{$ActiveCyclicPathsRef}]; last CYCLESIZE; } 2717 return 0; 2718 } 2719 $Size = $CyclicPath->GetLength() - 1; 2720 2721 return $Size; 2722 } 2723 2724 # Get of specified cycle size... 2725 # 2726 sub _GetCycle { 2727 my($This, $Mode, $CycleSize, $VertexID1, $VertexID2) = @_; 2728 my($ActiveCyclicPathsRef, $CyclicPath, $Size); 2729 2730 if (!$This->HasGraphProperty('ActiveCyclicPaths')) { 2731 return $This->_GetEmptyCycles(); 2732 } 2733 if ($Mode =~ /^VertexCycle$/i) { 2734 if (!$This->HasVertexProperty('ActiveCyclicPaths', $VertexID1)) { 2735 return $This->_GetEmptyCycles(); 2736 } 2737 } 2738 elsif ($Mode =~ /^EdgeCycle$/i) { 2739 if (!$This->HasEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2)) { 2740 return $This->_GetEmptyCycles(); 2741 } 2742 } 2743 2744 MODE: { 2745 if ($Mode =~ /^GraphCycle$/i) { $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); last MODE; } 2746 if ($Mode =~ /^VertexCycle$/i) { $ActiveCyclicPathsRef = $This->GetVertexProperty('ActiveCyclicPaths', $VertexID1); last MODE; } 2747 if ($Mode =~ /^EdgeCycle$/i) { $ActiveCyclicPathsRef = $This->GetEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2); last MODE; } 2748 return $This->_GetEmptyCycles(); 2749 } 2750 2751 if (!@{$ActiveCyclicPathsRef}) { 2752 return $This->_GetEmptyCycles(); 2753 } 2754 2755 CYCLESIZE: { 2756 if ($CycleSize =~ /^SmallestCycle$/i) { $CyclicPath = $ActiveCyclicPathsRef->[0]; last CYCLESIZE; } 2757 if ($CycleSize =~ /^LargestCycle$/i) { $CyclicPath = $ActiveCyclicPathsRef->[$#{$ActiveCyclicPathsRef}]; last CYCLESIZE; } 2758 return $This->_GetEmptyCycles(); 2759 } 2760 return $CyclicPath; 2761 } 2762 2763 # Get num of cycles in graph... 2764 # 2765 sub _GetNumOfCyclesWithSize { 2766 my($This, $Mode, $SizeMode, $SpecifiedSize, $VertexID1, $VertexID2) = @_; 2767 my($ActiveCyclicPathsRef); 2768 2769 if (!$This->HasGraphProperty('ActiveCyclicPaths')) { 2770 return 0; 2771 } 2772 if ($Mode =~ /^VertexCycle$/i) { 2773 if (!$This->HasVertexProperty('ActiveCyclicPaths', $VertexID1)) { 2774 return 0; 2775 } 2776 } 2777 elsif ($Mode =~ /^EdgeCycle$/i) { 2778 if (!$This->HasEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2)) { 2779 return 0; 2780 } 2781 } 2782 2783 if ($SizeMode =~ /^(SizeLessThan|SizeGreaterThan|SpecifiedSize)$/i) { 2784 if (!defined $SpecifiedSize) { 2785 carp "Warning: ${ClassName}->_GetNumOfCyclesWithSize: Cycle size muse be defined..."; 2786 return 0; 2787 } 2788 if ($SpecifiedSize < 0) { 2789 carp "Warning: ${ClassName}->_GetNumOfCyclesWithSize: Specified cycle size, $SpecifiedSize, must be > 0 ..."; 2790 return 0; 2791 } 2792 } 2793 2794 MODE: { 2795 if ($Mode =~ /^GraphCycle$/i) { $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); last MODE; } 2796 if ($Mode =~ /^VertexCycle$/i) { $ActiveCyclicPathsRef = $This->GetVertexProperty('ActiveCyclicPaths', $VertexID1); last MODE; } 2797 if ($Mode =~ /^EdgeCycle$/i) { $ActiveCyclicPathsRef = $This->GetEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2); last MODE; } 2798 return 0; 2799 } 2800 2801 if (!@{$ActiveCyclicPathsRef}) { 2802 return 0; 2803 } 2804 my($NumOfCycles); 2805 2806 $NumOfCycles = $This->_GetCycles($Mode, $ActiveCyclicPathsRef, $SizeMode, $SpecifiedSize); 2807 2808 return $NumOfCycles; 2809 } 2810 2811 # Get cycles in graph... 2812 # 2813 sub _GetCyclesWithSize { 2814 my($This, $Mode, $SizeMode, $SpecifiedSize, $VertexID1, $VertexID2) = @_; 2815 my($ActiveCyclicPathsRef); 2816 2817 if (!$This->HasGraphProperty('ActiveCyclicPaths')) { 2818 return $This->_GetEmptyCycles(); 2819 } 2820 if ($Mode =~ /^VertexCycle$/i) { 2821 if (!$This->HasVertexProperty('ActiveCyclicPaths', $VertexID1)) { 2822 return $This->_GetEmptyCycles(); 2823 } 2824 } 2825 elsif ($Mode =~ /^EdgeCycle$/i) { 2826 if (!$This->HasEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2)) { 2827 return $This->_GetEmptyCycles(); 2828 } 2829 } 2830 2831 if ($SizeMode =~ /^(SizeLessThan|SizeGreaterThan|SpecifiedSize)$/i) { 2832 if (!defined $SpecifiedSize) { 2833 carp "Warning: ${ClassName}->_GetCyclesWithSize: Cycle size must be defined..."; 2834 return $This->_GetEmptyCycles(); 2835 } 2836 if ($SpecifiedSize < 0) { 2837 carp "Warning: ${ClassName}->_GetCyclesWithSize: Specified cycle size, $SpecifiedSize, must be > 0 ..."; 2838 return $This->_GetEmptyCycles(); 2839 } 2840 } 2841 2842 MODE: { 2843 if ($Mode =~ /^GraphCycle$/i) { $ActiveCyclicPathsRef = $This->GetGraphProperty('ActiveCyclicPaths'); last MODE; } 2844 if ($Mode =~ /^VertexCycle$/i) { $ActiveCyclicPathsRef = $This->GetVertexProperty('ActiveCyclicPaths', $VertexID1); last MODE; } 2845 if ($Mode =~ /^EdgeCycle$/i) { $ActiveCyclicPathsRef = $This->GetEdgeProperty('ActiveCyclicPaths', $VertexID1, $VertexID2); last MODE; } 2846 return $This->_GetEmptyCycles(); 2847 } 2848 2849 if (!@{$ActiveCyclicPathsRef}) { 2850 return $This->_GetEmptyCycles(); 2851 } 2852 return $This->_GetCycles($Mode, $ActiveCyclicPathsRef, $SizeMode, $SpecifiedSize); 2853 } 2854 2855 # Get cycles information... 2856 # 2857 sub _GetCycles { 2858 my($This, $Mode, $ActiveCyclicPathsRef, $SizeMode, $SpecifiedSize) = @_; 2859 2860 if (!@{$ActiveCyclicPathsRef}) { 2861 return $This->_GetEmptyCycles(); 2862 } 2863 2864 if ($SizeMode =~ /^AllSizes$/i) { 2865 return wantarray ? @{$ActiveCyclicPathsRef} : scalar @{$ActiveCyclicPathsRef}; 2866 } 2867 2868 # Get appropriate cycles... 2869 my($Size, $CyclicPath, @FilteredCyclicPaths); 2870 @FilteredCyclicPaths = (); 2871 2872 for $CyclicPath (@{$ActiveCyclicPathsRef}) { 2873 $Size = $CyclicPath->GetLength() - 1; 2874 SIZEMODE: { 2875 if ($SizeMode =~ /^OddSize$/i) { if ($Size % 2) { push @FilteredCyclicPaths, $CyclicPath; } last SIZEMODE; } 2876 if ($SizeMode =~ /^EvenSize$/i) { if (!($Size % 2)) { push @FilteredCyclicPaths, $CyclicPath; } last SIZEMODE; } 2877 if ($SizeMode =~ /^SizeLessThan$/i) { if ($Size < $SpecifiedSize) { push @FilteredCyclicPaths, $CyclicPath; } last SIZEMODE; } 2878 if ($SizeMode =~ /^SizeGreaterThan$/i) { if ($Size > $SpecifiedSize) { push @FilteredCyclicPaths, $CyclicPath; } last SIZEMODE; } 2879 if ($SizeMode =~ /^SpecifiedSize$/i) { if ($Size == $SpecifiedSize) { push @FilteredCyclicPaths, $CyclicPath; } last SIZEMODE; } 2880 return undef; 2881 } 2882 } 2883 return wantarray ? @FilteredCyclicPaths : scalar @FilteredCyclicPaths; 2884 } 2885 2886 # Return empty cyles array... 2887 # 2888 sub _GetEmptyCycles { 2889 my($This) = @_; 2890 my(@CyclicPaths); 2891 2892 @CyclicPaths = (); 2893 2894 return wantarray ? @CyclicPaths : scalar @CyclicPaths; 2895 } 2896 2897 # Does graph contains fused cycles? 2898 sub HasFusedCycles { 2899 my($This) = @_; 2900 2901 return ($This->HasGraphProperty('FusedActiveCyclicPaths')) ? 1 : 0; 2902 } 2903 2904 # Return a reference to fused cycle sets lists containing references to lists of cyclic path objects 2905 # in each fused cycle set and a reference to a list containing non-fused cyclic paths... 2906 # 2907 sub GetFusedAndNonFusedCycles { 2908 my($This) = @_; 2909 my($FusedCycleSetsRef, $NonFusedCyclesRef); 2910 2911 $FusedCycleSetsRef = $This->HasGraphProperty('FusedActiveCyclicPaths') ? $This->GetGraphProperty('FusedActiveCyclicPaths') : undef; 2912 $NonFusedCyclesRef = $This->HasGraphProperty('NonFusedActiveCyclicPaths') ? $This->GetGraphProperty('NonFusedActiveCyclicPaths') : undef; 2913 2914 return ($FusedCycleSetsRef, $NonFusedCyclesRef); 2915 } 2916 2917 # Get vertices of connected components as a list containing references to 2918 # lists of vertices for each component sorted in order of its decreasing size... 2919 # 2920 sub GetConnectedComponentsVertices { 2921 my($This) = @_; 2922 my($PathsTraversal); 2923 2924 $PathsTraversal = new Graph::PathsTraversal($This); 2925 $PathsTraversal->PerformDepthFirstSearch(); 2926 2927 return $PathsTraversal->GetConnectedComponentsVertices(); 2928 } 2929 2930 # Get a list of topologically sorted vertrices starting from a specified vertex or 2931 # an arbitrary vertex in the graph... 2932 # 2933 sub GetTopologicallySortedVertices { 2934 my($This, $RootVertexID) = @_; 2935 my($PathsTraversal); 2936 2937 $PathsTraversal = new Graph::PathsTraversal($This); 2938 $PathsTraversal->PerformBreadthFirstSearch($RootVertexID); 2939 2940 return $PathsTraversal->GetVertices(); 2941 } 2942 2943 # Get a list of paths starting from a specified vertex with length upto specified length 2944 # and no sharing of edges in paths traversed. By default, cycles are included in paths. 2945 # A path containing a cycle is terminated at a vertex completing the cycle. 2946 # 2947 sub GetPathsStartingAtWithLengthUpto { 2948 my($This, $StartVertexID, $Length, $AllowCycles) = @_; 2949 my($PathsTraversal); 2950 2951 $PathsTraversal = new Graph::PathsTraversal($This); 2952 $PathsTraversal->PerformPathsSearchWithLengthUpto($StartVertexID, $Length, $AllowCycles); 2953 2954 return $PathsTraversal->GetPaths(); 2955 } 2956 2957 # Get a list of paths starting from a specified vertex with specified length 2958 # and no sharing of edges in paths traversed. By default, cycles are included in paths. 2959 # A path containing a cycle is terminated at a vertex completing the cycle. 2960 # 2961 sub GetPathsStartingAtWithLength { 2962 my($This, $StartVertexID, $Length, $AllowCycles) = @_; 2963 my($PathsTraversal); 2964 2965 $PathsTraversal = new Graph::PathsTraversal($This); 2966 $PathsTraversal->PerformPathsSearchWithLength($StartVertexID, $Length, $AllowCycles); 2967 2968 return $PathsTraversal->GetPaths(); 2969 } 2970 2971 # Get a list of paths with all possible lengths starting from a specified vertex 2972 # with no sharing of edges in paths traversed. By default, cycles are included in paths. 2973 # A path containing a cycle is terminated at a vertex completing the cycle. 2974 # 2975 sub GetPathsStartingAt { 2976 my($This, $StartVertexID, $AllowCycles) = @_; 2977 my($PathsTraversal); 2978 2979 $PathsTraversal = new Graph::PathsTraversal($This); 2980 $PathsTraversal->PerformPathsSearch($StartVertexID, $AllowCycles); 2981 2982 return $PathsTraversal->GetPaths(); 2983 } 2984 2985 # Get a list of all paths starting from a specified vertex with length upto a specified length 2986 # with sharing of edges in paths traversed. By default, cycles are included in paths. 2987 # A path containing a cycle is terminated at a vertex completing the cycle. 2988 # 2989 sub GetAllPathsStartingAtWithLengthUpto { 2990 my($This, $StartVertexID, $Length, $AllowCycles) = @_; 2991 my($PathsTraversal); 2992 2993 $PathsTraversal = new Graph::PathsTraversal($This); 2994 $PathsTraversal->PerformAllPathsSearchWithLengthUpto($StartVertexID, $Length, $AllowCycles); 2995 2996 return $PathsTraversal->GetPaths(); 2997 } 2998 2999 # Get a list of all paths starting from a specified vertex with specified length 3000 # with sharing of edges in paths traversed. By default, cycles are included in paths. 3001 # A path containing a cycle is terminated at a vertex completing the cycle. 3002 # 3003 sub GetAllPathsStartingAtWithLength { 3004 my($This, $StartVertexID, $Length, $AllowCycles) = @_; 3005 my($PathsTraversal); 3006 3007 $PathsTraversal = new Graph::PathsTraversal($This); 3008 $PathsTraversal->PerformAllPathsSearchWithLength($StartVertexID, $Length, $AllowCycles); 3009 3010 return $PathsTraversal->GetPaths(); 3011 } 3012 3013 3014 # Get a list of all paths with all possible lengths starting from a specified vertex 3015 # with sharing of edges in paths traversed. By default, cycles are included in paths. 3016 # A path containing a cycle is terminated at a vertex completing the cycle. 3017 # 3018 sub GetAllPathsStartingAt { 3019 my($This, $StartVertexID, $AllowCycles) = @_; 3020 my($PathsTraversal); 3021 3022 $PathsTraversal = new Graph::PathsTraversal($This); 3023 $PathsTraversal->PerformAllPathsSearch($StartVertexID, $AllowCycles); 3024 3025 return $PathsTraversal->GetPaths(); 3026 } 3027 3028 # Get a reference to list of paths starting from each vertex in graph with length upto specified 3029 # length and no sharing of edges in paths traversed. By default, cycles are included in paths. 3030 # A path containing a cycle is terminated at a vertex completing the cycle. 3031 # 3032 sub GetPathsWithLengthUpto { 3033 my($This, $Length, $AllowCycles) = @_; 3034 3035 $AllowCycles = (defined $AllowCycles) ? $AllowCycles : 1; 3036 3037 return $This->_GetPaths('PathsWithLengthUpto', $Length, $AllowCycles); 3038 } 3039 3040 # Get a reference to list of paths starting from each vertex in graph with specified 3041 # length and no sharing of edges in paths traversed. By default, cycles are included in paths. 3042 # A path containing a cycle is terminated at a vertex completing the cycle. 3043 # 3044 sub GetPathsWithLength { 3045 my($This, $Length, $AllowCycles) = @_; 3046 3047 $AllowCycles = (defined $AllowCycles) ? $AllowCycles : 1; 3048 3049 return $This->_GetPaths('PathsWithLength', $Length, $AllowCycles); 3050 } 3051 3052 # Get a reference to list of paths with all possible lengths starting from each vertex 3053 # with no sharing of edges in paths traversed. By default, cycles are included in paths. 3054 # A path containing a cycle is terminated at a vertex completing the cycle. 3055 # 3056 # 3057 sub GetPaths { 3058 my($This, $AllowCycles) = @_; 3059 3060 $AllowCycles = (defined $AllowCycles) ? $AllowCycles : 1; 3061 3062 return $This->_GetPaths('PathsWithAllLengths', undef, $AllowCycles); 3063 } 3064 3065 # Get a reference to list of all paths starting from each vertex in graph with length upto a specified 3066 # length with sharing of edges in paths traversed. By default, cycles are included in paths. A path 3067 # containing a cycle is terminated at a vertex completing the cycle. 3068 # 3069 # Note: 3070 # . Duplicate paths are not removed. 3071 # 3072 sub GetAllPathsWithLengthUpto { 3073 my($This, $Length, $AllowCycles) = @_; 3074 3075 $AllowCycles = (defined $AllowCycles) ? $AllowCycles : 1; 3076 3077 return $This->_GetPaths('AllPathsWithLengthUpto', $Length, $AllowCycles); 3078 } 3079 3080 # Get a reference to list of all paths starting from each vertex in graph with specified 3081 # length with sharing of edges in paths traversed. By default, cycles are included in paths. A path 3082 # containing a cycle is terminated at a vertex completing the cycle. 3083 # 3084 # Note: 3085 # . Duplicate paths are not removed. 3086 # 3087 sub GetAllPathsWithLength { 3088 my($This, $Length, $AllowCycles) = @_; 3089 3090 $AllowCycles = (defined $AllowCycles) ? $AllowCycles : 1; 3091 3092 return $This->_GetPaths('AllPathsWithLength', $Length, $AllowCycles); 3093 } 3094 3095 # Get a reference to list of all paths with all possible lengths starting from each vertex in graph 3096 # with sharing of edges in paths traversed. By default, cycles are included in paths. A path 3097 # containing a cycle is terminated at a vertex completing the cycle. 3098 # 3099 # Note: 3100 # . Duplicate paths are not removed. 3101 # 3102 sub GetAllPaths { 3103 my($This, $AllowCycles) = @_; 3104 3105 $AllowCycles = (defined $AllowCycles) ? $AllowCycles : 1; 3106 3107 return $This->_GetPaths('AllPathsWithAllLengths', undef, $AllowCycles); 3108 } 3109 3110 3111 # Retrieve appropriate paths for each vertex in graph and return a referernce to list 3112 # containing path objects... 3113 # 3114 sub _GetPaths { 3115 my($This, $Mode, $Length, $AllowCycles) = @_; 3116 my($VertexID, @EmptyPaths, @Paths); 3117 3118 @Paths = (); @EmptyPaths = (); 3119 3120 for $VertexID ($This->GetVertices()) { 3121 my($Status, $PathsTraversal); 3122 3123 $PathsTraversal = new Graph::PathsTraversal($This); 3124 MODE: { 3125 if ($Mode =~ /^PathsWithLengthUpto$/i) { $Status = $PathsTraversal->PerformPathsSearchWithLengthUpto($VertexID, $Length, $AllowCycles); last MODE; } 3126 if ($Mode =~ /^PathsWithLength$/i) { $Status = $PathsTraversal->PerformPathsSearchWithLength($VertexID, $Length, $AllowCycles); last MODE; } 3127 if ($Mode =~ /^PathsWithAllLengths$/i) { $Status = $PathsTraversal->PerformPathsSearch($VertexID, $AllowCycles); last MODE; } 3128 3129 if ($Mode =~ /^AllPathsWithLengthUpto$/i) { $Status = $PathsTraversal->PerformAllPathsSearchWithLengthUpto($VertexID, $Length, $AllowCycles); last MODE; } 3130 if ($Mode =~ /^AllPathsWithLength$/i) { $Status = $PathsTraversal->PerformAllPathsSearchWithLength($VertexID, $Length, $AllowCycles); last MODE; } 3131 if ($Mode =~ /^AllPathsWithAllLengths$/i) { $Status = $PathsTraversal->PerformAllPathsSearch($VertexID, $AllowCycles); last MODE; } 3132 3133 return \@EmptyPaths; 3134 } 3135 if (!defined $Status) { 3136 return \@EmptyPaths; 3137 } 3138 push @Paths, $PathsTraversal->GetPaths(); 3139 } 3140 return \@Paths; 3141 } 3142 3143 # Get a list of paths between two vertices. For cyclic graphs, the list contains 3144 # may contain two paths... 3145 # 3146 sub GetPathsBetween { 3147 my($This, $StartVertexID, $EndVertexID) = @_; 3148 my($Path, $ReversePath, @Paths); 3149 3150 @Paths = (); 3151 3152 $Path = $This->_GetPathBetween($StartVertexID, $EndVertexID); 3153 if (!defined $Path) { 3154 return \@Paths; 3155 } 3156 3157 $ReversePath = $This->_GetPathBetween($EndVertexID, $StartVertexID); 3158 if (!defined $ReversePath) { 3159 return \@Paths; 3160 } 3161 if ($Path eq $ReversePath) { 3162 push @Paths, $Path; 3163 } 3164 else { 3165 # Make sure first vertex in reverse path corresponds to specified start vertex ID... 3166 $ReversePath->Reverse(); 3167 push @Paths, ($Path->GetLength <= $ReversePath->GetLength()) ? ($Path, $ReversePath) : ($ReversePath, $Path); 3168 } 3169 return @Paths; 3170 } 3171 3172 # Get a path beween two vertices... 3173 # 3174 sub _GetPathBetween { 3175 my($This, $StartVertexID, $EndVertexID) = @_; 3176 my($PathsTraversal, @Paths); 3177 3178 $PathsTraversal = new Graph::PathsTraversal($This); 3179 $PathsTraversal->PerformPathsSearchBetween($StartVertexID, $EndVertexID); 3180 3181 @Paths = $PathsTraversal->GetPaths(); 3182 3183 return (@Paths) ? $Paths[0] : undef; 3184 } 3185 3186 # Get a list containing lists of neighborhood vertices around a specified vertex with in a 3187 # specified radius... 3188 # 3189 sub GetNeighborhoodVerticesWithRadiusUpto { 3190 my($This, $StartVertexID, $Radius) = @_; 3191 my($PathsTraversal); 3192 3193 $PathsTraversal = new Graph::PathsTraversal($This); 3194 $PathsTraversal->PerformNeighborhoodVerticesSearchWithRadiusUpto($StartVertexID, $Radius); 3195 3196 return $PathsTraversal->GetVerticesNeighborhoods(); 3197 } 3198 3199 # Get a list containing lists of neighborhood vertices around a specified vertex at all 3200 # radii levels... 3201 # 3202 sub GetNeighborhoodVertices { 3203 my($This, $StartVertexID) = @_; 3204 my($PathsTraversal); 3205 3206 $PathsTraversal = new Graph::PathsTraversal($This); 3207 $PathsTraversal->PerformNeighborhoodVerticesSearch($StartVertexID); 3208 3209 return $PathsTraversal->GetVerticesNeighborhoods(); 3210 } 3211 3212 # Get neighborhood vertices around a specified vertex, along with their successor connected vertices, collected 3213 # with in a specified radius as a list containing references to lists with first value corresponding to vertex 3214 # ID and second value as reference to a list containing its successor connected vertices. 3215 # 3216 # For a neighborhood vertex at each radius level, the successor connected vertices correspond to the 3217 # neighborhood vertices at the next radius level. Consequently, the neighborhood vertices at the last 3218 # radius level don't contain any successor vertices which fall outside the range of specified radius. 3219 # 3220 sub GetNeighborhoodVerticesWithSuccessorsAndRadiusUpto { 3221 my($This, $StartVertexID, $Radius) = @_; 3222 my($PathsTraversal); 3223 3224 $PathsTraversal = new Graph::PathsTraversal($This); 3225 $PathsTraversal->PerformNeighborhoodVerticesSearchWithSuccessorsAndRadiusUpto($StartVertexID, $Radius); 3226 3227 return $PathsTraversal->GetVerticesNeighborhoodsWithSuccessors(); 3228 } 3229 3230 # Get neighborhood vertices around a specified vertex, along with their successor connected vertices, collected 3231 # at all neighborhood radii as a list containing references to lists with first value corresponding to vertex 3232 # ID and second value as reference to a list containing its successor connected vertices. 3233 # 3234 # For a neighborhood vertex at each radius level, the successor connected vertices correspond to the 3235 # neighborhood vertices at the next radius level. Consequently, the neighborhood vertices at the last 3236 # radius level don't contain any successor vertices which fall outside the range of specified radius. 3237 # 3238 sub GetNeighborhoodVerticesWithSuccessors { 3239 my($This, $StartVertexID) = @_; 3240 my($PathsTraversal); 3241 3242 $PathsTraversal = new Graph::PathsTraversal($This); 3243 $PathsTraversal->PerformNeighborhoodVerticesSearchWithSuccessors($StartVertexID); 3244 3245 return $PathsTraversal->GetVerticesNeighborhoodsWithSuccessors(); 3246 } 3247 3248 # Get adjacency matrix for the graph as a Matrix object with row and column indices 3249 # corresponding to graph vertices returned by GetVertices method. 3250 # 3251 # For a simple graph G with n vertices, the adjacency matrix for G is a n x n square matrix and 3252 # its elements Mij are: 3253 # 3254 # . 0 if i == j 3255 # . 1 if i != j and vertex Vi is adjacent to vertex Vj 3256 # . 0 if i != j and vertex Vi is not adjacent to vertex Vj 3257 # 3258 sub GetAdjacencyMatrix { 3259 my($This) = @_; 3260 my($GraphMatrix); 3261 3262 $GraphMatrix = new Graph::GraphMatrix($This); 3263 $GraphMatrix->GenerateAdjacencyMatrix(); 3264 3265 return $GraphMatrix->GetMatrix(); 3266 } 3267 3268 # Get Siedel adjacency matrix for the graph as a Matrix object with row and column indices 3269 # corresponding to graph vertices returned by GetVertices method. 3270 # 3271 # For a simple graph G with n vertices, the Siedal adjacency matrix for G is a n x n square matrix and 3272 # its elements Mij are: 3273 # 3274 # . 0 if i == j 3275 # . -1 if i != j and vertex Vi is adjacent to vertex Vj 3276 # . 1 if i != j and vertex Vi is not adjacent to vertex Vj 3277 # 3278 sub GetSiedelAdjacencyMatrix { 3279 my($This) = @_; 3280 my($GraphMatrix); 3281 3282 $GraphMatrix = new Graph::GraphMatrix($This); 3283 $GraphMatrix->GenerateSiedelAdjacencyMatrix(); 3284 3285 return $GraphMatrix->GetMatrix(); 3286 } 3287 3288 # Get distance matrix for the graph as a Matrix object with row and column indices 3289 # corresponding to graph vertices returned by GetVertices method. 3290 # 3291 # For a simple graph G with n vertices, the distance matrix for G is a n x n square matrix and 3292 # its elements Mij are: 3293 # 3294 # . 0 if i == j 3295 # . d if i != j and d is the shortest distance between vertex Vi and vertex Vj 3296 # 3297 # Note: 3298 # . In the final matrix, BigNumber values correspond to vertices with no edges. 3299 # 3300 sub GetDistanceMatrix { 3301 my($This) = @_; 3302 my($GraphMatrix); 3303 3304 $GraphMatrix = new Graph::GraphMatrix($This); 3305 $GraphMatrix->GenerateDistanceMatrix(); 3306 3307 return $GraphMatrix->GetMatrix(); 3308 } 3309 3310 # Get incidence matrix for the graph as a Matrix object with row and column indices 3311 # corresponding to graph vertices and edges returned by GetVertices and GetEdges 3312 # methods respectively. 3313 # 3314 # For a simple graph G with n vertices and e edges, the incidence matrix for G is a n x e matrix 3315 # its elements Mij are: 3316 # 3317 # . 1 if vertex Vi and the edge Ej are incident; in other words, Vi and Ej are related 3318 # . 0 otherwise 3319 # 3320 sub GetIncidenceMatrix { 3321 my($This) = @_; 3322 my($GraphMatrix); 3323 3324 $GraphMatrix = new Graph::GraphMatrix($This); 3325 $GraphMatrix->GenerateIncidenceMatrix(); 3326 3327 return $GraphMatrix->GetMatrix(); 3328 } 3329 3330 # Get degree matrix for the graph as a Matrix object with row and column indices 3331 # corresponding to graph vertices returned by GetVertices method. 3332 # 3333 # For a simple graph G with n vertices, the degree matrix for G is a n x n square matrix and 3334 # its elements Mij are: 3335 # 3336 # . deg(Vi) if i == j and deg(Vi) is the degree of vertex Vi 3337 # . 0 otherwise 3338 # 3339 sub GetDegreeMatrix { 3340 my($This) = @_; 3341 my($GraphMatrix); 3342 3343 $GraphMatrix = new Graph::GraphMatrix($This); 3344 $GraphMatrix->GenerateDegreeMatrix(); 3345 3346 return $GraphMatrix->GetMatrix(); 3347 } 3348 3349 # Get Laplacian matrix for the graph as a Matrix object with row and column indices 3350 # corresponding to graph vertices returned by GetVertices method. 3351 # 3352 # For a simple graph G with n vertices, the Laplacian matrix for G is a n x n square matrix and 3353 # its elements Mij are: 3354 # 3355 # . deg(Vi) if i == j and deg(Vi) is the degree of vertex Vi 3356 # . -1 if i != j and vertex Vi is adjacent to vertex Vj 3357 # . 0 otherwise 3358 # 3359 # Note: The Laplacian matrix is the difference between the degree matrix and adjacency matrix. 3360 # 3361 sub GetLaplacianMatrix { 3362 my($This) = @_; 3363 my($GraphMatrix); 3364 3365 $GraphMatrix = new Graph::GraphMatrix($This); 3366 $GraphMatrix->GenerateLaplacianMatrix(); 3367 3368 return $GraphMatrix->GetMatrix(); 3369 } 3370 3371 # Get normalized Laplacian matrix for the graph as a Matrix object with row and column indices 3372 # corresponding to graph vertices returned by GetVertices method. 3373 # 3374 # For a simple graph G with n vertices, the normalized Laplacian matrix L for G is a n x n square matrix and 3375 # its elements Lij are: 3376 # 3377 # . 1 if i == j and deg(Vi) != 0 3378 # . -1/SQRT(deg(Vi) * deg(Vj)) if i != j and vertex Vi is adjacent to vertex Vj 3379 # . 0 otherwise 3380 # 3381 # 3382 sub GetNormalizedLaplacianMatrix { 3383 my($This) = @_; 3384 my($GraphMatrix); 3385 3386 $GraphMatrix = new Graph::GraphMatrix($This); 3387 $GraphMatrix->GenerateNormalizedLaplacianMatrix(); 3388 3389 return $GraphMatrix->GetMatrix(); 3390 } 3391 3392 # Get admittance matrix for the graph as a Matrix object with row and column indices 3393 # corresponding to graph vertices returned by GetVertices method. 3394 # 3395 sub GetAdmittanceMatrix { 3396 my($This) = @_; 3397 3398 return $This->GetLaplacianMatrix(); 3399 } 3400 3401 # Get Kirchhoff matrix for the graph as a Matrix object with row and column indices 3402 # corresponding to graph vertices returned by GetVertices method. 3403 # 3404 sub GetKirchhoffMatrix { 3405 my($This) = @_; 3406 3407 return $This->GetLaplacianMatrix(); 3408 } 3409