File Coverage

blib/lib/Algorithm/CriticalPath.pm
Criterion Covered Total %
statement 65 65 100.0
branch 6 6 100.0
condition 16 18 88.8
subroutine 6 6 100.0
pod 1 1 100.0
total 94 96 97.9


line stmt bran cond sub pod time code
1             package Algorithm::CriticalPath;
2            
3 1     1   2265624 use 5.010;
  1         3  
  1         40  
4 1     1   803 use Mouse;
  1         335798  
  1         7  
5            
6            
7            
8             =head1 NAME
9            
10             Algorithm::CriticalPath - Perform a critical path analysis over a Graph Object, by Ded MedVed
11            
12             =head1 VERSION
13            
14             Version 0.07
15            
16             =cut
17            
18             our $VERSION = '0.07';
19            
20            
21 1     1   393 use Graph;
  1         8  
  1         22  
22 1     1   6 use Carp;
  1         2  
  1         76  
23 1     1   7 use Data::Dumper;
  1         2  
  1         622  
24            
25             has 'graph' => (
26             is => 'ro'
27             , isa => 'Graph'
28             , required => 1
29             );
30            
31             has 'vertices' => (
32             is => 'rw'
33             , isa => 'ArrayRef[Str]'
34             );
35             has 'cost' => (
36             is => 'rw'
37             , isa => 'Num'
38             );
39            
40            
41             sub BUILD {
42            
43 10     10 1 18565 my ($self) = @_;
44            
45 10 100 66     93 if ( ! defined $self->graph()
      66        
      100        
      100        
      100        
      100        
46             || $self->graph()->has_a_cycle()
47             || $self->graph()->is_pseudo_graph()
48             || $self->graph()->is_refvertexed()
49             || $self->graph()->is_undirected()
50             || $self->graph()->is_multiedged()
51             || $self->graph()->is_multivertexed()
52             )
53             {
54 5         3250 croak 'Invalid graph type for critical path analysis' ;
55             } ;
56            
57             # this is ropey - should use guaranteed unique names
58 5         6173 my $start = 'GCP::dummyStart';
59 5         9 my $end = 'GCP::dummyEnd';
60            
61            
62             # this is ropey, should use a BFS search method to return the depth-ordered rankings of vertices.
63 5         26 my $g = $self->graph()->deep_copy();
64 5         8519 my @rank;
65 5         13 my $i = 0 ;
66 5         24 while ( $g->vertices() > 0 ) {
67            
68 8         460 @{$rank[$i]} = $g->source_vertices();
  8         1899  
69 8         18 push @{$rank[$i]}, $g->isolated_vertices();
  8         34  
70            
71 8         1867 for my $s (@{$rank[$i]}) {
  8         20  
72 10         374 $g->delete_vertex($s);
73             }
74 8         1553 $i++;
75             }
76            
77             # $copy adds in the dummy start and end nodes, so we don't destroy the original.
78 5         201 my $copy = $self->graph()->deep_copy();
79 5         7514 $copy->add_weighted_vertex($start,0);
80 5         1313 $copy->add_weighted_vertex($end,0);
81            
82 5         803 for my $n ($copy->source_vertices()) {
83 3         2295 $copy->add_edge($start, $n);
84             }
85 5         1322 for my $n ($copy->sink_vertices()) {
86 4         1805 $copy->add_edge($n,$end);
87             }
88            
89 5         1217 for my $n ($copy->isolated_vertices()) {
90 5         1076 $copy->add_edge($start, $n);
91 5         12814 $copy->add_edge($n,$end);
92             }
93            
94 5         1762 unshift @rank, [$start];
95 5         14 push @rank, [$end];
96            
97 5         38 my %costToHere = map { $_ => 0 } $copy->vertices();
  20         351  
98            
99 5         12 my %criticalPathToHere;
100 5         16 $criticalPathToHere{$start} = [$start];
101            
102 5         11 for my $row ( @rank ) {
103 18         315 for my $node ( @$row ) {
104 20         65 for my $s ( $copy->successors($node) ) {
105 22 100       1385 if ( $costToHere{$node} + $copy->get_vertex_weight($s) > $costToHere{$s} ) {
106 15         1714 $costToHere{$s} = $costToHere{$node} + $copy->get_vertex_weight($s);
107 15         1585 @{$criticalPathToHere{$s}} = @{$criticalPathToHere{$node}};
  15         51  
  15         31  
108 15         25 push @{$criticalPathToHere{$s}}, $s;
  15         78  
109             }
110             }
111             }
112             }
113            
114             # we don't want to see the dummy nodes on the returned critical path.
115 5 100       396 @{$criticalPathToHere{$end}} = grep { $_ ne ${start} && $_ ne ${end} } @{$criticalPathToHere{$end}} ;
  5         21  
  16         81  
  5         16  
116            
117 5         9 $self->vertices(\@{$criticalPathToHere{$end}});
  5         61  
118 5         149 $self->cost($costToHere{$end});
119            
120            
121             } ;
122            
123             __PACKAGE__->meta->make_immutable();
124            
125            
126             1;
127             __DATA__