Sanitize stuff and add propositions tests

This commit is contained in:
Alexander Ignatov 2021-12-31 01:38:27 +02:00
parent 05f004f938
commit fd64717ffc
13 changed files with 172 additions and 24 deletions

View File

@ -3,16 +3,16 @@ import FuzzyRelations
public extension FuzzySetComposition {
static func generalizedModusPonens<P: FuzzySet, Q: FuzzySet>(
premiseAntecedent: P,
premiseConsequent: Q,
antecedent: P,
consequent: Q,
fact: IterableFuzzySet<U, Seq>,
method: ImplicationMethod = .mamdani
) -> Self where P.Universe == U, Q.Universe == V {
.init(
set: fact,
relation: .implication(
antecedent: premiseAntecedent,
consequent: premiseConsequent,
antecedent: antecedent,
consequent: consequent,
method: method
)
)

View File

@ -1,5 +1,11 @@
import FuzzySets
/// Describes a function used for forming a fuzzy implication.
///
/// A function `I` must satisfy the following axioms in order to be called a fuzzy implication:
/// 1. Boundary condition: `S(1, 1) = 1`, `S(1, 0) = 0`, `S(0, 0) = 1`
/// 2. Non-increasing `y`: `If x_1 <= x_2 Then I(x_1, y) >= I(x_2, y)`
/// 3. Non-decreasing `x`: `If y_1 <= y_2 Then I(x, y_1) <= I(x, y_2)`
public enum ImplicationMethod {
case larsen
case lukasiewicz

View File

@ -1,29 +1,29 @@
import FuzzySets
public protocol FuzzyStatement {
public protocol FuzzyProposition {
associatedtype U
func apply(_ u: U, settings: OperationSettings) -> Grade
}
public extension FuzzyStatement {
public extension FuzzyProposition {
func callAsFunction(_ u: U, settings: OperationSettings = .init()) -> Grade {
apply(u, settings: settings)
}
}
extension AnyFuzzySet: FuzzyStatement {
extension AnyFuzzySet: FuzzyProposition {
public func apply(_ u: Universe, settings: OperationSettings) -> Grade {
grade(forElement: u)
}
}
extension IterableFuzzySet: FuzzyStatement {
extension IterableFuzzySet: FuzzyProposition {
public func apply(_ u: Universe, settings: OperationSettings) -> Grade {
grade(forElement: u)
}
}
extension DiscreteMutableFuzzySet: FuzzyStatement {
extension DiscreteMutableFuzzySet: FuzzyProposition {
public func apply(_ u: Universe, settings: OperationSettings) -> Grade {
grade(forElement: u)
}

View File

@ -21,7 +21,3 @@ public struct OperationSettings {
self.xor = xor
}
}
open class ReasoningScheme {
// TODO
}

View File

@ -0,0 +1,24 @@
import FuzzySets
public struct FuzzyConjunction<A: FuzzyProposition, B: FuzzyProposition>: 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 && <A: FuzzyProposition, B: FuzzyProposition>(lhs: A, rhs: B) -> FuzzyConjunction<A, B> {
.init(lhs, rhs)
}

View File

@ -0,0 +1,24 @@
import FuzzySets
public struct FuzzyDisjunction<A: FuzzyProposition, B: FuzzyProposition>: 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 || <A: FuzzyProposition, B: FuzzyProposition>(lhs: A, rhs: B) -> FuzzyDisjunction<A, B> {
.init(lhs, rhs)
}

View File

@ -0,0 +1,19 @@
import FuzzySets
public struct FuzzyNegation<FS: FuzzyProposition>: 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 ! <FS: FuzzyProposition>(statement: FS) -> FuzzyNegation<FS> {
.init(statement)
}

View File

@ -1,6 +1,6 @@
import FuzzySets
public struct FuzzyRule<P: FuzzyStatement, Q: FuzzyStatement>: FuzzyStatement {
public struct FuzzyRule<P: FuzzyProposition, Q: FuzzyProposition>: FuzzyProposition {
public let antecedent: P
public let consequent: Q
@ -9,10 +9,7 @@ public struct FuzzyRule<P: FuzzyStatement, Q: FuzzyStatement>: FuzzyStatement {
self.consequent = consequent
}
public func apply(
_ values: (P.U, Q.U),
settings: OperationSettings = .init()
) -> Grade {
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)
@ -21,6 +18,6 @@ public struct FuzzyRule<P: FuzzyStatement, Q: FuzzyStatement>: FuzzyStatement {
}
infix operator -->: TernaryPrecedence // lower than ==, &&, ||, etc
public func --> <P: FuzzyStatement, Q: FuzzyStatement> (lhs: P, rhs: Q) -> FuzzyRule<P, Q> {
public func --> <P: FuzzyProposition, Q: FuzzyProposition> (lhs: P, rhs: Q) -> FuzzyRule<P, Q> {
.init(antecedent: lhs, consequent: rhs)
}

View File

@ -0,0 +1,24 @@
import FuzzySets
public struct FuzzyXOR<A: FuzzyProposition, B: FuzzyProposition>: 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 ^^ <A: FuzzyProposition, B: FuzzyProposition>(lhs: A, rhs: B) -> FuzzyXOR<A, B> {
.init(lhs, rhs)
}

View File

@ -0,0 +1,5 @@
import FuzzySets
open class ReasoningScheme {
// TODO
}

View File

@ -18,7 +18,7 @@ public enum TNormFunction {
case custom((Grade, Grade) -> Grade)
var function: (Grade, Grade) -> Grade {
public var function: (Grade, Grade) -> Grade {
switch self {
case .minimum:
return min
@ -72,7 +72,7 @@ public enum SNormFunction {
case custom((Grade, Grade) -> Grade)
var function: (Grade, Grade) -> Grade {
public var function: (Grade, Grade) -> Grade {
switch self {
case .maximum:
return max
@ -105,7 +105,7 @@ public enum ComplementFunction {
case custom((Grade) -> Grade)
var function: (Grade) -> Grade {
public var function: (Grade) -> Grade {
switch self {
case .standard:
return { 1 - $0 }
@ -127,7 +127,7 @@ public enum DifferenceFunction {
case custom((Grade, Grade) -> Grade)
var function: (Grade, Grade) -> Grade {
public var function: (Grade, Grade) -> Grade {
switch self {
case .tNormAndComplement(let t, let c):
return { t.function($0, c.function($1)) }
@ -145,7 +145,7 @@ public enum SymmetricDifferenceFunction {
case tNormSNormAndComplement(TNormFunction, SNormFunction, ComplementFunction)
case custom((Grade, Grade) -> Grade)
var function: (Grade, Grade) -> Grade {
public var function: (Grade, Grade) -> Grade {
switch self {
case .absoluteValue:
return { abs($0 - $1) }

View File

@ -0,0 +1,53 @@
import XCTest
import FuzzySets
import FuzzyLogic
final class PropositionTests: XCTestCase {
func test_operators_appliedCorrectly() {
enum Person {
case alice
case bob
}
let grades: [Person: Grade] = [
.alice: 0.8,
.bob: 0.6,
]
let efficient = DiscreteMutableFuzzySet(grades)
let sut1 = !efficient
let sut2 = efficient && efficient
let sut3 = efficient || efficient
let sut4 = efficient --> efficient
let sut5 = efficient ^^ efficient
let result1 = sut1(.alice)
let result2 = sut2((.alice, .bob))
let result3 = sut3((.alice, .bob))
let result4 = sut4((.alice, .bob))
let result5 = sut5((.alice, .bob))
XCTAssertApproximatelyEqual(result1, 0.2)
XCTAssertApproximatelyEqual(result2, 0.6)
XCTAssertApproximatelyEqual(result3, 0.8)
XCTAssertApproximatelyEqual(result4, 0.6)
XCTAssertApproximatelyEqual(result5, 0.2)
}
func test_operatorsNesting_appliedCorrectly() {
enum Person {
case alice
case bob
}
let grades: [Person: Grade] = [
.alice: 0.8,
.bob: 0.6,
]
let efficient = DiscreteMutableFuzzySet(grades)
let sut = efficient || efficient --> efficient
let result = sut(((.alice, .bob), .bob))
XCTAssertApproximatelyEqual(result, 0.6)
}
}