File Coverage

blib/lib/Graph/Weighted.pm
Criterion Covered Total %
statement 92 115 80.0
branch 30 36 83.3
condition 19 25 76.0
subroutine 12 14 85.7
pod 7 7 100.0
total 160 197 81.2


line stmt bran cond sub pod time code
1             package Graph::Weighted;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: A weighted graph implementation
5              
6             our $VERSION = '0.9100';
7              
8 2     2   1489 use warnings;
  2         4  
  2         66  
9 2     2   10 use strict;
  2         4  
  2         47  
10              
11 2     2   871 use parent qw( Graph );
  2         619  
  2         12  
12              
13 2     2   229115 use Carp qw( croak );
  2         7  
  2         115  
14              
15 2     2   13 use constant WEIGHT => 'weight';
  2         4  
  2         2697  
16              
17              
18             sub populate {
19 8     8 1 9211 my ($self, $data, $attr) = @_;
20              
21             # Set the default attribute.
22 8   50     50 $attr ||= WEIGHT;
23              
24             # What type of data are we given?
25 8         18 my $data_ref = ref $data;
26              
27 8 100 66     35 if ($data_ref eq 'ARRAY' || $data_ref eq 'Math::Matrix') {
    50          
    50          
28 6         10 my $vertex = 0; # Initial vertex id.
29 6         11 for my $neighbors (@$data) {
30 14         35 $self->_from_array($vertex, $neighbors, $attr);
31 14         2007 $vertex++; # Move on to the next vertex...
32             }
33             }
34             elsif ($data_ref eq 'Math::MatrixReal') {
35 0         0 my $vertex = 0;
36 0         0 for my $neighbors (@{ $data->[0] }) {
  0         0  
37 0         0 $self->_from_array($vertex, $neighbors, $attr);
38 0         0 $vertex++;
39             }
40             }
41             elsif ($data_ref eq 'HASH') {
42 2         8 for my $vertex (keys %$data) {
43 5         419 for my $entry ( keys %{ $data->{$vertex} } ) {
  5         16  
44 11 100       248 if ( $entry eq 'label' ) {
45 1         3 my $label = delete $data->{$vertex}{$entry};
46 1         5 $self->set_vertex_attribute($vertex, $entry, $label);
47             }
48             }
49             $self->_from_hash(
50 5         15 $vertex, $data->{$vertex}, $attr
51             );
52             }
53             }
54             else {
55 0         0 croak "Unknown data type: $data\n";
56             }
57             }
58              
59             sub _from_array {
60 14     14   29 my ($self, $vertex, $neighbors, $attr) = @_;
61              
62             # Initial vertex weight
63 14         32 my $vertex_weight = 0;
64              
65             # Make nodes and edges.
66 14         38 for my $n (0 .. @$neighbors - 1) {
67 55         79 my $w = $neighbors->[$n]; # Weight of the edge to the neighbor.
68 55 100       136 next unless $w; # TODO Skip zero weight nodes if requested?
69              
70             # Add a node-node edge to the graph.
71 16         60 $self->add_edge($vertex, $n);
72              
73 16         3311 $self->set_edge_attribute($vertex, $n, $attr, $w);
74              
75             # Tally the weight of the vertex.
76 16         5295 $vertex_weight += $w;
77             }
78              
79             # Set the weight of the graph node.
80 14         43 $self->set_vertex_attribute($vertex, $attr, $vertex_weight);
81             }
82              
83             sub _from_hash {
84 5     5   11 my ($self, $vertex, $neighbors, $attr) = @_;
85              
86             # Initial vertex weight
87 5         10 my $vertex_weight = 0;
88              
89             # Handle terminal nodes.
90 5 50       11 if (ref $neighbors) {
91             # Make nodes and edges.
92 5         13 for my $n (keys %$neighbors) {
93 10         19 my $w = $neighbors->{$n}; # Weight of the edge to the neighbor.
94              
95             # Add a node-node edge to the graph.
96 10         35 $self->add_edge($vertex, $n);
97              
98 10         2339 $self->set_edge_attribute($vertex, $n, $attr, $w);
99              
100             # Tally the weight of the vertex.
101 10         3835 $vertex_weight += $w;
102             }
103             }
104             else {
105 0         0 $vertex_weight = $neighbors;
106             }
107              
108             # Set the weight of the graph node.
109 5         26 $self->set_vertex_attribute($vertex, $attr, $vertex_weight);
110             }
111              
112              
113             sub get_cost {
114 36     36 1 7103 my ($self, $v, $attr) = @_;
115 36 50       98 croak 'ERROR: No vertex given to get_cost()' unless defined $v;
116              
117             # Default to weight.
118 36   100     117 $attr ||= WEIGHT;
119              
120             # Return the edge attribute if given a list.
121 36 100 50     104 return $self->get_edge_attribute(@$v, $attr) || 0 if ref $v eq 'ARRAY';
122              
123             # Return the vertex attribute if given a scalar.
124 14   100     44 return $self->get_vertex_attribute($v, $attr) || 0;
125             }
126              
127              
128             sub vertex_span {
129 1     1 1 5 my ($self, $attr) = @_;
130              
131             # Get the cost of each vertex
132 1         2 my $mass = {};
133 1         3 for my $vertex ( $self->vertices ) {
134 5         465 $mass->{$vertex} = $self->get_cost($vertex, $attr);
135             }
136              
137             # Find the smallest & biggest costs
138 1         91 my ($smallest, $biggest);
139 1         4 for my $vertex ( keys %$mass ) {
140 5         6 my $current = $mass->{$vertex};
141 5 100 100     18 if ( !defined $smallest || $smallest > $current ) {
142 3         4 $smallest = $current;
143             }
144 5 100 100     15 if ( !defined $biggest || $biggest < $current ) {
145 2         4 $biggest = $current;
146             }
147             }
148              
149             # Collect the lightest & heaviest vertices
150 1         3 my ($lightest, $heaviest) = ([], []);
151 1         3 for my $vertex ( keys %$mass ) {
152 5 100       9 push @$lightest, $vertex if $mass->{$vertex} == $smallest;
153 5 100       12 push @$heaviest, $vertex if $mass->{$vertex} == $biggest;
154             }
155              
156 1         5 return $lightest, $heaviest;
157             }
158              
159              
160             sub edge_span {
161 1     1 1 950 my ($self, $attr) = @_;
162              
163             # Get the cost of each edge
164 1         2 my $mass = {};
165 1         4 for my $edge ( $self->edges ) {
166 7         2323 $mass->{ $edge->[0] . '_' . $edge->[1] } = $self->get_cost($edge, $attr);
167             }
168              
169             # Find the smallest & biggest costs
170 1         394 my ($smallest, $biggest);
171 1         4 for my $edge ( keys %$mass ) {
172 7         12 my $current = $mass->{$edge};
173 7 100 66     22 if ( !defined $smallest || $smallest > $current ) {
174 1         2 $smallest = $current;
175             }
176 7 100 100     21 if ( !defined $biggest || $biggest < $current ) {
177 2         2 $biggest = $current;
178             }
179             }
180              
181             # Collect the lightest & heaviest edges
182 1         3 my ($lightest, $heaviest) = ([], []);
183 1         6 for my $edge ( sort keys %$mass ) {
184 7         17 my $arrayref = [ split /_/, $edge ];
185 7 100       17 push @$lightest, $arrayref if $mass->{$edge} == $smallest;
186 7 100       31 push @$heaviest, $arrayref if $mass->{$edge} == $biggest;
187             }
188              
189 1         5 return $lightest, $heaviest;
190             }
191              
192              
193              
194             sub path_cost {
195 4     4 1 3399 my ($self, $path, $attr) = @_;
196              
197 4 100       17 return unless $self->has_path( @$path );
198              
199 3         1032 my $path_cost = 0;
200              
201 3         10 for my $i ( 0 .. @$path - 2 ) {
202 6         1125 $path_cost += $self->get_cost( [ $path->[$i], $path->[ $i + 1 ] ], $attr );
203             }
204              
205 3         1120 return $path_cost;
206             }
207              
208              
209             sub MST_edge_sum {
210 0     0 1   my ($self, $tree) = @_;
211              
212 0           my $sum = 0;
213              
214 0           my @edges = split /,/, $tree;
215              
216 0           for my $edge (@edges) {
217 0           my @edge = split /=/, $edge;
218 0           $sum += $self->get_cost(\@edge);
219             }
220              
221 0           return $sum;
222             }
223              
224              
225             sub dump {
226 0     0 1   my $self = shift;
227 0   0       my $attr = shift || 'weight';
228              
229 0           for my $vertex ( sort { $a <=> $b } $self->vertices ) {
  0            
230 0           my $label = $self->get_vertex_attribute($vertex, 'label');
231 0 0         printf "%svertex: %s %s=%.2f\n",
232             ( $label ? "$label " : '' ),
233             $vertex,
234             $attr,
235             $self->get_cost( $vertex, $attr );
236 0           for my $successor ( sort { $a <=> $b } $self->successors($vertex) ) {
  0            
237 0           printf "\tedge to: %s %s=%.2f\n",
238             $successor,
239             $attr,
240             $self->get_cost( [ $vertex, $successor ], $attr );
241             }
242             }
243             }
244              
245             1;
246              
247             __END__