Last active
August 29, 2015 14:21
-
-
Save ikkentim/98828aa4e17a01522bce to your computer and use it in GitHub Desktop.
Fuzzy Logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
namespace FuzzyLogic | |
{ | |
internal class Program | |
{ | |
private static void Main(string[] args) | |
{ | |
var module = new FuzzyModule(); | |
var distToTarget = module.CreateFLV("DistToTarget"); | |
var targetClose = distToTarget.Add("Target_Close", new FuzzyLeftShoulder(0, 25, 150)); | |
var targetMedium = distToTarget.Add("Target_Medium", new FuzzyTriangle(25, 150, 300)); | |
var targetFar = distToTarget.Add("Target_Far", new FuzzyRightShoulder(150, 300, 1000)); | |
var desirability = module.CreateFLV("Desirability"); | |
var veryDesirable = desirability.Add("VeryDesirable", new FuzzyRightShoulder(50, 75, 100)); | |
var desirable = desirability.Add("Desirable", new FuzzyTriangle(25, 50, 75)); | |
var undesirable = desirability.Add("Undesirable", new FuzzyLeftShoulder(0, 25, 50)); | |
module.Add(targetClose.Very(), veryDesirable); | |
module.Add(targetClose, desirable); | |
module.Add(targetMedium, undesirable.Very()); | |
module.Add(targetFar, undesirable.Very()); | |
// ========================= | |
var input = 10; | |
module["DistToTarget"].Fuzzify(input); | |
var crisp = module.Defuzzify("Desirability"); | |
Console.WriteLine("For a distance of {0} m the target is {1} desirable", input, crisp); | |
// ========================= | |
var nameLength = module.CreateFLV("NameLength"); | |
var shortName = nameLength.Add("Short", new FuzzyLeftShoulder(1, 4, 6)); | |
var mediumName = nameLength.Add("Medium", new FuzzyTriangle(4, 6, 8)); | |
var longName = nameLength.Add("Long", new FuzzyRightShoulder(6, 8, 50)); | |
var awesomeness = module.CreateFLV("Awesomeness"); | |
var lowAwesomeness = awesomeness.Add("Low", new FuzzyLeftShoulder(0, 25, 35)); | |
var belowNormalAwesomeness = awesomeness.Add("BelowNormal", new FuzzyTriangle(25, 35, 45)); | |
var normalAwesomeness = awesomeness.Add("Normal", new FuzzyTrapezium(35, 45, 65, 75)); | |
var highAwesomeness = awesomeness.Add("High", new FuzzyTriangle(65, 75, 85)); | |
var awesomeAwesomeness = awesomeness.Add("Awesome", new FuzzyRightShoulder(75, 85, 100)); | |
module.Add(shortName.Very(), awesomeAwesomeness.Very()); | |
module.Add(shortName.Fairly() && mediumName.Fairly(), highAwesomeness.Very()); | |
module.Add(mediumName, normalAwesomeness.Very() && highAwesomeness.Fairly() && belowNormalAwesomeness.Fairly()); | |
module.Add(mediumName || longName.Fairly(), belowNormalAwesomeness && lowAwesomeness.Fairly()); | |
module.Add(longName, lowAwesomeness.Fairly()); | |
module.Add(longName.Very(), lowAwesomeness.Very()); | |
// ========================= | |
Test(module, 1); | |
Test(module, 2); | |
Test(module, 3); | |
Test(module, 4); | |
Test(module, 5); | |
Test(module, 7); | |
Test(module, 10); | |
Test(module, 15); | |
Test(module, 20); | |
Test(module, 25); | |
Test(module, 40); | |
// ========================= | |
Console.ReadLine(); | |
} | |
private static void Test(FuzzyModule module, int length) | |
{ | |
module["NameLength"].Fuzzify(length); | |
var crisp = module.Defuzzify("Awesomeness"); | |
Console.WriteLine("With a name length of {0} you are {1}% awesome!", length, crisp); | |
} | |
} | |
public class FuzzyModule | |
{ | |
private readonly List<FuzzyRule> _rules = new List<FuzzyRule>(); | |
private readonly Dictionary<string, FuzzyVariable> _variables = new Dictionary<string, FuzzyVariable>(); | |
public FuzzyVariable this[string name] | |
{ | |
get { return _variables[name]; } | |
} | |
public FuzzyVariable CreateFLV(string name) | |
{ | |
if (name == null) throw new ArgumentNullException("name"); | |
if (_variables.ContainsKey(name)) throw new Exception(); | |
return _variables[name] = new FuzzyVariable(); | |
} | |
public void Add(FuzzyRule rule) | |
{ | |
_rules.Add(rule); | |
} | |
public void Add(FuzzyTerm antecent, FuzzyTerm consequence) | |
{ | |
_rules.Add(new FuzzyRule(antecent, consequence)); | |
} | |
public float Defuzzify(string name) | |
{ | |
foreach (var rule in _rules) | |
rule.SetConfidenceOfConsequentToZero(); | |
foreach (var rule in _rules) | |
rule.Calculate(); | |
return this[name].Defuzzify(); | |
} | |
} | |
public class FuzzyVariable | |
{ | |
private readonly Dictionary<string, FuzzySet> _sets = new Dictionary<string, FuzzySet>(); | |
private float _maxRange; | |
private float _minRange; | |
public FuzzySet this[string name] | |
{ | |
get { return _sets[name]; } | |
} | |
public FuzzySet Add(string name, FuzzySet set) | |
{ | |
_sets[name] = set; | |
AdjustRangeToFit(set.Min, set.Max); | |
return set; | |
} | |
public void Fuzzify(float value) | |
{ | |
if (value < _minRange || value > _maxRange) | |
throw new ArgumentOutOfRangeException("value", value, "out of range."); | |
foreach (var set in _sets) | |
{ | |
set.Value.SetDOM(set.Value.CalculateDOM(value)); | |
} | |
} | |
public float Defuzzify() | |
{ | |
//MaxAv | |
float bottom = 0f; | |
float top = 0f; | |
foreach (var set in _sets) | |
{ | |
bottom += set.Value.DOM; | |
top += set.Value.RepresentativeValue * set.Value.DOM; | |
} | |
if (bottom == 0) return 0; | |
return top / bottom; | |
} | |
private void AdjustRangeToFit(float min, float max) | |
{ | |
_minRange = Math.Min(_minRange, min); | |
_maxRange = Math.Max(_maxRange, max); | |
} | |
} | |
public class FuzzyRule | |
{ | |
private readonly FuzzyTerm _antecent; | |
private readonly FuzzyTerm _consequence; | |
public FuzzyRule(FuzzyTerm antecent, FuzzyTerm consequence) | |
{ | |
if (antecent == null) throw new ArgumentNullException("antecent"); | |
if (consequence == null) throw new ArgumentNullException("consequence"); | |
_antecent = antecent; | |
_consequence = consequence; | |
} | |
public void SetConfidenceOfConsequentToZero() | |
{ | |
_consequence.ClearDOM(); | |
} | |
public void Calculate() | |
{ | |
_consequence.OrWithDOM(_antecent); | |
} | |
} | |
public abstract class FuzzySet : FuzzyTerm | |
{ | |
private float _dom; | |
protected FuzzySet(float representativeValue) | |
{ | |
RepresentativeValue = representativeValue; | |
} | |
public float RepresentativeValue { get; private set; } | |
public abstract float Min { get; } | |
public abstract float Max { get; } | |
public abstract float CalculateDOM(float value); | |
#region Overrides of FuzzyTerm | |
public override float DOM | |
{ | |
get { return _dom; } | |
} | |
public void SetDOM(float value) | |
{ | |
if (value < 0 || value > 1) | |
throw new ArgumentOutOfRangeException("value", value, "Must be within range 0.0 - 1.0"); | |
_dom = value; | |
} | |
public override void ClearDOM() | |
{ | |
SetDOM(0); | |
} | |
public override void OrWithDOM(float value) | |
{ | |
if (value > _dom) SetDOM(value); | |
} | |
#endregion | |
} | |
public class FuzzyTriangle : FuzzySet | |
{ | |
private readonly float _left; | |
private readonly float _peak; | |
private readonly float _right; | |
public FuzzyTriangle(float left, float middle, float right) | |
: base(middle) | |
{ | |
_peak = middle; | |
_left = left; | |
_right = right; | |
} | |
#region Overrides of FuzzySet | |
public override float Min | |
{ | |
get { return _left; } | |
} | |
public override float Max | |
{ | |
get { return _right; } | |
} | |
public override float CalculateDOM(float value) | |
{ | |
if (value < _left || value > _right) return 0; | |
if (value < _peak) | |
return _peak == _left ? 1 : (value - _left) / (_peak - _left); | |
if (value > _peak) | |
return _peak == _right ? 1 : (_right - value) / (_right - _peak); | |
return 1; | |
} | |
#endregion | |
} | |
public class FuzzyTrapezium : FuzzySet | |
{ | |
private readonly float _left; | |
private readonly float _middleleft; | |
private readonly float _middleright; | |
private readonly float _right; | |
public FuzzyTrapezium(float left, float middleleft, float middleright, float right) | |
: base((middleright - middleleft) / 2 + middleleft) | |
{ | |
_left = left; | |
_middleleft = middleleft; | |
_middleright = middleright; | |
_right = right; | |
} | |
#region Overrides of FuzzySet | |
public override float Min | |
{ | |
get { return _left; } | |
} | |
public override float Max | |
{ | |
get { return _right; } | |
} | |
public override float CalculateDOM(float value) | |
{ | |
if (value < _left || value > _right) return 0; | |
if (value < _middleleft) | |
return _middleleft == _left ? 1 : (value - _left) / (_middleleft - _left); | |
if (value > _middleright) | |
return _middleright == _right ? 1 : (_right - value) / (_right - _middleright); | |
return 1; | |
} | |
#endregion | |
} | |
public class FuzzyLeftShoulder : FuzzySet | |
{ | |
private readonly float _left; | |
private readonly float _peak; | |
private readonly float _right; | |
public FuzzyLeftShoulder(float left, float middle, float right) | |
: base((middle - left) / 2 + left) | |
{ | |
_peak = middle; | |
_left = left; | |
_right = right; | |
} | |
#region Overrides of FuzzySet | |
public override float Min | |
{ | |
get { return _left; } | |
} | |
public override float Max | |
{ | |
get { return _right; } | |
} | |
public override float CalculateDOM(float value) | |
{ | |
if (value < _left || value > _right) return 0; | |
if (value > _peak) | |
return _peak == _right ? 1 : (_right - value) / (_right - _peak); | |
return 1; | |
} | |
#endregion | |
} | |
public class FuzzyRightShoulder : FuzzySet | |
{ | |
private readonly float _left; | |
private readonly float _peak; | |
private readonly float _right; | |
public FuzzyRightShoulder(float left, float middle, float right) | |
: base((right - middle) / 2 + middle) | |
{ | |
_peak = middle; | |
_left = left; | |
_right = right; | |
} | |
#region Overrides of FuzzySet | |
public override float Min | |
{ | |
get { return _left; } | |
} | |
public override float Max | |
{ | |
get { return _right; } | |
} | |
public override float CalculateDOM(float value) | |
{ | |
if (value < _left || value > _right) return 0; | |
if (value < _peak) | |
return _peak == _left ? 1 : (value - _left) / (_peak - _left); | |
return 1; | |
} | |
#endregion | |
} | |
public abstract class FuzzyTerm | |
{ | |
public abstract float DOM { get; } | |
public abstract void ClearDOM(); | |
public abstract void OrWithDOM(float value); | |
public static implicit operator float(FuzzyTerm term) | |
{ | |
if (term == null) throw new ArgumentNullException("term"); | |
return term.DOM; | |
} | |
public static FuzzyTerm operator &(FuzzyTerm left, FuzzyTerm right) | |
{ | |
return new FuzzyAnd(left, right); | |
} | |
public static FuzzyTerm operator |(FuzzyTerm left, FuzzyTerm right) | |
{ | |
return new FuzzyOr(left, right); | |
} | |
public FuzzyTerm Very() | |
{ | |
return new FuzzyVery(this); | |
} | |
public FuzzyTerm Fairly() | |
{ | |
return new FuzzyFairly(this); | |
} | |
public static bool operator true(FuzzyTerm term) | |
{ | |
return term != null; | |
} | |
public static bool operator false(FuzzyTerm term) | |
{ | |
return term == null; | |
} | |
} | |
public class FuzzyAnd : FuzzyTerm | |
{ | |
private readonly FuzzyTerm _left; | |
private readonly FuzzyTerm _right; | |
public FuzzyAnd(FuzzyTerm left, FuzzyTerm right) | |
{ | |
if (left == null) throw new ArgumentNullException("left"); | |
if (right == null) throw new ArgumentNullException("right"); | |
_left = left; | |
_right = right; | |
} | |
#region Implementation of FuzzyTerm | |
public override float DOM | |
{ | |
get { return Math.Min(_left, _right); } | |
} | |
public override void ClearDOM() | |
{ | |
_left.ClearDOM(); | |
_right.ClearDOM(); | |
} | |
public override void OrWithDOM(float value) | |
{ | |
_left.OrWithDOM(value); | |
_right.OrWithDOM(value); | |
} | |
#endregion | |
} | |
public class FuzzyVery : FuzzyTerm | |
{ | |
private readonly FuzzyTerm _set; | |
public FuzzyVery(FuzzyTerm set) | |
{ | |
if (set == null) throw new ArgumentNullException("set"); | |
_set = set; | |
} | |
#region Implementation of FuzzyTerm | |
public override float DOM | |
{ | |
get { return _set * _set; } | |
} | |
public override void ClearDOM() | |
{ | |
_set.ClearDOM(); | |
} | |
public override void OrWithDOM(float value) | |
{ | |
_set.OrWithDOM(value * value); | |
} | |
#endregion | |
} | |
public class FuzzyFairly : FuzzyTerm | |
{ | |
private readonly FuzzyTerm _set; | |
public FuzzyFairly(FuzzyTerm set) | |
{ | |
if (set == null) throw new ArgumentNullException("set"); | |
_set = set; | |
} | |
#region Implementation of FuzzyTerm | |
public override float DOM | |
{ | |
get { return (float)Math.Sqrt(_set); } | |
} | |
public override void ClearDOM() | |
{ | |
_set.ClearDOM(); | |
} | |
public override void OrWithDOM(float value) | |
{ | |
_set.OrWithDOM((float)Math.Sqrt(value)); | |
} | |
#endregion | |
} | |
public class FuzzyOr : FuzzyTerm | |
{ | |
private readonly FuzzyTerm _left; | |
private readonly FuzzyTerm _right; | |
public FuzzyOr(FuzzyTerm left, FuzzyTerm right) | |
{ | |
if (left == null) throw new ArgumentNullException("left"); | |
if (right == null) throw new ArgumentNullException("right"); | |
_left = left; | |
_right = right; | |
} | |
#region Implementation of FuzzyTerm | |
public override float DOM | |
{ | |
get { return Math.Max(_left, _right); } | |
} | |
public override void ClearDOM() | |
{ | |
throw new NotImplementedException("Invalid context"); | |
} | |
public override void OrWithDOM(float value) | |
{ | |
throw new NotImplementedException("Invalid context"); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment