Skip to content

Instantly share code, notes, and snippets.

@indragiek
Last active September 21, 2015 05:54
Show Gist options
  • Save indragiek/10010660a8dc50df060e to your computer and use it in GitHub Desktop.
Save indragiek/10010660a8dc50df060e to your computer and use it in GitHub Desktop.
/// Used to lift a bounding value for a `Double` into the type system.
/// This is an ugly workaround for the lack of dependent types in Swift.
public protocol BoundingValueType {
static var value: Double { get }
}
/// Type that represents a bounding value of 1.0
public struct _1: BoundingValueType {
public static var value: Double { return 1.0 }
}
/// Type that represents a bounding value of 0.0
public struct _0: BoundingValueType {
public static var value: Double { return 0.0 }
}
/// A wrapper for a `Double` that enforces fixed boundary values.
/// The valid range is [LowerBound, UpperBound]
public struct BoundedDouble<LowerBound: BoundingValueType, UpperBound: BoundingValueType> {
public let value: Double
/// Failable initializer that only succeeds if `value` is within
/// the bounds specified by the parametrized types `LowerBound`
/// and `UpperBound`
public init?(value: Double) {
if value >= LowerBound.value && value <= UpperBound.value {
self.value = value
} else {
self.value = 0.0
return nil
}
}
}
/// Protocol type representing a fuzzy set. Used for the purposes
/// of writing protocol extensions for specialized cases.
public protocol FuzzySetType {
typealias Element
init(membership: Element -> BoundedDouble<_0, _1>)
}
public extension FuzzySetType where Element: Equatable {
/// Creates a fuzzy set using a membership function that evaluates
/// to 1 for `element` and 0 otherwise.
public static func singleton(element: Element) -> FuzzySet<Element> {
return FuzzySet { $0 == element ? 1 : 0 }
}
}
public extension FuzzySetType where Element: Hashable {
/// Creates a fuzzy set using a membership function that evaluates
/// to the membership value specified for an element in `map`, and
/// 0 otherwise.
public static func pointwise(map: [Element: Double]) -> FuzzySet<Element> {
return FuzzySet { map[$0] ?? 0 }
}
}
public extension FuzzySetType where Element == Double {
/// Creates a fuzzy set using a triangular membership function.
/// `a` and `c` locate the "feet" of the triangle, and `b` locates
/// the peak.
// http://www.mathworks.com/help/fuzzy/trimf.html
public static func triangular(a: Element, b: Element, c: Element) -> FuzzySet<Element> {
return FuzzySet { x -> Element in
return max(min((x - a) / (b - a), (c - x) / (c - b)), 0)
}
}
/// Creates a fuzzy set using a trapezoidal membership function.
/// `a` and `d` locate the feet of the trapezoid, and `b` and `c`
/// specify the shoulders.
// http://www.mathworks.com/help/fuzzy/trapmf.html
public static func trapezoidal(a: Element, b: Element, c: Element, d: Element) -> FuzzySet<Element> {
return FuzzySet { x -> Element in
return max(min((x - a) / (b - a), (d - x) / (d - c)), 0)
}
}
/// Creates a fuzzy set using a Gaussian membership function.
// http://www.mathworks.com/help/fuzzy/gaussmf.html
public static func gaussian(c: Element, sigma: Element) -> FuzzySet<Element> {
return FuzzySet { exp(-pow($0 - c, 2) / (2 * pow(sigma, 2))) }
}
/// Creates a fuzzy set using a sigmoidal membership function.
// http://www.mathworks.com/help/fuzzy/sigmf.html
public static func sigmoidal(a: Element, c: Element) -> FuzzySet<Element> {
return FuzzySet { 1 / (1 + exp(-a * ($0 - c))) }
}
/// Creates a fuzzy set using a bell-shaped membership function.
/// `c` locates the center of the curve.
// http://www.mathworks.com/help/fuzzy/gbellmf.html
public static func bell(a: Element, b: Element, c: Element) -> FuzzySet<Element> {
return FuzzySet { 1 / (1 + pow(abs(($0 - c) / a), 2 * b)) }
}
}
/// A set whose elements have degrees of membership.
public struct FuzzySet<Element>: FuzzySetType {
private let membership: Element -> Double
/// Initializes the fuzzy set using a membership function that returns
/// a membership grade in the range [0.0, 1.0] for every element.
public init(membership: Element -> BoundedDouble<_0, _1>) {
self.membership = { element in
return membership(element).value
}
}
private init(_ boundedMembership: Element -> Double) {
self.membership = boundedMembership
}
/// Returns the empty fuzzy set, where the membership function
/// m(x) = 0
public static func empty() -> FuzzySet<Element> {
return FuzzySet { _ in 0 }
}
/// Returns the universal fuzzy set, where the membership function
/// m(x) = 1
public static func universal() -> FuzzySet<Element> {
return FuzzySet { _ in 1 }
}
/// Evaluates the membership function at `x` and returns the result.
public subscript(x: Element) -> Double {
return self.membership(x)
}
/// Returns a new fuzzy set that is the union of the receiver and `set`.
public func union(set: FuzzySet<Element>) -> FuzzySet<Element> {
return FuzzySet { max(self[$0], set[$0]) }
}
/// Returns a new fuzzy set that is the intersection of the receiver
/// and `set`
public func intersect(set: FuzzySet<Element>) -> FuzzySet<Element> {
return FuzzySet { min(self[$0], set[$0]) }
}
/// Returns a new fuzzy set that is the complement of the receiver.
public func complement() -> FuzzySet<Element> {
return FuzzySet { 1 - self[$0] }
}
/// Returns a new fuzzy set that concentrates the membership function
/// around points with higher membership values.
public func concentrate() -> FuzzySet<Element> {
return FuzzySet { pow(self[$0], 2) }
}
/// Opposite of `concentrate`
public func dilate() -> FuzzySet<Element> {
return FuzzySet { sqrt(self[$0]) }
}
}
@jtbandes
Copy link

Another solution: use IntMax rather than Double, mapping 0.0 to .allZeros and 1.0 to ~.allZeros. Then it's impossible to have an invalid value ;)

@indragiek
Copy link
Author

@jtbandes Wouldn't that still require runtime checks to ensure that the number isn't out of the 0.0-1.0 range?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment