File Coverage

blib/lib/FASTX/sw.pm
Criterion Covered Total %
statement 106 135 78.5
branch 27 40 67.5
condition 11 18 61.1
subroutine 15 22 68.1
pod 9 9 100.0
total 168 224 75.0


line stmt bran cond sub pod time code
1             package FASTX::sw;
2              
3            
4             # file: Sequence/Alignment.pm
5            
6 13     13   885 use strict;
  13         34  
  13         458  
7 13     13   113 use Carp;
  13         28  
  13         809  
8 13     13   86 use vars '@DEFAULTS';
  13         29  
  13         718  
9 13     13   82 use constant DEBUG=>0;
  13         44  
  13         1218  
10            
11 13     13   86 use vars qw(@ISA @EXPORT @EXPORT_OK);
  13         26  
  13         1709  
12            
13             require Exporter;
14             @ISA = 'Exporter';
15             @EXPORT = ();
16             @EXPORT_OK = qw(align align_segs);
17            
18             # The default scoring matrix introduces a penalty of -2 for opening
19             # a gap in the sequence, but no penalty for extending an already opened
20             # gap. A nucleotide mismatch has a penalty of -1, and a match has a
21             # positive score of +1. An ambiguous nucleotide ("N") can match
22             # anything with no penalty.
23 13         812 use constant DEFAULT_MATRIX => { 'wildcard_match' => 0,
24             'match' => 1,
25             'mismatch' => -1,
26             'gap' => -4,
27             'gap_extend' => 0,
28             'wildcard' => 'N',
29 13     13   97 };
  13         27  
30            
31 13     13   400 use constant SCORE => 0;
  13         44  
  13         787  
32 13     13   84 use constant EVENT => 1;
  13         24  
  13         615  
33 13     13   75 use constant EXTEND => 0;
  13         43  
  13         731  
34 13     13   110 use constant GAP_SRC => 1;
  13         25  
  13         732  
35 13     13   119 use constant GAP_TGT => 2;
  13         30  
  13         21616  
36            
37             my @EVENTS = qw(extend gap_src gap_tgt);
38              
39            
40            
41             # Construct a new alignment object. May be time consuming.
42             sub new {
43 2     2 1 625 my ($class,$src,$tgt,$matrix) = @_;
44 2 50 33     19 croak 'Usage: Realign->new($src,$tgt [,\%matrix])'
45             unless $src && $tgt;
46 2   50     17 my $self = bless {
47             src => $src,
48             target => $tgt,
49             matrix => $matrix || {},
50             },$class;
51 2         4 my $implementor = $class;
52              
53 2         24 my ($score,$alignment) = $implementor->_do_alignment($src,$tgt,$self->{matrix});
54 2         220 $self->{score} = $score;
55 2         5 $self->{alignment} = $alignment;
56 2         14 return $self;
57             }
58              
59            
60            
61             # return the score of the aligned region
62 0     0 1 0 sub score { return shift()->{'score'}; }
63              
64            
65            
66             # return the start of the aligned region
67             sub start {
68 0     0 1 0 return shift()->{'alignment'}->[0];
69             }
70              
71            
72            
73             # return the end of the aligned region
74             sub end {
75 0     0 1 0 my $alignment = shift()->{'alignment'};
76 0         0 return $alignment->[$#$alignment];
77             }
78              
79            
80            
81             # return the alignment as an array
82 0     0 1 0 sub alignment { shift()->{'alignment'}; }
83              
84            
85            
86             # return the alignment as three padded strings for pretty-printing, etc.
87             sub pads {
88 2     2 1 8 my ($align,$src,$tgt) = @{shift()}{'alignment','src','target'};
  2         10  
89 2         5 my ($ps,$pt,$last);
90 2 50       20 $ps = '-' x ($align->[0]) if defined $align->[0]; # pad up the source
91 2 50       12 $pt = substr($tgt,0,$align->[0]) if defined $align->[0];
92 2   100     30 $last = $align->[0] || 0;
93 2         10 for (my $i=0;$i<@$align;$i++) {
94 489         631 my $t = $align->[$i];
95 489 100       666 if (defined $t) {
96 488 50       742 $pt .= $t-$last > 1 ? substr($tgt,$last+1,$t-$last): substr($tgt,$t,1);
97 488         619 $ps .= '-' x ($t-$last-1);
98 488         558 $last = $t;
99             } else {
100 1         3 $pt .= '-';
101             }
102 489         873 $ps .= substr($src,$i,1);
103             }
104             # clean up the ends
105 2         5 $ps .= substr($src,@$align);
106 2         6 $pt .= substr($tgt,$last+1);
107 2 50       10456 $pt .= '-' x (length($ps) - length($pt)) if length($ps) > length($pt);
108 2 50       31 $ps .= '-' x (length($pt) - length($ps)) unless length($ps) > length($pt);
109             my $match = join('',
110 2 100       97 map { uc substr($ps,$_,1) eq uc substr($pt,$_,1) ? '|' : ' ' }
  819         1778  
111             (0..length($pt)-1));
112 2         60 return ($ps,$match,$pt);
113             }
114            
115            
116            
117             # take two sequences as strings, align them and return
118             # a three element array consisting of gapped seq1, match string, and
119             # gapped seq2.
120             sub align {
121 0     0 1 0 my ($seq1,$seq2,$matrix) = @_;
122 0         0 my $align = __PACKAGE__->new($seq1,$seq2,$matrix);
123 0         0 return $align->pads;
124             }
125              
126            
127            
128             sub align_segs {
129 0     0 1 0 my ($gap1,$align,$gap2) = align(@_);
130 0         0 return __PACKAGE__->pads_to_segments($gap1,$align,$gap2);
131             }
132            
133            
134             sub pads_to_segments {
135 0     0 1 0 my $self = shift;
136 0         0 my ($gap1,$align,$gap2) = @_;
137            
138             # create arrays that map residue positions to gap positions
139 0         0 my @maps;
140 0         0 for my $seq ($gap1,$gap2) {
141 0         0 my @seq = split '',$seq;
142 0         0 my @map;
143 0         0 my $residue = 0;
144 0         0 for (my $i=0;$i<@seq;$i++) {
145 0         0 $map[$i] = $residue;
146 0 0       0 $residue++ if $seq[$i] ne '-';
147             }
148 0         0 push @maps,\@map;
149             }
150            
151 0         0 my @result;
152 0         0 while ($align =~ /(\S+)/g) {
153 0         0 my $align_end = pos($align) - 1;
154 0         0 my $align_start = $align_end - length($1) + 1;
155 0         0 push @result,[@{$maps[0]}[$align_start,$align_end],
156 0         0 @{$maps[1]}[$align_start,$align_end]];
  0         0  
157             }
158 0 0       0 return wantarray ? @result : \@result;
159             }
160            
161             sub _do_alignment {
162 2     2   4 my $class = shift;
163 2         10 local $^W = 0;
164 2         5 my($src,$tgt,$custom_matrix) = @_;
165 2         5 my @alignment;
166 2   50     7 $custom_matrix ||= {};
167 2         4 my %matrix = (%{DEFAULT_MATRIX()},%$custom_matrix);
  2         16  
168            
169 2         5 my ($max_score,$max_row,$max_col);
170 2         20 my $scores = [([0,EXTEND])x(length($tgt)+1)];
171            
172 2         4 print join(' ',map {sprintf("%-4s",$_)} (' ',split('',$tgt))),"\n" if DEBUG;
173 2         5 my $wildcard = $matrix{wildcard};
174 2         8 for (my $row=0;$row
175 624         2773 my $s = uc substr($src,$row,1);
176 624         2185 my @row = ([0,EXTEND]);
177 624         1558 for (my $col=0;$col
178 414456         630410 my $t = uc substr($tgt,$col,1);
179            
180             # what happens if we extend the both strands one character?
181 414456         525006 my $extend = $scores->[$col][SCORE];
182             $extend += ($t eq $wildcard || $s eq $wildcard) ? $matrix{wildcard_match} :
183             ($t eq $s) ? $matrix{match} :
184 414456 100 33     1167740 $matrix{mismatch};
    50          
185            
186             # what happens if we extend the src strand one character, gapping the tgt?
187             my $gap_tgt = $row[$#row][SCORE] + (($row[$#row][EVENT]==GAP_TGT) ? $matrix{gap_extend}
188 414456 100       767141 : $matrix{gap});
189            
190             # what happens if we extend the tgt strand one character, gapping the src?
191             my $gap_src = $scores->[$col+1][SCORE] + (($scores->[$col+1][EVENT] == GAP_SRC) ? $matrix{gap_extend}
192 414456 100       783637 : $matrix{gap});
193            
194             # find the best score among the possibilities
195 414456         466945 my $score;
196 414456 100 100     917504 if ($gap_src >= $gap_tgt && $gap_src >= $extend) {
    100          
197 197837         345401 $score = [$gap_src,GAP_SRC];
198             }
199             elsif ($gap_tgt >= $extend) {
200 132393         219365 $score = [$gap_tgt,GAP_TGT];
201             }
202             else {
203 84226         155209 $score = [$extend,EXTEND];
204             }
205            
206             # save it for posterity
207 414456         575338 push(@row,$score);
208 414456 100       932069 ($max_score,$max_row,$max_col) = ($score->[SCORE],$row,$col) if $score->[SCORE] >= $max_score;
209             }
210 624         1035 print join(' ',($s,map {sprintf("%4d",$_->[SCORE])} @row[1..$#row])),"\n" if DEBUG;
211 624         14559 $scores = \@row;
212 624         54752 push(@alignment,[@row[1..$#row]]);
213             }
214 2         18 my $alignment = $class->_trace_back($max_row,$max_col,\@alignment);
215 2         6 if (DEBUG) {
216             for (my $i=0;$i<@$alignment;$i++) {
217             printf STDERR ("%3d %1s %3d %1s\n",
218             $i,substr($src,$i,1),
219             $alignment->[$i],defined $alignment->[$i] ? substr($tgt,$alignment->[$i],1):'');
220             }
221             }
222            
223 2         51861 return ($max_score,$alignment);
224             }
225            
226             sub _trace_back {
227 2     2   5 my $self = shift;
228 2         5 my ($row,$col,$m) = @_;
229 2         4 my @alignment;
230 2   66     15 while ($row >= 0 && $col >= 0) {
231 489         546 printf STDERR "row=%d, col=%d score=%d event=%s\n",$row,$col,$m->[$row][$col][SCORE],$EVENTS[$m->[$row][$col][EVENT]] if DEBUG;
232 489         589 $alignment[$row] = $col;
233 489         1503 my $score = $m->[$row][$col];
234 489 100       1698 $row--,$col--,next if $score->[EVENT] == EXTEND;
235 1 50       5 $col--, next if $score->[EVENT] == GAP_TGT;
236 1 50       9 undef($alignment[$row]),$row--,next if $score->[EVENT] == GAP_SRC;
237             }
238 2         9 return \@alignment;
239             }
240            
241             1;
242              
243             __END__