Add consequence method returning a fuzzy set

This commit is contained in:
Alexander Ignatov 2022-01-04 00:23:25 +02:00
parent 968e1db94f
commit dd19a7eeac
5 changed files with 78 additions and 37 deletions

View File

@ -44,9 +44,9 @@ let ruleBase = FuzzyRuleBase {
funding.is(.inadequate) || Ø --> risk.is(.high)
}
let flc = FuzzyLinguisticController(rules: ruleBase, settings: .init(implication: .mamdani))
let flc = FuzzyLogicController(rules: ruleBase, settings: .init(implication: .mamdani))
flc.consequenceGrade(for: 50, usingFact: .singleton((8.8, 42))) // result is 0.675
flc.consequenceGrade(for: 50, usingSingletonFact: (8.8, 42)) // result is 0.675
```
## Modules

View File

@ -1,32 +0,0 @@
import FuzzySets
import FuzzyRelations
open class FuzzyLinguisticController<AntecedentUniverse, ConsequentUniverse> {
public var settings: OperationSettings
public var ruleBase: FuzzyRuleBase<(AntecedentUniverse, ConsequentUniverse)>
public var aggregatorFuction: (Grade, Grade) -> Grade
public init(
rules: FuzzyRuleBase<(AntecedentUniverse, ConsequentUniverse)> = .init(),
settings: OperationSettings = .init()
) {
self.ruleBase = rules
self.settings = settings
self.aggregatorFuction = settings.disjunction.function
}
open func consequenceGrade<S: Sequence>(
for value: ConsequentUniverse,
usingFact fact: IterableFuzzySet<AntecedentUniverse, S>
) -> Grade {
ruleBase
.map {
FuzzySetComposition(set: fact, relation: $0.asRelation())
}
.map {
$0.grade(forElement: value)
}
.reduce(0, aggregatorFuction)
}
}

View File

@ -0,0 +1,58 @@
import FuzzySets
import FuzzyRelations
open class FuzzyLogicController<AntecedentUniverse, ConsequentUniverse> {
public var settings: OperationSettings
public var ruleBase: FuzzyRuleBase<(AntecedentUniverse, ConsequentUniverse)>
public var aggregatorFunction: (Grade, Grade) -> Grade
public init(
rules: FuzzyRuleBase<(AntecedentUniverse, ConsequentUniverse)> = .init(),
settings: OperationSettings = .init(),
aggregatorFunction: ((Grade, Grade) -> Grade)? = nil
) {
self.ruleBase = rules
self.settings = settings
self.aggregatorFunction = aggregatorFunction ?? settings.disjunction.function
}
open func consequence<S: Sequence>(
usingFact fact: IterableFuzzySet<AntecedentUniverse, S>
) -> AnyFuzzySet<ConsequentUniverse> {
ruleBase
.map {
FuzzySetComposition(set: fact, relation: $0.asRelation())
.eraseToAnyFuzzySet()
}
.reduce(AnyFuzzySet<ConsequentUniverse>.empty) { partialResult, currentSet in
.init { [aggregatorFunction] element in
aggregatorFunction(partialResult(element), currentSet(element))
}
}
}
/// Apply generalized Modus Ponens to all rules using `fact` as input to the controller and return the grade membership of `value` in the fuzzy set output.
open func consequenceGrade<S: Sequence>(
for value: ConsequentUniverse,
usingFact fact: IterableFuzzySet<AntecedentUniverse, S>
) -> Grade {
ruleBase
.map {
FuzzySetComposition(set: fact, relation: $0.asRelation())
.grade(forElement: value)
}
.reduce(0, aggregatorFunction)
}
/// Apply generalized Modus Ponens to all rules using `fact` as input to the controller and return the grade membership of `value` in the fuzzy set output.
///
/// Helper for the scenario when `AntecedentUniverse` does not conform to `Equatable`.
/// One such example might be having a tuple as a universe of discourse - they still do not conform to `Equatable`, even if all of their elements are `Equatable`.
open func consequenceGrade(
for value: ConsequentUniverse,
usingSingletonFact fact: AntecedentUniverse
) -> Grade {
consequenceGrade(for: value, usingFact: .init([fact], membershipFunction: .one))
}
}

View File

@ -19,3 +19,11 @@ extension FuzzySetComposition: FuzzySet {
}.max() ?? 0
}
}
extension FuzzySetComposition: AnyFuzzySetRepresentable {
public func eraseToAnyFuzzySet() -> AnyFuzzySet<V> {
.init {
self($0)
}
}
}

View File

@ -33,14 +33,18 @@ final class ControlTests: XCTestCase {
funding.is(.inadequate) || Ø --> risk.is(.high)
}
let sut = FuzzyLinguisticController(rules: ruleBase)
let sut2 = FuzzyLinguisticController(rules: ruleBase, settings: .init(implication: .larsen))
let sut = FuzzyLogicController(rules: ruleBase)
let sut2 = FuzzyLogicController(rules: ruleBase, settings: .init(implication: .larsen))
let fact = IterableFuzzySet.singleton((8.8, 42.0))
let tuple = (8.8, 42.0)
let fact = IterableFuzzySet.singleton(tuple)
let result1 = sut.consequenceGrade(for: 100, usingFact: fact)
let result2 = sut.consequenceGrade(for: 0, usingFact: fact)
let result3 = sut.consequenceGrade(for: 50, usingFact: fact)
let result11 = sut.consequenceGrade(for: 100, usingSingletonFact: tuple)
let result22 = sut.consequenceGrade(for: 0, usingSingletonFact: tuple)
let result33 = sut.consequenceGrade(for: 50, usingSingletonFact: tuple)
let result4 = sut2.consequenceGrade(for: 100, usingFact: fact)
let result5 = sut2.consequenceGrade(for: 0, usingFact: fact)
@ -49,6 +53,9 @@ final class ControlTests: XCTestCase {
XCTAssertApproximatelyEqual(1.0, result1)
XCTAssertApproximatelyEqual(0.675, result2)
XCTAssertApproximatelyEqual(0.0, result3)
XCTAssertApproximatelyEqual(1.0, result11)
XCTAssertApproximatelyEqual(0.675, result22)
XCTAssertApproximatelyEqual(0.0, result33)
XCTAssertApproximatelyEqual(1.0, result4)
XCTAssertApproximatelyEqual(0.675, result5)
XCTAssertApproximatelyEqual(0.0, result6)