Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@

## 1.0.2 -- 2025-03-28
* Added deffuzzification function to the library, allowing users to convert fuzzy sets (Lset Double l) back into numbers.
* This update is prepared for the next major release, which will include fuzzy control systems, if than rules and more.
* This update is prepared for the next major release, which will include fuzzy control systems, if than rules and more.

## 1.1.0 -- 2025-04-11
* Added new functions for computing cardinality of fuzzy sets.
* Fixed some minor bugs in the implementation of defuzzification functions.
* Updated documentation.
* Added tutorial modules to get started with the library.
* Added toMatrix and fromMatrix functions to convert between fuzzy sets and matrices.
* Added fuzzification functions to convert crisp values into fuzzy sets.
* Added support for linguistic variables and terms, allowing users to define fuzzy sets based on natural language descriptions.
* Added inference engine for fuzzy control systems, enabling users to create fuzzy rules and evaluate them based on input data.
23 changes: 17 additions & 6 deletions fuzzySets.cabal
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
cabal-version: 2.4
name: fuzzySets
version: 1.0.2
version: 1.1.0
category: Math
license: BSD-3-Clause
author: Lukas Balog <lukasbalog66@gmail.com>
maintainer: Lukas Balog <lukasbalog66@gmail.com>
homepage: https://github.com/luckyluke66/L-Sets

synopsis:
Library for constructing and manipulating fuzzy sets and fuzzy relations.
A Haskell library for working with fuzzy sets and fuzzy relations.

description:
In mathematics Fuzzy Set is a generalization of standart set
In set elements can either belong to that set or not.
When it comes to fuzzy sets, elements belong to a fuzzy set in a degree.
In classical set theory, an element either belongs to a set or it does not.
Fuzzy sets generalize this idea by allowing graded membership.

This package provide functions for working with fuzzy sets and fuzzy relations.
This package provides tools for constructing and manipulating fuzzy sets
and fuzzy relations.
If you are new to the library, the "Fuzzy.Tutorial" module is the best
place to start before diving into the reference documentation.

extra-doc-files: CHANGELOG.md

Expand All @@ -28,19 +30,28 @@ library
hs-source-dirs: src

exposed-modules:
Fuzzy
Fuzzy.Control
Fuzzy.Relations
Fuzzy.Sets
Fuzzy.Tutorial
FuzzySet
Fuzzy.Sets.LSet
Fuzzy.Sets.MembershipFunctions
Fuzzy.Sets.Cardinality
Fuzzy.Sets.FuzzyCardinality
Fuzzy.Sets.Properties
Fuzzy.Control.Defuzzification
Fuzzy.Control.Fuzzification
Fuzzy.Control.InferenceRules
Fuzzy.Relations.LRelation
Fuzzy.Relations.MembershipFunctions
Fuzzy.Relations.RelationComposition
Fuzzy.Relations.Properties
Lattices.ResiduatedLattice
Lattices
Lattices.UnitInterval
Lattices.UnitIntervalStructures
Lattices.UnitIntervalStructures.Godel
Lattices.UnitIntervalStructures.Lukasiewicz
Lattices.UnitIntervalStructures.Product
Expand Down
9 changes: 9 additions & 0 deletions src/Fuzzy.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- | Top-level overview of the fuzzy-set library.
--
-- This module exists primarily to provide package-level Haddock
-- documentation for the major namespaces:
--
-- * 'Fuzzy.Sets' for fuzzy sets and their cardinalities, also provides membership functions and basic operations on fuzzy sets
-- * 'Fuzzy.Relations' for fuzzy relations and their properties
-- * 'Fuzzy.Control' for defuzzification utilities
module Fuzzy () where
9 changes: 9 additions & 0 deletions src/Fuzzy/Control.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- | High-level fuzzy-control utilities.
--
-- At the moment this namespace focuses on defuzzification operators that turn
-- fuzzy sets over numeric universes back into crisp values.
module Fuzzy.Control (
module Fuzzy.Control.Defuzzification
) where

import Fuzzy.Control.Defuzzification
22 changes: 13 additions & 9 deletions src/Fuzzy/Control/Defuzzification.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
-- | Defuzzification operators for fuzzy sets over numeric universes.
module Fuzzy.Control.Defuzzification where

import Fuzzy.Sets.LSet (LSet, toList)
Expand All @@ -8,41 +9,44 @@ import Data.List (maximumBy)
import Data.Ord (comparing)

-- | Defuzzify a fuzzy set using the center of gravity (centroid) method.
--
--
-- Returns 0 for an empty universe.
centerOfGravity :: ResiduatedLattice l => LSet Double l -> Double
centerOfGravity = centerOfGravityMod id


-- | Defuzzify with a membership modifier function 'c'.
--
-- The modifier is applied to each membership value before computing the weighted mean.
-- The modifier is applied to each membership value before computing COG.
-- Returns 0 when the modified sigma count is 0.
centerOfGravityMod :: ResiduatedLattice l => (l -> l) -> LSet Double l -> Double
centerOfGravityMod c set = if card == 0 then 0 else numer / card
where
where
pairs = toList set
card = sigmaCountMod c set
numer = sum [x * realToFrac mem | (x, mem) <- pairs]
numer = sum [u * realToFrac (c mem) | (u, mem) <- pairs]

-- | Defuzzify by taking the midpoint of the maximum interval.
--
-- For a non-empty set returns (max + min) / 2 based on the universe support.
-- For a non-empty set returns @(max + min) / 2@ based on the universe support.
centerOfMaxima :: ResiduatedLattice l => LSet Double l -> Double
centerOfMaxima set = (sup + inf) / 2
where
centerOfMaxima set = (sup + inf) / 2
where
sup = maximum $ universe set
inf = minimum $ universe set


-- | Mean of maxima with a modifier function.
--
-- Uses 'sigmaCountMod' for total modified membership and normalizes by universe size.
-- Returns 0 for empty universes.
meanOfMaximaMod :: ResiduatedLattice l => (l -> l) -> LSet Double l -> Double
meanOfMaximaMod c set = if sizeUniverse == 0 then 0 else card / sizeUniverse
where
where
card = sigmaCountMod c set
sizeUniverse = fromIntegral $ universeCardinality set


-- | Return the universe element with maximal membership degree.
--
-- If multiple elements have the same maximal degree, the first encountered is returned.
Expand All @@ -52,4 +56,4 @@ maxMembership set
| null pairs = 0
| otherwise = fst $ maximumBy (comparing snd) pairs
where
pairs = toList set
pairs = toList set
125 changes: 125 additions & 0 deletions src/Fuzzy/Control/Fuzzification.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
module Fuzzy.Control.Fuzzification where

import Lattices.ResiduatedLattice
import Fuzzy.Sets.LSet

-- | A linguistic variable in fuzzy logic.
--
-- A linguistic variable represents a concept (e.g. "temperature")
-- described by a set of fuzzy linguistic terms (e.g. "cold", "warm", "hot").
--
-- Each term is a fuzzy set over the same universe of discourse.
--
-- @a@ is the type of the universe (e.g. Double, Int, etc.)
-- @l@ is the underlying residuated lattice used for membership values.
data (ResiduatedLattice l, Eq a) => LinguisticVariable a l = LV
{ name :: String
-- ^ Name of the linguistic variable (e.g. "temperature")

, terms :: [(String, LSet a l)]
-- ^ Named fuzzy sets representing linguistic terms
-- (e.g. ("cold", coldSet), ("warm", warmSet))
}


-- | Safely construct a linguistic variable.
--
-- Ensures all fuzzy sets share the same universe of discourse.
--
-- === Example
--
-- @
-- mkSafeLinguisticVariable "temperature"
-- [ ("cold", coldSet)
-- , ("warm", warmSet)
-- , ("hot", hotSet)
-- ]
-- @
--
-- === Safety
--
-- Returns a Left error if:
-- * The list of terms is empty
-- * Not all fuzzy sets share the same universe
--
-- === Returns
--
-- * @Right LinguisticVariable@ if valid
-- * @Left String@ describing the inconsistency otherwise
mkSafeLinguisticVariable
:: (ResiduatedLattice l, Eq a)
=> String
-> [(String, LSet a l)]
-> Either String (LinguisticVariable a l)
mkSafeLinguisticVariable name terms =
case terms of
[] -> Left "mkSafeLinguisticVariable: empty term list"
((_, firstSet):rest) ->
let baseUniverse = universe firstSet
mismatches =
[ termName
| (termName, set) <- rest
, universe set /= baseUniverse
]
in if null mismatches
then Right (LV name terms)
else Left $
"mkSafeLinguisticVariable: inconsistent universes in terms: "
++ show mismatches


-- | Unsafe constructor for a linguistic variable.
--
-- This function will crash at runtime if the input is invalid.
-- It is intended only for quick prototyping or trusted input.
--
-- Prefer 'mkSafeLinguisticVariable'.
mkUnsafeLinguisticVariable
:: (ResiduatedLattice l, Eq a)
=> String
-> [(String, LSet a l)]
-> LinguisticVariable a l
mkUnsafeLinguisticVariable name terms =
case mkSafeLinguisticVariable name terms of
Left err -> error err
Right lv -> lv


-- | Extracts the universe of discourse from a linguistic variable.
--
-- Assumes all terms share the same universe (guaranteed by safe constructor).
universeLv :: (ResiduatedLattice l, Eq a) => LinguisticVariable a l -> [a]
universeLv (LV _ ((_, firstSet):_)) = universe firstSet


-- | Extracts the names of all linguistic terms.
--
-- === Example
--
-- @
-- termNames temperature
-- -- ["cold","warm","hot"]
-- @
termNames :: (ResiduatedLattice l, Eq a) => LinguisticVariable a l -> [String]
termNames (LV _ terms) = map fst terms


-- | Fuzzifies a crisp input value into a fuzzy distribution over terms.
--
-- For each linguistic term, computes the membership degree of @x@
-- in the corresponding fuzzy set.
--
-- === Example
--
-- @
-- fuzzify 22 temperature
-- -- fromList [("cold",0.1),("warm",0.8),("hot",0.0)]
-- @
--
-- === Output
--
-- A fuzzy set over term names, where each membership value represents
-- how strongly the input belongs to that linguistic term.
fuzzify :: (ResiduatedLattice l, Eq a) => a-> LinguisticVariable a l -> LSet String l
fuzzify x (LV _ terms) =
fromList [(name, member set x) | (name, set) <- terms]
100 changes: 100 additions & 0 deletions src/Fuzzy/Control/InferenceRules.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
module Fuzzy.Control.InferenceRules where

import Lattices.ResiduatedLattice
import Fuzzy.Sets.LSet
import FuzzySet
import Fuzzy.Sets.MembershipFunctions(constant)

-- | A fuzzy inference rule of the form:
--
-- > IF antecedent1 AND antecendent2 AND ... AND antecendentn THEN consequent
--
-- The antecedent and consequent are fuzzy sets over different universes.
--
-- @a@ is the input (crisp) universe
-- @b@ is the output universe
-- @l@ is the residuated lattice used for membership values
data (ResiduatedLattice l, Eq a, Eq b) => Rule a b l = Rule
{ antecedents :: [LSet a l]
-- ^ Fuzzy set describing the condition (IF-part)

, consequent :: LSet b l
-- ^ Fuzzy set describing the result (THEN-part)
}

-- | A rule base is a collection of fuzzy inference rules.
type RuleBase a b l = [Rule a b l]


-- | Scales a fuzzy set by an activation degree using a t-norm.
--
-- This corresponds to rule firing strength modulation:
--
-- > A(x)' = α ⊗ A(x)
--
-- where @α@ is the rule activation degree, ⊗ is the t-norm a A is the fuzzy set.
--
-- === Parameters
--
-- * @alpha@ - firing strength of the rule
-- * @set@ - consequent fuzzy set
--
-- === Returns
--
-- A new fuzzy set with reduced or unchanged membership values.
scale :: (ResiduatedLattice l, Eq a) => l -> LSet a l -> LSet a l
scale alpha set = setTnorm set (fromFunction (constant alpha) (universe set))


-- | Evaluates a single fuzzy rule for a given input.
--
-- Steps:
--
-- 1. Compute rule activation degree:
-- @alpha = member antecedent x@
--
-- 2. Scale the consequent by this degree
--
-- === Example
--
-- @
-- evalRule 25 rule
-- -- returns a partially activated fuzzy set
-- @
evalRule:: (ResiduatedLattice l, Eq a, Eq b) => a -> Rule a b l -> LSet b l
evalRule x (Rule ant cons) =
let alpha = member (aggregate ant) x
in scale alpha cons


-- | Aggregates multiple fuzzy sets into one combined fuzzy set.
--
-- Uses fuzzy union.
--
-- === Behavior
--
-- This corresponds to combining contributions from multiple rules.
aggregate :: (ResiduatedLattice l, Eq a) => [LSet a l]-> LSet a l
aggregate = foldr union mkEmptySet


-- | Performs fuzzy inference over a rule base for a given input.
--
-- === Pipeline
--
-- 1. Evaluate each rule with 'evalRule'
-- 2. Aggregate all resulting fuzzy sets
--
-- === Result
--
-- A single fuzzy set over the output universe representing
-- the inferred fuzzy conclusion.
--
-- === Example
--
-- @
-- infer rules 25
-- -- fuzzy output set over b
-- @
infer :: (ResiduatedLattice l, Eq a, Eq b) => RuleBase a b l-> a -> LSet b l
infer rules x = aggregate (map (evalRule x) rules)
Loading
Loading