fuzzy_associative_memory 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/bin/hvac_system_example.rb +2 -2
- data/lib/fuzzy_associative_memory/linguistic_variable.rb +24 -8
- data/lib/fuzzy_associative_memory/rule.rb +8 -4
- data/lib/fuzzy_associative_memory/ruleset.rb +23 -15
- data/lib/fuzzy_associative_memory/set.rb +4 -0
- data/lib/fuzzy_associative_memory/trapezoid.rb +1 -0
- data/lib/fuzzy_associative_memory/triangle.rb +1 -0
- metadata +2 -2
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Changelog for fuzzy-associative-memory
|
2
2
|
|
3
|
+
## 1.2, 23 August 2013
|
4
|
+
* RuleSet::calculate() gets a refactor / efficiency rewrite
|
5
|
+
* Argument sanity-checking
|
6
|
+
* Improvements to Gnuplot visualization (logarithmic axis, return filename to caller)
|
7
|
+
|
3
8
|
## 1.1, 26 July 2013
|
4
9
|
* Fuzzy Linguistic Variables are hard to visualize, especially when they get complex. To remedy that, you can now instruct an FLV to shell out to your installed Gnuplot and plot itself. The images are saved in your system's tmpdir.
|
5
10
|
|
data/bin/hvac_system_example.rb
CHANGED
@@ -32,7 +32,7 @@ hot = FuzzyAssociativeMemory::Trapezoid.new(80, 90, 90, 90) # 20 deg wide
|
|
32
32
|
|
33
33
|
temperature_in.sets = [cold, cool, just_right, warm, hot]
|
34
34
|
# Comment out if you don't have Gnuplot installed:
|
35
|
-
temperature_in.gnuplot
|
35
|
+
temperature_in.gnuplot({:logarithmic_x=>false})
|
36
36
|
|
37
37
|
# The output side -- the consequent -- expressed as a number of fuzzy sets,
|
38
38
|
# with each set representing a natural-language description. The 'resultant
|
@@ -47,7 +47,7 @@ blast = FuzzyAssociativeMemory::Triangle.new(70, 100, 130) # 60 CFM wide
|
|
47
47
|
|
48
48
|
fan_speed.sets = [stop, slow, medium, fast, blast]
|
49
49
|
# Comment out if you don't have Gnuplot installed:
|
50
|
-
fan_speed.gnuplot
|
50
|
+
fan_speed.gnuplot({:logarithmic_x=>false})
|
51
51
|
|
52
52
|
# Natural-language marriage of the inputs to the outputs, e.g.
|
53
53
|
# "If the temperature is cool, the fan motor speed should be slow."
|
@@ -21,9 +21,19 @@ class FuzzyAssociativeMemory::LinguisticVariable
|
|
21
21
|
@sets << set
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
# Shell out to your system's installed Gnuplot binary to create a graphical depiction
|
25
|
+
# of this FLV.
|
26
|
+
#
|
27
|
+
# * *Args* :
|
28
|
+
# - +options+ -> a hash of options; see below
|
29
|
+
#
|
30
|
+
def gnuplot(options = {})
|
25
31
|
return if @sets.empty?
|
26
32
|
|
33
|
+
opts = {
|
34
|
+
:logarithmic_x => false # Default to non-log X axis
|
35
|
+
}.merge(options)
|
36
|
+
|
27
37
|
datafile = Tempfile.new('fam_')
|
28
38
|
begin
|
29
39
|
@sets.each_with_index do |s, i|
|
@@ -55,21 +65,26 @@ class FuzzyAssociativeMemory::LinguisticVariable
|
|
55
65
|
|
56
66
|
# set term dumb
|
57
67
|
commands = %Q(
|
58
|
-
set terminal svg
|
59
|
-
set autoscale
|
68
|
+
set terminal svg size 1024,400
|
60
69
|
set xlabel 'value'
|
61
70
|
set ylabel 'membership'
|
62
|
-
set
|
63
|
-
set
|
71
|
+
set xtics autofreq
|
72
|
+
set ytics autofreq
|
64
73
|
set output "#{fn}"
|
65
74
|
set title "Fuzzy sets for #{name}"
|
66
75
|
set mytics 4
|
67
76
|
set mxtics 4
|
68
|
-
set size ratio 0.
|
69
|
-
set grid
|
70
|
-
|
77
|
+
set size ratio 0.25
|
78
|
+
set grid mxtics xtics ytics
|
79
|
+
|
71
80
|
)
|
72
81
|
|
82
|
+
if opts[:logarithmic_x]
|
83
|
+
commands += "set xr [[#{[min, 1].max}:#{max}]\nset logscale x\n"
|
84
|
+
else
|
85
|
+
commands += "set xr [#{min}:#{max}]\n"
|
86
|
+
end
|
87
|
+
|
73
88
|
# plot "#{datafile.path}" using 2:3 notitle with linespoints lw 2
|
74
89
|
|
75
90
|
plotarr=[]
|
@@ -92,6 +107,7 @@ class FuzzyAssociativeMemory::LinguisticVariable
|
|
92
107
|
datafile.unlink
|
93
108
|
end
|
94
109
|
|
110
|
+
return fn if defined?(fn)
|
95
111
|
end
|
96
112
|
|
97
113
|
def [](n)
|
@@ -15,7 +15,7 @@ class FuzzyAssociativeMemory::Rule
|
|
15
15
|
#
|
16
16
|
# * *Args* :
|
17
17
|
# - +antecedent_array+ -> an array of one or more input fuzzy sets
|
18
|
-
# - +boolean+ -> term to join the antecedents, may be: nil,
|
18
|
+
# - +boolean+ -> term to join the antecedents, may be: nil, :and, :or
|
19
19
|
# - +consequent+ -> the output fuzzy set
|
20
20
|
# - +natural_language+ -> a rule description (your own words), useful in output
|
21
21
|
#
|
@@ -28,9 +28,9 @@ class FuzzyAssociativeMemory::Rule
|
|
28
28
|
raise ArgumentError, "Consequent must be provided" unless consequent
|
29
29
|
|
30
30
|
if antecedent_array.size > 1
|
31
|
-
raise ArgumentError, "boolean must be sym :and or :or" unless [:and, :or].include? boolean
|
31
|
+
raise ArgumentError, "boolean must be sym :and or :or for multi-element antecedent arrays" unless [:and, :or].include? boolean
|
32
32
|
else
|
33
|
-
raise ArgumentError, "boolean must be nil" unless boolean.nil?
|
33
|
+
raise ArgumentError, "boolean must be nil for single-element antecedent arrays" unless boolean.nil?
|
34
34
|
end
|
35
35
|
|
36
36
|
@natural_language = natural_language
|
@@ -48,8 +48,12 @@ class FuzzyAssociativeMemory::Rule
|
|
48
48
|
# - the degree of fit for this rule
|
49
49
|
#
|
50
50
|
def fire(value_array)
|
51
|
-
|
51
|
+
raise ArgumentError, "value array passed to Rule::fire() cannot be nil" if value_array.nil?
|
52
|
+
raise ArgumentError, "value array must be an collection of inputs but is a #{value_array.class}" unless value_array.is_a? Enumerable
|
53
|
+
raise ArgumentError, "value array passed to Rule::fire() cannot be empty" if value_array.empty?
|
52
54
|
|
55
|
+
mus = Array.new
|
56
|
+
# puts value_array.join(", ")
|
53
57
|
@antecedents.each_with_index do |antecedent, index|
|
54
58
|
mus[index] = antecedent.mu(value_array[index])
|
55
59
|
end
|
@@ -25,7 +25,6 @@ class FuzzyAssociativeMemory::Ruleset
|
|
25
25
|
|
26
26
|
def calculate(*input_values)
|
27
27
|
@consequent_mus = {}
|
28
|
-
@kept_consequents = {}
|
29
28
|
@consequents = []
|
30
29
|
|
31
30
|
puts ">>> Firing all rules..." if $verbosity
|
@@ -35,26 +34,25 @@ class FuzzyAssociativeMemory::Ruleset
|
|
35
34
|
# have been fired more than once and we'll need that knowledge in a
|
36
35
|
# moment...
|
37
36
|
cons, mu = rule.fire(input_values)
|
37
|
+
|
38
|
+
# Since any given consequent may have been activated more than once, we
|
39
|
+
# need to get just a single µ value out -- we only care about the 'best'
|
40
|
+
# µ. A popular way of doing so is to OR the values together, i.e. keep the
|
41
|
+
# maximum µ value and discard the others.
|
38
42
|
if @consequent_mus.has_key?(cons)
|
39
|
-
@consequent_mus[cons]
|
43
|
+
if mu > @consequent_mus[cons]
|
44
|
+
@consequent_mus[cons] = mu
|
45
|
+
end
|
40
46
|
else
|
41
|
-
@consequent_mus[cons] =
|
47
|
+
@consequent_mus[cons] = mu
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
45
|
-
# Since any given consequent may have been activated more than once, we
|
46
|
-
# need to get just a single µ value out -- we only care about the 'best'
|
47
|
-
# µ. A popular way of doing so is to OR the values together, i.e. keep the
|
48
|
-
# maximum µ value and discard the others.
|
49
|
-
@consequent_mus.each do |cons, mu_array|
|
50
|
-
@kept_consequents[cons] = mu_array.max
|
51
|
-
end
|
52
|
-
|
53
51
|
# Using each µ value, alter the consequent fuzzy set's polgyon. This is
|
54
52
|
# called implication, and 'weights' the consequents properly. There are
|
55
53
|
# several common ways of doing it, such as Larsen (scaling) and Mamdani
|
56
54
|
# (clipping).
|
57
|
-
@
|
55
|
+
@consequent_mus.each do |cons, mu|
|
58
56
|
case @implication
|
59
57
|
when :mamdani
|
60
58
|
@consequents << cons.mamdani(mu)
|
@@ -72,8 +70,18 @@ class FuzzyAssociativeMemory::Ruleset
|
|
72
70
|
# of Maxima" summation mechanism. MaxAv is defined as:
|
73
71
|
# (∑ representative value * height) / (∑ height) for all output sets
|
74
72
|
# where 'representative value' is shape-dependent.
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
numerator=0
|
74
|
+
denominator=0
|
75
|
+
|
76
|
+
@consequents.each do |cons|
|
77
|
+
numerator += cons.centroid_x * cons.height
|
78
|
+
denominator += cons.height
|
79
|
+
end
|
80
|
+
|
81
|
+
return numerator/denominator
|
82
|
+
|
83
|
+
# numerator_terms = @consequents.map { |set| set.centroid_x * set.height }
|
84
|
+
# denominator_terms = @consequents.map { |set| set.height }
|
85
|
+
# numerator_terms.inject{|sum,x| sum + x } / denominator_terms.inject{|sum,x| sum + x }
|
78
86
|
end
|
79
87
|
end
|
@@ -22,6 +22,7 @@ class FuzzyAssociativeMemory::Trapezoid < FuzzyAssociativeMemory::FuzzySet
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def mu(value)
|
25
|
+
raise ArgumentError, "value passed to Trapezoid::mu() cannot be nil" if value.nil?
|
25
26
|
if value < @left || value > @right
|
26
27
|
0.0
|
27
28
|
elsif value >= @left && value < @top_left
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fuzzy_associative_memory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
4
5
|
prerelease:
|
5
|
-
version: 1.1.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Chris Powell
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-23 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: |2
|
15
15
|
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.
|