morphological_metrics 1.3.0 → 1.3.1

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.
@@ -1,12 +1,12 @@
1
- module MM
2
- class Pairs
3
- def linear vector
4
- vector.each_cons(2).to_a
5
- end
6
-
7
- def combinatorial vector
8
- vector.combination(2).to_a
9
- end
10
- end
11
- end
12
-
1
+ module MM
2
+ class Pairs
3
+ def linear vector
4
+ vector.each_cons(2).to_a
5
+ end
6
+
7
+ def combinatorial vector
8
+ vector.combination(2).to_a
9
+ end
10
+ end
11
+ end
12
+
@@ -1,145 +1,156 @@
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
-
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
+ def [] i
36
+ if i == 0
37
+ self.numerator
38
+ elsif i == 1
39
+ self.denominator
40
+ end
41
+ end
42
+
43
+ # Works very similarly to the Prime::prime_division method, except that
44
+ # factors in the numerator are positive, and factors in the denominator are
45
+ # negative.
46
+ def factors
47
+ n_factors = ::Prime.prime_division(@numerator)
48
+ d_factors = ::Prime.prime_division(@denominator).map {|d| d[1] *= -1; d}
49
+ n_factors.concat(d_factors).sort_by {|x| x[0]}
50
+ end
51
+
52
+ def abs
53
+ if self < MM::Ratio.new(0, 1)
54
+ self * MM::Ratio.new(-1,1)
55
+ else
56
+ self
57
+ end
58
+ end
59
+
60
+ def <=> other
61
+ # Ensure that the comparison makes sense
62
+ return nil unless other.respond_to? :-
63
+
64
+ case
65
+ when (self - other).to_f > 0
66
+ return 1
67
+ when (self - other).to_f < 0
68
+ return -1
69
+ end
70
+ return 0
71
+ end
72
+
73
+ def eql? other
74
+ other.is_a?(MM::Ratio) && (self == other)
75
+ end
76
+
77
+ def hash
78
+ [@numerator, @denominator, MM::Ratio].hash
79
+ end
80
+
81
+ def cents
82
+ Math.log2(self.to_f) * 1200.0
83
+ end
84
+
85
+ def self.from_s r
86
+ if r.respond_to? :split
87
+ if r =~ /\s/
88
+ r.split(/\s/).inject([]) {|memo, ratio|
89
+ memo << self.from_s(ratio)
90
+ }
91
+ else
92
+ string_to_ratio r
93
+ end
94
+ else
95
+ r.map {|s| self.from_s s}
96
+ end
97
+ end
98
+
99
+ def self.string_to_ratio string
100
+ m = string.match(/(\d+)\/(\d+)/)
101
+ MM::Ratio.new(m[1].to_i, m[2].to_i)
102
+ end
103
+
104
+ # Loads a sequence of MM::Ratios from a YAML file.
105
+ def self.from_yaml yaml_string
106
+ YAML.load(yaml_string).map {|r| MM::Ratio.from_s r}
107
+ end
108
+
109
+ def to_f
110
+ @numerator.to_f / @denominator
111
+ end
112
+
113
+ def to_s
114
+ "#{@numerator}/#{@denominator}"
115
+ end
116
+
117
+ def reciprocal
118
+ MM::Ratio.new(@denominator, @numerator)
119
+ end
120
+
121
+ def prime_limit
122
+ self.map { |r|
123
+ r.prime_division.map { |s|
124
+ s.first
125
+ }.max
126
+ }.compact.max
127
+ end
128
+
129
+ def each
130
+ if block_given?
131
+ [@numerator, @denominator].each do |r|
132
+ yield r
133
+ end
134
+ else
135
+ [@numerator, @denominator].each
136
+ end
137
+ end
138
+
139
+ def self.to_vector point
140
+ point.each_cons(2).map {|r| r[0] / r[1]}
141
+ end
142
+
143
+ def self.from_vector vector
144
+ vector.inject([MM::Ratio.new(1,1)]) {|m, r| m << (m.last / r)}
145
+ end
146
+
147
+ def self.change_interval point, index, interval
148
+ vector = MM::Ratio.to_vector(point)
149
+ if interval == :reciprocal
150
+ interval = vector[index].reciprocal
151
+ end
152
+ vector[index] = interval
153
+ MM::Ratio.from_vector(vector)
154
+ end
155
+ end
156
+
@@ -1,30 +1,30 @@
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
-
11
- def self.absolute pairs
12
- max = (pairs.map(&:max)).max
13
- pairs.map {|x| x.map {|y| y.to_f / max}}
14
- end
15
-
16
- # Scale each vector to its own max
17
- def self.relative pairs
18
- maxes = pairs.map(&:max)
19
- pairs.zip(maxes).map {|pair, max| pair.map {|x| x.to_f / max}}
20
- end
21
-
22
- # Note: a bit hacky. But anything starting with "get_" should be considered
23
- # a meta-scaling method. This method returns a Proc that has a particular
24
- # scaling value hard-coded into it, for re-use and re-use.
25
- def self.get_global max
26
- ->(pairs) {pairs.map {|x| x.map {|y| y.to_f / max}}}
27
- end
28
- end
29
- end
30
-
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
+
11
+ def self.absolute pairs
12
+ max = (pairs.map(&:max)).max
13
+ pairs.map {|x| x.map {|y| y.to_f / max}}
14
+ end
15
+
16
+ # Scale each vector to its own max
17
+ def self.relative pairs
18
+ maxes = pairs.map(&:max)
19
+ pairs.zip(maxes).map {|pair, max| pair.map {|x| x.to_f / max}}
20
+ end
21
+
22
+ # Note: a bit hacky. But anything starting with "get_" should be considered
23
+ # a meta-scaling method. This method returns a Proc that has a particular
24
+ # scaling value hard-coded into it, for re-use and re-use.
25
+ def self.get_global max
26
+ ->(pairs) {pairs.map {|x| x.map {|y| y.to_f / max}}}
27
+ end
28
+ end
29
+ end
30
+
@@ -1,81 +1,81 @@
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
- @iterations += 1
24
- # The adjacent points are all sorted
25
- # raise StopIteration if cost_function(point) > current_cost
26
- add_to_path point
27
- sorted_adjacent_points = get_sorted_adjacent_points
28
- # If we've made it, return it.
29
- if made_it?
30
- @current_point
31
- else
32
- begin
33
- find_from_point sorted_adjacent_points.next
34
- rescue StopIteration => er
35
- # When the list of adjacent points runs out, backtrack
36
- backtrack
37
- retry unless @current_point.nil?
38
- end
39
- end
40
- end
41
-
42
- def add_to_path point
43
- @current_point = point
44
- @path << point
45
- end
46
-
47
- def backtrack
48
- @banned << @path.pop
49
- puts "Path: #{@path.size}, Banned: #{@banned.size}" if ENV["DEBUG_RB"] && ENV["DEBUG_RB"].to_i > 1
50
- @current_point = @path.last
51
- end
52
-
53
- def calculate_cost candidates
54
- candidates.map {|x| cost_function x}
55
- end
56
-
57
- def made_it?
58
- current_cost < @delta
59
- end
60
-
61
- def cost_function *args
62
- @cost_function.call(*args)
63
- end
64
-
65
- def current_cost
66
- cost_function @current_point
67
- end
68
-
69
- def get_adjacent_points *args
70
- @adjacent_points_function.call(@current_point, *args)
71
- end
72
-
73
- def get_sorted_adjacent_points *args
74
- get_adjacent_points(*args)
75
- .reject {|c| @path.include? c}
76
- .reject {|c| @banned.include? c}
77
- .sort_by {|x| cost_function x}
78
- .to_enum
79
- end
80
- end
81
-
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
+ @iterations += 1
24
+ # The adjacent points are all sorted
25
+ # raise StopIteration if cost_function(point) > current_cost
26
+ add_to_path point
27
+ sorted_adjacent_points = get_sorted_adjacent_points
28
+ # If we've made it, return it.
29
+ if made_it?
30
+ @current_point
31
+ else
32
+ begin
33
+ find_from_point sorted_adjacent_points.next
34
+ rescue StopIteration => er
35
+ # When the list of adjacent points runs out, backtrack
36
+ backtrack
37
+ retry unless @current_point.nil?
38
+ end
39
+ end
40
+ end
41
+
42
+ def add_to_path point
43
+ @current_point = point
44
+ @path << point
45
+ end
46
+
47
+ def backtrack
48
+ @banned << @path.pop
49
+ puts "Path: #{@path.size}, Banned: #{@banned.size}" if ENV["DEBUG_RB"] && ENV["DEBUG_RB"].to_i > 1
50
+ @current_point = @path.last
51
+ end
52
+
53
+ def calculate_cost candidates
54
+ candidates.map {|x| cost_function x}
55
+ end
56
+
57
+ def made_it?
58
+ current_cost < @delta
59
+ end
60
+
61
+ def cost_function *args
62
+ @cost_function.call(*args)
63
+ end
64
+
65
+ def current_cost
66
+ cost_function @current_point
67
+ end
68
+
69
+ def get_adjacent_points *args
70
+ @adjacent_points_function.call(@current_point, *args)
71
+ end
72
+
73
+ def get_sorted_adjacent_points *args
74
+ get_adjacent_points(*args)
75
+ .reject {|c| @path.include? c}
76
+ .reject {|c| @banned.include? c}
77
+ .sort_by {|x| cost_function x}
78
+ .to_enum
79
+ end
80
+ end
81
+