align 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format nested
3
+ -b
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.3.0"
10
+ gem "yard", "~> 0.6.0"
11
+ # kramdown provides pure-ruby markdown for yard.
12
+ gem "kramdown", "~> 0.13"
13
+ # Code coverage the way I like.
14
+ gem 'simplecov', '>= 0.4.0'
15
+ gem "bundler", "~> 1.0.0"
16
+ gem "jeweler", "~> 1.5.2"
17
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Michael Ryan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ # align
2
+ align provides implementations of global and local alignment algorithms.
3
+ This library was created as it seemed that the Ruby landscape seemed to lack
4
+ implementations of the [Needleman-Wunsch](http://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm)
5
+ and [Smith–Waterman](http://en.wikipedia.org/wiki/Smith-Waterman_algorithm)
6
+ algorithms. These algorithms are most often associated with the field of
7
+ [Bioinformatics](http://en.wikipedia.org/wiki/Bioinformatics), and allow for
8
+ an efficient mechanisms for aligning protein and nucleotide sequences.
9
+
10
+ The goal of this library is to provide implementations of algorithms
11
+ in a way that they might be used to align anything from simple strings to
12
+ complicated objects. As time goes on, I'll be sure to make an effort to provide
13
+ examples of how this may be done.
14
+
15
+ ## Contributing to align
16
+
17
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
18
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
19
+ * Fork the project
20
+ * Start a feature/bugfix branch
21
+ * Commit and push until you are happy with your contribution
22
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
23
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
24
+
25
+ ## Copyright
26
+
27
+ Copyright (c) 2011 Michael Ryan. See LICENSE.txt for further details.
28
+
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "align"
16
+ gem.homepage = "http://github.com/justfalter/align"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Provides sequence alignment algorithms}
19
+ gem.description = %Q{Provides sequence alignment algorithms}
20
+ gem.email = "falter@gmail.com"
21
+ gem.authors = ["Mike Ryan"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ desc "Runs specs with coverage via Simplecov"
36
+ task :coverage do
37
+ ENV['COVERAGE'] = "true"
38
+ Rake::Task[:spec].invoke
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'yard'
44
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,2 @@
1
+ require 'align/needleman_wunsch'
2
+ require 'align/smith_waterman'
@@ -0,0 +1,25 @@
1
+ module Align
2
+ # Basic Scoring interface
3
+ class BasicScoring
4
+ # @param [Numeric] align_match Price for alignment.
5
+ # @param [Numeric] align_mismatch Penalty for misalignment
6
+ # @param [Numeric] gap_penalty Gap penalty for insert/delete
7
+ def initialize(align_match, align_mismatch, gap_penalty)
8
+ @align_match = align_match
9
+ @align_mismatch = align_mismatch
10
+ @gap_penalty = gap_penalty
11
+ end
12
+
13
+ def score_align(a,b)
14
+ (a == b) ? @align_match : @align_mismatch
15
+ end
16
+
17
+ def score_insert(a)
18
+ @gap_penalty
19
+ end
20
+
21
+ def score_delete(a)
22
+ @gap_penalty
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,157 @@
1
+ require 'align/basic_scoring'
2
+ require 'align/pairwise_algorithm'
3
+
4
+ module Align
5
+ # Align two sequences via [NeedlemanWunsch.align]
6
+ # References:
7
+ # [http://www.avatar.se/molbioinfo2001/dynprog/dynamic.html]
8
+ class NeedlemanWunsch < PairwiseAlgorithm
9
+ attr_reader :cols, :rows, :matrix
10
+
11
+ # Default scoring for
12
+ SCORING_DEFAULT = Align::BasicScoring.new(1,0,0)
13
+ SCORING_ALT1 = Align::BasicScoring.new(1,-1,-1)
14
+
15
+ # @param [#[], #size] seq1 The first sequence
16
+ # @param [#[], #size] seq2 The second sequence
17
+ # @param [Hash] opts Options
18
+ # @option opts [NeedlemanWunschScoring] :scoring (NeedlemanWunschScoring) An instance of a scoring object.
19
+ # @option opts [Object] :skip_obj (nil) An object to shove into the gaps of
20
+ # the aligned sequences
21
+ def initialize(seq1, seq2, opts = {})
22
+ super(seq1, seq2, opts[:scoring] || SCORING_DEFAULT)
23
+
24
+ @rows = @seq1.size + 1
25
+ @cols = @seq2.size + 1
26
+
27
+ @skip_obj = opts[:skip_obj] || nil
28
+
29
+ @matrix = Array.new(@rows) do
30
+ Array.new(@cols)
31
+ end
32
+
33
+ fill()
34
+ end
35
+
36
+ def score
37
+ @matrix[@rows-1][@cols-1]
38
+ end
39
+
40
+ # Fills the matrix with the alignment map.
41
+ def fill
42
+ @matrix[0][0] = 0
43
+ # Set up the first column on each row.
44
+ 1.upto(@rows-1) {|i| @matrix[i][0] = @matrix[i-1][0] + @scoring.score_delete(@seq1[i])}
45
+ # Set up the first row
46
+ 1.upto(@cols-1) {|j| @matrix[0][j] = @matrix[0][j-1] + @scoring.score_insert(@seq2[j])}
47
+
48
+ 1.upto(@rows-1) do |i|
49
+ prv_row = @matrix[i-1]
50
+ cur_row = @matrix[i]
51
+
52
+ 1.upto(@cols-1) do |j|
53
+
54
+ seq1_obj = @seq1[i-1]
55
+ seq2_obj = @seq2[j-1]
56
+
57
+ # Calculate the score.
58
+ score_align = prv_row[j-1] + @scoring.score_align(seq1_obj, seq2_obj)
59
+ score_delete = prv_row[j] + @scoring.score_delete(seq1_obj)
60
+ score_insert = cur_row[j-1] + @scoring.score_insert(seq2_obj)
61
+ max = max3(score_align, score_delete, score_insert)
62
+
63
+ @matrix[i][j] = max
64
+ end
65
+ end
66
+ end # fill
67
+
68
+ # Traces backward, finding the alignment.
69
+ # @yield [i,j,step]
70
+ # @yieldparam i [Integer] The location in sequence one
71
+ # @yieldparam j [Integer] The location in sequence two
72
+ # @yieldparam step [Integer] The direction we took
73
+ def traceback
74
+ i = @rows - 1
75
+ j = @cols - 1
76
+
77
+ while (i > 0 && j > 0)
78
+ score = @matrix[i][j]
79
+
80
+ seq1_obj = @seq1[i-1]
81
+ seq2_obj = @seq2[j-1]
82
+
83
+ score_align = @matrix[i-1][j-1] + @scoring.score_align(seq1_obj, seq2_obj)
84
+ score_delete = @matrix[i-1][j] + @scoring.score_delete(seq1_obj)
85
+ score_insert = @matrix[i][j-1] + @scoring.score_insert(seq2_obj)
86
+
87
+ flags = 0
88
+ need_select = false
89
+
90
+ if score == score_align
91
+ flags = :align
92
+ i-=1
93
+ j-=1
94
+ elsif score == score_delete
95
+ flags = :delete
96
+ i-=1
97
+ else
98
+ flags = :insert
99
+ j-=1
100
+ end
101
+
102
+ yield(i,j,flags)
103
+ end # while
104
+
105
+ while i > 0
106
+ i-=1
107
+ yield(i,j,:delete)
108
+ end
109
+
110
+ while j > 0
111
+ j-=1
112
+ yield(i,j,:insert)
113
+ end
114
+ end # traceback
115
+
116
+ # Like traceback, but returns an array of the traceback instead of
117
+ # yielding blocks.
118
+ def traceback_array
119
+ trace = []
120
+ traceback do |i,j,flags|
121
+ trace << [i,j,flags]
122
+ end
123
+ trace
124
+ end # traceback_array
125
+
126
+ # Returns the sequences in aligned arrays. Gaps are filled with :skip_obj
127
+ # @return Two arrays containing the sequences, and their elements.
128
+ def align
129
+ alignment_1 = []
130
+ alignment_2 = []
131
+
132
+ traceback do |i, j, flags|
133
+ seq1_val = seq2_val = @skip_obj
134
+ case flags
135
+ when :align
136
+ seq1_val = @seq1[i]
137
+ seq2_val = @seq2[j]
138
+ when :insert
139
+ seq2_val = @seq2[j]
140
+ when :delete
141
+ seq1_val = @seq1[i]
142
+ end
143
+ alignment_1.unshift seq1_val
144
+ alignment_2.unshift seq2_val
145
+ end
146
+
147
+ [alignment_1, alignment_2]
148
+ end
149
+
150
+ # Aligns two sequences together.
151
+ # @param see (#initialize)
152
+ def self.align(seq1, seq2, opts = {})
153
+ self.new(seq1, seq2, opts).align
154
+ end
155
+
156
+ end
157
+ end
@@ -0,0 +1,38 @@
1
+ module Align
2
+ # Provides a base for algorithms that align two sequences.
3
+ class PairwiseAlgorithm
4
+ attr_reader :seq1, :seq2, :scoring
5
+
6
+ def initialize(seq1, seq2, scoring)
7
+ @seq1 = seq1
8
+ @seq2 = seq2
9
+ @scoring = scoring
10
+ end
11
+
12
+ # Max of 2
13
+ def max2(a,b)
14
+ a >= b ? a : b
15
+ end
16
+
17
+ # Determines the maximum value of three variables. 3-4 times faster than
18
+ # [a,b,c].max.
19
+ def max3(a,b,c)
20
+ (a >= b) ? ((a >= c)? a : c) : ((b >= c)? b : c)
21
+ end
22
+
23
+ # Returns the max of 4 integers
24
+ def max4(a,b,c,d)
25
+ x = a >= b ? a : b
26
+ y = c >= d ? c : d
27
+ (x >= y) ? x : y
28
+ end
29
+
30
+
31
+
32
+ # Returns the sequences in aligned arrays. Gaps are filled with :skip_obj
33
+ # @return Two arrays containing the sequences, and their elements.
34
+ def align
35
+ raise NotImplementedError.new("#{self.class}#align")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,157 @@
1
+ require 'align/basic_scoring'
2
+ require 'align/pairwise_algorithm'
3
+
4
+ module Align
5
+ # Align two sequences via [SmithWaterman.align]
6
+ # References:
7
+ # [http://www.avatar.se/molbioinfo2001/dynprog/dynamic.html]
8
+ class SmithWaterman < PairwiseAlgorithm
9
+ attr_reader :cols, :rows, :matrix, :max_score, :max_row, :max_col
10
+
11
+ # Default scoring for
12
+ SCORING_DEFAULT = BasicScoring.new(2,-1,-3)
13
+
14
+ # @param [#[], #size] seq1 The first sequence
15
+ # @param [#[], #size] seq2 The second sequence
16
+ # @param [Hash] opts Options
17
+ # @option opts [SmithWatermanScoring] :scoring (SmithWatermanScoring) An instance of a scoring object.
18
+ # @option opts [Object] :skip_obj (nil) An object to shove into the gaps of
19
+ # the aligned sequences
20
+ def initialize(seq1, seq2, opts = {})
21
+ super(seq1, seq2, opts[:scoring] || SCORING_DEFAULT)
22
+
23
+ @max_score = nil
24
+ @max_row = nil
25
+ @max_col = nil
26
+
27
+ @rows = @seq1.size + 1
28
+ @cols = @seq2.size + 1
29
+
30
+ @skip_obj = opts[:skip_obj] || nil
31
+
32
+ @matrix = Array.new(@rows) do
33
+ Array.new(@cols)
34
+ end
35
+
36
+ fill()
37
+ end
38
+
39
+ # Fills the matrix with the alignment map.
40
+ def fill
41
+ @matrix[0][0] = 0
42
+ # Set up the first column on each row.
43
+ 1.upto(@rows-1) {|i| @matrix[i][0] = 0}
44
+ # Set up the first row
45
+ 1.upto(@cols-1) {|j| @matrix[0][j] = 0}
46
+
47
+ 1.upto(@rows-1) do |i|
48
+ prv_row = @matrix[i-1]
49
+ cur_row = @matrix[i]
50
+
51
+ 1.upto(@cols-1) do |j|
52
+
53
+ seq1_obj = @seq1[i-1]
54
+ seq2_obj = @seq2[j-1]
55
+
56
+ # Calculate the score.
57
+ score_align = prv_row[j-1] + @scoring.score_align(seq1_obj, seq2_obj)
58
+ score_delete = prv_row[j] + @scoring.score_delete(seq1_obj)
59
+ score_insert = cur_row[j-1] + @scoring.score_insert(seq2_obj)
60
+ max = max4(score_align, score_delete, score_insert, 0)
61
+
62
+ if @max_score.nil? || max >= @max_score
63
+ @max_score = max
64
+ @max_row = i
65
+ @max_col = j
66
+ end
67
+
68
+ @matrix[i][j] = max
69
+ end
70
+ end
71
+ end # fill
72
+
73
+ # Traces backward, finding the alignment.
74
+ # @param [Integer] i The row to traceback from
75
+ # @param [Integer] j The column to traceback from
76
+ # @yield [i,j,step]
77
+ # @yieldparam i [Integer] The location in sequence one
78
+ # @yieldparam j [Integer] The location in sequence two
79
+ # @yieldparam step [Integer] The direction we took
80
+ def traceback(i = @max_row, j = @max_col)
81
+
82
+ while (i > 0 && j > 0) && @matrix[i][j] > 0
83
+ score = @matrix[i][j]
84
+
85
+ seq1_obj = @seq1[i-1]
86
+ seq2_obj = @seq2[j-1]
87
+
88
+ score_align = @matrix[i-1][j-1] + @scoring.score_align(seq1_obj, seq2_obj)
89
+ score_delete = @matrix[i-1][j] + @scoring.score_delete(seq1_obj)
90
+ score_insert = @matrix[i][j-1] + @scoring.score_insert(seq2_obj)
91
+
92
+ flags = 0
93
+ need_select = false
94
+
95
+ if score == score_align
96
+ flags = :align
97
+ i-=1
98
+ j-=1
99
+ elsif score == score_delete
100
+ flags = :delete
101
+ i-=1
102
+ else
103
+ flags = :insert
104
+ j-=1
105
+ end
106
+
107
+ yield(i,j,flags)
108
+ end # while
109
+
110
+ end # traceback
111
+
112
+ # Like traceback, but returns an array of the traceback instead of
113
+ # yielding blocks.
114
+ # @param [Integer] r The row to traceback from
115
+ # @param [Integer] c The column to traceback from
116
+ def traceback_array(r = @max_row, c = @max_col)
117
+ trace = []
118
+ traceback(r,c) do |i,j,flags|
119
+ trace << [i,j,flags]
120
+ end
121
+ trace
122
+ end # traceback_array
123
+
124
+ # Returns the sequences in aligned arrays. Gaps are filled with :skip_obj
125
+ # @return Two arrays containing the sequences, and their elements.
126
+ # @param [Integer] r The row to traceback from
127
+ # @param [Integer] c The column to traceback from
128
+ def align(r = @max_row, c = @max_col)
129
+ alignment_1 = []
130
+ alignment_2 = []
131
+
132
+ traceback(r,c) do |i, j, flags|
133
+ seq1_val = seq2_val = @skip_obj
134
+ case flags
135
+ when :align
136
+ seq1_val = @seq1[i]
137
+ seq2_val = @seq2[j]
138
+ when :insert
139
+ seq2_val = @seq2[j]
140
+ when :delete
141
+ seq1_val = @seq1[i]
142
+ end
143
+ alignment_1.unshift seq1_val
144
+ alignment_2.unshift seq2_val
145
+ end
146
+
147
+ [alignment_1, alignment_2]
148
+ end
149
+
150
+ # Aligns two sequences together.
151
+ # @param see (#initialize)
152
+ def self.align(seq1, seq2, opts = {})
153
+ self.new(seq1, seq2, opts).align
154
+ end
155
+
156
+ end
157
+ end
@@ -0,0 +1,156 @@
1
+ require_relative 'spec_helper'
2
+ require 'align/needleman_wunsch'
3
+
4
+ shared_examples_for "a needleman-wunsch alignment algorithm" do
5
+ examples = [
6
+ ['bcefg', 'abcdef', '-bc-efg', 'abcdef-'],
7
+ ['GAATTCAGTTA', 'GGATCGA', 'GAATTCAGTTA', 'GGA-TC-G--A']
8
+ ]
9
+
10
+ examples.each do |seq1, seq2, exp1, exp2|
11
+ context "when aligning '#{seq1}' and '#{seq2}'" do
12
+ before :each do
13
+ @opts[:skip_obj] = "-"
14
+ @align1, @align2 = @klass.align(seq1, seq2, @opts)
15
+ end
16
+
17
+ it "should align 1 as '#{exp1}'" do
18
+ @align1.join('').should == exp1
19
+ end
20
+ it "should align 2 as '#{exp2}'" do
21
+ @align2.join('').should == exp2
22
+ end
23
+ end
24
+
25
+ context "when aligning '#{seq2}' and '#{seq1}'" do
26
+ before :each do
27
+ @opts[:skip_obj] = "-"
28
+ @align1, @align2 = @klass.align(seq2, seq1, @opts)
29
+ end
30
+
31
+ it "should align 1 as '#{exp2}'" do
32
+ @align1.join('').should == exp2
33
+ end
34
+ it "should align 2 as '#{exp1}'" do
35
+ @align2.join('').should == exp1
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ context "with two similar sequences" do
42
+ before :each do
43
+ @seq1 = "GAATTCAGTTA"
44
+ @seq2 = "GGATCGA"
45
+ @nw = @klass.new(@seq1, @seq2, @opts)
46
+ end
47
+
48
+ # Actual matrix
49
+ # #0 1 2 3 4 5 6 7 8 9 A B
50
+ # #- G A A T T C A G T T A
51
+ # 0 - [0,0,0,0,0,0,0,0,0,0,0,0]
52
+ # 1 G [0,1,1,1,1,1,1,1,1,1,1,1]
53
+ # 2 G [0,1,1,1,1,1,1,1,2,2,2,2]
54
+ # 3 A [0,1,2,2,2,2,2,2,2,2,2,3]
55
+ # 4 T [0,1,2,2,3,3,3,3,3,3,3,3]
56
+ # 5 C [0,1,2,2,3,3,4,4,4,4,4,4]
57
+ # 6 G [0,1,2,2,3,3,4,4,5,5,5,5]
58
+ # 7 A [0,1,2,3,3,3,4,5,5,5,5,6]
59
+
60
+ # Actual matrix
61
+ # #0 1 2 3 4 5 6 7 8 9 A B
62
+ # #- G A A T T C A G T T A
63
+ # 0 - [0,0,0,0,0,0,0,0,0,0,0,0]
64
+ # 1 G [0,X,1,1,1,1,1,1,1,1,1,1]
65
+ # 2 G [0,1,X,1,1,1,1,1,2,2,2,2]
66
+ # 3 A [0,1,2,X,X,2,2,2,2,2,2,3]
67
+ # 4 T [0,1,2,2,3,X,3,3,3,3,3,3]
68
+ # 5 C [0,1,2,2,3,3,X,X,4,4,4,4]
69
+ # 6 G [0,1,2,2,3,3,4,4,X,X,X,5]
70
+ # 7 A [0,1,2,3,3,3,4,5,5,5,5,X]
71
+
72
+ subject {@nw}
73
+ its(:rows) { should == @seq1.size + 1}
74
+ its(:cols) { should == @seq2.size + 1}
75
+
76
+ it "should use the score for the lower, right" do
77
+ @nw.score.should == @score
78
+ end
79
+
80
+ # it "should have a properly built score matrix" do
81
+ # @nw.matrix.should ==
82
+ # [
83
+ # [0,0,0,0,0,0,0,0],
84
+ # [0,1,1,1,1,1,1,1],
85
+ # [0,1,1,2,2,2,2,2],
86
+ # [0,1,1,2,2,2,2,3],
87
+ # [0,1,1,2,3,3,3,3],
88
+ # [0,1,1,2,3,3,3,3],
89
+ # [0,1,1,2,3,4,4,4],
90
+ # [0,1,1,2,3,4,4,5],
91
+ # [0,1,2,2,3,4,5,5],
92
+ # [0,1,2,2,3,4,5,5],
93
+ # [0,1,2,2,3,4,5,5],
94
+ # [0,1,2,3,3,4,5,6],
95
+ # ]
96
+ # end
97
+
98
+ it "should be possible to return the proper traceback" do
99
+ @nw.traceback_array.should == [
100
+ [10,6, :align],
101
+ [9,6, :delete],
102
+ [8,6, :delete],
103
+ [7,5, :align],
104
+ [6,5, :delete],
105
+ [5,4, :align],
106
+ [4,3, :align],
107
+ [3,3, :delete],
108
+ [2,2, :align],
109
+ [1,1, :align],
110
+ [0,0, :align]
111
+ ]
112
+ end
113
+
114
+ it "should be possible to construct the proper traceback via yield" do
115
+ traceback = []
116
+ @nw.traceback do |i,j, last_move|
117
+ traceback << [i,j, last_move]
118
+ end
119
+ traceback.should == [
120
+ [10,6, :align],
121
+ [9,6, :delete],
122
+ [8,6, :delete],
123
+ [7,5, :align],
124
+ [6,5, :delete],
125
+ [5,4, :align],
126
+ [4,3, :align],
127
+ [3,3, :delete],
128
+ [2,2, :align],
129
+ [1,1, :align],
130
+ [0,0, :align]
131
+ ]
132
+ end
133
+ end # "with two similar sequences"
134
+ end
135
+
136
+ describe Align::NeedlemanWunsch do
137
+ before :each do
138
+ @klass = Align::NeedlemanWunsch
139
+ @opts = {}
140
+ @score = 6
141
+ end
142
+ context "by default" do
143
+ it_should_behave_like "a needleman-wunsch alignment algorithm"
144
+ end
145
+
146
+ context "using SCORING_ALT1" do
147
+ before :each do
148
+ @klass = Align::NeedlemanWunsch
149
+ @opts[:scoring] = Align::NeedlemanWunsch::SCORING_ALT1
150
+ @score = 1
151
+ end
152
+
153
+ it_should_behave_like "a needleman-wunsch alignment algorithm"
154
+ end
155
+ end
156
+
@@ -0,0 +1,38 @@
1
+ require_relative 'spec_helper'
2
+ require 'align/pairwise_algorithm'
3
+ require 'align/basic_scoring'
4
+
5
+ describe Align::PairwiseAlgorithm do
6
+ before :each do
7
+ @seq1 = "GAATTCAGTTA"
8
+ @seq2 = "GGATCGA"
9
+ @scoring = Align::BasicScoring.new(1,-1,-1)
10
+ end
11
+ subject {Align::PairwiseAlgorithm.new(@seq1, @seq2, @scoring)}
12
+ its(:seq1) {should == @seq1}
13
+ its(:seq2) {should == @seq2}
14
+ its(:scoring) {should == @scoring}
15
+ describe :align do
16
+ it "should raise a NotImplementedError" do
17
+ lambda {subject.align}.should raise_error(NotImplementedError)
18
+ end
19
+ end
20
+
21
+ it "#max2 should find the max amongst two numbers" do
22
+ [1,2].permutation.each do |a,b|
23
+ subject.max2(a,b).should == 2
24
+ end
25
+ end
26
+
27
+ it "#max3 should find the max amongst three numbers" do
28
+ [1,2,3].permutation.each do |a,b,c|
29
+ subject.max3(a,b,c).should == 3
30
+ end
31
+ end
32
+
33
+ it "#max4 should find the max amongst four numbers" do
34
+ [1,2,3,4].permutation.each do |a,b,c,d|
35
+ subject.max4(a,b,c,d).should == 4
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'spec_helper'
2
+ require 'align/smith_waterman'
3
+ require 'benchmark'
4
+
5
+ describe Align::SmithWaterman do
6
+ before :each do
7
+ @klass = Align::SmithWaterman
8
+ @opts = {}
9
+ end
10
+
11
+ examples = [
12
+ ['CAGCCUCGCUUAG', 'AAUGCCAUUGACGG', 'GCC-UCG', 'GCCAUUG'],
13
+ ['AAUGCCAUUGACGG', 'CAGCCUCGCUUAG', 'GCCAUUG', 'GCC-UCG'],
14
+ ['ACACACTA', 'AGCACACA', 'CACAC', 'CACAC']
15
+ ]
16
+
17
+ examples.each do |seq1, seq2, exp1, exp2|
18
+ context "when aligning '#{seq1}' and '#{seq2}'" do
19
+ before :each do
20
+ @opts[:skip_obj] = "-"
21
+ @align1, @align2 = @klass.align(seq1, seq2, @opts)
22
+ end
23
+
24
+ it "should align 1 as '#{exp1}'" do
25
+ @align1.join('').should == exp1
26
+ end
27
+ it "should align 2 as '#{exp2}'" do
28
+ @align2.join('').should == exp2
29
+ end
30
+ end
31
+ end
32
+
33
+ context "with two similar sequences" do
34
+ before :each do
35
+ @seq1 = "CAGCCUCGCUUAG"
36
+ @seq2 = "AAUGCCAUUGACGG"
37
+ @nw = @klass.new(@seq1, @seq2, @opts)
38
+ end
39
+
40
+ subject {@nw}
41
+ its(:rows) { should == @seq1.size + 1}
42
+ its(:cols) { should == @seq2.size + 1}
43
+ its(:max_score) { should == 6 }
44
+ its(:max_row) {should == 8 }
45
+ its(:max_col) {should == 10}
46
+
47
+ it "should be possible to return the proper traceback" do
48
+ @nw.traceback_array.should ==
49
+ [
50
+ [7, 9, :align],
51
+ [6, 8, :align],
52
+ [5, 7, :align],
53
+ [5, 6, :insert],
54
+ [4, 5, :align],
55
+ [3, 4, :align],
56
+ [2, 3, :align]
57
+ ]
58
+ end
59
+
60
+ it "should be possible to construct the proper traceback via yield" do
61
+ traceback = []
62
+ @nw.traceback do |i,j, last_move|
63
+ traceback << [i,j, last_move]
64
+ end
65
+ traceback.should ==
66
+ [
67
+ [7, 9, :align],
68
+ [6, 8, :align],
69
+ [5, 7, :align],
70
+ [5, 6, :insert],
71
+ [4, 5, :align],
72
+ [3, 4, :align],
73
+ [2, 3, :align]
74
+ ]
75
+ end
76
+ end # "with two similar sequences"
77
+
78
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ if ENV['COVERAGE']
5
+ require 'simplecov'
6
+ SimpleCov.start do
7
+ add_filter '/spec/'
8
+ add_filter '/.bundle/'
9
+ end
10
+ end
11
+
12
+ require 'rspec'
13
+ require 'align'
14
+
15
+
16
+ # Requires supporting files with custom matchers and macros, etc,
17
+ # in ./support/ and its subdirectories.
18
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
19
+
20
+ RSpec.configure do |config|
21
+
22
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: align
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.2
6
+ platform: ruby
7
+ authors:
8
+ - Mike Ryan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-25 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.0
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 0.6.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: kramdown
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: "0.13"
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: simplecov
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.4.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: bundler
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 1.0.0
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: jeweler
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 1.5.2
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ description: Provides sequence alignment algorithms
83
+ email: falter@gmail.com
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - LICENSE.txt
90
+ - README.md
91
+ files:
92
+ - .document
93
+ - .rspec
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - VERSION
99
+ - lib/align.rb
100
+ - lib/align/basic_scoring.rb
101
+ - lib/align/needleman_wunsch.rb
102
+ - lib/align/pairwise_algorithm.rb
103
+ - lib/align/smith_waterman.rb
104
+ - spec/needleman_wunsch_spec.rb
105
+ - spec/pairwise_algorithm_spec.rb
106
+ - spec/smith_waterman_spec.rb
107
+ - spec/spec_helper.rb
108
+ has_rdoc: true
109
+ homepage: http://github.com/justfalter/align
110
+ licenses:
111
+ - MIT
112
+ post_install_message:
113
+ rdoc_options: []
114
+
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 2168051897343409579
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: "0"
132
+ requirements: []
133
+
134
+ rubyforge_project:
135
+ rubygems_version: 1.6.0
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Provides sequence alignment algorithms
139
+ test_files:
140
+ - spec/needleman_wunsch_spec.rb
141
+ - spec/pairwise_algorithm_spec.rb
142
+ - spec/smith_waterman_spec.rb
143
+ - spec/spec_helper.rb