fuzzy_associative_memory 1.3.1 → 1.3.2
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/.gitignore +3 -0
- data/CHANGELOG.md +3 -0
- data/Guardfile +24 -0
- data/Rakefile +23 -5
- data/fuzzy_associative_memory.gemspec +17 -0
- data/lib/fuzzy_associative_memory/linguistic_variable.rb +1 -1
- data/lib/fuzzy_associative_memory/rule.rb +5 -12
- data/lib/fuzzy_associative_memory/triangle.rb +1 -3
- data/spec/spec_helper.rb +19 -0
- data/spec/trapezoid_spec.rb +189 -0
- data/spec/triangle_spec.rb +97 -0
- metadata +52 -17
- data/test/all_suite.rb +0 -11
- data/test/trapezoid_test.rb +0 -96
- data/test/triangle_test.rb +0 -64
data/.gitignore
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Changelog for fuzzy-associative-memory
|
2
2
|
|
3
|
+
## 1.3.2, 12 September 2013
|
4
|
+
* Correct a regression in Rule
|
5
|
+
|
3
6
|
## 1.3.1, 2 September 2013
|
4
7
|
* Massive performance and efficiency effort. This version of the Gem clocks in at 3.5x the speed of my 1.1 series in synthetic benchmarks. If you use the FAM in a tight loop, this will help a lot.
|
5
8
|
|
data/Guardfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec, :all_on_start => true, :all_after_pass => true do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
|
9
|
+
# Rails example
|
10
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
11
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
12
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
13
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
14
|
+
watch('config/routes.rb') { "spec/routing" }
|
15
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
16
|
+
|
17
|
+
# Capybara features specs
|
18
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
19
|
+
|
20
|
+
# Turnip features and steps
|
21
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
22
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
23
|
+
end
|
24
|
+
|
data/Rakefile
CHANGED
@@ -1,8 +1,26 @@
|
|
1
|
-
require 'rake/testtask'
|
1
|
+
# require 'rake/testtask'
|
2
2
|
|
3
|
-
task :default => [:test]
|
3
|
+
# task :default => [:test]
|
4
4
|
|
5
|
-
desc "Run the test suite once"
|
6
|
-
task :test do
|
7
|
-
|
5
|
+
# desc "Run the test suite once"
|
6
|
+
# task :test do
|
7
|
+
# ruby "test/all_suite.rb"
|
8
|
+
# end
|
9
|
+
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
|
12
|
+
desc 'Default: run specs.'
|
13
|
+
task :default=>:spec
|
14
|
+
|
15
|
+
desc "Run specs"
|
16
|
+
RSpec::Core::RakeTask.new do |t|
|
17
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
18
|
+
# Put spec opts in a file named .rspec in root
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Generate code coverage"
|
22
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
23
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
24
|
+
t.rcov = true
|
25
|
+
t.rcov_opts = ['--exclude', 'spec']
|
8
26
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'fuzzy_associative_memory'
|
3
|
+
s.version = '1.3.2'
|
4
|
+
s.date = '2013-09-12'
|
5
|
+
s.summary = "A fuzzy logic 'Fuzzy Associative Memory' (FAM) for fuzzy control systems, decision-making, artificial intelligence / AI, game agents & bots, etc."
|
6
|
+
s.description = <<-EOF
|
7
|
+
A Fuzzy Associative Memory (FAM for short) is a Fuzzy Logic tool for decision making. Fuzzy logic FAMs have a wide range of practical applications: Control systems, such as governing a fan to keep a room at the "just right" temperature; Game AI, such as imbuing bots with human-like decision-making behavior; Prediction systems, linking causes with effects. A FAM uses Fuzzy Sets to establish a set of rules that are linguistic in nature. The linguistic rules, and the fuzzy sets they contain, are defined by a human "expert" (presumably, you). The rules therefore codify intelligence and map this knowledge from the human domain to the digital.
|
8
|
+
EOF
|
9
|
+
s.authors = ["Chris Powell"]
|
10
|
+
s.email = 'cpowell@prylis.com'
|
11
|
+
s.files = `git ls-files`.split($/)
|
12
|
+
s.homepage = 'http://github.com/cpowell/fuzzy-associative-memory'
|
13
|
+
s.license = 'LGPL'
|
14
|
+
s.rdoc_options = ["--main", "README.md"]
|
15
|
+
s.add_development_dependency 'rspec'
|
16
|
+
s.add_development_dependency "rake"
|
17
|
+
end
|
@@ -80,7 +80,7 @@ class FuzzyAssociativeMemory::LinguisticVariable
|
|
80
80
|
)
|
81
81
|
|
82
82
|
if opts[:logarithmic_x]
|
83
|
-
commands += "set xr [
|
83
|
+
commands += "set xr [#{[min, 1].max}:#{max}]\nset logscale x\n"
|
84
84
|
else
|
85
85
|
commands += "set xr [#{min}:#{max}]\n"
|
86
86
|
end
|
@@ -37,6 +37,7 @@ class FuzzyAssociativeMemory::Rule
|
|
37
37
|
@antecedents = antecedent_array
|
38
38
|
@consequent = consequent
|
39
39
|
@boolean = boolean
|
40
|
+
@mus = []
|
40
41
|
end
|
41
42
|
|
42
43
|
# Triggers the rule. The antecedent(s) is/are fired with the supplied inputs
|
@@ -52,23 +53,15 @@ class FuzzyAssociativeMemory::Rule
|
|
52
53
|
raise ArgumentError, "value array must be an collection of inputs but is a #{value_array.class}" unless value_array.is_a? Array
|
53
54
|
raise ArgumentError, "value array passed to Rule::fire() cannot be empty" if value_array.empty?
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
for i in 0..ant_len-1
|
59
|
-
v = @antecedents[i].mu(value_array[i])
|
60
|
-
@max = v if @max.nil? || v > @max
|
61
|
-
@min = v if @min.nil? || v < @min
|
56
|
+
for i in 0..@antecedents.size-1
|
57
|
+
@mus[i] = @antecedents[i].mu(value_array[i])
|
62
58
|
end
|
63
59
|
|
64
60
|
if @boolean==:and
|
65
|
-
return @min # AND / Intersection == minimum
|
61
|
+
return @mus.min # AND / Intersection == minimum
|
66
62
|
else
|
67
|
-
return @max # OR / Union == maximum
|
63
|
+
return @mus.max # OR / Union == maximum
|
68
64
|
end
|
69
|
-
|
70
|
-
# puts "Fired rule '#{@natural_language}': µ choices are [#{@mus.join(',')}], final µ is #{mu}" if $verbosity
|
71
|
-
# [@consequent, mu]
|
72
65
|
end
|
73
66
|
|
74
67
|
end
|
@@ -44,12 +44,10 @@ class FuzzyAssociativeMemory::Triangle < FuzzyAssociativeMemory::FuzzySet
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def mamdani(clip_height)
|
47
|
-
left = @left
|
48
47
|
top_left = @left + (clip_height * (@center - @left))
|
49
48
|
top_right = @right - (clip_height * (@right - @center))
|
50
|
-
right = @right
|
51
49
|
|
52
|
-
FuzzyAssociativeMemory::Trapezoid.new(left, top_left, top_right, right, clip_height)
|
50
|
+
FuzzyAssociativeMemory::Trapezoid.new(@left, top_left, top_right, @right, clip_height)
|
53
51
|
end
|
54
52
|
|
55
53
|
def to_s
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
require 'fuzzy_associative_memory'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
config.filter_run :focus
|
13
|
+
|
14
|
+
# Run specs in random order to surface order dependencies. If you find an
|
15
|
+
# order dependency and want to debug it, you can fix the order by providing
|
16
|
+
# the seed, which is printed after each run.
|
17
|
+
# --seed 1234
|
18
|
+
config.order = 'random'
|
19
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Trapezoid" do
|
4
|
+
before :all do
|
5
|
+
# per-context setup
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
# per-example setup
|
10
|
+
@t = FuzzyAssociativeMemory::Trapezoid.new(7, 10, 13, 16)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
# per-example teardown
|
15
|
+
end
|
16
|
+
|
17
|
+
after :all do
|
18
|
+
# per-context teardown
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has centroid where expected" do
|
22
|
+
expect(@t.centroid_x).to eq(11.5)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has DOM 0 outside left bound" do
|
26
|
+
expect(@t.mu(2)).to eq(0)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has DOM 0 outside right bound" do
|
30
|
+
expect(@t.mu(17)).to eq(0)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "has DOM 1 at peak" do
|
34
|
+
expect(@t.mu(12)).to eq(1.0)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has DOM 1 at top left" do
|
38
|
+
expect(@t.mu(10)).to eq(1.0)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "has DOM 1 at top right" do
|
42
|
+
expect(@t.mu(13)).to eq(1.0)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has DOM 0.5 at half right offset" do
|
46
|
+
expect(@t.mu(14.5)).to eq(0.5)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "has DOM 0.5 at half left offset" do
|
50
|
+
expect(@t.mu(8.5)).to eq(0.5)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "has DOM 0 at right" do
|
54
|
+
expect(@t.mu(16)).to eq(0)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "has DOM 0 at left" do
|
58
|
+
expect(@t.mu(7)).to eq(0)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "has correct DOM at 3/5 offset" do
|
62
|
+
@t = FuzzyAssociativeMemory::Triangle.new(0, 5, 10)
|
63
|
+
expect(@t.mu(3.0)).to eq(0.6)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "has correct DOM at 7/10 offset" do
|
67
|
+
@t = FuzzyAssociativeMemory::Triangle.new(0, 5, 10)
|
68
|
+
expect(@t.mu(7.0)).to eq(0.6)
|
69
|
+
end
|
70
|
+
|
71
|
+
context "Centroid calculation" do
|
72
|
+
it "has a proper centroid 1" do
|
73
|
+
@t=FuzzyAssociativeMemory::Trapezoid.new(0, 20, 50, 50)
|
74
|
+
expect(@t.centroid_x).to be_within(0.0005).of(29.583)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "has a proper centroid 2" do
|
78
|
+
@t=FuzzyAssociativeMemory::Trapezoid.new(0, 10, 20, 30)
|
79
|
+
expect(@t.centroid_x).to eq(15)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "has a proper centroid 3" do
|
83
|
+
@t=FuzzyAssociativeMemory::Trapezoid.new(50, 80, 100, 100)
|
84
|
+
expect(@t.centroid_x).to be_within(0.0005).of(81.4283)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "has the correct DOM when fully positive" do
|
89
|
+
@t = FuzzyAssociativeMemory::Trapezoid.new(20, 30, 40, 50)
|
90
|
+
expect(@t.mu(41)).to eq(0.9)
|
91
|
+
end
|
92
|
+
|
93
|
+
context "Larsen implication" do
|
94
|
+
before do
|
95
|
+
@scaled = @t.larsen(0.15)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "stays a trapezoid" do
|
99
|
+
expect(@scaled).to be_a(FuzzyAssociativeMemory::Trapezoid)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "takes the height of the larsen value" do
|
103
|
+
expect(@scaled.height).to eq(0.15)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "keeps same corner points" do
|
107
|
+
expect(@scaled.left).to eq(7)
|
108
|
+
expect(@scaled.top_left).to eq(10)
|
109
|
+
expect(@scaled.top_right).to eq(13)
|
110
|
+
expect(@scaled.right).to eq(16)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "Mamdani implication" do
|
115
|
+
before do
|
116
|
+
@scaled = @t.mamdani(0.83)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "stays a trapezoid" do
|
120
|
+
expect(@scaled).to be_a(FuzzyAssociativeMemory::Trapezoid)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "keeps same left and right corner points" do
|
124
|
+
expect(@scaled.left).to eq(7)
|
125
|
+
expect(@scaled.right).to eq(16)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "takes the height of the mamdani value" do
|
129
|
+
expect(@scaled.height).to eq(0.83)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "Mamdani with one point at zero" do
|
134
|
+
before do
|
135
|
+
@t = FuzzyAssociativeMemory::Trapezoid.new(0, 30, 100, 110)
|
136
|
+
@scaled = @t.mamdani(0.83)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "stays a trapezoid" do
|
140
|
+
expect(@scaled).to be_a(FuzzyAssociativeMemory::Trapezoid)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "keeps same left and right corner points" do
|
144
|
+
expect(@scaled.left).to eq(0)
|
145
|
+
expect(@scaled.right).to eq(110)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "gets the correct new top left" do
|
149
|
+
expect(@scaled.top_left).to eq(24.9)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "gets the correct new top right" do
|
153
|
+
expect(@scaled.top_right).to eq(101.7)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "takes the height of the mamdani value" do
|
157
|
+
expect(@scaled.height).to eq(0.83)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "Mamdani with all points positive" do
|
162
|
+
before do
|
163
|
+
@t = FuzzyAssociativeMemory::Trapezoid.new(50, 80, 150, 160)
|
164
|
+
@scaled = @t.mamdani(0.83)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "stays a trapezoid" do
|
168
|
+
expect(@scaled).to be_a(FuzzyAssociativeMemory::Trapezoid)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "keeps same left and right corner points" do
|
172
|
+
expect(@scaled.left).to eq(50)
|
173
|
+
expect(@scaled.right).to eq(160)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "gets the correct new top left" do
|
177
|
+
expect(@scaled.top_left).to eq(74.9)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "gets the correct new top right" do
|
181
|
+
expect(@scaled.top_right).to eq(151.7)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "takes the height of the mamdani value" do
|
185
|
+
expect(@scaled.height).to eq(0.83)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Triangle" do
|
4
|
+
before :all do
|
5
|
+
# per-context setup
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
# per-example setup
|
10
|
+
@t = FuzzyAssociativeMemory::Triangle.new(7, 10, 13)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
# per-example teardown
|
15
|
+
end
|
16
|
+
|
17
|
+
after :all do
|
18
|
+
# per-context teardown
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has centroid of peak for isoceles triangle" do
|
22
|
+
expect(@t.centroid_x).to eq(10)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has DOM 0 outside left bound" do
|
26
|
+
expect(@t.mu(2)).to eq(0)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has DOM 0 outside right bound" do
|
30
|
+
expect(@t.mu(15)).to eq(0)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "has DOM 1 at peak" do
|
34
|
+
expect(@t.mu(10)).to eq(1.0)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has DOM 0.5 at half right offset" do
|
38
|
+
expect(@t.mu(11.5)).to eq(0.5)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "has DOM 0.5 at half left offset" do
|
42
|
+
expect(@t.mu(8.5)).to eq(0.5)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has correct DOM at 3/5 offset" do
|
46
|
+
@t = FuzzyAssociativeMemory::Triangle.new(0, 5, 10)
|
47
|
+
expect(@t.mu(3.0)).to eq(0.6)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "has correct DOM at 7/10 offset" do
|
51
|
+
@t = FuzzyAssociativeMemory::Triangle.new(0, 5, 10)
|
52
|
+
expect(@t.mu(7.0)).to eq(0.6)
|
53
|
+
end
|
54
|
+
|
55
|
+
context "Larsen implication" do
|
56
|
+
before do
|
57
|
+
@scaled = @t.larsen(0.15)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "takes the height of the larsen value" do
|
61
|
+
expect(@scaled.height).to eq(0.15)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "keeps right, left and center" do
|
65
|
+
expect(@scaled.left).to eq(7)
|
66
|
+
expect(@scaled.right).to eq(13)
|
67
|
+
expect(@scaled.center).to eq(10)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "Mamdani implication" do
|
72
|
+
before do
|
73
|
+
@scaled = @t.mamdani(0.83)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "becomes a trapezoid" do
|
77
|
+
expect(@scaled).to be_a(FuzzyAssociativeMemory::Trapezoid)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "keeps left and right" do
|
81
|
+
expect(@scaled.left).to eq(7)
|
82
|
+
expect(@scaled.right).to eq(13)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "gets a proper top left" do
|
86
|
+
expect(@scaled.top_left).to eq(9.49)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "gets a proper top right" do
|
90
|
+
expect(@scaled.top_right).to eq(10.51)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "takes the height of the mamdani value" do
|
94
|
+
expect(@scaled.height).to eq(0.83)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fuzzy_associative_memory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,40 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-09-
|
13
|
-
dependencies:
|
12
|
+
date: 2013-09-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
none: false
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
none: false
|
28
|
+
prerelease: false
|
29
|
+
type: :development
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
none: false
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
none: false
|
44
|
+
prerelease: false
|
45
|
+
type: :development
|
14
46
|
description: |2
|
15
47
|
A Fuzzy Associative Memory (FAM for short) is a Fuzzy Logic tool for decision making. Fuzzy logic FAMs have a wide range of practical applications: Control systems, such as governing a fan to keep a room at the "just right" temperature; Game AI, such as imbuing bots with human-like decision-making behavior; Prediction systems, linking causes with effects. A FAM uses Fuzzy Sets to establish a set of rules that are linguistic in nature. The linguistic rules, and the fuzzy sets they contain, are defined by a human "expert" (presumably, you). The rules therefore codify intelligence and map this knowledge from the human domain to the digital.
|
16
48
|
email: cpowell@prylis.com
|
@@ -18,13 +50,12 @@ executables: []
|
|
18
50
|
extensions: []
|
19
51
|
extra_rdoc_files: []
|
20
52
|
files:
|
21
|
-
-
|
22
|
-
-
|
23
|
-
-
|
24
|
-
-
|
25
|
-
-
|
26
|
-
-
|
27
|
-
- lib/fuzzy_associative_memory/triangle.rb
|
53
|
+
- .gitignore
|
54
|
+
- CHANGELOG.md
|
55
|
+
- Guardfile
|
56
|
+
- LICENSE
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
28
59
|
- bin/hvac_system_example.rb
|
29
60
|
- bin/plot of distance to target.png
|
30
61
|
- bin/plot of distance to target.svg
|
@@ -37,13 +68,17 @@ files:
|
|
37
68
|
- bin/plot of weapon desirability.png
|
38
69
|
- bin/plot of weapon desirability.svg
|
39
70
|
- bin/weapon_choice_example.rb
|
40
|
-
-
|
41
|
-
-
|
42
|
-
-
|
43
|
-
-
|
44
|
-
-
|
45
|
-
-
|
46
|
-
-
|
71
|
+
- fuzzy_associative_memory.gemspec
|
72
|
+
- lib/fuzzy_associative_memory.rb
|
73
|
+
- lib/fuzzy_associative_memory/linguistic_variable.rb
|
74
|
+
- lib/fuzzy_associative_memory/rule.rb
|
75
|
+
- lib/fuzzy_associative_memory/ruleset.rb
|
76
|
+
- lib/fuzzy_associative_memory/set.rb
|
77
|
+
- lib/fuzzy_associative_memory/trapezoid.rb
|
78
|
+
- lib/fuzzy_associative_memory/triangle.rb
|
79
|
+
- spec/spec_helper.rb
|
80
|
+
- spec/trapezoid_spec.rb
|
81
|
+
- spec/triangle_spec.rb
|
47
82
|
homepage: http://github.com/cpowell/fuzzy-associative-memory
|
48
83
|
licenses:
|
49
84
|
- LGPL
|
data/test/all_suite.rb
DELETED
data/test/trapezoid_test.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
gem 'minitest'
|
2
|
-
require 'minitest/autorun'
|
3
|
-
|
4
|
-
$:.push File.expand_path('../../lib/', __FILE__)
|
5
|
-
require 'fuzzy_associative_memory'
|
6
|
-
|
7
|
-
# ruby ./test/trapezoid_test.rb
|
8
|
-
|
9
|
-
class TrapezoidTest < MiniTest::Unit::TestCase
|
10
|
-
def setup
|
11
|
-
@t = FuzzyAssociativeMemory::Trapezoid.new(7, 10, 13, 16)
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_dom_is_zero_outside_left_bound
|
15
|
-
assert_equal(0, @t.mu(2))
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_dom_is_zero_at_left
|
19
|
-
assert_equal(0, @t.mu(7))
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_dom_is_half_at_half_left_offset
|
23
|
-
assert_equal(0.5, @t.mu(8.5))
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_dom_is_one_at_top_left
|
27
|
-
assert_equal(1.0, @t.mu(10))
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_dom_is_one_at_top_right
|
31
|
-
assert_equal(1.0, @t.mu(13))
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_dom_is_half_at_half_right_offset
|
35
|
-
assert_equal(0.5, @t.mu(14.5))
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_dom_is_zero_at_right
|
39
|
-
assert_equal(0, @t.mu(16))
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_dom_is_zero_outside_right_bound
|
43
|
-
assert_equal(0, @t.mu(17))
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_dom_is_correct_at_three_fifths_offset
|
47
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(0, 5, 10, 15)
|
48
|
-
assert_equal(0.6, t.mu(3.0))
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_dom_is_correct_at_seven_tenths_offset
|
52
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(0, 5, 10, 15)
|
53
|
-
assert_equal(0.6, t.mu(12.0))
|
54
|
-
end
|
55
|
-
|
56
|
-
def test_centroid_calculation_1
|
57
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(0, 20, 50, 50)
|
58
|
-
assert_equal(29.583333333333332, t.centroid_x)
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_centroid_calculation_2
|
62
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(0, 10, 20, 30)
|
63
|
-
assert_equal(15.0, t.centroid_x)
|
64
|
-
end
|
65
|
-
|
66
|
-
def test_centroid_calculation_when_not_starting_at_zero
|
67
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(50, 80, 100, 100)
|
68
|
-
assert_equal(81.42857142857143, t.centroid_x)
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_dom_1
|
72
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(20, 30, 40, 50)
|
73
|
-
assert_equal(0.9, t.mu(41))
|
74
|
-
end
|
75
|
-
|
76
|
-
def test_mamdani_clipping_1
|
77
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(0, 30, 100, 110)
|
78
|
-
t2 = t.mamdani(0.83)
|
79
|
-
assert_equal(0, t2.left)
|
80
|
-
assert_equal(24.9, t2.top_left)
|
81
|
-
assert_equal(101.7, t2.top_right)
|
82
|
-
assert_equal(110, t2.right)
|
83
|
-
assert_equal(0.83, t2.height)
|
84
|
-
end
|
85
|
-
|
86
|
-
def test_mamdani_clipping_2
|
87
|
-
t = FuzzyAssociativeMemory::Trapezoid.new(50, 80, 150, 160)
|
88
|
-
t2 = t.mamdani(0.83)
|
89
|
-
assert_equal(50, t2.left)
|
90
|
-
assert_equal(74.9, t2.top_left)
|
91
|
-
assert_equal(151.7, t2.top_right)
|
92
|
-
assert_equal(160, t2.right)
|
93
|
-
assert_equal(0.83, t2.height)
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
data/test/triangle_test.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
gem 'minitest'
|
2
|
-
require 'minitest/autorun'
|
3
|
-
|
4
|
-
$:.push File.expand_path('../../lib/', __FILE__)
|
5
|
-
require 'fuzzy_associative_memory'
|
6
|
-
|
7
|
-
# ruby ./test/triangle_test.rb
|
8
|
-
|
9
|
-
class TriangleTest < MiniTest::Unit::TestCase
|
10
|
-
def setup
|
11
|
-
@t = FuzzyAssociativeMemory::Triangle.new(7, 10, 13)
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_dom_is_zero_outside_left_bound
|
15
|
-
assert_equal(0, @t.mu(2))
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_dom_is_zero_outside_right_bound
|
19
|
-
assert_equal(0, @t.mu(15))
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_dom_is_one_at_peak
|
23
|
-
assert_equal(1.0, @t.mu(10))
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_dom_is_half_at_half_right_offset
|
27
|
-
assert_equal(0.5, @t.mu(11.5))
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_dom_is_half_at_half_left_offset
|
31
|
-
assert_equal(0.5, @t.mu(8.5))
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_dom_is_correct_at_three_fifths_offset
|
35
|
-
t = FuzzyAssociativeMemory::Triangle.new(0, 5, 10)
|
36
|
-
assert_equal(0.6, t.mu(3.0))
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_dom_is_correct_at_seven_tenths_offset
|
40
|
-
t = FuzzyAssociativeMemory::Triangle.new(0, 5, 10)
|
41
|
-
assert_equal(0.6, t.mu(7.0))
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_centroid_calculation
|
45
|
-
t = FuzzyAssociativeMemory::Triangle.new(5, 10, 15)
|
46
|
-
assert_equal(10.0, t.centroid_x)
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_larsen_scaling
|
50
|
-
scaled = @t.larsen(0.15)
|
51
|
-
assert_equal(0.15, scaled.height)
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_mamdani_clipping_1
|
55
|
-
t2 = @t.mamdani(0.83)
|
56
|
-
assert(t2.is_a? FuzzyAssociativeMemory::Trapezoid)
|
57
|
-
assert_equal(7, t2.left)
|
58
|
-
assert_equal(9.49, t2.top_left)
|
59
|
-
assert_equal(10.51, t2.top_right)
|
60
|
-
assert_equal(13, t2.right)
|
61
|
-
assert_equal(0.83, t2.height)
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|