Add consequence method returning a fuzzy set
This commit is contained in:
parent
968e1db94f
commit
dd19a7eeac
|
@ -44,9 +44,9 @@ let ruleBase = FuzzyRuleBase {
|
||||||
funding.is(.inadequate) || Ø --> risk.is(.high)
|
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
|
## Modules
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,3 +19,11 @@ extension FuzzySetComposition: FuzzySet {
|
||||||
}.max() ?? 0
|
}.max() ?? 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension FuzzySetComposition: AnyFuzzySetRepresentable {
|
||||||
|
public func eraseToAnyFuzzySet() -> AnyFuzzySet<V> {
|
||||||
|
.init {
|
||||||
|
self($0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,14 +33,18 @@ final class ControlTests: XCTestCase {
|
||||||
funding.is(.inadequate) || Ø --> risk.is(.high)
|
funding.is(.inadequate) || Ø --> risk.is(.high)
|
||||||
}
|
}
|
||||||
|
|
||||||
let sut = FuzzyLinguisticController(rules: ruleBase)
|
let sut = FuzzyLogicController(rules: ruleBase)
|
||||||
let sut2 = FuzzyLinguisticController(rules: ruleBase, settings: .init(implication: .larsen))
|
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 result1 = sut.consequenceGrade(for: 100, usingFact: fact)
|
||||||
let result2 = sut.consequenceGrade(for: 0, usingFact: fact)
|
let result2 = sut.consequenceGrade(for: 0, usingFact: fact)
|
||||||
let result3 = sut.consequenceGrade(for: 50, 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 result4 = sut2.consequenceGrade(for: 100, usingFact: fact)
|
||||||
let result5 = sut2.consequenceGrade(for: 0, usingFact: fact)
|
let result5 = sut2.consequenceGrade(for: 0, usingFact: fact)
|
||||||
|
@ -49,6 +53,9 @@ final class ControlTests: XCTestCase {
|
||||||
XCTAssertApproximatelyEqual(1.0, result1)
|
XCTAssertApproximatelyEqual(1.0, result1)
|
||||||
XCTAssertApproximatelyEqual(0.675, result2)
|
XCTAssertApproximatelyEqual(0.675, result2)
|
||||||
XCTAssertApproximatelyEqual(0.0, result3)
|
XCTAssertApproximatelyEqual(0.0, result3)
|
||||||
|
XCTAssertApproximatelyEqual(1.0, result11)
|
||||||
|
XCTAssertApproximatelyEqual(0.675, result22)
|
||||||
|
XCTAssertApproximatelyEqual(0.0, result33)
|
||||||
XCTAssertApproximatelyEqual(1.0, result4)
|
XCTAssertApproximatelyEqual(1.0, result4)
|
||||||
XCTAssertApproximatelyEqual(0.675, result5)
|
XCTAssertApproximatelyEqual(0.675, result5)
|
||||||
XCTAssertApproximatelyEqual(0.0, result6)
|
XCTAssertApproximatelyEqual(0.0, result6)
|
||||||
|
|
Loading…
Reference in New Issue