Describe convertions in README and use Zadeh's notation

This commit is contained in:
Alexander Ignatov 2022-01-05 20:47:32 +02:00
parent 9a4241395f
commit b42f827cc8
4 changed files with 76 additions and 42 deletions

View File

@ -101,27 +101,35 @@ API Reference automatically collected with `jazzy` is published [here](https://a
## `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:
1. `struct AnyFuzzySet` - allows type erasure. It only stores a `MembershipFunction` and has non-mutable methods.
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 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 FuzzySetOperations` - all 3 concrete types implement it. It requires the following methods that operate on fuzzy sets:
* `alphaCut(_:alpha:)`
1. `struct AnyFuzzySet` - allows type erasure. It only stores a `MembershipFunction` and has non-mutable methods.
* `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

View File

@ -19,7 +19,7 @@ public struct DiscreteMutableFuzzySet<Universe: Hashable>: ExpressibleByDictiona
extension DiscreteMutableFuzzySet: FuzzySet {
/// - Complexity: O(1)
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) {
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
@ -169,36 +188,49 @@ public extension DiscreteMutableFuzzySet {
// MARK: - Debug
extension DiscreteMutableFuzzySet: CustomStringConvertible {
/// Pretty-print contents of `self` using Zadeh's notation.
public var description: String {
let guts = grades.map {
"\($0.value)/\($0.key)"
}.joined(separator: ", ")
}.joined(separator: " + ")
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
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() {
grades = grades.filter {
$0.value != defaultGrade

View File

@ -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
public extension IterableFuzzySet {

View File

@ -41,8 +41,8 @@ final class DiscreteMutableFuzzySetTests: XCTestCase {
"b": 1,
]
let sut = DiscreteMutableFuzzySet(parameters)
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 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 result = String(describing: sut)