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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@

## 1.0.1 -- 2025-05-01
* Changed constrains for dependencies in .cabal file to allow for more flexibility in package versions.

## 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.
6 changes: 4 additions & 2 deletions fuzzySets.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 2.4
name: fuzzySets
version: 1.0.1
version: 1.0.2
category: Math
license: BSD-3-Clause
author: Lukas Balog <lukasbalog66@gmail.com>
Expand All @@ -17,7 +17,7 @@ description:

This package provide functions for working with fuzzy sets and fuzzy relations.

extra-source-files: CHANGELOG.md
extra-doc-files: CHANGELOG.md

source-repository head
type: git
Expand All @@ -33,6 +33,7 @@ library
Fuzzy.Sets.MembershipFunctions
Fuzzy.Sets.Cardinality
Fuzzy.Sets.Properties
Fuzzy.Control.Defuzzification
Fuzzy.Relations.LRelation
Fuzzy.Relations.MembershipFunctions
Fuzzy.Relations.RelationComposition
Expand Down Expand Up @@ -77,3 +78,4 @@ test-suite test
Fuzzy.Sets.PropertiesTest
Fuzzy.Relations.LRelationTest
Fuzzy.Relations.PropertiesTest
Fuzzy.Control.DefuzzificationTest
55 changes: 55 additions & 0 deletions src/Fuzzy/Control/Defuzzification.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module Fuzzy.Control.Defuzzification where

import Fuzzy.Sets.LSet (LSet, toList)
import Fuzzy.Sets.Cardinality
import Lattices.ResiduatedLattice (ResiduatedLattice)
import FuzzySet
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.
-- 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
pairs = toList set
card = sigmaCountMod c set
numer = sum [x * realToFrac mem | (x, 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.
centerOfMaxima :: ResiduatedLattice l => LSet Double l -> Double
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
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.
-- For an empty set, returns 0.
maxMembership :: ResiduatedLattice l => LSet Double l -> Double
maxMembership set
| null pairs = 0
| otherwise = fst $ maximumBy (comparing snd) pairs
where
pairs = toList set
8 changes: 4 additions & 4 deletions src/Fuzzy/Sets/Cardinality.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Fuzzy.Sets.Cardinality(
sigmaCount,
thresholdSigmaCount,
normalizedSigmaCount,
sigmaCountWithModifier,
sigmaCountMod,
-- * Modifier functions
modifierFunction,
sigmoidModifier,
Expand Down Expand Up @@ -41,11 +41,11 @@ For a fuzzy set A, |A| = Σ c(A(u)) for all u ∈ U

>>> let set = fromPairs [(1, 0.2), (2, 0.7), (3, 0.5)] :: LSet Int UILukasiewicz
>>> let modifier = sigmoidModifier 2.0 0.5
>>> sigmaCountWithModifier modifier set
>>> sigmaCountMod modifier set
1.4
-}
sigmaCountWithModifier :: (FuzzySet set a l) => (l -> l) -> set -> Double
sigmaCountWithModifier c set = sum [realToFrac $ (c . f) x | x <- universe set]
sigmaCountMod :: (FuzzySet set a l) => (l -> l) -> set -> Double
sigmaCountMod c set = sum [realToFrac $ (c . f) x | x <- universe set]
where f = member set


Expand Down
38 changes: 38 additions & 0 deletions test/Fuzzy/Control/DefuzzificationTest.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module Fuzzy.Control.DefuzzificationTest (
defuzzificationTests
) where

import Test.Tasty
import Test.Tasty.HUnit
import Fuzzy.Control.Defuzzification
import Fuzzy.Sets.LSet
import Fuzzy.Sets.Cardinality
import Lattices.UnitIntervalStructures.Godel

-- Test data
sampleSet :: LSet Double UIGodel
sampleSet = fromList [(1, 0.2), (2, 0.8)]

emptySet :: LSet Double UIGodel
emptySet = mkEmptySet

-- Test group
defuzzificationTests :: TestTree
defuzzificationTests = testGroup "Defuzzification Tests" [
testCase "centerOfGravity on non-empty set" $
assertEqual "centroid correct" 1.8 (centerOfGravity sampleSet),
testCase "centerOfGravity on empty set" $
assertEqual "empty set yields 0" 0.0 (centerOfGravity emptySet),
testCase "centerOfGravityMod with alpha-cut" $
assertEqual "modifier influences denominator only" 1.8 (centerOfGravityMod (alphaCutModifier 0.5) sampleSet),
testCase "centerOfMaxima uses universe bounds" $
assertEqual "midpoint between min and max" 1.5 (centerOfMaxima sampleSet),
testCase "meanOfMaximaMod with identity modifier" $
assertEqual "normalized sigma count" 0.5 (meanOfMaximaMod (\x -> x) sampleSet),
testCase "meanOfMaximaMod with alpha-cut modifier" $
assertEqual "alpha-cut count over universe size" 0.5 (meanOfMaximaMod (alphaCutModifier 0.5) sampleSet),
testCase "maxMembership on non-empty set" $
assertEqual "highest membership element" 2.0 (maxMembership sampleSet),
testCase "maxMembership on empty set" $
assertEqual "empty set returns 0" 0.0 (maxMembership emptySet)
]
4 changes: 3 additions & 1 deletion test/Tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Fuzzy.Sets.PropertiesTest
import Fuzzy.Sets.MembershipFunctionsTest
import Fuzzy.Relations.LRelationTest
import Fuzzy.Relations.PropertiesTest
import Fuzzy.Control.DefuzzificationTest

-- Run all tests
main :: IO ()
Expand All @@ -21,5 +22,6 @@ main = defaultMain $ testGroup "All Tests" [
membershipFunctionsTests,
propertiesTests,
lrelationTests,
relPropertiesTests
relPropertiesTests,
defuzzificationTests
]
Loading