Skip to content

imagibee/Fuzzy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fuzzy

A lightweight fuzzy logic library inspired by Mamdani

The primary goal of this project is to to provide a tested library of C# classes and functions that support the development of fuzzy logic controllers within C# projects.

I have intentionally avoided using interpreted strings to name fuzzy variables, define fuzzy rules, etc. Instead I have chosen to embrace C#'s built-in features for symbol naming and anonymous functions. This approach reduces complexity and improves performance at the cost of being less purist which I feel is a good tradeoff for a lightweight library.

API

Here is an overview of the API. Refer to the source code for details. There is also an example in the next section, or feel free to look at the unit tests if that is helpful.

  • static class Imagibee.Fuzzy - contains the whole library
  • class Imagibee.Fuzzy.Input - for defining trapezoidal, triangular, or box membership functions and fuzzifying physical values
  • class Imagibee.Fuzzy.InputGroup - a convenient way to fuzzify a group of inputs that derive their fuzzy values from the same physical value
  • class Imagibee.Fuzzy.Rule - for combining fuzzy inputs into IF/THEN rules
  • function Imagibee.Fuzzy.DefuzzifyByCentroid - defuzzify rules to a physical value
  • function Imagibee.Fuzzy.DefineInputsByPeaks - An even more convenient way to define inputs that should work for most cases
  • class Imagibee.Fuzzy.PeakDefinition - used by DefineInputsByPeaks

Example - fuzzy tip calculator

Here is an example that demonstrates how you might want to use this library. It shows how you could implement a fuzzy-logic tip calculator. The kind of thing you would use to calculate a tip when you eat at a restaraunt. For the sake of this exampe, the physical value of the tip ranges between 7.5% to 25%. The tip is the ultimate value we want to get from our calculator so we can pay our waiter fairly. The service rating ranges from 1-5 stars, and it is a physical input value based on how good you thought the service was. The food rating also ranges from 1-5 stars, and it is another physical input value based on how good you thought the food was.

Here are the rules ...

  • IF the service was excellent THEN the tip should be generous
  • IF the service was ok THEN the tip should be average
  • IF the service was poor OR the food was terrible THEN the tip should be low

And here is how I would code the rules ...

using Imagibee;

public class MyTipCalculator
{
    // Construct a MyTipCalculator
    public MyTipCalculator(
        double lowTip,
        double averageTip,
        double generousTip)
    {
        // Define membership function trapezoids based on physical star values
        serviceWasExcellent = new(3, 5, 5, double.MaxValue);
        serviceWasOk = new(1, 3, 3, 5);
        serviceWasPoor = new(double.MinValue, 1, 1, 3);
        foodWasTerrible = new(double.MinValue, 1, 1, 3);
#if NET8_0_OR_GREATER
        // If you are using .net8 or later you can use params instead of explicit arrays
        service = new Fuzzy.InputGroup(
            serviceWasPoor, serviceWasOk, serviceWasExcellent);
#else
        service = new Fuzzy.InputGroup(
            new Fuzzy.Input[]
            {
                serviceWasPoor,
                serviceWasOk,
                serviceWasExcellent
            });
#endif
        // Define the fuzzy IF/THEN rules
        rules = new Fuzzy.Rule[]
        {
            new(generousTip, () => serviceWasExcellent.FX),
            new(averageTip, () => serviceWasOk.FX),
            new(lowTip, () => Fuzzy.OR(serviceWasPoor.FX, foodWasTerrible.FX)),
        };
    }

    public double Calculate(double serviceStars, double foodStars)
    {
        // Convert physical star values to fuzzy values
        service.Fuzzify(serviceStars);
        foodWasTerrible.Fuzzify(foodStars);
        // Apply rules to convert fuzzy inputs to a physical tip value
        return Fuzzy.DefuzzifyByCentroid(rules);
    }

    // private data
    readonly Fuzzy.Input serviceWasExcellent;
    readonly Fuzzy.Input serviceWasOk;
    readonly Fuzzy.Input serviceWasPoor;
    readonly Fuzzy.Input foodWasTerrible;
    readonly Fuzzy.InputGroup service;
    readonly Fuzzy.Rule[] rules;
}

And here are the tests that were used to validate this example ...

MyTipCalculator tip = new(7.5, 15, 25);
Assert.AreEqual(25, tip.Calculate(5, 3), ALLOWEDERROR);
Assert.AreEqual(20, tip.Calculate(4, 3), ALLOWEDERROR);
Assert.AreEqual(17.5, tip.Calculate(3.5, 3), ALLOWEDERROR);
Assert.AreEqual(15, tip.Calculate(3, 3), ALLOWEDERROR);
Assert.AreEqual(14.1666666, tip.Calculate(3.5, 2), ALLOWEDERROR);
Assert.AreEqual(12.5, tip.Calculate(3, 2), ALLOWEDERROR);
Assert.AreEqual(11.25, tip.Calculate(3, 1), ALLOWEDERROR);
Assert.AreEqual(10, tip.Calculate(2, 1), ALLOWEDERROR);
Assert.AreEqual(7.5, tip.Calculate(1, 1), ALLOWEDERROR);

Testing

Run Scripts/test.

License

MIT

Issues

Report and track issues here.

Contributing

To make minor changes (such as bug fixes) simply make a pull request. Please open an issue to discuss other changes.

About

A lightweight fuzzy logic c# library inspired by Mamdani

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages