File Coverage

blib/lib/Graph/Weighted.pm
Criterion Covered Total %
statement 92 108 85.1
branch 30 36 83.3
condition 19 25 76.0
subroutine 12 13 92.3
pod 6 6 100.0
total 159 188 84.5


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.9002';
7              
8 2     2   1449 use warnings;
  2         5  
  2         65  
9 2     2   24 use strict;
  2         4  
  2         50  
10              
11 2     2   858 use parent qw( Graph );
  2         663  
  2         12  
12              
13 2     2   232354 use Carp qw( croak );
  2         5  
  2         119  
14              
15 2     2   14 use constant WEIGHT => 'weight';
  2         3  
  2         2468  
16              
17              
18             sub populate {
19 8     8 1 8955 my ($self, $data, $attr) = @_;
20              
21             # Set the default attribute.
22 8   50     47 $attr ||= WEIGHT;
23              
24             # What type of data are we given?
25 8         17 my $data_ref = ref $data;
26              
27 8 100 66     38 if ($data_ref eq 'ARRAY' || $data_ref eq 'Math::Matrix') {
    50          
    50          
28 6         10 my $vertex = 0; # Initial vertex id.
29 6         13 for my $neighbors (@$data) {
30 14         35 $self->_from_array($vertex, $neighbors, $attr);
31 14         1949 $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         9 for my $vertex (keys %$data) {
43 5         493 for my $entry ( keys %{ $data->{$vertex} } ) {
  5         19  
44 11 100       213 if ( $entry eq 'label' ) {
45 1         2 my $label = delete $data->{$vertex}{$entry};
46 1         4 $self->set_vertex_attribute($vertex, $entry, $label);
47             }
48             }
49             $self->_from_hash(
50 5         18 $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   31 my ($self, $vertex, $neighbors, $attr) = @_;
61              
62             # Initial vertex weight
63 14         22 my $vertex_weight = 0;
64              
65             # Make nodes and edges.
66 14         38 for my $n (0 .. @$neighbors - 1) {
67 55         72 my $w = $neighbors->[$n]; # Weight of the edge to the neighbor.
68 55 100       107 next unless $w; # TODO Skip zero weight nodes if requested?
69              
70             # Add a node-node edge to the graph.
71 16         57 $self->add_edge($vertex, $n);
72              
73 16         3314 $self->set_edge_attribute($vertex, $n, $attr, $w);
74              
75             # Tally the weight of the vertex.
76 16         5161 $vertex_weight += $w;
77             }
78              
79             # Set the weight of the graph node.
80 14         41 $self->set_vertex_attribute($vertex, $attr, $vertex_weight);
81             }
82              
83             sub _from_hash {
84 5     5   13 my ($self, $vertex, $neighbors, $attr) = @_;
85              
86             # Initial vertex weight
87 5         8 my $vertex_weight = 0;
88              
89             # Handle terminal nodes.
90 5 50       12 if (ref $neighbors) {
91             # Make nodes and edges.
92 5         13 for my $n (keys %$neighbors) {
93 10         17 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         2345 $self->set_edge_attribute($vertex, $n, $attr, $w);
99              
100             # Tally the weight of the vertex.
101 10         3783 $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         43 $self->set_vertex_attribute($vertex, $attr, $vertex_weight);
110             }
111              
112              
113             sub get_cost {
114 36     36 1 7156 my ($self, $v, $attr) = @_;
115 36 50       75 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     106 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     46 return $self->get_vertex_attribute($v, $attr) || 0;
125             }
126              
127              
128             sub vertex_span {
129 1     1 1 6 my ($self, $attr) = @_;
130              
131             # Get the cost of each vertex
132 1         2 my $mass = {};
133 1         4 for my $vertex ( $self->vertices ) {
134 5         421 $mass->{$vertex} = $self->get_cost($vertex, $attr);
135             }
136              
137             # Find the smallest & biggest costs
138 1         90 my ($smallest, $biggest);
139 1         4 for my $vertex ( keys %$mass ) {
140 5         11 my $current = $mass->{$vertex};
141 5 100 100     14 if ( !defined $smallest || $smallest > $current ) {
142 2         3 $smallest = $current;
143             }
144 5 100 100     17 if ( !defined $biggest || $biggest < $current ) {
145 4         5 $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       11 push @$lightest, $vertex if $mass->{$vertex} == $smallest;
153 5 100       11 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 971 my ($self, $attr) = @_;
162              
163             # Get the cost of each edge
164 1         3 my $mass = {};
165 1         4 for my $edge ( $self->edges ) {
166 7         2301 $mass->{ $edge->[0] . '_' . $edge->[1] } = $self->get_cost($edge, $attr);
167             }
168              
169             # Find the smallest & biggest costs
170 1         348 my ($smallest, $biggest);
171 1         5 for my $edge ( keys %$mass ) {
172 7         10 my $current = $mass->{$edge};
173 7 100 66     23 if ( !defined $smallest || $smallest > $current ) {
174 1         2 $smallest = $current;
175             }
176 7 100 100     22 if ( !defined $biggest || $biggest < $current ) {
177 2         4 $biggest = $current;
178             }
179             }
180              
181             # Collect the lightest & heaviest edges
182 1         4 my ($lightest, $heaviest) = ([], []);
183 1         6 for my $edge ( sort keys %$mass ) {
184 7         16 my $arrayref = [ split /_/, $edge ];
185 7 100       17 push @$lightest, $arrayref if $mass->{$edge} == $smallest;
186 7 100       15 push @$heaviest, $arrayref if $mass->{$edge} == $biggest;
187             }
188              
189 1         6 return $lightest, $heaviest;
190             }
191              
192              
193              
194             sub path_cost {
195 4     4 1 3474 my ($self, $path, $attr) = @_;
196              
197 4 100       21 return unless $self->has_path( @$path );
198              
199 3         1058 my $path_cost = 0;
200              
201 3         10 for my $i ( 0 .. @$path - 2 ) {
202 6         1112 $path_cost += $self->get_cost( [ $path->[$i], $path->[ $i + 1 ] ], $attr );
203             }
204              
205 3         1056 return $path_cost;
206             }
207              
208              
209             sub dump {
210 0     0 1   my $self = shift;
211 0   0       my $attr = shift || 'weight';
212              
213 0           for my $vertex ( sort { $a <=> $b } $self->vertices ) {
  0            
214 0           my $label = $self->get_vertex_attribute($vertex, 'label');
215 0 0         printf "%svertex: %s %s=%.2f\n",
216             ( $label ? "$label " : '' ),
217             $vertex,
218             $attr,
219             $self->get_cost( $vertex, $attr );
220 0           for my $successor ( sort { $a <=> $b } $self->successors($vertex) ) {
  0            
221 0           printf "\tedge to: %s %s=%.2f\n",
222             $successor,
223             $attr,
224             $self->get_cost( [ $vertex, $successor ], $attr );
225             }
226             }
227             }
228              
229             1;
230              
231             __END__