Describe convertions in README and use Zadeh's notation
This commit is contained in:
parent
9a4241395f
commit
b42f827cc8
34
README.md
34
README.md
|
@ -101,27 +101,35 @@ API Reference automatically collected with `jazzy` is published [here](https://a
|
||||||
|
|
||||||
## `FuzzySets` Module
|
## `FuzzySets` Module
|
||||||
|
|
||||||
* `protocol FuzzySet`: This abstraction requires a fuzzy set to provide a `grade(forElement:)` method which accepts a parameter of an `associatedtype Universe` and returns its membership `Grade` in the set. There are 3 provided concrete implementations in this module:
|
### `protocol FuzzySet`
|
||||||
1. `struct AnyFuzzySet` - allows type erasure. It only stores a `MembershipFunction` and has non-mutable methods.
|
This abstraction requires a fuzzy set to provide a `grade(forElement:)` method which accepts a parameter of an `associatedtype Universe` and returns its membership `Grade` in the set. There are 3 provided concrete implementations in this module:
|
||||||
2. `struct IterableFuzzySet` - stores a `MembershipFunction` as well as a `Sequence` of elements of the associated type `Universe`. Implements `Sequence` so that it can easily be iterated over them. The elements of the iteration over an `IterableFuzzySet` are `struct`s containing `grade` and `element` properties. It has non-mutable methods only.
|
|
||||||
3. `struct DiscreteMutableFuzzySet` - it is "discrete" because it doesn't stores a `MembershipFunction` but instead keeps its elements and their grade in a `Dictionary`, and it is "mutable" because it contains mutable equivalents of all other methods that operate over the set (including `subscript`). A default value of `0` is returned for the grade of an element that is not in the dictionary (a different default value can be provided as well).
|
|
||||||
|
|
||||||
* `protocol FuzzySetOperations` - all 3 concrete types implement it. It requires the following methods that operate on fuzzy sets:
|
1. `struct AnyFuzzySet` - allows type erasure. It only stores a `MembershipFunction` and has non-mutable methods.
|
||||||
* `alphaCut(_:alpha:)`
|
|
||||||
|
|
||||||
* `complement(method:)`
|
2. `struct IterableFuzzySet` - stores a `MembershipFunction` as well as a `Sequence` of elements of the associated type `Universe`. Implements `Sequence` so that it can easily be iterated over them. The elements of the iteration over an `IterableFuzzySet` are `struct`s containing `grade` and `element` properties. It has non-mutable methods only. Includes `support`, `core` and `height` computed properties.
|
||||||
|
|
||||||
* `intersection(_:method:)`
|
3. `struct DiscreteMutableFuzzySet` - it is "discrete" because it doesn't stores a `MembershipFunction` but instead keeps its elements and their grade in a `Dictionary`, and it is "mutable" because it contains mutable equivalents of all other methods that operate over the set (including `subscript`). A default value of `0` is returned for the grade of an element that is not in the dictionary (a different default value can be provided as well). Includes `support`, `core` and `height` computed properties. A bonus feature is its debug print using Zadeh's notation.
|
||||||
|
|
||||||
* `union(_:method:)`
|
Convertions between the 3 types are easy and possible using the `eraseToAnyFuzzySet`, `makeIterable` and `makeDiscreteMutable` methods defined on them.
|
||||||
|
|
||||||
* `difference(_:method:)`
|
### `protocol FuzzySetOperations`
|
||||||
|
All 3 concrete types implement it. It requires the following methods that operate on fuzzy sets:
|
||||||
|
|
||||||
* `symmetricDifference(_:method:)`
|
* `alphaCut(_:alpha:)`
|
||||||
|
|
||||||
* `power(_:n:)`
|
* `complement(method:)`
|
||||||
|
|
||||||
* `appliedCustomFunction(_:function:)`
|
* `intersection(_:method:)`
|
||||||
|
|
||||||
|
* `union(_:method:)`
|
||||||
|
|
||||||
|
* `difference(_:method:)`
|
||||||
|
|
||||||
|
* `symmetricDifference(_:method:)`
|
||||||
|
|
||||||
|
* `power(_:n:)`
|
||||||
|
|
||||||
|
* `appliedCustomFunction(_:function:)`
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ public struct DiscreteMutableFuzzySet<Universe: Hashable>: ExpressibleByDictiona
|
||||||
extension DiscreteMutableFuzzySet: FuzzySet {
|
extension DiscreteMutableFuzzySet: FuzzySet {
|
||||||
/// - Complexity: O(1)
|
/// - Complexity: O(1)
|
||||||
public func grade(forElement element: Universe) -> Grade {
|
public func grade(forElement element: Universe) -> Grade {
|
||||||
grades[element] ?? defaultGrade
|
grades[element, default: defaultGrade]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +120,25 @@ public extension DiscreteMutableFuzzySet {
|
||||||
mutating func applyPower(_ n: Double) {
|
mutating func applyPower(_ n: Double) {
|
||||||
applyFunction { Double.pow($0, n) }
|
applyFunction { Double.pow($0, n) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func applyFunction(_ function: (Grade) -> Grade) {
|
||||||
|
let newGradeTuples = grades.map {
|
||||||
|
($0.key, function($0.value))
|
||||||
|
}
|
||||||
|
let newMap = Dictionary(uniqueKeysWithValues: newGradeTuples)
|
||||||
|
grades = newMap
|
||||||
|
defaultGrade = function(defaultGrade)
|
||||||
|
sanitize()
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func applyFunction(
|
||||||
|
_ function: (Grade, Grade) -> Grade,
|
||||||
|
whenMergingWith anotherSet: Self
|
||||||
|
) {
|
||||||
|
grades.merge(anotherSet.grades, uniquingKeysWith: function)
|
||||||
|
defaultGrade = function(defaultGrade, anotherSet.defaultGrade)
|
||||||
|
sanitize()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
@ -169,36 +188,49 @@ public extension DiscreteMutableFuzzySet {
|
||||||
// MARK: - Debug
|
// MARK: - Debug
|
||||||
|
|
||||||
extension DiscreteMutableFuzzySet: CustomStringConvertible {
|
extension DiscreteMutableFuzzySet: CustomStringConvertible {
|
||||||
|
/// Pretty-print contents of `self` using Zadeh's notation.
|
||||||
public var description: String {
|
public var description: String {
|
||||||
let guts = grades.map {
|
let guts = grades.map {
|
||||||
"\($0.value)/\($0.key)"
|
"\($0.value)/\($0.key)"
|
||||||
}.joined(separator: ", ")
|
}.joined(separator: " + ")
|
||||||
return "<FuzzySet: {\(guts)}, other values == \(defaultGrade)>"
|
return "<FuzzySet: {\(guts)}, other values == \(defaultGrade)>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Convertion
|
||||||
|
|
||||||
|
public extension AnyFuzzySet where Universe: Hashable {
|
||||||
|
func makeDiscreteMutable(
|
||||||
|
takeOnly set: Set<Universe>,
|
||||||
|
forOthersUse defaultValue: Grade = 0
|
||||||
|
) -> DiscreteMutableFuzzySet<Universe> {
|
||||||
|
.init(
|
||||||
|
.init(uniqueKeysWithValues: set.map { ($0, self[$0]) }),
|
||||||
|
defaultGrade: defaultValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDiscreteMutable<S: Sequence>(
|
||||||
|
takeOnly sequence: S,
|
||||||
|
forOthersUse defaultValue: Grade = 0
|
||||||
|
) -> DiscreteMutableFuzzySet<Universe>
|
||||||
|
where S.Element == Universe {
|
||||||
|
.init(
|
||||||
|
.init(uniqueKeysWithValues: sequence.map { ($0, self[$0]) }),
|
||||||
|
defaultGrade: defaultValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension IterableFuzzySet where Universe: Hashable {
|
||||||
|
func makeDiscreteMutable() -> DiscreteMutableFuzzySet<Universe> {
|
||||||
|
.init(.init(uniqueKeysWithValues: sequence.map { ($0, self[$0]) }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
extension DiscreteMutableFuzzySet {
|
extension DiscreteMutableFuzzySet {
|
||||||
public mutating func applyFunction(_ function: (Grade) -> Grade) {
|
|
||||||
let newGradeTuples = grades.map {
|
|
||||||
($0.key, function($0.value))
|
|
||||||
}
|
|
||||||
let newMap = Dictionary(uniqueKeysWithValues: newGradeTuples)
|
|
||||||
grades = newMap
|
|
||||||
defaultGrade = function(defaultGrade)
|
|
||||||
sanitize()
|
|
||||||
}
|
|
||||||
|
|
||||||
public mutating func applyFunction(
|
|
||||||
_ function: (Grade, Grade) -> Grade,
|
|
||||||
whenMergingWith anotherSet: Self
|
|
||||||
) {
|
|
||||||
grades.merge(anotherSet.grades, uniquingKeysWith: function)
|
|
||||||
defaultGrade = function(defaultGrade, anotherSet.defaultGrade)
|
|
||||||
sanitize()
|
|
||||||
}
|
|
||||||
|
|
||||||
private mutating func sanitize() {
|
private mutating func sanitize() {
|
||||||
grades = grades.filter {
|
grades = grades.filter {
|
||||||
$0.value != defaultGrade
|
$0.value != defaultGrade
|
||||||
|
|
|
@ -142,12 +142,6 @@ public extension DiscreteMutableFuzzySet where Universe: CaseIterable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension IterableFuzzySet where Universe: Hashable {
|
|
||||||
func makeDiscreteMutable() -> DiscreteMutableFuzzySet<Universe> {
|
|
||||||
.init(.init(uniqueKeysWithValues: sequence.map { ($0, self[$0]) }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
|
||||||
public extension IterableFuzzySet {
|
public extension IterableFuzzySet {
|
||||||
|
|
|
@ -41,8 +41,8 @@ final class DiscreteMutableFuzzySetTests: XCTestCase {
|
||||||
"b": 1,
|
"b": 1,
|
||||||
]
|
]
|
||||||
let sut = DiscreteMutableFuzzySet(parameters)
|
let sut = DiscreteMutableFuzzySet(parameters)
|
||||||
let expected1 = "<FuzzySet: {0.69/a, 1.0/b}, other values == 0.0>"
|
let expected1 = "<FuzzySet: {0.69/a + 1.0/b}, other values == 0.0>"
|
||||||
let expected2 = "<FuzzySet: {1.0/b, 0.69/a}, other values == 0.0>"
|
let expected2 = "<FuzzySet: {1.0/b + 0.69/a}, other values == 0.0>"
|
||||||
|
|
||||||
let result = String(describing: sut)
|
let result = String(describing: sut)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue