File Coverage

blib/lib/SNA/Network.pm
Criterion Covered Total %
statement 132 132 100.0
branch 3 4 75.0
condition 4 5 80.0
subroutine 34 34 100.0
pod 12 12 100.0
total 185 187 98.9


line stmt bran cond sub pod time code
1             package SNA::Network;
2              
3 14     14   390426 use warnings;
  14         35  
  14         496  
4 14     14   80 use strict;
  14         27  
  14         459  
5              
6 14     14   74 use Carp;
  14         34  
  14         1805  
7 14     14   15975 use English;
  14         71832  
  14         83  
8              
9 14     14   13108 use Scalar::Util qw(weaken);
  14         30  
  14         1586  
10 14     14   71 use List::Util qw(sum);
  14         29  
  14         1451  
11              
12 14     14   8183 use SNA::Network::Node;
  14         42  
  14         195  
13 14     14   7643 use SNA::Network::Edge;
  14         38  
  14         141  
14 14     14   8625 use SNA::Network::Filter::Pajek;
  14         38  
  14         935  
15 14     14   8191 use SNA::Network::Filter::Guess;
  14         46  
  14         1177  
16 14     14   9203 use SNA::Network::Algorithm::Betweenness;
  14         39  
  14         784  
17 14     14   9194 use SNA::Network::Algorithm::Connectivity;
  14         39  
  14         770  
18 14     14   7767 use SNA::Network::Algorithm::Cores;
  14         38  
  14         694  
19 14     14   7903 use SNA::Network::Algorithm::HITS;
  14         35  
  14         743  
20 14     14   8414 use SNA::Network::Algorithm::Louvain;
  14         48  
  14         857  
21 14     14   8880 use SNA::Network::Algorithm::PageRank;
  14         34  
  14         812  
22 14     14   7671 use SNA::Network::Generator::ByDensity;
  14         36  
  14         650  
23 14     14   8689 use SNA::Network::Generator::ConfigurationModel;
  14         37  
  14         696  
24 14     14   8233 use SNA::Network::Generator::MCMC;
  14         38  
  14         772  
25              
26 14     14   82 use Module::List::Pluggable qw(import_modules);
  14         23  
  14         16202  
27             import_modules('SNA::Network::Plugin');
28              
29              
30             =head1 NAME
31              
32             SNA::Network - A toolkit for Social Network Analysis
33              
34             =head1 VERSION
35              
36             Version 0.19
37              
38             =cut
39              
40             our $VERSION = '0.19';
41              
42              
43             =head1 SYNOPSIS
44              
45             Quick summary of what the module does.
46              
47             use SNA::Network;
48              
49             my $net = SNA::Network->new();
50             $net->create_node_at_index(index => 0, name => 'A');
51             $net->create_node_at_index(index => 1, name => 'B');
52             $net->create_edge(source_index => 0, target_index => 1, weight => 1);
53             ...
54            
55             =head1 DESCRIPTION
56              
57             SNA::Network is a bundle of modules for network algorithms,
58             specifically designed for the needs of Social Network Analysis (SNA),
59             but can be used for any other graph algorithms of course.
60              
61             It represents a standard directed and weighted network,
62             which can also be used as an undirected and/or unweighted network of course.
63             It is freely extensible by using own hash entries.
64              
65             Data structures have been designed for SNA-typical sparse network operations,
66             and consist of Node and Edge objects, linked via references to each other.
67              
68             Functionality is implemented in sub-modules in the B namespace,
69             and all methods are imported into B.
70             So you can read the documentation in the sub-modules and call the methods
71             from your B instance.
72              
73             Methods are called with named parameter style, e.g.
74              
75             $net->method( param1 => value1, param2 => value2, ...);
76            
77             Only in cases, where methods have only one parameter,
78             this one is passed by value.
79              
80             This module was implemented mainly because I had massive problems
81             understanding the internal structures of Perl's L module.
82             Despite it uses lots of arrays instead of hashes for attributes
83             and bit setting for properties, it was terribly slow for my purposes,
84             especially in network manipulation (consistent node removal).
85             It currently has much more features and plugins though,
86             and is suitable for different network types.
87             This package is focussing on directed networks only,
88             with the possibility to model undirected ones as well.
89              
90              
91             =head1 METHODS
92              
93             =head2 new
94              
95             Creates a new empty network.
96             There are no parameters.
97             After creation, use methods to add nodes and edges,
98             or load a network from a file.
99              
100             =cut
101              
102             sub new {
103 25     25 1 1400 my ($package) = @_;
104 25         173 return bless { nodes => [], edges => [] }, $package;
105             }
106              
107              
108             =head2 create_node_at_index
109              
110             Creates a node at the given index.
111             Pass node attributes as additional named parameters, index is mandatory.
112             Returns the created L object.
113              
114             =cut
115              
116             sub create_node_at_index {
117 660     660 1 3036 my ($self, %node_attributes) = @_;
118 660         2206 return $self->{nodes}->[$node_attributes{index}] = SNA::Network::Node->new(%node_attributes);
119             }
120              
121              
122             =head2 create_node
123              
124             Creates a node at the next index.
125             Pass node attributes as additional named parameters, index is forbidden.
126             Returns the created L object with the right index field.
127              
128             =cut
129              
130             sub create_node {
131 12     12 1 1377 my ($self, %node_attributes) = @_;
132 12 50       36 croak "illegally passed index to create_node method" if defined $node_attributes{index};
133 12         46 my $index = int $self->nodes;
134 12         44 return $self->create_node_at_index( index => $index, %node_attributes );
135             }
136              
137              
138             =head2 create_edge
139              
140             Creates a new edge between nodes with the given B and B.
141             A B is optional, it defaults to 1.
142             Pass any additional attributes as key/value pairs.
143             Returns the created L object.
144              
145             =cut
146              
147             sub create_edge {
148 4027     4027 1 12943 my ($self, %params) = @_;
149 4027         7946 my $source_node = $self->node_at_index( $params{source_index} );
150 4027         9221 my $target_node = $self->node_at_index( $params{target_index} );
151 4027         4396 my $index = int @{ $self->{edges} };
  4027         6896  
152 4027         5819 my $weight = $params{weight};
153 4027         5830 delete $params{source_index};
154 4027         5310 delete $params{target_index};
155 4027         5586 delete $params{weight};
156 4027         13962 my $edge = SNA::Network::Edge->new(
157             source => $source_node,
158             target => $target_node,
159             weight => $weight,
160             index => $index,
161             %params,
162             );
163 4027         6436 push @{ $self->{edges} }, $edge;
  4027         8176  
164 4027         4640 push @{ $source_node->{outgoing_edges} }, $edge;
  4027         7296  
165 4027         10728 weaken $source_node->{outgoing_edges}->[-1];
166 4027         4558 push @{ $target_node->{incoming_edges} }, $edge;
  4027         8447  
167 4027         11164 weaken $target_node->{incoming_edges}->[-1];
168 4027         21313 return $edge;
169             }
170              
171              
172             =head2 nodes
173              
174             Returns the array of L objects belonging to this network.
175              
176             =cut
177              
178             sub nodes {
179 173     173 1 5257 my ($self) = @_;
180 173         184 return @{ $self->{nodes} };
  173         845  
181             }
182              
183              
184             =head2 node_at_index
185              
186             Returns the L object at the given index.
187              
188             =cut
189              
190             sub node_at_index {
191 8249     8249 1 12220 my ($self, $index) = @_;
192 8249         16361 return $self->{nodes}->[$index];
193             }
194              
195              
196             =head2 edges
197              
198             Returns the array of L objects belonging to this network.
199              
200             =cut
201              
202             sub edges {
203 21116     21116 1 23644 my ($self) = @_;
204 21116         18468 return @{ $self->{edges} };
  21116         59983  
205             }
206              
207              
208             =head2 total_weight
209              
210             Returns the sum of all weights of the L objects belonging to this network.
211              
212             =cut
213              
214             sub total_weight {
215 4     4 1 9 my ($self) = @_;
216 4         15 return sum map { $_->weight } $self->edges;
  1501         4296  
217             }
218              
219              
220             =head2 delete_nodes
221              
222             Delete the passed node objects.
223             These have to be sorted by index!
224             All related edges get deleted as well.
225             Indexes get restored after this operation.
226              
227             =cut
228              
229             sub delete_nodes {
230 1     1 1 4 my ($self, @nodes_to_delete) = @_;
231             # nodes have to be sorted by index!
232            
233 1         3 foreach (@nodes_to_delete) {
234 2         7 $self->delete_edges( $_->edges() );
235             }
236            
237 7 100 100     97 $self->{nodes} = [ grep {
      50        
238 1         4 ($nodes_to_delete[0] && $_ == $nodes_to_delete[0]) ? shift @nodes_to_delete && 0 : 1
239             } $self->nodes() ];
240 1         4 $self->_restore_node_indexes();
241             }
242              
243              
244             sub _restore_node_indexes {
245 1     1   3 my ($self) = @_;
246 1         2 my $i = 0;
247 1         3 foreach ($self->nodes()) {
248 5         6 $_->{index} = $i++;
249 5         9 undef $_->{weak_component_index};
250             }
251             }
252              
253              
254             =head2 delete_edges
255              
256             Delete the passed edge objects.
257              
258             =cut
259              
260             sub delete_edges {
261 3     3 1 9 my ($self, @edges_to_delete) = @_;
262            
263 3         6 foreach my $edge (@edges_to_delete) {
264            
265             # delete references in endpoint nodes
266              
267 13         36 $edge->source->{outgoing_edges} = [ grep {
268 8         28 $_ != $edge
269             } $edge->source->outgoing_edges ];
270            
271 8         27 for (0 .. int $edge->source->outgoing_edges - 1) {
272 5         21 weaken $edge->source->{outgoing_edges}->[$_];
273             }
274              
275 14         39 $edge->target->{incoming_edges} = [ grep {
276 8         26 $_ != $edge
277             } $edge->target->incoming_edges ];
278            
279 8         28 for (0 .. int $edge->target->incoming_edges - 1) {
280 6         21 weaken $edge->target->{incoming_edges}->[$_];
281             }
282            
283             # delete references in edge index
284 43         113 $self->{edges} = [ grep {
285 8         18 $_ != $edge
286             } $self->edges ];
287             }
288            
289            
290 3         8 $self->_restore_edge_indexes;
291             }
292              
293              
294             sub _restore_edge_indexes {
295 3     3   4 my ($self) = @_;
296 3         4 my $i = 0;
297 3         8 foreach ($self->edges) {
298 9         37 $_->{index} = $i++;
299             }
300             }
301              
302              
303             =head2 communities
304              
305             Return a list of L objects, which were identified by a previously executed community identification algorithm, usually the L algorithm.
306             If no such algorithm was executed, returns C.
307              
308             =cut
309              
310             sub communities {
311 11     11 1 1507 my ($self) = @_;
312 11         17 return @{ $self->{communities_ref} };
  11         57  
313             }
314              
315              
316             =head2 modularity
317              
318             Return the modularity value based on the current communities of the network,
319             which were identified by a previously executed community identification algorithm,
320             usually the L algorithm.
321             If no such algorithm was executed, returns C.
322              
323             =cut
324              
325             sub modularity {
326 1     1 1 467 my ($self) = @_;
327 1         5 return sum map { $_->module_value } $self->communities;
  4         14  
328             }
329              
330              
331              
332             =head1 PLUGIN SYSTEM
333              
334             This package can be extenden with plugins,
335             which gives you the possibility, to add your own algorithms, filters, and so on.
336             Each class found in the namespace B
337             will be imported into the namespace of B,
338             and each class found in the namespace B
339             will be imported into the namespace of B.
340             With this mechanism, you can add methods to these classes.
341              
342             For example:
343              
344             package SNA::Network::Plugin::Foo;
345              
346             use warnings;
347             use strict;
348              
349             require Exporter;
350             use base qw(Exporter);
351             our @EXPORT = qw(foo);
352              
353             sub foo {
354             my ($self) = @_;
355             # $self is a reference to our network object
356             # do something with it here
357             ...
358             }
359              
360             adds a new foo method to B.
361              
362              
363             =head1 SEE ALSO
364              
365             =over 4
366              
367             =item * L
368              
369             =item * L
370              
371             =item * L
372              
373             =item * L
374              
375             =item * L
376              
377             =item * L
378              
379             =item * L
380              
381             =item * L
382              
383             =item * L
384              
385             =item * L
386              
387             =item * L
388              
389             =item * L
390              
391             =item * L
392              
393             =item * L
394              
395             =back
396              
397              
398             =head1 AUTHOR
399              
400             Darko Obradovic, C<< >>
401              
402              
403             =head1 BUGS
404              
405             Please report any bugs or feature requests to C, or through
406             the web interface at L. I will be notified, and then you'll
407             automatically be notified of progress on your bug as I make changes.
408              
409              
410             =head1 SUPPORT
411              
412             You can find documentation for this module with the perldoc command.
413              
414             perldoc SNA::Network
415              
416              
417             You can also look for information at:
418              
419             =over 4
420              
421             =item * RT: CPAN's request tracker
422              
423             L
424              
425             =item * AnnoCPAN: Annotated CPAN documentation
426              
427             L
428              
429             =item * CPAN Ratings
430              
431             L
432              
433             =item * Search CPAN
434              
435             L
436              
437             =back
438              
439              
440             =head1 ACKNOWLEDGEMENTS
441              
442             This module has been developed as part of my work at the
443             German Research Center for Artificial Intelligence (DFKI) L.
444              
445              
446             =head1 COPYRIGHT & LICENSE
447              
448             Copyright 2009 Darko Obradovic, all rights reserved.
449              
450             This program is free software; you can redistribute it and/or modify it
451             under the same terms as Perl itself.
452              
453              
454             =cut
455              
456             1; # End of SNA::Network