morphological_metrics 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.autotest +30 -0
- data/.gemtest +0 -0
- data/.minitest.rb +3 -0
- data/.travis.yml +10 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +48 -0
- data/History.txt +6 -0
- data/Manifest.txt +27 -0
- data/README.rdoc +66 -0
- data/ROADMAP.txt +63 -0
- data/Rakefile +32 -0
- data/bin/mm +3 -0
- data/lib/mm/deltas.rb +35 -0
- data/lib/mm/metric.rb +191 -0
- data/lib/mm/pairs.rb +11 -0
- data/lib/mm/ratio.rb +148 -0
- data/lib/mm/scaling.rb +27 -0
- data/lib/mm/search.rb +97 -0
- data/lib/mm.rb +11 -0
- data/lib/shortcuts.yml +49 -0
- data/test/helpers.rb +22 -0
- data/test/mm/test_deltas.rb +43 -0
- data/test/mm/test_metric.rb +168 -0
- data/test/mm/test_mm.rb +6 -0
- data/test/mm/test_pairs.rb +27 -0
- data/test/mm/test_ratio.rb +132 -0
- data/test/mm/test_scaling.rb +32 -0
- data/test/mm/test_search.rb +89 -0
- metadata +142 -0
data/lib/mm/ratio.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'prime'
|
3
|
+
|
4
|
+
module MM; end
|
5
|
+
|
6
|
+
class MM::Ratio
|
7
|
+
include Comparable
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize n, d
|
11
|
+
gcd = n.gcd d
|
12
|
+
@numerator = n / gcd
|
13
|
+
@denominator = d / gcd
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :numerator, :denominator
|
17
|
+
|
18
|
+
def * other
|
19
|
+
MM::Ratio.new(self.numerator * other.numerator, self.denominator * other.denominator)
|
20
|
+
end
|
21
|
+
|
22
|
+
def / other
|
23
|
+
self * other.reciprocal
|
24
|
+
end
|
25
|
+
|
26
|
+
def + other
|
27
|
+
MM::Ratio.new(self.numerator*other.denominator + other.numerator*self.denominator,
|
28
|
+
self.denominator*other.denominator)
|
29
|
+
end
|
30
|
+
|
31
|
+
def - other
|
32
|
+
self + (other * MM::Ratio.new(-1,1))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Works very similarly to the Prime::prime_division method, except that
|
36
|
+
# factors in the numerator are positive, and factors in the denominator are
|
37
|
+
# negative.
|
38
|
+
def factors
|
39
|
+
n_factors = ::Prime.prime_division(@numerator)
|
40
|
+
d_factors = ::Prime.prime_division(@denominator).map {|d| d[1] *= -1; d}
|
41
|
+
n_factors.concat(d_factors).sort_by {|x| x[0]}
|
42
|
+
end
|
43
|
+
|
44
|
+
def abs
|
45
|
+
if self < MM::Ratio.new(0, 1)
|
46
|
+
self * MM::Ratio.new(-1,1)
|
47
|
+
else
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def <=> other
|
53
|
+
# Ensure that the comparison makes sense
|
54
|
+
return nil unless other.respond_to? :-
|
55
|
+
|
56
|
+
case
|
57
|
+
when (self - other).to_f > 0
|
58
|
+
return 1
|
59
|
+
when (self - other).to_f < 0
|
60
|
+
return -1
|
61
|
+
end
|
62
|
+
return 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def eql? other
|
66
|
+
other.is_a?(MM::Ratio) && (self == other)
|
67
|
+
end
|
68
|
+
|
69
|
+
def hash
|
70
|
+
[@numerator, @denominator, MM::Ratio].hash
|
71
|
+
end
|
72
|
+
|
73
|
+
def cents
|
74
|
+
Math.log2(self.to_f) * 1200.0
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.from_s r
|
78
|
+
if r.respond_to? :split
|
79
|
+
if r =~ /\s/
|
80
|
+
r.split(/\s/).inject([]) {|memo, ratio|
|
81
|
+
memo << self.from_s(ratio)
|
82
|
+
}
|
83
|
+
else
|
84
|
+
string_to_ratio r
|
85
|
+
end
|
86
|
+
else
|
87
|
+
r.map {|s| self.from_s s}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.string_to_ratio string
|
92
|
+
m = string.match(/(\d+)\/(\d+)/)
|
93
|
+
MM::Ratio.new(m[1].to_i, m[2].to_i)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Loads a sequence of MM::Ratios from a YAML file.
|
97
|
+
def self.from_yaml yaml_string
|
98
|
+
YAML.load(yaml_string).map {|r| MM::Ratio.from_s r}
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_f
|
102
|
+
@numerator.to_f / @denominator
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
"#{@numerator}/#{@denominator}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def reciprocal
|
110
|
+
MM::Ratio.new(@denominator, @numerator)
|
111
|
+
end
|
112
|
+
|
113
|
+
def prime_limit
|
114
|
+
self.map { |r|
|
115
|
+
r.prime_division.map { |s|
|
116
|
+
s.first
|
117
|
+
}.max
|
118
|
+
}.compact.max
|
119
|
+
end
|
120
|
+
|
121
|
+
def each
|
122
|
+
if block_given?
|
123
|
+
[@numerator, @denominator].each do |r|
|
124
|
+
yield r
|
125
|
+
end
|
126
|
+
else
|
127
|
+
[@numerator, @denominator].each
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.to_vector point
|
132
|
+
point.each_cons(2).map {|r| r[0] / r[1]}
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.from_vector vector
|
136
|
+
vector.inject([MM::Ratio.new(1,1)]) {|m, r| m << (m.last / r)}
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.change_interval point, index, interval
|
140
|
+
vector = MM::Ratio.to_vector(point)
|
141
|
+
if interval == :reciprocal
|
142
|
+
interval = vector[index].reciprocal
|
143
|
+
end
|
144
|
+
vector[index] = interval
|
145
|
+
MM::Ratio.from_vector(vector)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
data/lib/mm/scaling.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module MM
|
2
|
+
module Scaling
|
3
|
+
# All of these functions require a sequence of distance pair evaluations of
|
4
|
+
# the given metric. They all output scaled versions.
|
5
|
+
|
6
|
+
# Scale to the max across both vector
|
7
|
+
def self.none pairs
|
8
|
+
pairs
|
9
|
+
end
|
10
|
+
def self.absolute pairs
|
11
|
+
max = (pairs.map(&:max)).max
|
12
|
+
pairs.map {|x| x.map {|y| y.to_f / max}}
|
13
|
+
end
|
14
|
+
# Scale each vector to its own max
|
15
|
+
def self.relative pairs
|
16
|
+
maxes = pairs.map(&:max)
|
17
|
+
pairs.zip(maxes).map {|pair, max| pair.map {|x| x.to_f / max}}
|
18
|
+
end
|
19
|
+
# Note: a bit hacky. But anything starting with "get_" should be considered
|
20
|
+
# a meta-scaling method. This method returns a Proc that has a particular
|
21
|
+
# scaling value hard-coded into it, for re-use and re-use.
|
22
|
+
def self.get_global max
|
23
|
+
->(pairs) {pairs.map {|x| x.map {|y| y.to_f / max}}}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/lib/mm/search.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module MM; end
|
2
|
+
|
3
|
+
# All you need to do is add an adjacent_points_function and cost_function
|
4
|
+
class MM::Search
|
5
|
+
attr_accessor :candidates, :delta, :starting_point, :path, :banned, :iterations
|
6
|
+
attr_writer :cost_function, :adjacent_points_function
|
7
|
+
|
8
|
+
def initialize starting_point, delta = 0.001
|
9
|
+
@starting_point = starting_point
|
10
|
+
@delta = delta
|
11
|
+
@current_point = @starting_point
|
12
|
+
@path = []
|
13
|
+
@banned = []
|
14
|
+
@iterations = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finds a vector beginning from the starting point
|
18
|
+
def find
|
19
|
+
find_from_point @starting_point
|
20
|
+
end
|
21
|
+
|
22
|
+
# def find_from_point point
|
23
|
+
# if cost_function(point) < current_cost
|
24
|
+
# add_to_path point
|
25
|
+
# if made_it?
|
26
|
+
# return @current_point
|
27
|
+
# else
|
28
|
+
# get_sorted_adjacent_points.each do |possible_point|
|
29
|
+
# find_from_point possible_point
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# else
|
33
|
+
# backtrack
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
|
37
|
+
|
38
|
+
def find_from_point point
|
39
|
+
@iterations += 1
|
40
|
+
# The adjacent points are all sorted
|
41
|
+
# raise StopIteration if cost_function(point) > current_cost
|
42
|
+
add_to_path point
|
43
|
+
sorted_adjacent_points = get_sorted_adjacent_points
|
44
|
+
# If we've made it, return it.
|
45
|
+
if made_it?
|
46
|
+
@current_point
|
47
|
+
else
|
48
|
+
begin
|
49
|
+
find_from_point sorted_adjacent_points.next
|
50
|
+
rescue StopIteration => er
|
51
|
+
# When the list of adjacent points runs out, backtrack
|
52
|
+
backtrack
|
53
|
+
retry unless @current_point.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_to_path point
|
59
|
+
@current_point = point
|
60
|
+
@path << point
|
61
|
+
end
|
62
|
+
|
63
|
+
def backtrack
|
64
|
+
@banned << @path.pop
|
65
|
+
puts "Path: #{@path.size}, Banned: #{@banned.size}" if ENV["DEBUG_RB"] && ENV["DEBUG_RB"].to_i > 1
|
66
|
+
@current_point = @path.last
|
67
|
+
end
|
68
|
+
|
69
|
+
def calculate_cost candidates
|
70
|
+
candidates.map {|x| cost_function x}
|
71
|
+
end
|
72
|
+
|
73
|
+
def made_it?
|
74
|
+
current_cost < @delta
|
75
|
+
end
|
76
|
+
|
77
|
+
def cost_function *args
|
78
|
+
@cost_function.call(*args)
|
79
|
+
end
|
80
|
+
|
81
|
+
def current_cost
|
82
|
+
cost_function @current_point
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_adjacent_points *args
|
86
|
+
@adjacent_points_function.call(@current_point, *args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_sorted_adjacent_points *args
|
90
|
+
get_adjacent_points(*args)
|
91
|
+
.reject {|c| @path.include? c}
|
92
|
+
.reject {|c| @banned.include? c}
|
93
|
+
.sort_by {|x| cost_function x}
|
94
|
+
.to_enum
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
data/lib/mm.rb
ADDED
data/lib/shortcuts.yml
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
:olm:
|
2
|
+
:ordered: true
|
3
|
+
:pair: :linear
|
4
|
+
:scale: :none
|
5
|
+
:intra_delta: :abs
|
6
|
+
:inter_delta: :abs
|
7
|
+
:ocm:
|
8
|
+
:ordered: true
|
9
|
+
:pair: :combinatorial
|
10
|
+
:scale: :none
|
11
|
+
:intra_delta: :abs
|
12
|
+
:inter_delta: :abs
|
13
|
+
:ulm:
|
14
|
+
:ordered: false
|
15
|
+
:pair: :linear
|
16
|
+
:scale: :none
|
17
|
+
:intra_delta: :abs
|
18
|
+
:inter_delta: :mean
|
19
|
+
:ucm:
|
20
|
+
:ordered: false
|
21
|
+
:pair: :combinatorial
|
22
|
+
:scale: :none
|
23
|
+
:intra_delta: :abs
|
24
|
+
:inter_delta: :mean
|
25
|
+
:old:
|
26
|
+
:ordered: true
|
27
|
+
:pair: :linear
|
28
|
+
:scale: :none
|
29
|
+
:intra_delta: :direction
|
30
|
+
:inter_delta: :abs
|
31
|
+
:ocd:
|
32
|
+
:ordered: true
|
33
|
+
:pair: :combinatorial
|
34
|
+
:scale: :none
|
35
|
+
:intra_delta: :direction
|
36
|
+
:inter_delta: :abs
|
37
|
+
:uld:
|
38
|
+
:ordered: false
|
39
|
+
:pair: :linear
|
40
|
+
:scale: :none
|
41
|
+
:intra_delta: :direction
|
42
|
+
:inter_delta: :mean
|
43
|
+
:ucd:
|
44
|
+
:ordered: false
|
45
|
+
:pair: :combinatorial
|
46
|
+
:scale: :none
|
47
|
+
:intra_delta: :direction
|
48
|
+
:inter_delta: :mean
|
49
|
+
|
data/test/helpers.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module TestHelpers
|
2
|
+
# Asserts that each item in exp matches each item in act
|
3
|
+
def assert_nested_in_delta exp, act, delta = 0.001, msg = nil
|
4
|
+
exp.zip(act) do |x|
|
5
|
+
if block_given?
|
6
|
+
yield x, delta, msg
|
7
|
+
else
|
8
|
+
assert_in_delta(*x, delta, msg)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Asserts that nested values 2-deep are within a certain delta
|
14
|
+
def assert_nested_in_delta_2_deep *args
|
15
|
+
assert_nested_in_delta(*args) do |x, delta, msg|
|
16
|
+
x[0].zip(x[1]) do |y|
|
17
|
+
assert_in_delta(*y, delta, msg)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'mm/deltas'
|
2
|
+
require_relative '../helpers.rb'
|
3
|
+
|
4
|
+
class TestMM < Minitest::Test; end
|
5
|
+
|
6
|
+
class TestMM::TestDeltas < Minitest::Test
|
7
|
+
include TestHelpers
|
8
|
+
|
9
|
+
def test_abs_ordered_inter_delta
|
10
|
+
input = [[5, 12], [4, 2], [3, 11], [6, 7]]
|
11
|
+
exp = [7, 2, 8, 1]
|
12
|
+
result = input.map {|x| MM::Deltas.abs(x)}
|
13
|
+
assert_equal exp, result
|
14
|
+
end
|
15
|
+
def test_mean_inter_delta
|
16
|
+
input = [[5, 4, 3, 6], [12, 2, 11, 7]]
|
17
|
+
exp = [4.5, 8]
|
18
|
+
result = input.map {|x| MM::Deltas.mean(x)}
|
19
|
+
assert_equal exp, result
|
20
|
+
end
|
21
|
+
def test_distance_ordered_inter_delta
|
22
|
+
input = [[-1, -1], [1, 1], [-1, 1], [-1, -1]]
|
23
|
+
exp = [0, 0, 2, 0]
|
24
|
+
result = input.map {|x| MM::Deltas.abs(x)}
|
25
|
+
assert_equal exp, result
|
26
|
+
end
|
27
|
+
def test_tenney_ordered_intra_delta
|
28
|
+
input = [[Rational(1,1), Rational(3,2)], [Rational(3,2), Rational(5,4)]]
|
29
|
+
exp = [2.585, 4.907]
|
30
|
+
assert_nested_in_delta exp, input.map {|x| MM::Deltas.tenney(x)}
|
31
|
+
end
|
32
|
+
def test_log_ratio_ordered_intra_delta
|
33
|
+
input = [[Rational(1,1), Rational(3,2)], [Rational(3,2), Rational(5,4)]]
|
34
|
+
exp = [0.585, 0.263]
|
35
|
+
assert_nested_in_delta exp, input.map {|x| MM::Deltas.log_ratio(x)}
|
36
|
+
end
|
37
|
+
def test_ratio_ordered_intra_delta
|
38
|
+
input = [[Rational(1,1), Rational(3,2)], [Rational(3,2), Rational(5,4)]]
|
39
|
+
exp = [Rational(2,3), Rational(6,5)]
|
40
|
+
assert_nested_in_delta exp, input.map {|x| MM::Deltas.ratio(x)}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
require 'mm/metric'
|
4
|
+
|
5
|
+
class TestMM < Minitest::Test; end
|
6
|
+
|
7
|
+
class TestMM::TestMetric < Minitest::Test
|
8
|
+
def setup
|
9
|
+
@ordered ||= true
|
10
|
+
@pair ||= :linear
|
11
|
+
@scale ||= :none
|
12
|
+
@intra_delta ||= :abs
|
13
|
+
@inter_delta ||= :abs
|
14
|
+
@metric = MM::Metric.new(ordered: @ordered, pair: @pair, scale: @scale, intra_delta: @intra_delta, inter_delta: @inter_delta)
|
15
|
+
|
16
|
+
# Setting up the sample vectors for many of the examples
|
17
|
+
@v1 = [1, 6, 2, 5, 11]
|
18
|
+
@v2 = [3, 15, 13, 2, 9]
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_creates_new_metric
|
22
|
+
assert @metric.is_a? MM::Metric
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_metric_responds_to_call
|
26
|
+
assert_respond_to @metric, :call
|
27
|
+
end
|
28
|
+
|
29
|
+
class TestGetPairs < self
|
30
|
+
class TestLinearPairs < self
|
31
|
+
def test_gets_linear_pairs
|
32
|
+
exp = [
|
33
|
+
[[1, 6], [6, 2], [2, 5], [5, 11]],
|
34
|
+
[[3, 15], [15, 13], [13, 2], [2, 9]]
|
35
|
+
]
|
36
|
+
assert_equal exp, @metric.send(:get_pairs, @v1, @v2)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class TestCombinatorialPairs < self
|
41
|
+
def setup
|
42
|
+
@pair = :combinatorial
|
43
|
+
super
|
44
|
+
end
|
45
|
+
def test_gets_combinatorial_pairs
|
46
|
+
exp = [
|
47
|
+
[[1, 6], [1, 2], [1, 5], [1, 11], [6, 2], [6, 5],
|
48
|
+
[6, 11], [2, 5], [2, 11], [5, 11]],
|
49
|
+
[[3, 15], [3, 13], [3, 2], [3, 9], [15, 13], [15, 2],
|
50
|
+
[15, 9], [13, 2], [13, 9], [2, 9]]
|
51
|
+
]
|
52
|
+
assert_equal exp, @metric.send(:get_pairs, @v1, @v2)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class TestDeltas < self
|
58
|
+
class TestIntraDelta < self
|
59
|
+
def test_gets_intra_delta
|
60
|
+
pairs = [
|
61
|
+
[[1, 6], [6, 2], [2, 5], [5, 11]],
|
62
|
+
[[3, 15], [15, 13], [13, 2], [2, 9]]
|
63
|
+
]
|
64
|
+
exp = [
|
65
|
+
[5, 4, 3, 6],
|
66
|
+
[12, 2, 11, 7]
|
67
|
+
]
|
68
|
+
assert_equal exp, @metric.send(:intra_delta, pairs)
|
69
|
+
end
|
70
|
+
def test_intra_delta_proc
|
71
|
+
@metric.intra_delta = ->(*vp) {nil}
|
72
|
+
assert_instance_of Proc, @metric.instance_variable_get(:@intra_delta)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class TestInterDelta < self
|
77
|
+
def setup
|
78
|
+
super
|
79
|
+
@diffs = [
|
80
|
+
[5, 4, 3, 6],
|
81
|
+
[12, 2, 11, 7]
|
82
|
+
]
|
83
|
+
end
|
84
|
+
def test_gets_inter_delta_ordered
|
85
|
+
exp = 4.5
|
86
|
+
assert_equal exp, @metric.send(:inter_delta, @diffs)
|
87
|
+
end
|
88
|
+
def test_inter_delta_proc
|
89
|
+
@metric.inter_delta = ->(*diffs) {nil}
|
90
|
+
assert_instance_of Proc, @metric.instance_variable_get(:@inter_delta)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class TestScaling < self
|
96
|
+
def setup
|
97
|
+
super
|
98
|
+
@unscaled = [
|
99
|
+
[5, 4, 3, 6],
|
100
|
+
[12, 2, 11, 7]
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_assigns_scaling_proc
|
105
|
+
@metric.scale = ->(pairs) {}
|
106
|
+
scale = @metric.instance_variable_get :@scale
|
107
|
+
assert_equal Proc, scale.class
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_no_scaling
|
111
|
+
assert_equal @unscaled, @metric.send(:scale, @unscaled)
|
112
|
+
end
|
113
|
+
|
114
|
+
# TODO: This is a complicated test and I don't like it
|
115
|
+
def test_absolute_scaling
|
116
|
+
@metric.scale = :absolute
|
117
|
+
@exp = [
|
118
|
+
[0.417, 0.333, 0.25, 0.5],
|
119
|
+
[1.0, 0.167, 0.917, 0.583]
|
120
|
+
]
|
121
|
+
actual = @metric.send :scale, @unscaled
|
122
|
+
actual.each_with_index do |v, i|
|
123
|
+
v.each_with_index do |w, j|
|
124
|
+
assert_in_delta @exp[i][j], w, 0.001
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_relative_scaling
|
130
|
+
@metric.scale = :relative
|
131
|
+
@exp = [
|
132
|
+
[0.833, 0.667, 0.5, 1.0],
|
133
|
+
[1.0, 0.167, 0.917, 0.583]
|
134
|
+
]
|
135
|
+
actual = @metric.send :scale, @unscaled
|
136
|
+
actual.each_with_index do |v, i|
|
137
|
+
v.each_with_index do |w, j|
|
138
|
+
assert_in_delta @exp[i][j], w, 0.001
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class TestMagnitudeMetric < self
|
145
|
+
# Definitions of expected results
|
146
|
+
@exp = {
|
147
|
+
:olm => {:scale_none => 4.5, :scale_absolute => 0.375},
|
148
|
+
:ocm => {:scale_none => 5.2, :scale_absolute => 0.4},
|
149
|
+
:ulm => {:scale_none => 3.5, :scale_absolute => 0.29167},
|
150
|
+
:ucm => {:scale_none => 2.4, :scale_absolute => 0.1846},
|
151
|
+
:old => {:scale_none => 0.25},
|
152
|
+
:ocd => {:scale_none => 0.4},
|
153
|
+
:uld => {:scale_none => 0.25},
|
154
|
+
:ucd => {:scale_none => 0.4}
|
155
|
+
}
|
156
|
+
|
157
|
+
@exp.each do |metric, expected|
|
158
|
+
expected.each do |scaling, e|
|
159
|
+
define_method "test_#{metric}_#{scaling}" do
|
160
|
+
m = ::MM::Metric.send(metric)
|
161
|
+
m.scale = /_(.*)$/.match(scaling)[1].to_sym
|
162
|
+
assert_in_delta e, m.call(@v1, @v2), 0.001
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
data/test/mm/test_mm.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# require "minitest/autorun"
|
2
|
+
require "mm/pairs"
|
3
|
+
|
4
|
+
class TestMM < Minitest::Test; end
|
5
|
+
|
6
|
+
class TestMM::TestPairs < Minitest::Test
|
7
|
+
def setup
|
8
|
+
@pairs = MM::Pairs.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_linear_flat_array
|
12
|
+
assert_equal [[0, 1], [1, 2], [2, 3]], @pairs.linear([0, 1, 2, 3])
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_linear_nested_array
|
16
|
+
assert_equal [[[0, 1], [2, 3]], [[2, 3], [4, 5]]], @pairs.linear([[0, 1], [2, 3], [4, 5]])
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_combinatorial_flat_array
|
20
|
+
assert_equal [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]], @pairs.combinatorial([0, 1, 2, 3])
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_combinatorial_nested_array
|
24
|
+
assert_equal [[[0, 1], [2, 3]], [[0, 1], [4, 5]], [[2, 3], [4, 5]]], @pairs.combinatorial([[0, 1], [2, 3], [4, 5]])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|