File Coverage

blib/lib/Bio/Metabolic/Network.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Bio::Metabolic::Network - Perl extension for biochemical reaction networks
5              
6             =head1 SYNOPSIS
7              
8             use Bio::Metabolic::Network;
9              
10             my $net = Bio::Metabolic::Network->new($reaction1, $reaction2, ... );
11              
12              
13             =head1 DESCRIPTION
14              
15             This class implements objects representing biochemical reaction networks.
16             A reaction network is defined a number of biochemical reactions.
17              
18             =head2 EXPORT
19              
20             None
21              
22             =head2 OVERLOADED OPERATORS
23              
24             String Conversion
25             $string = "$network";
26             print "\$network = '$network'\n";
27              
28             Comparison
29             if ($network1 <= $network2)...
30              
31              
32             =head1 AUTHOR
33              
34             Oliver Ebenhoeh, oliver.ebenhoeh@rz.hu-berlin.de
35              
36             =head1 SEE ALSO
37              
38             Bio::Metabolic Bio::Metabolic::Substrate Bio::Metabolic::Substrate::Cluster Bio::Metabolic::Reaction.
39              
40             =cut
41              
42             package Bio::Metabolic::Network;
43              
44             require 5.005_62;
45 5     5   30 use strict;
  5         9  
  5         203  
46 5     5   28 use warnings;
  5         9  
  5         198  
47              
48             require Exporter;
49              
50 5     5   28 use Bio::Metabolic::Substrate;
  5         8  
  5         103  
51 5     5   26 use Bio::Metabolic::Substrate::Cluster;
  5         7  
  5         218  
52 5     5   7357 use PDL;
  0            
  0            
53             use PDL::Matrix;
54              
55             #use Math::Symbolic;
56             #use Math::Symbolic::VectorCalculus;
57              
58             #use PDL::Matrix::Extras;
59              
60             use Carp;
61              
62             use overload
63             "\"\"" => \&network_to_string,
64             "+" => \&add_networks,
65             "<=" => \&is_in,
66             ">=" => sub {
67             my $n1 = shift;
68             my $n2 = shift;
69             return ( $n2 <= $n1 );
70             },
71             "==" => sub {
72             my $n1 = shift;
73             my $n2 = shift;
74             return ( $n1 <= $n2 && $n2 <= $n1 );
75             };
76              
77             our @ISA = qw(Exporter);
78              
79             # Items to export into callers namespace by default. Note: do not export
80             # names by default without a very good reason. Use EXPORT_OK instead.
81             # Do not simply export all your public functions/methods/constants.
82              
83             # This allows declaration use Bio::Metabolic::Network ':all';
84             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
85             # will save memory.
86             our %EXPORT_TAGS = (
87             'all' => [
88             qw(
89              
90             )
91             ]
92             );
93              
94             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
95              
96             our @EXPORT = qw(
97              
98             );
99             our $VERSION = '0.07';
100              
101             our %OutputFormat = (
102             'substrate' => "%20s",
103             'entry' => "%5d"
104             );
105              
106             # Below is stub documentation for your module. You better edit it!
107              
108             =head1 METHODS
109              
110             =head2 Constructor new
111              
112             Returns a new Bio::Metabolic::Network object.
113             Passed arguments must be Bio::Metabolic::Reaction objects.
114              
115             Every network object is associated with a matrx, the stoichiometric matrix.
116             This matrix is defined by the reactions and gets determined upon creation.
117              
118             =cut
119              
120             sub new {
121             my $pkg = shift;
122             my @reactions = @_ ? $_[0] =~ /ARRAY/ ? @{ $_[0] } : @_ : ();
123              
124             # my $reactions = @_ ? shift : [ ];
125              
126             # $reactions = [$reactions, @_] unless ref($reactions) =~ /ARRAY/;
127              
128             my $new_network = bless { reactions => \@reactions } => $pkg;
129             $new_network->new_matrix;
130              
131             return $new_network;
132             }
133              
134             =head2 Method copy
135              
136             Returns a clone of the network.
137             However, the references to the reactions point to exactly the same reactions.
138             If one gets modified, it effects all networks with that reaction.
139              
140             =cut
141              
142             sub copy {
143             my $orig = shift;
144             return ref($orig)->new( $orig->reactions );
145             }
146              
147             =head2 Method reactions
148              
149             Returns an arrayref of the reactions.
150              
151             =cut
152              
153             sub reactions {
154             return shift->{'reactions'};
155             }
156              
157             =head2 Method has_reaction
158              
159             Argument is a reaction. Returns 1 if the network contains the raction, 0 otherwise.
160              
161             =cut
162              
163             sub has_reaction {
164             my ( $network, $reaction ) = @_;
165              
166             my $nr = 0;
167             my @rlist = @{ $network->reactions };
168              
169             # print "rlist has ".eval(@rlist)." elements\n";
170             foreach my $netr (@rlist) {
171             $nr++ if ( $netr == $reaction );
172             }
173              
174             return $nr;
175             }
176              
177             =head2 Method add_reaction
178              
179             Argument is a Bio::Metabolic::Reaction.
180             Alters the object in-line, adding the reaction to the list.
181              
182             =cut
183              
184             sub add_reaction {
185             my ( $network, $reaction ) = @_;
186              
187             unless ( $network->has_reaction($reaction) ) {
188             push( @{ $network->reactions }, $reaction );
189             $network->new_matrix;
190             }
191             }
192              
193             =head2 Method remove_reaction
194              
195             Argument is a Bio::Metabolic::Reaction.
196             Altering the object in-line, removeing the reaction from the network.
197             Return undef if network did not have the reaction, 1 on success.
198              
199             =cut
200              
201             sub remove_reaction {
202             my ( $network, $reaction ) = @_;
203              
204             return undef unless $network->has_reaction($reaction);
205              
206             my $cut = 0;
207             my $netr = $network->reactions;
208             my $cnt = 0;
209             while ( $cnt < @$netr ) {
210             if ( $netr->[$cnt] == $reaction ) {
211             splice( @$netr, $cnt, 1 );
212             $network->new_matrix;
213             $cut++;
214             }
215             else {
216             $cnt++;
217             }
218             }
219              
220             # print "remove_reaction: $cut removed.\n";
221             return $cut;
222             }
223              
224             =head2 Method network_to_string
225              
226             Returns a string representation of the network
227              
228             =cut
229              
230             sub network_to_string {
231             my $network = shift;
232              
233             my @rlist = @{ $network->reactions };
234             my $nr = @rlist;
235             my $retstr = "$nr reactions:\n";
236             foreach my $r (@rlist) {
237             $retstr .= $r . "-------------------------------------------\n";
238             }
239             return $retstr;
240             }
241              
242             =head2 Method add_networks
243              
244             Adds an arbitrary number of networks, returning a new object containing all reactions that are
245             contained at least present in one of the networks.
246              
247             =cut
248              
249             sub add_networks {
250             my @nets = @_;
251              
252             # this is due to an extra value passed by the overload Module
253             pop(@nets) if ( ref( $nets[ @nets - 1 ] ) ne ref( $nets[0] ) );
254              
255             croak("add_network needs at least one network!") if @nets == 0;
256              
257             my $newnet = ref( $nets[0] )->new;
258              
259             foreach my $network (@nets) {
260             foreach my $reaction ( @{ $network->reactions } ) {
261             $newnet->add_reaction($reaction);
262             }
263             }
264              
265             return $newnet;
266             }
267              
268             =head2 Method is_in
269              
270             $net1->is_in($net2) Returns 1 if all reactions in $net1 also occur in $net2,
271             i.e. if $net1 is a subnetwork of $net2.
272              
273             =cut
274              
275             sub is_in {
276             my ( $net1, $net2 ) = @_;
277              
278             foreach my $reaction ( @{ $net1->reactions } ) {
279             return 0 unless $net2->has_reaction($reaction);
280             }
281              
282             return 1;
283             }
284              
285             =head2 Method dist
286              
287             Provides a distance measure between networks. Returns the number of reactions that are
288             different in the two networks.
289              
290             =cut
291              
292             sub dist {
293             my ( $net1, $net2 ) = @_;
294              
295             my @r1 = @{ $net1->reactions };
296             my @r2 = @{ $net2->reactions };
297              
298             my $dist = @r1 > @r2 ? @r1 : @r2;
299             foreach my $reaction (@r1) {
300             $dist-- if $net2->has_reaction($reaction);
301             }
302              
303             return $dist;
304             }
305              
306             =head2 Method substrates
307              
308             Returns a Bio::Metabolic::Substrate::Cluster containing all substrates participating in at least
309             one reaction.
310             =cut
311              
312             sub substrates {
313             my $network = shift;
314              
315             my $cluster = Bio::Metabolic::Substrate::Cluster->new;
316              
317             return $cluster->add_clusters(
318             map( $_->substrates, @{ $network->reactions } ) );
319             }
320              
321             =head2 Method matrix
322              
323             Returns the stoichiometric matrix of the network as a PD::Matrix object.
324              
325             =cut
326              
327             sub matrix {
328             my $network = shift;
329              
330             $network->{matrix} = shift if @_;
331             return $network->{matrix};
332             }
333              
334             =head2 Method new_matrix
335              
336             determines the stoichiometric matrix of the network defined by its reactions.
337              
338             =cut
339              
340             sub new_matrix {
341             my $network = shift;
342              
343             my $reactions = $network->reactions;
344             my $substrates = $network->substrates;
345              
346             my @slist = $substrates->list;
347             my $cols = @$reactions;
348             my $rows = @slist;
349              
350             # croak("cannot create matrix from nothing!") if $cols == 0 || $rows == 0;
351             if ( $cols == 0 || $rows == 0 ) {
352             $network->matrix( PDL::Matrix->null );
353             return undef;
354             }
355              
356             my $matrix = mzeroes( $rows, $cols );
357              
358             for ( my $r = 0 ; $r < $cols ; $r++ ) {
359             foreach my $substrate ( $reactions->[$r]->substrates->list ) {
360             my $s = $substrates->which($substrate);
361              
362             $matrix->set( $s, $r,
363             $matrix->at( $s, $r ) +
364             $reactions->[$r]->stoichiometry->{ $substrate->name } );
365             }
366             }
367              
368             $network->matrix($matrix);
369             }
370              
371             =head2 Method print_matrix
372              
373             Prints the matrix in a way that describes which substrates are associated with which rows
374             and which reactions with which columns.
375              
376             =cut
377              
378             sub print_matrix {
379             my $network = shift;
380              
381             my $m = $network->matrix;
382             my @slist = $network->substrates->list;
383              
384             # changed 7.11.02
385             # my ($cols,$rows) = $m->dims;
386             my ( $rows, $cols ) = $m->mdims;
387              
388             my $retstr = sprintf( $OutputFormat{'substrate'}, "" );
389             for ( my $r = 0 ; $r < $cols ; $r++ ) {
390             $retstr .= sprintf( $OutputFormat{'entry'}, $r );
391             }
392             $retstr .= "\n";
393              
394             for ( my $s = 0 ; $s < $rows ; $s++ ) {
395             $retstr .= sprintf( $OutputFormat{'substrate'}, $slist[$s] . ": [" );
396             for ( my $r = 0 ; $r < $cols ; $r++ ) {
397              
398             # changed 7.11.02
399             # $retstr .= sprintf($OutputFormat{'entry'},$m->at($r,$s));
400             $retstr .= sprintf( $OutputFormat{'entry'}, $m->at( $s, $r ) );
401             }
402             $retstr .= "]\n";
403             }
404              
405             return $retstr;
406             }
407              
408             sub can_convert {
409             my $network = shift;
410              
411             my @substrates = ();
412             if (@_) {
413             if ( ref( $_[0] ) eq 'Bio::Metabolic::Substrate::Cluster' ) {
414             @substrates = shift->list;
415             }
416             elsif ( ref( $_[0] ) eq 'ARRAY' ) {
417             @substrates = @{ shift() };
418             }
419             else {
420             @substrates = @_;
421             }
422             }
423              
424             my @ex_indices = ();
425             my $slist = $network->substrates;
426             foreach my $ext (@substrates) {
427             push( @ex_indices, $slist->which($ext) ) if defined $slist->which($ext);
428             }
429              
430             # print "deleting rows (".join(',',@ex_indices).")\n";
431             my $reduced = $network->matrix->copy;
432             $reduced->delrows(@ex_indices);
433              
434             # print "reduced matrix is : $reduced\n";
435              
436             my $kernel = $reduced->kernel();
437              
438             # print "kernel is $kernel\n";
439             return undef if $kernel->isempty;
440              
441             # my ($kerneldim,$nor) = $kernel->dims();
442              
443             my $convert = $network->matrix x $kernel;
444              
445             # print "convert: $convert\n";
446             foreach my $ext (@ex_indices) {
447              
448             # changed 8.11.02
449             # my $res = $convert->slice(":,($ext)");
450             my $res = $convert->slice("($ext),:");
451             return undef if ( $res->where( $res != 0 )->isempty );
452             }
453              
454             return $kernel;
455             }
456              
457             sub is_elementary {
458             my $net = shift;
459              
460             my $kernel = $net->can_convert(@_);
461              
462             return undef unless defined($kernel);
463              
464             my @kdims = $kernel->mdims;
465              
466             return undef if $kdims[1] != 1;
467              
468             return undef if which( $kernel->slice(":,(0)") == 0 )->nelem > 0;
469              
470             my $conversion = $net->matrix x $kernel;
471             my @cdims = $conversion->mdims;
472              
473             return undef
474             if which( $conversion->slice(":,(0)") == 0 )->nelem == $cdims[0];
475              
476             return $kernel;
477             }
478              
479             1;
480             __END__