File Coverage

blib/lib/Chemistry/Reaction.pm
Criterion Covered Total %
statement 103 133 77.4
branch 25 40 62.5
condition 1 3 33.3
subroutine 7 9 77.7
pod 5 5 100.0
total 141 190 74.2


line stmt bran cond sub pod time code
1             package Chemistry::Reaction;
2             $VERSION = '0.02';
3              
4             =head1 NAME
5              
6             Chemistry::Reaction - Explicit chemical reactions
7              
8             =head1 SYNOPSIS
9              
10             use Chemistry::Reaction;
11             use Chemistry::File::SMILES;
12              
13             my $s = Chemistry::Pattern->parse('C=CC=C.C=C', format=>'smiles');
14             my $p = Chemistry::Pattern->parse('C1=CCCCC1', format=>'smiles');
15             my %m;
16             for (my $i = 1; $i <= $s->atoms; $i++) {
17             $m{$s->atoms($i)} = $p->atoms($i);
18             }
19             my $r = Chemistry::Reaction->new($s, $p, \%m);
20              
21             =head1 DESCRIPTION
22              
23             This package, along with Chemistry::Pattern, provides an
24             implementation of explicit chemical reactions.
25              
26             An explicit chemical reaction is a representation of the
27             transformation that takes place in a given chemical reaction. In an
28             explicit chemical reaction, a substrate molecule is transformed into a
29             product molecule by breaking existing bonds and creating new bonds
30             between atoms.
31              
32             The representation of an explicit chemical reaction is a molecule in
33             which the order of a bond before the chemical reaction is
34             distinguished from the order of the bond after the chemical
35             reaction. Thus, the breaking of an existing bond is represented by one
36             of the following before/after pairs:
37              
38             3/2, 2/1, 1/0 (breaking of a single bond or reduce order by one)
39             3/1, 2/0 (breaking of a double bond or reduce order by two)
40             3/0 (breaking of a triple bond)
41              
42             The creation of a new bond is represented by one of the following
43             before/after pairs:
44              
45             0/1, 1/2, 2/3 (creation of a single bond or increase order by one)
46             0/2, 1/3 (creation of a double bond or increase order by two)
47             0/3 (creation of a triple bond)
48              
49             An explicit chemical reaction $react can be forward or reverse applied
50             once to a molecule $mol at the first subgraph of $mol found which is
51             isomorphic to the substrate or product of $react:
52              
53             my $subst = $react->substrate;
54             if ($subst->match($mol)) {
55             $react->forward($mol, $subst->atom_map);
56             }
57              
58             Also, an explicit chemical reaction $react can be forward or reverse
59             applied once to a molecule $mol at each subgraph of $mol which is
60             isomorphic to the substrate or product of $react:
61              
62             my $subst = $react->substrate;
63             my @products;
64             while ($subst->match($mol)) {
65             my $new_mol = $mol->clone; # start from a fresh molecule
66             my @map = $subst->atom_map;
67             # translate atom map to the clone
68             my @m = map { $new_mol->by_id($_->id) } @map;
69             $react->forward($new_mol, @m);
70             push @products, $new_mol;
71             }
72              
73             Furthermore, an explicit chemical reaction $react can be forward or
74             reverse applied as long as possible to a molecule $mol at the first
75             subgraph of $mol found which is isomorphic to the substrate or product
76             of $react:
77              
78             my $subst = $react->substrate;
79             while ($subst->match($mol)) {
80             $react->forward($mol, $subst->atom_map);
81             }
82              
83             =cut
84              
85 1     1   197110 use 5.006;
  1         4  
  1         47  
86 1     1   7 use strict;
  1         2  
  1         51  
87 1     1   8 use warnings;
  1         6  
  1         44  
88 1     1   5 use base qw(Chemistry::Pattern);
  1         2  
  1         1912  
89              
90             =head1 METHODS
91              
92             =over 4
93              
94             =item Chemistry::Reaction->new($subst, $prod, \%map)
95              
96             Create a new Reaction object that describes the transformation of the
97             $subst substrate into the $prod product, according to the %map mapping
98             of substrate atoms to product atoms.
99              
100             =cut
101              
102             sub new {
103 4     4 1 19583 my ($class, $substrate, $product, $mapping, %args) = @_;
104              
105 4 50       22 die sprintf(
106             "$class substrate and product must coincide on atoms (%s ne %s)\n",
107             $substrate->formula, $product->formula)
108             if $substrate->formula ne $product->formula;
109              
110 4         17482 my $order1 = 0;
111 4         28 foreach my $bond ($substrate->bonds) {
112 14         85 $order1 += $bond->order;
113             }
114 4         21 my $order2 = 0;
115 4         11 foreach my $bond ($product->bonds) {
116 18         100 $order2 += $bond->order;
117             }
118 4 50       28 die "$class substrate and product must coincide on total bond order\n"
119             if $order1 != $order2;
120              
121 4         12 foreach my $atom ($substrate->atoms) {
122 21 50       332 die sprintf(
123             "$class substrate and product must coincide on atom symbols "
124             ."(%s ne %s)\n", $atom->symbol, $mapping->{$atom}->symbol)
125             if $atom->symbol ne $mapping->{$atom}->symbol;
126             }
127              
128 4         75 foreach my $atom ($product->atoms) {
129 21         212 $atom->attr("reaction/mapped", 1);
130             }
131 4         45 foreach my $atom ($substrate->atoms) {
132 21         255 $mapping->{$atom}->attr("reaction/mapped", 0);
133             }
134 4         57 foreach my $atom ($product->atoms) {
135 21 50       153 die "$class atom mapping must be bijective\n"
136             if $atom->attr("reaction/mapped");
137             }
138              
139             # the substrate of the reaction is cloned in order to isolate all
140             # changes to bond orders from the given $substrate
141            
142 4         40 my $self = $substrate->clone;
143 4   33     1197 bless $self, ref $class || $class;
144              
145 4         28 $self->$_($args{$_}) for (keys %args);
146             # the $mapping array gives the product atom which each substrate
147             # atom is mapped to, and the %unmapping hash gives back the
148             # substrate atom which each product atom is mapped to
149            
150 4         173 my %unmapping;
151 4         10 foreach my $a1 (keys %$mapping) {
152 21         157 my $a2 = ${$mapping}{$a1};
  21         31  
153 21         48 $unmapping{$a2} = $a1;
154             }
155              
156             # the %bonds hash gives an array of substrate and product bond
157             # orders for each pair of atoms which are bonded in substrate or in
158             # the product of the reaction
159              
160             # first of all, set $bonds{$a1}{$a2} to an array containing only the
161             # substrate bond order, for each pair of atoms $a1 and $a2 which are
162             # bonded in the substrate
163              
164 4         34 my %bonds;
165 4         17 foreach my $bond ($self->bonds) {
166 14         203 my @atoms = $bond->atoms;
167 14 50       108 @atoms[0,1] = @atoms[1,0] unless $atoms[0] lt $atoms[1];
168 14         89 $bonds{$atoms[0]}{$atoms[1]} = [$bond->order];
169             }
170              
171             # then, for each pair of atoms $a1 and $a2 which are bonded in the
172             # product, append their product bond order to the array
173             # $bonds{$a1}{$a2}, preceded by zero (as substrate bond order) if
174             # these atoms are not bonded in the substrate
175            
176 4         70 foreach my $bond ($product->bonds) {
177 18         133 my @atoms = $bond->atoms;
178 18         123 my $a1 = $unmapping{$atoms[0]};
179 18         145 my $a2 = $unmapping{$atoms[1]};
180 18 100       121 @atoms[0,1] = @atoms[1,0] unless $atoms[0] lt $atoms[1];
181 18 100       122 $bonds{$a1}{$a2} = [0] unless defined $bonds{$a1}{$a2};
182 18         17 push @{$bonds{$a1}{$a2}}, $bond->order;
  18         54  
183             }
184              
185             # finally, for each pair of atoms $a1 and $a2 which are bonded in
186             # the substrate but not in the product, append zero (as product bond
187             # order) to the array $bonds{$a1}{$a2}
188              
189 4         28 foreach my $a1 (keys %bonds) {
190 15         13 foreach my $a2 (keys %{$bonds{$a1}}) {
  15         31  
191 21         22 my $a = $bonds{$a1}{$a2};
192 21 100       53 push @$a, 0 unless defined $a->[1];
193             }
194             }
195              
196             # now, for each pair of atoms $a1 and $a2 which are bonded in the
197             # substrate or in the product, the array array $bonds{$a1}{$a2}
198             # contains exactly two entries: the substrate bond order (zero if
199             # not bonded) and the product bond order (zero if not bonded)
200              
201             # for each bond $bond in the substrate, the substrate bond order is
202             # stored in $bond->attr("reaction/before"), and the product bond
203             # order (if any) is stored in $bond->attr("reaction/after")
204            
205 4         13 foreach my $bond ($self->bonds) {
206 14         127 my @atoms = $bond->atoms;
207 14         82 my @a = @{$bonds{$atoms[0]}{$atoms[1]}};
  14         29  
208 14         191 $bond->attr("reaction/before", $a[0]);
209 14         142 $bond->attr("reaction/after", $a[1]);
210             }
211            
212             # further, for each bond $bond in the product but not in the
213             # substrate, $bond->attr("reaction/before") is set to zero and the
214             # product bond order is stored in $bond->attr("reaction/after")
215            
216 4         47 foreach my $bond ($product->bonds) {
217 18         92 my @atoms = $bond->atoms;
218 18         119 my $a1 = $unmapping{$atoms[0]};
219 18         126 my $a2 = $unmapping{$atoms[1]};
220 18 100       138 @atoms[0,1] = @atoms[1,0] unless $atoms[0] lt $atoms[1];
221 18         93 my @a = @{$bonds{$a1}{$a2}};
  18         41  
222 18 100       46 if ($a[0] == 0) {
223 7         38 my $b = $self->new_bond(atoms =>
224             [$self->by_id($a1), $self->by_id($a2)]);
225 7         777 $b->attr("reaction/before", $a[0]);
226 7         74 $b->attr("reaction/after", $a[1]);
227             }
228             }
229            
230 4         58 return $self;
231             }
232              
233             =item $react->substrate
234              
235             Return a Chemistry::Pattern object that represents the substrate
236             molecules of the explicit chemical reaction $react.
237              
238             =cut
239              
240             # the substrate molecule is obtained from a clone of the reaction by
241             # breaking all bonds with substrate order equal to zero and setting
242             # the order of each remaining $bond to $bond->attr("reaction/before")
243              
244             sub substrate {
245 16     16 1 106838 my $react = shift;
246 16         70 my $self = $react->clone;
247 16         4459 foreach my $bond ($self->bonds) {
248 84 100       1792 if ($bond->attr("reaction/before") == 0) {
249 28         290 $bond->delete;
250             } else {
251 56         520 $bond->order($bond->attr("reaction/before"));
252 56         621 $bond->del_attr("reaction/before");
253 56         351 $bond->del_attr("reaction/after");
254             }
255             }
256 16         1607 return $self;
257             }
258              
259             =item $react->product
260              
261             Return a Chemistry::Pattern object that represents the product
262             molecules of the explicit chemical reaction $react.
263              
264             =cut
265              
266             # the product molecule is obtained from a clone of the reaction by
267             # breaking all bonds with product order equal to zero and setting the
268             # order of each remaining $bond to $bond->attr("reaction/after")
269              
270             sub product {
271 0     0 1 0 my $react = shift;
272 0         0 my $self = $react->clone;
273 0         0 foreach my $bond ($self->bonds) {
274 0 0       0 if ($bond->attr("reaction/after") == 0) {
275 0         0 $bond->delete;
276             } else {
277 0         0 $bond->order($bond->attr("reaction/after"));
278 0         0 $bond->del_attr("reaction/before");
279 0         0 $bond->del_attr("reaction/after");
280             }
281             }
282 0         0 return $self;
283             }
284              
285             # the map of substrate atoms to product atoms is just the identity
286             # mapping from $react->substrate->atoms to $react->product->atoms
287              
288             =item $react->forward($mol, @map)
289              
290             Forward application of the explicit chemical reaction $react to the
291             molecule $mol, according to the mapping @map of substrate atoms to
292             $mol atoms. The substrate of the explicit chemical reaction $react
293             must be a subgraph of the molecule $mol. Return the modified molecule
294             $mol.
295              
296             =cut
297              
298             sub forward {
299 10     10 1 25828 my ($react, $mol, @map) = @_;
300              
301             # the %occ hash gives the occurrence of each $react atom in $mol
302              
303 10         17 my %occ;
304 10         30 for (my $i = 0; $i < $react->atoms; $i++) {
305 53         980 $occ{$react->atoms($i+1)} = $map[$i];
306             }
307              
308             # for each bond $bond in the reaction $react, either change the
309             # corresponding bond $b in the molecule $mol to the bond resulting
310             # from the forward application of the reaction, or break an existing
311             # bond $b, or form a new bond $b between the corresponding atoms $a1
312             # and $a2 in $mol
313              
314 10         187 foreach my $bond ($react->bonds) {
315 53         1657 my @atoms = $bond->atoms;
316 53         322 my $a1 = $atoms[0];
317 53         56 my $a2 = $atoms[1];
318 53         56 my $b; # bond between $occ{$a1} and $occ{$a2} in $mol
319 53         125 foreach my $bb ($occ{$a1}->bonds) {
320 79         1049 foreach my $aa ($bb->atoms) {
321 158 100       1477 if ($aa eq $occ{$a2}) {
322 36         347 $b = $bb;
323 36         55 last;
324             }
325             }
326 79 100       603 if ($b) { last; }
  36         182  
327             }
328 53 100       126 if ($b) {
329 36         215 $b->order($b->order
330             -$bond->attr('reaction/before')
331             +$bond->attr('reaction/after'));
332 36 100       684 if ($b->order == 0) {
333 3         24 $mol->delete_bond($b);
334             }
335             } else {
336 17         48 $mol->new_bond(atoms =>
337             [$mol->by_id($occ{$a1}), $mol->by_id($occ{$a2})],
338             order => $bond->attr('reaction/after'));
339             }
340             }
341             }
342              
343             =item $react->reverse($mol, @map)
344              
345             Reverse application of the explicit chemical reaction $react to the
346             molecule $mol, according to the mapping @map of product atoms to $mol
347             atoms. The product of the explicit chemical reaction $react must be a
348             subgraph of the molecule $mol. Return the modified molecule $mol.
349              
350             =cut
351              
352             sub reverse {
353 0     0 1   my ($react, $mol, @map) = @_;
354              
355             # the %occ hash gives the occurrence of each $react atom in $mol
356              
357 0           my %occ;
358 0           for (my $i = 0; $i < $react->atoms; $i++) {
359 0           $occ{$react->atoms($i+1)} = $map[$i];
360             }
361              
362             # for each bond $bond in the reaction $react, either change the
363             # corresponding bond $b in the molecule $mol to the bond resulting
364             # from the reverse application of the reaction, or break an existing
365             # bond $b, or form a new bond $b between the corresponding atoms $a1
366             # and $a2 in $mol
367              
368 0           foreach my $bond ($react->bonds) {
369 0           my @atoms = $bond->atoms;
370 0           my $a1 = $atoms[0];
371 0           my $a2 = $atoms[1];
372 0           my $b; # bond between $occ{$a1} and $occ{$a2} in $mol
373 0           foreach my $bb ($occ{$a1}->bonds) {
374 0           foreach my $aa ($bb->atoms) {
375 0 0         if ($aa eq $occ{$a2}) {
376 0           $b = $bb;
377 0           last;
378             }
379             }
380 0 0         if ($b) { last; }
  0            
381             }
382 0 0         if ($b) {
383 0           $b->order($b->order
384             -$bond->attr('reaction/after')
385             +$bond->attr('reaction/before'));
386 0 0         if ($b->order == 0) {
387 0           $mol->delete_bond($b);
388             }
389             } else {
390 0           $mol->new_bond(atoms =>
391             [$mol->by_id($occ{$a1}), $mol->by_id($occ{$a2})],
392             order => $bond->attr('reaction/before'));
393             }
394             }
395             }
396              
397             =back
398              
399             =head1 VERSION
400              
401             0.02
402              
403             =head1 SEE ALSO
404              
405             L, L, L
406              
407             Rosselló, F. and G. Valiente, Analysis of metabolic pathways by graph
408             transformation, in: Proc. 2nd Int. Conf. Graph Transformation, Lecture
409             Notes in Computer Science 3256 (2004), pp. 73--85.
410              
411             Rosselló, F. and G. Valiente, Chemical graphs, chemical reaction
412             graphs, and chemical graph transformation, in: Proc. 2nd Int. Workshop
413             on Graph-Based Tools, Electronic Notes in Theoretical Computer Science
414             (2004), in press.
415              
416             The PerlMol website L
417              
418             =head1 AUTHOR
419              
420             Ivan Tubert-Brohman Eitub@cpan.orgE and Gabriel Valiente
421             Evaliente@lsi.upc.esE
422              
423             =head1 COPYRIGHT
424              
425             Copyright (c) 2004 Ivan Tubert-Brohman and Gabriel Valiente. All
426             rights reserved. This program is free software; you can redistribute
427             it and/or modify it under the same terms as Perl itself.
428              
429             =cut