Sanitize stuff and add propositions tests
This commit is contained in:
parent
05f004f938
commit
fd64717ffc
|
@ -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
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -21,7 +21,3 @@ public struct OperationSettings {
|
|||
self.xor = xor
|
||||
}
|
||||
}
|
||||
|
||||
open class ReasoningScheme {
|
||||
// TODO
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import FuzzySets
|
||||
|
||||
open class ReasoningScheme {
|
||||
// TODO
|
||||
}
|
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue