fuzzyrb 1.1.0 → 1.2.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.
data.tar.gz.sig CHANGED
Binary file
@@ -1,9 +1,17 @@
1
+ == 1.2.0 / 2007-11-16
2
+
3
+ * Some bugfixes
4
+ * Changed how evaluate method is invoked. Now it takes hash of params.
5
+ * Closed code in a module.
6
+ * Added some docs.
7
+
1
8
  == 1.1.0 / 2007-11-15
9
+
2
10
  * Added Takegi-Sugeno rules
3
11
  * Added first minimum defuziffication method
4
-
5
12
  * Changed inference to implementation
6
13
  * Changed weightCenter to centerOfGravity
14
+
7
15
  == 1.0.0 / 2007-11-09
8
16
 
9
17
  * 1 major enhancement
@@ -2,6 +2,7 @@ History.txt
2
2
  Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
+ examples/compare_models.rb
5
6
  index.html
6
7
  lib/fuzzy.rb
7
8
  lib/fuzzy_implication.rb
@@ -13,3 +14,4 @@ test/test_fuzzy.rb
13
14
  test/test_fuzzy_implication.rb
14
15
  test/test_fuzzy_rule.rb
15
16
  test/test_fuzzy_set.rb
17
+ test/test_point.rb
data/README.txt CHANGED
@@ -8,15 +8,17 @@ Implements Fuzzy Sets in Ruby. I am very beginner at this topic, so it is very b
8
8
 
9
9
  == FEATURES/PROBLEMS:
10
10
 
11
- * Fuzzy Sets defined as line segments
11
+ * Fuzzy Sets defined as sequence of line segments.
12
12
  * Fuzzy Rules. Only conjunction of arguments is possible.
13
13
  * No error handling.
14
- * Deffuzification as center of gravity.
15
- * Reasoning - apply matching rule and combine the results.
14
+ * Defuzzification as center of gravity and first maximum
15
+ * Minimum and Multiplication T-Norms.
16
+ * Mamdami and Larsen aggregation methods.
17
+ * Reasoning - apply matching rule and combine the results. Mamdami or Takagi-Sugeno system.
16
18
 
17
19
  == SYNOPSIS:
18
20
 
19
- Cannot be used from console. See test/ for sample usage.
21
+ Cannot be used from console. See test/ and example/ for sample usage.
20
22
 
21
23
  == REQUIREMENTS:
22
24
 
@@ -46,5 +48,6 @@ You should have received a copy of the GNU General Public License
46
48
  along with this program. If not, see <http://www.gnu.org/licenses/>.
47
49
 
48
50
  == Author
51
+
49
52
  Roman 'MrStone' Kamyk (mailto:roman.kamyk@gmail.com),
50
53
  Student of Poznan University Of Technology, Computing Science Institute, Inteligent Decision Support Systems
data/Rakefile CHANGED
@@ -10,15 +10,16 @@ namespace :hoe do
10
10
  p.author = 'Roman Kamyk'
11
11
  p.email = 'roman.kamyk@gmail.com'
12
12
  p.summary = 'Fuzzy Sets for Ruby'
13
- p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
+ p.description = p.paragraphs_of('README.txt', 1..2).join("\n\n")
14
14
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
15
- p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
+ @changes = p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
16
  end
17
17
  end
18
18
 
19
- task :publish => ['hoe:release', 'hoe:post_news', 'hoe:publish_docs'] do
19
+ task :commit_changes do
20
20
  sh "svn ci -m '#{@changes}'"
21
21
  sh "hg ci -m '#{@changes}'"
22
22
  end
23
23
 
24
+ task :publish => ['hoe:release', 'hoe:post_news', 'hoe:publish_docs', 'commit_changes']
24
25
  # vim: syntax=Ruby
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/ruby
2
+ # (c) Copyright 2007 Roman Kamyk.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ ##############################################################################
17
+ # In this example we try to model the function f(x, y) = x + y using different
18
+ # parameters. As a result we get couple graphs of difference between the model
19
+ # and the function and sum of errors (differences)
20
+
21
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
22
+ require 'fuzzy'
23
+ @smallInput = FuzzySet.trapezoid([0, 0, 0, 10])
24
+ @largeInput = FuzzySet.trapezoid([0, 10, 10, 10])
25
+ @smallOutput = FuzzySet.trapezoid([0, 0, 0, 10])
26
+ @mediumOutput = FuzzySet.trapezoid([0, 10, 10, 20])
27
+ @largeOutput = FuzzySet.trapezoid([10, 20, 20, 20])
28
+ @rule1 = FuzzyRule.new([@smallInput, @smallInput], @smallOutput)
29
+ @rule2 = FuzzyRule.new([@smallInput, @largeInput], @mediumOutput)
30
+ @rule3 = FuzzyRule.new([@largeInput, @smallInput], @mediumOutput)
31
+ @rule4 = FuzzyRule.new([@largeInput, @largeInput], @largeOutput)
32
+ @ms = MamdamiImplication.new([@rule1, @rule2, @rule3, @rule4])
33
+
34
+ @rulets1 = FuzzyRule.new([@smallInput, @smallInput], Proc.new { |a, b| 0})
35
+ @rulets2 = FuzzyRule.new([@smallInput, @largeInput], Proc.new { |a, b| 10})
36
+ @rulets3 = FuzzyRule.new([@largeInput, @smallInput], Proc.new { |a, b| 10})
37
+ @rulets4 = FuzzyRule.new([@largeInput, @largeInput], Proc.new { |a, b| 20})
38
+ @tss = TakagiSugenoImplication.new([@rulets1, @rulets2, @rulets3, @rulets4])
39
+
40
+ require 'rubygems'
41
+ require 'gnuplot'
42
+ @sum_of_diffs = {}
43
+ def count_error(implication, params)
44
+ Gnuplot.open do |gp|
45
+ Gnuplot::SPlot.new( gp ) do |plot|
46
+ plot.title "#{params.inspect}"
47
+ plot.xlabel "a"
48
+ plot.ylabel "b"
49
+ plot.set("ticslevel", "0.8")
50
+ plot.set("isosample", "40,40")
51
+
52
+ range = 0..10
53
+ x = range.collect {|v| v.to_f}
54
+ y = x
55
+ z = []
56
+ pp params
57
+ for i in range do
58
+ z << []
59
+ for j in range do
60
+ # print " T_Norm: #{t_norm}, implication: #{imp_type}, deffuzification: #{def_type}, difference: "
61
+ res = implication.evaluate([i, j],params)
62
+ diff = (res - (i+j)).abs
63
+ @sum_of_diffs[params] ||= 0
64
+ @sum_of_diffs[params] += diff
65
+ puts "[#{i}, #{j}] #{res} - #{i+j} = #{res - (i+j)}"
66
+ z[i] << diff
67
+ end
68
+ end
69
+ plot.data << Gnuplot::DataSet.new( [x, y, z] ) do |ds|
70
+ ds.with = "lines"
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ [:min, :mult].each do |t_norm|
77
+ count_error(@tss, :t_norm => t_norm, :implication => :takegiSugeno)
78
+ [:mamdani, :larsen].each do |imp_type|
79
+ [:CoG, :firstMaximum].each do |def_type|
80
+ count_error(@ms, :t_norm => t_norm, :implication => imp_type, :defuzzification => def_type)
81
+ end
82
+ end
83
+ # print "[#{i}, #{j}] T_norm: #{t_norm}, implication: Takagi Sugeno, difference: "
84
+ # res = @tss.evaluate([i, j], t_norm)
85
+ # sum_of_diffs[[t_norm, :ts]] ||= 0
86
+ # sum_of_diffs[[t_norm, :ts]] += (res - (i+j)).abs
87
+ # puts "#{res} - #{i+j} = #{res - (i+j)}"
88
+ end
89
+ pp @sum_of_diffs
@@ -2,22 +2,51 @@
2
2
  # Created by Roman Kamyk <roman.kamyk@gmail.com on 2007-11-09.
3
3
  # Copyright (C) 2007 Roman Kamyk
4
4
  #
5
- # This program is free software: you can redistribute it and/or modify
6
- # it under the terms of the GNU General Public License as published by
7
- # the Free Software Foundation, either version 3 of the License, or
8
- # (at your option) any later version.
9
- #
10
- # This program is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- # GNU General Public License for more details.
14
- #
15
- # You should have received a copy of the GNU General Public License
16
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  require 'pp'
19
-
20
19
  $:.unshift(File.dirname(__FILE__) + "/../lib/")
20
+
21
+ EPSILON = 0.0001
22
+ SCALE = 1
23
+
24
+ class Array
25
+ def uniq_values
26
+ uniqArray = []
27
+ self.each { |e1|
28
+ uniqArray << e1 unless uniqArray.find{ |o| o.eql?(e1) }
29
+ }
30
+ uniqArray
31
+ end
32
+
33
+ def uniq_values!()
34
+ test = self.dup
35
+ self.clear
36
+ test.each { |e1|
37
+ self << e1 unless self.find{ |o| o.eql?(e1) }
38
+ }
39
+ self
40
+ end
41
+
42
+ def non_decreasing
43
+ for i in 1..self.length-1
44
+ return false if self[i-1] > self[i]
45
+ end
46
+ return true
47
+ end
48
+ end
49
+
21
50
  require 'point'
22
51
  require 'line'
23
52
  require 'fuzzy_set'
@@ -25,5 +54,5 @@ require 'fuzzy_rule'
25
54
  require 'fuzzy_implication'
26
55
 
27
56
  module Fuzzyrb
28
- VERSION = "1.1.0"
57
+ VERSION = "1.2.0"
29
58
  end
@@ -1,31 +1,45 @@
1
- class FuzzyImplication
2
- def initialize(rules)
3
- @rules = rules
4
- end
5
- end
6
-
7
- class TakagiSugenoImplication < FuzzyImplication
8
- def evaluate(t_norm, values)
9
- sum = 0
10
- result = @rules.map { |rule|
11
- rule.evaluate(t_norm, :takagiSugeno, values)
12
- }.inject(0) { |s, rv| sum += rv[1]; s + rv[0] }
13
- return 0 if sum == 0
14
- result/sum
1
+ module Fuzzyrb
2
+ # Should not be used directly. Use <tt>TakagiSugenoImplication</tt> or
3
+ # <tt>MamdamiImplication</tt>
4
+ class FuzzyImplication
5
+ def initialize(rules)
6
+ @rules = rules
7
+ end
15
8
  end
16
- end
17
9
 
18
- class MamdamiImplication < FuzzyImplication
19
- def evaluate(t_norm, implication, defuzzification, values)
20
- result = @rules.map { |rule|
21
- rule.evaluate(t_norm, implication, values)
22
- }.inject { |s, r| s + r }
23
- if defuzzification == :CoG
24
- return result.centerOfGravity
25
- elsif defuzzification == :firstMin
26
- return result.firstMinimum
27
- else
28
- raise Exception.new("Invalid deffuzification method")
10
+ # Uses Takegi-Sugeno system to compute implication. Each rule as its
11
+ # conclusion has function of it's arguments.
12
+ class TakagiSugenoImplication < FuzzyImplication
13
+ # Evaluates the rule. Params are the same as for
14
+ # <tt>FuzzyRule</tt>
15
+ def evaluate(values, params)
16
+ # puts "\nEvaluating ts implication"
17
+ total_membership = 0
18
+ params[:implication] = :takagiSugeno
19
+ total_value = @rules.map { |rule|
20
+ rule.evaluate(values, params)
21
+ }.inject(0) { |s, rv| total_membership += rv[1]; s + rv[0]*rv[1]}
22
+ # puts "TM: #{total_membership}, TV: #{total_value}"
23
+ return 0 if total_membership == 0
24
+ total_value/total_membership
25
+ end
29
26
  end
30
- end
31
- end
27
+
28
+ class MamdamiImplication < FuzzyImplication
29
+ # Evaluates the rule. Params includes <tt>FuzzyRule</tt> params and:
30
+ # * <tt>:defuzzification</tt> which can be <tt>:CoG</tt> for center of gravity of
31
+ # <tt>:firstMaximum</tt> for first maximum.
32
+ def evaluate(values, params)
33
+ result = @rules.map { |rule|
34
+ rule.evaluate(values, params)
35
+ }.inject { |s, r| s + r }
36
+ if params[:defuzzification] == :CoG
37
+ return result.centerOfGravity
38
+ elsif params[:defuzzification] == :firstMaximum
39
+ return result.firstMaximum
40
+ else
41
+ raise Exception.new("Invalid defuzzification method")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,35 +1,46 @@
1
- class FuzzyRule
2
- def initialize(arguments, result)
3
- @arguments = arguments
4
- @result = result
5
- end
6
-
7
- def evaluate(t_norm, implication, values)
8
- if t_norm == :min
9
- val = argumentsValues(values).min
10
- elsif t_norm == :mult
11
- val = argumentsValues(values).inject(1) { |mult, v| mult*v}
12
- elsif
13
- raise Exception.new("Invalid t_norm")
1
+ module Fuzzyrb
2
+ class FuzzyRule
3
+ # Creates rule.
4
+ # First argument is an Array of premises (<tt>FuzzySet</tt>). Second is result. It is either <tt>FuzzySet</tt> or <tt>Proc</tt>.
5
+ def initialize(arguments, result)
6
+ @arguments = arguments
7
+ @result = result
8
+ end
9
+
10
+ # Evaluates rule.
11
+ # Required parameters:
12
+ # * <tt>:t_norm</tt> - t_norm to use. <tt>:min</tt> for minimum or
13
+ # <tt>:mult</tt> for multiplication
14
+ # * <tt>:implication</tt> - implication to use. Either <tt>:mamdani</tt>,
15
+ # <tt>:larsen</tt>, or <tt>:takegiSugeno</tt>. If you use
16
+ # <tt>:takegiSugeno</tt> make sure that rule result is a Proc.
17
+ def evaluate(values, params)
18
+ if params[:t_norm] == :min
19
+ val = argumentsValues(values).min
20
+ elsif params[:t_norm] == :mult
21
+ val = argumentsValues(values).inject(1) { |mult, v| mult*v}
22
+ elsif
23
+ raise Exception.new("Invalid t_norm")
24
+ end
25
+ if params[:implication] == :mamdani
26
+ return @result.min(val)
27
+ elsif params[:implication] == :larsen
28
+ return @result.scale(val)
29
+ elsif params[:implication] == :takagiSugeno
30
+ #rule value and certainity
31
+ return [@result.call(values), val]
32
+ else
33
+ raise Exception.new("Invalid type")
34
+ end
14
35
  end
15
- if implication == :mamdani
16
- return @result.min(val)
17
- elsif implication == :larsen
18
- return @result.scale(val)
19
- elsif implication == :takagiSugeno
20
- #rule value and certainity
21
- return [@result.call(values), val]
22
- else
23
- raise Exception.new("Invalid type")
36
+
37
+ private
38
+ def argumentsValues(values)
39
+ tmp = []
40
+ for i in 0..(values.length-1)
41
+ tmp << @arguments[i][values[i]]
42
+ end
43
+ tmp
24
44
  end
25
45
  end
26
-
27
- private
28
- def argumentsValues(values)
29
- tmp = []
30
- for i in 0..(values.length-1)
31
- tmp << @arguments[i][values[i]]
32
- end
33
- tmp
34
- end
35
46
  end
@@ -1,126 +1,134 @@
1
- EPSILON = 0.0001
2
- SCALE = 1
3
- class FuzzySet
4
- def self.trapezoid(array)
5
- raise Exception.new("Trapezoid must have array length 4") if array.length != 4
6
- points = []
7
- points << Point.new(array[0], 0)
8
- points << Point.new(array[1], SCALE)
9
- points << Point.new(array[2], SCALE) unless array[2] == array[1]
10
- points << Point.new(array[3], 0)
11
- FuzzySet.new(points)
12
- end
1
+ module Fuzzyrb
2
+ class FuzzySet
3
+ def self.trapezoid(array)
4
+ raise Exception.new("Trapezoid must have array length 4") if array.length != 4
5
+ raise Exception.new("Trapezoid arguments must be nondecreasing") unless array.non_decreasing
6
+ points = []
7
+ points << Point.new(array[0], 0) unless array[1] == array[0]
8
+ points << Point.new(array[1], SCALE)
9
+ points << Point.new(array[2], SCALE) unless array[2] == array[1]
10
+ points << Point.new(array[3], 0) unless array[3] == array[2]
11
+ FuzzySet.new(points)
12
+ end
13
13
 
14
- def initialize(points)
15
- @points = points.sort
16
- end
17
-
18
- def +(other)
19
- points = [@points, other.points, intersections(other)].flatten.uniq.sort
20
- res = points.reject { |point|
21
- self[point.x]-EPSILON > point.y or other[point.x]-EPSILON > point.y
22
- }
23
- FuzzySet.new(res)
24
- end
25
-
26
- def min(value)
27
- line = Line.new(Point.new(0, value), Point.new(1, value))
28
- points = [@points, intersections(line)].flatten.uniq.sort
29
- res = points.reject { |p| self[p.x] - EPSILON > value }
30
- FuzzySet.new(res)
31
- end
32
-
33
- # Choose min of current and _other_ set
34
- def &(other)
35
- points = [@points, crosspoints(other)].flatten.uniq.sort
36
- res = []
37
- points.each { |point|
38
- res << point if self[point.x]-EPSILON < point.y and self[point.x]+EPSILON > point.y and other[point.x]+EPSILON >= self[point.x]
39
- }
40
- FuzzySet.new(res)
41
- end
42
- attr_reader :points
43
-
44
- def centerOfGravity()
45
- nominator = 0.0
46
- denominator = 0.0
47
- for i in 1..@points.length-1
48
- line = Line.new(@points[i], @points[i-1])
49
- x2 = points[i].x
50
- x1 = points[i-1].x
51
- nominator += line.a * x2**3/3 + line.b * x2**2/2 - line.a * x1**3/3 - line.b * x1**2/2
52
- denominator += line.a * x2**2/2 + line.b * x2 - line.a * x1**2/2 - line.b * x1
14
+ def initialize(points)
15
+ @points = points.sort.uniq_values
53
16
  end
54
- nominator/denominator
55
- end
56
-
57
- def firstMinimum()
58
- maxIdx = 0
59
- for i in 0..@points.length-1
60
- if @points[i].y > @points[maxIdx].y
61
- maxIdx = i
17
+
18
+ def +(other)
19
+ points = [@points, other.points, intersections(other)].flatten.uniq_values.sort
20
+ res = points.reject { |point|
21
+ self[point.x]-EPSILON > point.y or other[point.x]-EPSILON > point.y
22
+ }
23
+ FuzzySet.new(res)
24
+ end
25
+
26
+ def min(value)
27
+ line = Line.new(Point.new(0, value), Point.new(1, value))
28
+ points = [@points, intersections(line)].flatten.uniq_values.sort
29
+ # reject all points that has higher membership than this fuzzy set
30
+ res = points.reject { |p| self[p.x] - EPSILON > value }
31
+ FuzzySet.new(res)
32
+ end
33
+
34
+ # Choose min of current and _other_ set
35
+ def &(other)
36
+ points = [@points, crosspoints(other)].flatten.uniq_values.sort
37
+ res = []
38
+ points.each { |point|
39
+ res << point if self[point.x]-EPSILON < point.y and self[point.x]+EPSILON > point.y and other[point.x]+EPSILON >= self[point.x]
40
+ }
41
+ FuzzySet.new(res)
42
+ end
43
+ attr_reader :points
44
+
45
+ def centerOfGravity()
46
+ nominator = 0.0
47
+ denominator = 0.0
48
+ # puts "Length: #{@points.length}"
49
+ for i in 1..@points.length-1
50
+ line = Line.new(@points[i], @points[i-1])
51
+ # puts "#{nominator}, #{denominator}, #{line.a}, #{line.b}"
52
+ # pp @points[i], @points[i-1]
53
+ x2 = points[i].x
54
+ x1 = points[i-1].x
55
+ nominator += line.a * x2**3/3 + line.b * x2**2/2 - line.a * x1**3/3 - line.b * x1**2/2
56
+ denominator += line.a * x2**2/2 + line.b * x2 - line.a * x1**2/2 - line.b * x1
62
57
  end
58
+
59
+ return 0 if nominator.abs <= EPSILON
60
+ raise Exception.new("Runtime exception: denominator cannot be null") if denominator == 0
61
+ nominator/denominator
63
62
  end
64
- @points[maxIdx].x
65
- end
66
-
67
- def scale(factor)
68
- fs = FuzzySet.new(@points.map { |p| p.clone() })
69
- fs.scale!(factor)
70
- end
71
-
72
- def scale!(factor)
73
- @points.each { |point| point.y *= factor / SCALE }
74
- self
75
- end
76
-
77
- def [](value)
78
- if value<@points[0].x
79
- return 0
80
- elsif value > @points.last.x
81
- return 0
63
+
64
+ def firstMaximum()
65
+ maxIdx = 0
66
+ for i in 0..@points.length-1
67
+ if @points[i].y > @points[maxIdx].y
68
+ maxIdx = i
69
+ end
70
+ end
71
+ @points[maxIdx].x
82
72
  end
83
- idx = 0
84
- while (@points[idx].x < value)
85
- idx += 1
73
+
74
+ def scale(factor)
75
+ fs = FuzzySet.new(@points.map { |p| p.clone() })
76
+ fs.scale!(factor)
86
77
  end
87
- return @points[idx].y if @points[idx].x == value
88
- x1 = @points[idx-1].x
89
- x2 = @points[idx].x
90
- y1 = @points[idx-1].y
91
- y2 = @points[idx].y
92
- return 1.0*(y2 - y1)/(x2-x1) * (value - x1) + y1
93
- end
94
-
95
- def toLines
96
- lines = []
97
- for i in 1..@points.length-1
98
- lines << Line.new(@points[i], @points[i-1])
78
+
79
+ def scale!(factor)
80
+ @points.each { |point| point.y *= factor / SCALE }
81
+ self
99
82
  end
100
- lines
101
- end
102
-
103
- private
104
- def intersections(other)
105
- if other.is_a?(Line)
106
- points = []
107
- toLines.each { |mline|
108
- points << mline.intersect(other)
109
- }
110
- points.select { |point| (self[point.x]-point.y).abs <= EPSILON }
111
- elsif other.is_a?(FuzzySet)
112
- myLines = toLines
113
- points = []
114
- other.toLines.each { |line|
115
- myLines.each { |mline|
116
- points << mline.intersect(line)
83
+
84
+ def [](value)
85
+ if value<@points[0].x
86
+ return @points[0].y
87
+ elsif value > @points.last.x
88
+ return @points.last.y
89
+ end
90
+ idx = 0
91
+ while (@points[idx].x < value)
92
+ idx += 1
93
+ end
94
+ return @points[idx].y if @points[idx].x == value
95
+ x1 = @points[idx-1].x
96
+ x2 = @points[idx].x
97
+ y1 = @points[idx-1].y
98
+ y2 = @points[idx].y
99
+ return 1.0*(y2 - y1)/(x2-x1) * (value - x1) + y1
100
+ end
101
+
102
+ def toLines
103
+ lines = []
104
+ for i in 1..@points.length-1
105
+ lines << Line.new(@points[i], @points[i-1])
106
+ end
107
+ lines
108
+ end
109
+
110
+ private
111
+ def intersections(other)
112
+ if other.is_a?(Line)
113
+ points = []
114
+ toLines.each { |mline|
115
+ points << mline.intersect(other)
117
116
  }
118
- }
119
- points.select { |point|
120
- (self[point.x]-point.y).abs + (other[point.x]-point.y).abs <= EPSILON
121
- }
122
- else
123
- raise Exception.new("Unable to count intersection")
117
+ points.select { |point| (self[point.x]-point.y).abs <= EPSILON }
118
+ elsif other.is_a?(FuzzySet)
119
+ myLines = toLines
120
+ points = []
121
+ other.toLines.each { |line|
122
+ myLines.each { |mline|
123
+ points << mline.intersect(line)
124
+ }
125
+ }
126
+ points.select { |point|
127
+ (self[point.x]-point.y).abs + (other[point.x]-point.y).abs <= EPSILON
128
+ }
129
+ else
130
+ raise Exception.new("Unable to count intersection")
131
+ end
124
132
  end
125
133
  end
126
- end
134
+ end
@@ -1,16 +1,18 @@
1
- class Line
2
- def initialize(p1, p2)
3
- @a = 1.0*(p2.y-p1.y)/(p2.x-p1.x)
4
- @b = 1.0*p1.y - @a*p1.x
5
- # pp [p1, p2, @a, @b]
1
+ module Fuzzyrb
2
+ class Line
3
+ def initialize(p1, p2)
4
+ @a = 1.0*(p2.y-p1.y)/(p2.x-p1.x)
5
+ @b = 1.0*p1.y - @a*p1.x
6
+ # pp [p1, p2, @a, @b]
7
+ end
8
+
9
+ def intersect(other)
10
+ x = 1.0*(b-other.b)/(other.a-a)
11
+ y = 1.0*a*x+b
12
+ # pp [self, other, x, y]
13
+ Point.new(x, y)
14
+ end
15
+
16
+ attr_accessor :a, :b
6
17
  end
7
-
8
- def intersect(other)
9
- x = 1.0*(b-other.b)/(other.a-a)
10
- y = 1.0*a*x+b
11
- # pp [self, other, x, y]
12
- Point.new(x, y)
13
- end
14
-
15
- attr_accessor :a, :b
16
18
  end
@@ -1,14 +1,25 @@
1
- class Point
2
- def initialize(x, y)
3
- @x = x
4
- @y = y
5
- end
6
- def <=>(other)
7
- self.x <=> other.x
1
+ module Fuzzyrb
2
+ class Point
3
+ def initialize(x, y)
4
+ @x = x
5
+ @y = y
6
+ end
7
+
8
+ def <=>(other)
9
+ self.x <=> other.x
10
+ end
11
+
12
+ def eql?(other)
13
+ if (self.x-other.x).abs <= EPSILON and (self.y - other.y).abs <= EPSILON
14
+ return true
15
+ end
16
+ return false
17
+ end
18
+
19
+ def initialize_copy(orig)
20
+ @x = orig.x
21
+ @y = orig.y
22
+ end
23
+ attr_accessor :x, :y
8
24
  end
9
- def initialize_copy(orig)
10
- @x = orig.x
11
- @y = orig.y
12
- end
13
- attr_accessor :x, :y
14
- end
25
+ end
@@ -3,6 +3,10 @@ $:.unshift(File.dirname(__FILE__) + "/../test/")
3
3
  require 'test/unit'
4
4
  require 'test/unit/ui/console/testrunner'
5
5
 
6
+ require 'fuzzy'
7
+ include Fuzzyrb
8
+
6
9
  require 'test_fuzzy_set'
7
10
  require 'test_fuzzy_rule'
8
- require 'test_fuzzy_implication'
11
+ require 'test_fuzzy_implication'
12
+ require 'test_point'
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/ruby
2
- $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+ $:.unshift(File.dirname(__FILE__) + "../lib/")
3
3
  require 'test/unit'
4
- require 'fuzzy'
5
4
 
6
5
  class TestFuzzyImplication < Test::Unit::TestCase
7
6
  def setup
@@ -21,15 +20,15 @@ class TestFuzzyImplication < Test::Unit::TestCase
21
20
  end
22
21
 
23
22
  def test_mamdani
24
- assert_in_delta 33.71, @fi.evaluate(:min, :mamdani, :CoG, [7, 18]), 0.005
23
+ assert_in_delta 33.71, @fi.evaluate([7, 18], {:t_norm => :min, :implication => :mamdani, :defuzzification => :CoG}), 0.005
25
24
  end
26
25
 
27
26
  def test_larsen
28
- assert_in_delta 33.58, @fi.evaluate(:min, :larsen, :CoG, [7, 18]), 0.005
27
+ assert_in_delta 33.58, @fi.evaluate([7, 18], {:t_norm => :min, :implication => :larsen, :defuzzification => :CoG}), 0.005
29
28
  end
30
29
 
31
30
  def test_takagi_sugeno
32
- assert_equal 0, @tsi.evaluate(:min, [10, 20])
33
- assert_equal 8.75, @tsi.evaluate(:min, [5, 19])
31
+ assert_equal 0, @tsi.evaluate([10, 20], :t_norm => :min)
32
+ assert_equal 19.25, @tsi.evaluate([5, 19], :t_norm => :min)
34
33
  end
35
34
  end
@@ -11,8 +11,8 @@ class TestFuzzyRule < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_larsen
14
- assert_equal [25, 0.25], @rule.evaluate(:min, :takagiSugeno, [7, 18])
15
- assert_equal [16, 1], @rule.evaluate(:min, :takagiSugeno, [5, 11])
14
+ assert_equal [25, 0.25], @rule.evaluate([7, 18], {:t_norm => :min, :implication => :takagiSugeno} )
15
+ assert_equal [16, 1], @rule.evaluate([5, 11], {:t_norm => :min, :implication => :takagiSugeno})
16
16
  end
17
17
  end
18
18
 
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
3
  require 'test/unit'
3
4
  require 'fuzzy'
4
5
 
@@ -25,6 +26,65 @@ class TestFuzzySet < Test::Unit::TestCase
25
26
  assert_equal(0, t[20])
26
27
  assert_equal(0, t[1000])
27
28
  end
28
- end
29
29
 
30
+ def test_trapezoid3
31
+ t = FuzzySet.trapezoid([0, 10, 10, 10])
32
+ assert_equal 0, t[0]
33
+ assert_equal(1, t[10])
34
+ assert_equal(1, t[20])
35
+ assert_equal(1, t[100])
36
+ assert_equal(0.5, t[5])
37
+ assert_equal(0, t[-100])
38
+ end
39
+
40
+ def test_trapezoid4
41
+ t = FuzzySet.trapezoid([0, 0, 0, 10])
42
+ assert_equal(1, t[0])
43
+ assert_equal(0.5, t[5])
44
+ assert_equal(0, t[10])
45
+ assert_equal(1, t[-100])
46
+ assert_equal(0, t[100])
47
+ end
30
48
 
49
+ def test_trapezoid5
50
+ t = FuzzySet.trapezoid([0, 0, 10, 20])
51
+ assert_equal(1, t[-100])
52
+ assert_equal(1, t[0])
53
+ assert_equal(1, t[10])
54
+ assert_equal(0.5, t[15])
55
+ assert_equal(0, t[20])
56
+ assert_equal(0, t[25])
57
+ end
58
+
59
+ def test_trapezoid6
60
+ t = FuzzySet.trapezoid([0, 1, 9, 10])
61
+ assert_in_delta(5, t.centerOfGravity, 0.01)
62
+ end
63
+
64
+ def test_nondecreasing
65
+ assert_raise Exception do
66
+ FuzzySet.trapezoid([10, 9, 8, 7])
67
+ end
68
+ assert_raise Exception do
69
+ FuzzySet.trapezoid([1, 0, 2, 3])
70
+ end
71
+ assert_raise Exception do
72
+ FuzzySet.trapezoid([1, 2, 3, 2])
73
+ end
74
+ end
75
+
76
+ def test_points1
77
+ t = FuzzySet.new([Point.new(0, 0), Point.new(5, 1), Point.new(10, 0), Point.new(15, 1), Point.new(20, 0)])
78
+ assert_equal(0, t[0])
79
+ assert_equal(1, t[5])
80
+ assert_equal(0.5, t[2.5])
81
+ assert_equal(0, t[10])
82
+ assert_equal(0, t[-10])
83
+ assert_equal(1, t[15])
84
+ assert_equal(0.5, t[17.5])
85
+ assert_equal(0, t[20])
86
+ assert_equal(0, t[50])
87
+ assert_in_delta(10, t.centerOfGravity, 0.0001)
88
+ assert_equal(5, t.firstMaximum)
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
3
+ require 'test/unit'
4
+ require 'fuzzy'
5
+
6
+ class TestPoint < Test::Unit::TestCase
7
+ def setup
8
+ end
9
+
10
+ def test_point1
11
+ p1 = Point.new(0, 0)
12
+ p2 = Point.new(0.0, 0.0)
13
+ assert_equal(1, [p1, p2].uniq_values.length)
14
+ end
15
+ end
metadata CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: fuzzyrb
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.1.0
7
- date: 2007-11-15 00:00:00 +01:00
6
+ version: 1.2.0
7
+ date: 2007-11-18 00:00:00 +01:00
8
8
  summary: Fuzzy Sets for Ruby
9
9
  require_paths:
10
10
  - lib
11
11
  email: roman.kamyk@gmail.com
12
12
  homepage: " by Roman Kamyk"
13
13
  rubyforge_project: fuzzyrb
14
- description: "== FEATURES/PROBLEMS: * Fuzzy Sets defined as line segments * Fuzzy Rules. Only conjunction of arguments is possible. * No error handling. * Deffuzification as center of gravity. * Reasoning - apply matching rule and combine the results. == SYNOPSIS: Cannot be used from console. See test/ for sample usage. == REQUIREMENTS:"
14
+ description: "== DESCRIPTION: Implements Fuzzy Sets in Ruby. I am very beginner at this topic, so it is very basic now. Any help will be appreciated. == FEATURES/PROBLEMS: * Fuzzy Sets defined as sequence of line segments. * Fuzzy Rules. Only conjunction of arguments is possible. * No error handling. * Defuzzification as center of gravity and first maximum * Minimum and Multiplication T-Norms. * Mamdami and Larsen aggregation methods. * Reasoning - apply matching rule and combine the results. Mamdami or Takagi-Sugeno system."
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
@@ -55,6 +55,7 @@ files:
55
55
  - Manifest.txt
56
56
  - README.txt
57
57
  - Rakefile
58
+ - examples/compare_models.rb
58
59
  - index.html
59
60
  - lib/fuzzy.rb
60
61
  - lib/fuzzy_implication.rb
@@ -66,11 +67,13 @@ files:
66
67
  - test/test_fuzzy_implication.rb
67
68
  - test/test_fuzzy_rule.rb
68
69
  - test/test_fuzzy_set.rb
70
+ - test/test_point.rb
69
71
  test_files:
70
72
  - test/test_fuzzy.rb
71
73
  - test/test_fuzzy_implication.rb
72
74
  - test/test_fuzzy_rule.rb
73
75
  - test/test_fuzzy_set.rb
76
+ - test/test_point.rb
74
77
  rdoc_options:
75
78
  - --main
76
79
  - README.txt
metadata.gz.sig CHANGED
Binary file