From 2b6a0516d962bc83bc1c6d204149f70bd0c66b6f Mon Sep 17 00:00:00 2001 From: Alexander Ignatov Date: Sat, 1 Jan 2022 15:02:28 +0200 Subject: [PATCH] Restructure FuzzyLogic module and add rule base DSL --- .../BinaryFuzzyRelation+implication.swift | 6 +++ .../FuzzyLinguisticController.swift | 32 +++++++++++++ .../FuzzyControl/FuzzyProposition.swift | 30 +++++++++++++ .../FuzzyControl/FuzzyRuleBase.swift | 45 +++++++++++++++++++ .../OperationSettings.swift | 0 .../Propositions/FuzzyConjunction.swift | 23 ++++++++++ .../Propositions/FuzzyDisjunction.swift | 23 ++++++++++ .../Propositions/FuzzyNegation.swift | 21 +++++++++ .../FuzzyControl/Propositions/FuzzyRule.swift | 24 ++++++++++ .../FuzzyControl/Propositions/FuzzyXOR.swift | 24 ++++++++++ .../Reasoning/FuzzyProposition.swift | 30 ------------- .../Propositions/FuzzyConjunction.swift | 24 ---------- .../Propositions/FuzzyDisjunction.swift | 24 ---------- .../Propositions/FuzzyNegation.swift | 19 -------- .../Reasoning/Propositions/FuzzyRule.swift | 23 ---------- .../Reasoning/Propositions/FuzzyXOR.swift | 24 ---------- .../Reasoning/ReasoningScheme.swift | 5 --- 17 files changed, 228 insertions(+), 149 deletions(-) create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyLinguisticController.swift create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyProposition.swift create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyRuleBase.swift rename Sources/FuzzyLogic/Reasoning/{ => FuzzyControl}/OperationSettings.swift (100%) create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyConjunction.swift create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyDisjunction.swift create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyNegation.swift create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyRule.swift create mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyXOR.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/FuzzyProposition.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/Propositions/FuzzyConjunction.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/Propositions/FuzzyDisjunction.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/Propositions/FuzzyNegation.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/Propositions/FuzzyRule.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/Propositions/FuzzyXOR.swift delete mode 100644 Sources/FuzzyLogic/Reasoning/ReasoningScheme.swift diff --git a/Sources/FuzzyLogic/Extensions/BinaryFuzzyRelation+implication.swift b/Sources/FuzzyLogic/Extensions/BinaryFuzzyRelation+implication.swift index 9953901..e41cc09 100644 --- a/Sources/FuzzyLogic/Extensions/BinaryFuzzyRelation+implication.swift +++ b/Sources/FuzzyLogic/Extensions/BinaryFuzzyRelation+implication.swift @@ -12,3 +12,9 @@ public extension BinaryFuzzyRelation { } } } + +public extension FuzzyRule { + func asRelation() -> BinaryFuzzyRelation where Input == (P, Q) { + .init { (p, q) in self((p, q)) } + } +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyLinguisticController.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyLinguisticController.swift new file mode 100644 index 0000000..64201fa --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyLinguisticController.swift @@ -0,0 +1,32 @@ +import FuzzySets +import FuzzyRelations + +open class FuzzyLinguisticController { + + 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( + _ value: ConsequentUniverse, + usingFact fact: IterableFuzzySet + ) -> Grade { + ruleBase + .map { + FuzzySetComposition(set: fact, relation: $0.asRelation()) + } + .map { + $0.grade(forElement: value) + } + .reduce(0, aggregatorFuction) + } +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyProposition.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyProposition.swift new file mode 100644 index 0000000..c6496be --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyProposition.swift @@ -0,0 +1,30 @@ +import FuzzySets + +public protocol FuzzyProposition { + associatedtype Input + func apply(_ x: Input, settings: OperationSettings) -> Grade +} + +public extension FuzzyProposition { + func callAsFunction(_ x: Input, settings: OperationSettings = .init()) -> Grade { + apply(x, settings: settings) + } +} + +extension AnyFuzzySet: FuzzyProposition { + public func apply(_ x: Universe, settings: OperationSettings) -> Grade { + grade(forElement: x) + } +} + +extension IterableFuzzySet: FuzzyProposition { + public func apply(_ x: Universe, settings: OperationSettings) -> Grade { + grade(forElement: x) + } +} + +extension DiscreteMutableFuzzySet: FuzzyProposition { + public func apply(_ x: Universe, settings: OperationSettings) -> Grade { + grade(forElement: x) + } +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyRuleBase.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyRuleBase.swift new file mode 100644 index 0000000..b4d393d --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/FuzzyRuleBase.swift @@ -0,0 +1,45 @@ +import FuzzySets + +public struct FuzzyRuleBase { + + public typealias Rule = FuzzyRule + + @resultBuilder + public struct FuzzyRuleBaseBuilder { + public static func buildBlock() -> [Rule] { [] } + + public static func buildBlock(_ rules: Rule...) -> [Rule] { rules } + + public static func buildOptional(_ component: [Rule]?) -> [Rule] { + component ?? [] + } + + public static func buildEither(first component: [Rule]) -> [Rule] { + component + } + + public static func buildEither(second component: [Rule]) -> [Rule] { + component + } + + public static func buildArray(_ components: [[Rule]]) -> [Rule] { + components.flatMap { $0 } + } + } + + public var rules: [Rule] + + public init(_ rules: [Rule] = []) { + self.rules = rules + } + + public init(@FuzzyRuleBaseBuilder _ rulesBlock: () -> [Rule]) { + self.rules = rulesBlock() + } +} + +extension FuzzyRuleBase: Sequence { + public func makeIterator() -> Array.Iterator { + rules.makeIterator() + } +} diff --git a/Sources/FuzzyLogic/Reasoning/OperationSettings.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/OperationSettings.swift similarity index 100% rename from Sources/FuzzyLogic/Reasoning/OperationSettings.swift rename to Sources/FuzzyLogic/Reasoning/FuzzyControl/OperationSettings.swift diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyConjunction.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyConjunction.swift new file mode 100644 index 0000000..32d700a --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyConjunction.swift @@ -0,0 +1,23 @@ +import FuzzySets + +public struct FuzzyConjunction: FuzzyProposition { + + private let function: (Input, OperationSettings) -> Grade + + public init(lhs: P, rhs: Q) where Input == (P.Input, Q.Input) { + self.function = { (x, settings) -> Grade in + settings.conjunction.function( + lhs(x.0, settings: settings), + rhs(x.1, settings: settings) + ) + } + } + + public func apply(_ values: Input, settings: OperationSettings = .init()) -> Grade { + function(values, settings) + } +} + +public func && (lhs: P, rhs: Q) -> FuzzyConjunction<(P.Input, Q.Input)> { + .init(lhs: lhs, rhs: rhs) +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyDisjunction.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyDisjunction.swift new file mode 100644 index 0000000..94fb08c --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyDisjunction.swift @@ -0,0 +1,23 @@ +import FuzzySets + +public struct FuzzyDisjunction: FuzzyProposition { + + private let function: (Input, OperationSettings) -> Grade + + public init(lhs: P, rhs: Q) where Input == (P.Input, Q.Input) { + self.function = { (x, settings) -> Grade in + settings.disjunction.function( + lhs(x.0, settings: settings), + rhs(x.1, settings: settings) + ) + } + } + + public func apply(_ values: Input, settings: OperationSettings = .init()) -> Grade { + function(values, settings) + } +} + +public func || (lhs: P, rhs: Q) -> FuzzyDisjunction<(P.Input, Q.Input)> { + .init(lhs: lhs, rhs: rhs) +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyNegation.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyNegation.swift new file mode 100644 index 0000000..6ab95ee --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyNegation.swift @@ -0,0 +1,21 @@ +import FuzzySets + +public struct FuzzyNegation: FuzzyProposition { + + public let function: (Input, OperationSettings) -> Grade + + public init(_ statement: F) where Input == F.Input { + self.function = { (input, settings) -> Grade in + settings.negation.function(statement(input, settings: settings)) + } + } + + public func apply(_ value: Input, settings: OperationSettings = .init()) -> Grade { + function(value, settings) + } +} + + +public prefix func ! (statement: F) -> FuzzyNegation { + .init(statement) +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyRule.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyRule.swift new file mode 100644 index 0000000..574c829 --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyRule.swift @@ -0,0 +1,24 @@ +import FuzzySets + +public struct FuzzyRule: FuzzyProposition { + + private let function: (Input, OperationSettings) -> Grade + + public init(antecedent: P, consequent: Q) where Input == (P.Input, Q.Input) { + self.function = { (x, settings) -> Grade in + settings.implication.function( + antecedent(x.0, settings: settings), + consequent(x.1, settings: settings) + ) + } + } + + public func apply(_ values: Input, settings: OperationSettings = .init()) -> Grade { + function(values, settings) + } +} + +infix operator -->: TernaryPrecedence // lower than ==, &&, ||, etc +public func --> (lhs: P, rhs: Q) -> FuzzyRule<(P.Input, Q.Input)> { + .init(antecedent: lhs, consequent: rhs) +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyXOR.swift b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyXOR.swift new file mode 100644 index 0000000..d488f13 --- /dev/null +++ b/Sources/FuzzyLogic/Reasoning/FuzzyControl/Propositions/FuzzyXOR.swift @@ -0,0 +1,24 @@ +import FuzzySets + +public struct FuzzyXOR: FuzzyProposition { + + private let function: (Input, OperationSettings) -> Grade + + public init(lhs: P, rhs: Q) where Input == (P.Input, Q.Input) { + self.function = { (x, settings) -> Grade in + settings.xor.function( + lhs(x.0, settings: settings), + rhs(x.1, settings: settings) + ) + } + } + + public func apply(_ values: Input, settings: OperationSettings = .init()) -> Grade { + function(values, settings) + } +} + +infix operator ^^: ComparisonPrecedence +public func ^^ (lhs: P, rhs: Q) -> FuzzyXOR<(P.Input, Q.Input)> { + .init(lhs: lhs, rhs: rhs) +} diff --git a/Sources/FuzzyLogic/Reasoning/FuzzyProposition.swift b/Sources/FuzzyLogic/Reasoning/FuzzyProposition.swift deleted file mode 100644 index 946bb41..0000000 --- a/Sources/FuzzyLogic/Reasoning/FuzzyProposition.swift +++ /dev/null @@ -1,30 +0,0 @@ -import FuzzySets - -public protocol FuzzyProposition { - associatedtype U - func apply(_ u: U, settings: OperationSettings) -> Grade -} - -public extension FuzzyProposition { - func callAsFunction(_ u: U, settings: OperationSettings = .init()) -> Grade { - apply(u, settings: settings) - } -} - -extension AnyFuzzySet: FuzzyProposition { - public func apply(_ u: Universe, settings: OperationSettings) -> Grade { - grade(forElement: u) - } -} - -extension IterableFuzzySet: FuzzyProposition { - public func apply(_ u: Universe, settings: OperationSettings) -> Grade { - grade(forElement: u) - } -} - -extension DiscreteMutableFuzzySet: FuzzyProposition { - public func apply(_ u: Universe, settings: OperationSettings) -> Grade { - grade(forElement: u) - } -} diff --git a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyConjunction.swift b/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyConjunction.swift deleted file mode 100644 index d569efe..0000000 --- a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyConjunction.swift +++ /dev/null @@ -1,24 +0,0 @@ -import FuzzySets - -public struct FuzzyConjunction: FuzzyProposition { - - public let first: A - public let second: B - - public init(_ first: A, _ second: B) { - self.first = first - self.second = second - } - - public func apply(_ values: (A.U, B.U), settings: OperationSettings = .init()) -> Grade { - settings.conjunction.function( - first(values.0, settings: settings), - second(values.1, settings: settings) - ) - } -} - - -public func && (lhs: A, rhs: B) -> FuzzyConjunction { - .init(lhs, rhs) -} diff --git a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyDisjunction.swift b/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyDisjunction.swift deleted file mode 100644 index be8407c..0000000 --- a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyDisjunction.swift +++ /dev/null @@ -1,24 +0,0 @@ -import FuzzySets - -public struct FuzzyDisjunction: FuzzyProposition { - - public let first: A - public let second: B - - public init(_ first: A, _ second: B) { - self.first = first - self.second = second - } - - public func apply(_ values: (A.U, B.U), settings: OperationSettings = .init()) -> Grade { - settings.disjunction.function( - first(values.0, settings: settings), - second(values.1, settings: settings) - ) - } -} - - -public func || (lhs: A, rhs: B) -> FuzzyDisjunction { - .init(lhs, rhs) -} diff --git a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyNegation.swift b/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyNegation.swift deleted file mode 100644 index a63a892..0000000 --- a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyNegation.swift +++ /dev/null @@ -1,19 +0,0 @@ -import FuzzySets - -public struct FuzzyNegation: FuzzyProposition { - - public let statement: FS - - public init(_ statement: FS) { - self.statement = statement - } - - public func apply(_ value: FS.U, settings: OperationSettings = .init()) -> Grade { - settings.negation.function(statement(value, settings: settings)) - } -} - - -public prefix func ! (statement: FS) -> FuzzyNegation { - .init(statement) -} diff --git a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyRule.swift b/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyRule.swift deleted file mode 100644 index dca35f6..0000000 --- a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyRule.swift +++ /dev/null @@ -1,23 +0,0 @@ -import FuzzySets - -public struct FuzzyRule: FuzzyProposition { - public let antecedent: P - public let consequent: Q - - public init(antecedent: P, consequent: Q) { - self.antecedent = antecedent - self.consequent = consequent - } - - public func apply(_ values: (P.U, Q.U), settings: OperationSettings = .init()) -> Grade { - settings.implication.function( - antecedent(values.0, settings: settings), - consequent(values.1, settings: settings) - ) - } -} - -infix operator -->: TernaryPrecedence // lower than ==, &&, ||, etc -public func --> (lhs: P, rhs: Q) -> FuzzyRule { - .init(antecedent: lhs, consequent: rhs) -} diff --git a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyXOR.swift b/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyXOR.swift deleted file mode 100644 index 30c5ffd..0000000 --- a/Sources/FuzzyLogic/Reasoning/Propositions/FuzzyXOR.swift +++ /dev/null @@ -1,24 +0,0 @@ -import FuzzySets - -public struct FuzzyXOR: FuzzyProposition { - - public let first: A - public let second: B - - public init(_ first: A, _ second: B) { - self.first = first - self.second = second - } - - public func apply(_ values: (A.U, B.U), settings: OperationSettings = .init()) -> Grade { - settings.xor.function( - first(values.0, settings: settings), - second(values.1, settings: settings) - ) - } -} - -infix operator ^^: ComparisonPrecedence -public func ^^ (lhs: A, rhs: B) -> FuzzyXOR { - .init(lhs, rhs) -} diff --git a/Sources/FuzzyLogic/Reasoning/ReasoningScheme.swift b/Sources/FuzzyLogic/Reasoning/ReasoningScheme.swift deleted file mode 100644 index 8cf399b..0000000 --- a/Sources/FuzzyLogic/Reasoning/ReasoningScheme.swift +++ /dev/null @@ -1,5 +0,0 @@ -import FuzzySets - -open class ReasoningScheme { - // TODO -}