Skip to content

Instantly share code, notes, and snippets.

@ikkentim
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ikkentim/98828aa4e17a01522bce to your computer and use it in GitHub Desktop.
Save ikkentim/98828aa4e17a01522bce to your computer and use it in GitHub Desktop.
Fuzzy Logic
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