rfuzzy 0.0.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
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 "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.2"
12
+ gem "rcov", ">= 0"
13
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2002 JSON.org
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ The Software shall be used for Good, not Evil.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,33 @@
1
+ = rfuzzy
2
+
3
+ Rfuzzy is a small, yet handy tool for express creation of fuzzy systems.
4
+ It's goal is to provide a quick way for defining any kind of expert system.
5
+
6
+ == Features
7
+
8
+ Adherence functions
9
+ * Easy creation of any adherence function.
10
+ * Performing t and s norms on functions.
11
+ * Defuzzification.
12
+
13
+ Fuzzy domains (fuzzy sets)
14
+ * Easy creation of domains.
15
+ * Domains described by named adherence functions.
16
+
17
+ Fuzzy rules
18
+ * Syntax similar to natural language.
19
+ * Proc's used as both antecendent and consequent of a rule.
20
+
21
+ Fuzzy systems
22
+ * Easy definition of input and output variables.
23
+ * Easy definition of rules
24
+
25
+ == Tutorials
26
+
27
+ More information can be found at:
28
+ http://student.agh.edu.pl/~majta/rfuzzy
29
+
30
+ == Copyright
31
+
32
+ This software is distributed under JSON License:
33
+ http://www.json.org/license.html
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "rfuzzy"
18
+ gem.homepage = "http://github.com/lolek09/rfuzzy"
19
+ gem.license = "JSON"
20
+ gem.summary = %Q{rfuzzy is a fuzzy logic library for Ruby}
21
+ gem.description = %Q{rfuzzy is a fuzzy logic library for Ruby}
22
+ gem.email = "karolmajta@gmail.com"
23
+ gem.authors = ["lolek09"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "rfuzzy #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/rfuzzy.rb ADDED
@@ -0,0 +1,17 @@
1
+ #get current dir
2
+ require 'pathname'
3
+ current_dir = Pathname.new(File.dirname(__FILE__))
4
+ # mixins
5
+ require current_dir + "rfuzzy/norm"
6
+ require current_dir + "rfuzzy/defuzz"
7
+ # classes
8
+ require current_dir + "rfuzzy/point"
9
+ require current_dir + "rfuzzy/function"
10
+ require current_dir + "rfuzzy/adherence"
11
+ require current_dir + "rfuzzy/fuzzy_domain"
12
+ require current_dir + "rfuzzy/fuzzy_variable"
13
+ require current_dir + "rfuzzy/rule"
14
+ require current_dir + "rfuzzy/fuzzy_system"
15
+ # helper classes
16
+ require current_dir + "rfuzzy/trapezoidal"
17
+ require current_dir + "rfuzzy/triangular"
@@ -0,0 +1,51 @@
1
+ # Adherence. Basically it's a Float in range [0;1] allowing norm operations.
2
+ class Adherence
3
+ include Norm
4
+
5
+ # Constructor. Usage
6
+ # <tt>.new(Float value)</tt>
7
+ # Value must be [0;1]
8
+ def initialize(val)
9
+ if val < 0 || val > 1
10
+ raise ArgumentError, "Adherence must be in [0;1]"
11
+ end
12
+ @adh = val
13
+ end
14
+
15
+ def to_f
16
+ return @adh
17
+ end
18
+
19
+ # Performs t-norm on itself and other. Usage:
20
+ # <tt>.and(Adherence other, Symbol method)
21
+ # <tt>.and(Adherence other)
22
+ def and(other, method = :auto)
23
+ if(method == :auto)
24
+ unless @@norm_method.nil?
25
+ method = @@norm_method
26
+ else
27
+ raise ArgumentError, "Specify method, or call Norm::norm_method"
28
+ end
29
+ end
30
+ return Adherence.new(self.method(NORM_METHODS[method][:t]).call(self.to_f, other.to_f))
31
+ end
32
+
33
+ # Performs s-norm on itself and other. Usage:
34
+ # <tt>.or(Adherence other, Symbol method)
35
+ # <tt>.or(Adherence other)
36
+ def or(other, method = :auto)
37
+ if(method == :auto)
38
+ unless @@norm_method.nil?
39
+ method = @@norm_method
40
+ else
41
+ raise ArgumentError, "Specify method, or call Norm::norm_method"
42
+ end
43
+ end
44
+ return Adherence.new(self.method(NORM_METHODS[method][:s]).call(self.to_f, other.to_f))
45
+ end
46
+
47
+ # Performs negation.
48
+ def not
49
+ return Adherence.new(1 - @value)
50
+ end
51
+ end
@@ -0,0 +1,91 @@
1
+ module Defuzz
2
+ DEFUZZ_METHODS = {
3
+ :lom => :least_of_maximum,
4
+ :mom => :middle_of_maximum,
5
+ :bom => :biggest_of_maximum,
6
+ :aom => :average_of_maximum,
7
+ :cog => :center_of_gravity
8
+ }
9
+
10
+ @@defuzz_method = nil
11
+
12
+ # Sets the method for defuzzification. Avalaible norms are defined in <em>DEFUZZ_METHODS</em>
13
+ def Defuzz.defuzz_method(m)
14
+ if DEFUZZ_METHODS.has_key?(m)
15
+ @@defuzz_method = m
16
+ else
17
+ sm = ""
18
+ DEFUZZ_METHODS.each_key do |k|
19
+ sm << "#{k} "
20
+ end
21
+ raise ArgumentError, "defuzzification method #{m} is not supported\nSupported methods: #{sm}"
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def least_of_maximum(fun)
28
+ found = fun.points[0]
29
+ fun.points.each do |p|
30
+ found = p if p.y > found.y
31
+ end
32
+ return found
33
+ end
34
+
35
+ def middle_of_maximum(fun)
36
+ found = []
37
+ found.push fun.points[0]
38
+ fun.points[1..fun.points.length].each do |p|
39
+ if p.y == found[0].y
40
+ found.push p
41
+ end
42
+ if p.y > found[0].y
43
+ found.clear
44
+ found.push p
45
+ end
46
+ end
47
+ return found[found.length/2]
48
+ end
49
+
50
+ def biggest_of_maximum(fun)
51
+ found = fun.points[0]
52
+ fun.points.each do |p|
53
+ found = p if p.y >= found.y
54
+ end
55
+ return found
56
+ end
57
+
58
+ def average_of_maximum(fun)
59
+ found = []
60
+ found.push fun.points[0]
61
+ fun.points[1..fun.points.length].each do |p|
62
+ if p.y == found[0].y
63
+ found.push p
64
+ end
65
+ if p.y > found[0].y
66
+ found.clear
67
+ found.push p
68
+ end
69
+ end
70
+ sum = 0
71
+ found.each do |p|
72
+ sum += p.x
73
+ end
74
+ average = sum / found.length.to_f
75
+ return Point.new(average, fun.at(average))
76
+ end
77
+
78
+ def center_of_gravity(fun)
79
+ sum_xy = 0
80
+ sum_y = 0
81
+ fun.points.each do |p|
82
+ sum_xy += p.x*p.y
83
+ sum_y += p.y
84
+ end
85
+ if sum_y == 0
86
+ return fun.points[fun.points.lenght/2]
87
+ else
88
+ Point.new(sum_xy/sum_y, fun.at(sum_xy/sum_y))
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,220 @@
1
+ # Basic 2d adherence function
2
+ class Function
3
+ include Norm
4
+ include Defuzz
5
+
6
+ EPSILON = 1.0e-10
7
+
8
+ @@step_size = nil
9
+
10
+ # Sets the step resolution used if automatic step detection is used
11
+ def self.step_size(s)
12
+ if s <= 0
13
+ raise ArgumentError, "Step size must be greater than 0"
14
+ else
15
+ @@step_size = s
16
+ end
17
+ end
18
+
19
+ attr_accessor :name, :points
20
+
21
+ #Default constructor. Usage:
22
+ #<tt>.new(String name, Point *points)</tt>
23
+ def initialize(name,*args, &block)
24
+ @name = name
25
+ @points = args
26
+ if block_given?
27
+ yield self
28
+ end
29
+ @points.sort
30
+ end
31
+
32
+ # Copy constructor.
33
+ def initialize_copy(other, &block)
34
+ initialize(other.name, other.points, block)
35
+ end
36
+
37
+ def to_s
38
+ str = "ADHERENCE FUNCTION\n\tPoints:\n"
39
+ @points.each do |point|
40
+ str << "\t#{point}\n"
41
+ end
42
+ return str
43
+ end
44
+
45
+ # For use with GNUplot
46
+ def to_gplot
47
+ str = ""
48
+ @points.each do |p|
49
+ str << "#{p.x} #{p.y}\n"
50
+ end
51
+ return str
52
+ end
53
+
54
+ # Returns a new Function with y coordinate multiplied by the given factor.
55
+ # If the new y value is greater than one 1.0 is assigned. This can be used
56
+ # for product-like Mamdani rules.
57
+ # Usage:
58
+ # <tt>.*(Float factor)</tt>
59
+ def *(fl)
60
+ fl = fl.to_f
61
+ unless fl >= 0
62
+ raise ArgumentError, "Argument should be positive"
63
+ end
64
+ new_function = Function.new("#{@name}*#{fl}")
65
+ @points.each do |p|
66
+ new_function.points.push Point.new(p.x, [p.y*fl, 1.0].max)
67
+ end
68
+ return new_function
69
+ end
70
+
71
+ # Returns a new Function with y coordinate less than given factor.
72
+ # This can be used
73
+ # for max-min-like Mamdani rules.
74
+ # Usage:
75
+ # <tt>.cut_at(Float factor)</tt>
76
+ def cut_at(fl)
77
+ unless fl >= 0 && fl <= 1
78
+ raise ArgumentError, "Argument should be from [0;1]"
79
+ end
80
+ new_function = Function.new("#{@name} MAX #{fl}")
81
+ @points.each do |p|
82
+ puts "#{fl} #{p.y}"
83
+ new_function.points.push Point.new(p.x, [p.y,fl].min)
84
+ end
85
+ return new_function
86
+ end
87
+
88
+ # Adds points to the function. Usage:
89
+ # <tt>.add(Point *points)</tt>
90
+ def add(*points)
91
+ points.each do |p|
92
+ if @points.include? p
93
+ @points.delete p
94
+ end
95
+ end
96
+ @points.concat points
97
+ @points.sort!
98
+ end
99
+
100
+ # Removes points from the function. Usage:
101
+ # <tt>.remove(Point *points)</tt>
102
+ def remove(*points)
103
+ points.each do |p|
104
+ @points.delete p
105
+ end
106
+ end
107
+
108
+ # Calculates function value at x using linear approximation. Function boundaries are from -Infinity to +Infinity.
109
+ # Usage:
110
+ # <tt>.at(Float x)</tt>
111
+ def at(x)
112
+ if @points.first.x >= x
113
+ return @points.first.y
114
+ elsif @points.last.x <= x
115
+ return @points.last.y
116
+ else
117
+ @points.each_index do |i|
118
+ if x >= @points[i].x && x < @points[i+1].x
119
+ dy = @points[i+1].y - @points[i].y
120
+ dx = @points[i+1].x - @points[i].x
121
+ du = (dy/dx)*(x - @points[i].x)
122
+ return @points[i].y + du
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ # Method for performing and (t-norm) operation.
129
+ # Usage:
130
+ # <tt>.and(Function f, Float step, Symbol method)
131
+ # <tt>.and(Function f, Float step)
132
+ # <tt>.and(Function f)
133
+ def and(other, step = :auto, method = :auto)
134
+ res = apply_norm(:t, other, step, method)
135
+ if block_given?
136
+ yield res
137
+ end
138
+ return res
139
+ end
140
+
141
+ # Method for performing or (s-norm) operation.
142
+ # Usage:
143
+ # <tt>.or(Function f, Float step, Symbol method)
144
+ # <tt>.or(Function f, Float step)
145
+ # <tt>.or(Function f)
146
+ def or(other, step = :auto, method = :auto)
147
+ res = apply_norm(:s, other, step, method)
148
+ if block_given?
149
+ yield res
150
+ end
151
+ return res
152
+ end
153
+
154
+ # Negation. Usage:
155
+ # .not
156
+ def not(&block)
157
+ new_function = Function.new("not #{@name}")
158
+ @points.each_index do |i|
159
+ new_function.points[i] = Point.new(@points[i].x, 1 - @points[i].y)
160
+ end
161
+ if block_given?
162
+ yield new_function
163
+ end
164
+ return new_function
165
+ end
166
+
167
+ # defuzzification
168
+ def defuzz(method = :auto)
169
+ if(method == :auto)
170
+ unless @@defuzz_method.nil?
171
+ method = @@defuzz_method
172
+ else
173
+ raise ArgumentError, "Specify method, or call Norm::norm_method"
174
+ end
175
+ end
176
+ method(DEFUZZ_METHODS[method]).call(self)
177
+ end
178
+
179
+ private
180
+
181
+ # Actually does the norm
182
+ def apply_norm(norm, other, step = :auto, method = :auto)
183
+ if(step == :auto)
184
+ unless @@step_size.nil?
185
+ step = @@step_size
186
+ else
187
+ raise ArgumentError, "Specify step, or call Function::step_size"
188
+ end
189
+ end
190
+ if(method == :auto)
191
+ unless @@norm_method.nil?
192
+ method = @@norm_method
193
+ else
194
+ raise ArgumentError, "Specify method, or call Norm::norm_method"
195
+ end
196
+ end
197
+ if(norm == :t)
198
+ op = "and"
199
+ elsif(norm == :s)
200
+ op = "or"
201
+ else
202
+ raise ArgumentError, "norm for Norm#apply_form must be :t or :s"
203
+ end
204
+ new_function = Function.new("(#{@name} #{op} #{other.name})")
205
+ start_point = [@points.min,other.points.min].min
206
+ stop_point = [@points.max,other.points.max].max
207
+ i = start_point.x
208
+ x = []
209
+ y = []
210
+ while(stop_point.x - i >= 0 - EPSILON)
211
+ x.push i
212
+ y.push method(NORM_METHODS[method][norm]).call(self.at(i), other.at(i))
213
+ i += step
214
+ end
215
+ x.each_index do |index|
216
+ new_function.points[index] = Point.new(x[index],y[index])
217
+ end
218
+ return new_function
219
+ end
220
+ end
@@ -0,0 +1,36 @@
1
+ # Class for defining fuzzy domains.
2
+ class FuzzyDomain
3
+ attr_reader :adherence_functions
4
+
5
+ # Constructor.
6
+ def initialize(*functions, &block)
7
+ @adherence_functions = {}
8
+ functions.each do |f|
9
+ unless @adherence_functions.has_key?(f.name)
10
+ @adherence_functions[f.name] = function
11
+ else
12
+ raise ArgumentError, "Lexical variable '#{f.name}' already describes this FuzzyVar!"
13
+ end
14
+ end
15
+ if block_given?
16
+ yield self
17
+ end
18
+ end
19
+
20
+ # Allows adding adherence functions with lexical variables describing the domain
21
+ def is_described_by(function, &block)
22
+ if block_given?
23
+ yield function
24
+ end
25
+ unless @adherence_functions.has_key?(function.name)
26
+ @adherence_functions[function.name] = function
27
+ else
28
+ raise ArgumentError, "Lexical variable '#{function.name}' already describes this FuzzyVar!"
29
+ end
30
+ end
31
+
32
+ # Removes adherence functions and lexical variables
33
+ def is_not_described_by(name)
34
+ @adherence_functions.delete(name)
35
+ end
36
+ end
@@ -0,0 +1,107 @@
1
+ # Used for representing expert system of any kind
2
+ class FuzzySystem
3
+ @domains
4
+ @inputs
5
+ @outputs
6
+ @defaults
7
+ @rules
8
+
9
+ # Constructor - yields self. Usage:
10
+ # <tt>.new()</tt>
11
+ def initialize
12
+ @domains = {}
13
+ @inputs = {}
14
+ @outputs = {}
15
+ @defaults = {}
16
+ @rules = []
17
+ if block_given?
18
+ yield self
19
+ end
20
+ end
21
+
22
+ # Declare an input in the system. This will result in creating
23
+ # i_[name] and i_[name]= used for getting and setting the values
24
+ # for this input. Usage:
25
+ # <tt>.input(String name, FuzzyDomain domain)</tt>
26
+ def input(name, domain)
27
+ @domains[name] = domain
28
+
29
+ body = Proc.new do
30
+ @inputs[name]
31
+ end
32
+ self.class.send(:define_method, "i_#{name}", body)
33
+
34
+ body = Proc.new do |val|
35
+ @inputs[name] = val
36
+ end
37
+ self.class.send(:define_method, "i_#{name}=", body)
38
+ end
39
+
40
+ # Declare an an output in the system. This will result in creating
41
+ # o_[name] and o_[name]= used for getting and setting the values
42
+ # for this output. Usage:
43
+ # <tt>.output(String name, FuzzyDomain domain)</tt>
44
+ def output(name, default = nil)
45
+ @defaults[name] = default
46
+
47
+ body = Proc.new do
48
+ unless @outputs[name].nil?
49
+ @outputs[name]
50
+ else
51
+ @defaults[name]
52
+ end
53
+ end
54
+ self.class.send(:define_method, "o_#{name}", body)
55
+
56
+ body = Proc.new do |val|
57
+ @outputs[name] = val
58
+ end
59
+ self.class.send(:define_method, "o_#{name}=", body)
60
+ end
61
+
62
+ # Used for adding rules to the system. Usage:
63
+ # <tt>.rule(Proc antecendent, Proc consequent)</tt>
64
+ def rule(ant, cons)
65
+ r = Rule.new(ant, cons)
66
+ @rules.push r
67
+ return @rules[@rules.length-1]
68
+ end
69
+
70
+ # Used for pushing input into the system. Takes a dict in which
71
+ # keys are declared input names, and values are current values (FuzzyVariables).
72
+ # This method performs a check if given data matches the one declared for the system.
73
+ # Usage:
74
+ # <tt>.inject(Hash inputs)</tt>
75
+ def inject(dict)
76
+ unless dict.keys == @domains.keys
77
+ raise ArgumentError, 'Input dictionary does not match the system inputs'
78
+ end
79
+ dict.each do |key, value|
80
+ if value.respond_to? :domain=
81
+ value.domain = @domains[key]
82
+ end
83
+ end
84
+ @inputs = dict
85
+ end
86
+
87
+ # Returns a dict of all calculated outputs.
88
+ # Usage:
89
+ # <tt>.eject()</tt>
90
+ def eject
91
+ return @outputs
92
+ end
93
+
94
+ # Resets all outputs. Usage:
95
+ # <tt>.reset()</tt>
96
+ def reset
97
+ @outputs = {}
98
+ end
99
+
100
+ # Calls .apply on every rule in the system in order they were declared. Usage:
101
+ # <tt>.process()</tt>
102
+ def process
103
+ @rules.each do |r|
104
+ r.apply
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,59 @@
1
+ # Represents fuzzy value belonging to a specific domain. Calculates the adherence on demand.
2
+ class FuzzyVariable
3
+ include Norm
4
+
5
+ attr_accessor :value, :domain
6
+
7
+ # Constructor. Usage
8
+ # <tt>.new(Float value)</tt>
9
+ def initialize(val, &block)
10
+ @value = val.to_f
11
+ if block_given?
12
+ yield self
13
+ end
14
+ end
15
+
16
+ def to_f
17
+ return @value
18
+ end
19
+
20
+ # returns new FuzzyVariable with value multiplied by float
21
+ # <tt>.*(float)</tt>
22
+ def *(float)
23
+ return FuzzyVariable.new(@value*float.to_f, @domain)
24
+ end
25
+
26
+ # Calculates the adherence of variable. Usage:
27
+ # <tt>.is(String lex_var, Symbol method)</tt>
28
+ # <tt>.is(String lex_var)
29
+ def is(lex_var, method = :auto)
30
+ if(method == :auto)
31
+ unless @@norm_method.nil?
32
+ method = @@norm_method
33
+ else
34
+ raise ArgumentError, "Specify method, or call Norm::norm_method"
35
+ end
36
+ end
37
+ if @domain.adherence_functions.has_key? lex_var
38
+ return Adherence.new(@domain.adherence_functions[lex_var].at(@value))
39
+ else
40
+ raise ArgumentError, "Specified adjective '#{lex_var}' does not belong to variable's domain"
41
+ end
42
+ end
43
+
44
+ # Calculates negation of variables adherence. Usage - same as .is
45
+ def is_not(lex_var, method = :auto)
46
+ if(method == :auto)
47
+ unless @@norm_method.nil?
48
+ method = @@norm_method
49
+ else
50
+ raise ArgumentError, "Specify method, or call Norm::norm_method"
51
+ end
52
+ end
53
+ if @domain.adherence_functions.has_key? lex_var
54
+ return Adherence.new(1-@domain.adherence_functions[lex_var].at(@value))
55
+ else
56
+ raise ArgumentError, "Specified adjective does not belong to variable's domain"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,42 @@
1
+ # Module with t-norms and s-norms
2
+ module Norm
3
+ NORM_METHODS = {
4
+ :maxmin => {:t => :t_norm_max_min, :s => :s_norm_max_min},
5
+ :prob => {:t => :t_norm_prob, :s => :s_norm_prob}
6
+ }
7
+
8
+ @@norm_method = nil
9
+
10
+ # Sets the method for norms. Valid methods are keys in <em>NORM_METHODS</em>
11
+ def Norm.norm_method(m)
12
+ if NORM_METHODS.has_key?(m)
13
+ @@norm_method = m
14
+ else
15
+ sm = ""
16
+ NORM_METHODS.each_key do |k|
17
+ sm << "#{k} "
18
+ end
19
+ raise ArgumentError, "method #{m} is not supported for t-norms and s-norms\nSupported methods: #{sm}"
20
+ end
21
+ end
22
+
23
+ # Max/min t-norm
24
+ def t_norm_max_min(x,y)
25
+ return [x,y].min
26
+ end
27
+
28
+ # Max/min s-norm
29
+ def s_norm_max_min(x,y)
30
+ return [x,y].max
31
+ end
32
+
33
+ # Probablistic t-norm
34
+ def t_norm_prob(x,y)
35
+ return x*y
36
+ end
37
+
38
+ # Probablistic s-norm
39
+ def s_norm_prob(x,y)
40
+ return x+y-x*y
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ # Point used for adherence functions.
2
+ # Should not be thought of as a point-in-space.
3
+ class Point
4
+ include Comparable
5
+
6
+ attr_accessor :x, :y
7
+ # Constructor. Usage:
8
+ # <tt>.new(Float x, Float y)</tt>
9
+ # y must be [0;1]
10
+ def initialize(x, y)
11
+ @x = x.to_f
12
+ if y < 0 || y > 1
13
+ raise RangeError, "Function values for fuzzy sets must be from [0;1]", caller
14
+ end
15
+ @y = y.to_f
16
+ end
17
+
18
+ def to_s
19
+ return "(#{@x},#{@y})"
20
+ end
21
+
22
+ # Convenient for use with GNUplot.
23
+ def to_gplot
24
+ return "#{@x} #{@y}\n"
25
+ end
26
+
27
+ # This is here only for sorting, should not be used for 'real' comparsion
28
+ def <=>(other)
29
+ @x <=> other.x
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ # Represents the Rule for FuzzySystem where both antescendent and consequent are Proc objects
2
+ class Rule
3
+ @antecendent
4
+ @consequent
5
+
6
+ # Usage:
7
+ # <tt>.new(Proc antecentent, Proc consequent)</tt>
8
+ def initialize(ant, cons)
9
+ @antecendent = ant
10
+ @consequent = cons
11
+ end
12
+
13
+ # Calls antecentent and then calls consequent
14
+ # passing the result from antecendent to consequent.
15
+ # In most cases the passed value is Adherence.
16
+ # Usage:
17
+ # <tt>.apply()</tt>
18
+ def apply
19
+ @consequent.call(@antecendent.call)
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # Trapezoidal adherence function
2
+ class Trapezoidal < Function
3
+ #Default constructor. Creates the trapezoidal function. Order of arguments is irrelevant. They will be sorted in ascending order.
4
+ #<tt>.new(Float a, Float b, Float c, Float d)</tt>
5
+ def initialize(name, a, b, c, d)
6
+ p = [a,b,c,d].sort
7
+ super(name, Point.new(p[0],0),Point.new(p[1],1),Point.new(p[2],1),Point.new(p[3],0))
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # Triangular adherence function
2
+ class Triangular < Function
3
+ #Default constructor. Creates the trapezoidal function. Order of arguments is irrelevant. They will be sorted in ascending order.
4
+ #<tt>.new(Float a, Float b, Float c)</tt>
5
+ def initialize(name, a, b, c)
6
+ p = [a,b,c].sort
7
+ super(name, Point.new(p[0],0),Point.new(p[1],1),Point.new(p[2],0))
8
+ end
9
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
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 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'rfuzzy'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestRfuzzy < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rfuzzy
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - lolek09
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-01 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ type: :development
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ name: shoulda
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ prerelease: false
37
+ type: :development
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 23
44
+ segments:
45
+ - 1
46
+ - 0
47
+ - 0
48
+ version: 1.0.0
49
+ name: bundler
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ prerelease: false
53
+ type: :development
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 11
60
+ segments:
61
+ - 1
62
+ - 6
63
+ - 2
64
+ version: 1.6.2
65
+ name: jeweler
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ prerelease: false
69
+ type: :development
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ name: rcov
80
+ version_requirements: *id004
81
+ description: rfuzzy is a fuzzy logic library for Ruby
82
+ email: karolmajta@gmail.com
83
+ executables: []
84
+
85
+ extensions: []
86
+
87
+ extra_rdoc_files:
88
+ - LICENSE.txt
89
+ - README.rdoc
90
+ files:
91
+ - .document
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.rdoc
95
+ - Rakefile
96
+ - VERSION
97
+ - lib/rfuzzy.rb
98
+ - lib/rfuzzy/adherence.rb
99
+ - lib/rfuzzy/defuzz.rb
100
+ - lib/rfuzzy/function.rb
101
+ - lib/rfuzzy/fuzzy_domain.rb
102
+ - lib/rfuzzy/fuzzy_system.rb
103
+ - lib/rfuzzy/fuzzy_variable.rb
104
+ - lib/rfuzzy/norm.rb
105
+ - lib/rfuzzy/point.rb
106
+ - lib/rfuzzy/rule.rb
107
+ - lib/rfuzzy/trapezoidal.rb
108
+ - lib/rfuzzy/triangular.rb
109
+ - test/helper.rb
110
+ - test/test_rfuzzy.rb
111
+ has_rdoc: true
112
+ homepage: http://github.com/lolek09/rfuzzy
113
+ licenses:
114
+ - JSON
115
+ post_install_message:
116
+ rdoc_options: []
117
+
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ hash: 3
126
+ segments:
127
+ - 0
128
+ version: "0"
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ hash: 3
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ requirements: []
139
+
140
+ rubyforge_project:
141
+ rubygems_version: 1.5.2
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: rfuzzy is a fuzzy logic library for Ruby
145
+ test_files: []
146
+