Package fuzz :: Module fset
[hide private]
[frames] | no frames]

Source Code for Module fuzz.fset

  1  """\ 
  2  Discrete fuzzy set module. Contains basic fuzzy set and element class 
  3  definitions. 
  4   
  5  @author: Aaron Mavrinac 
  6  @organization: University of Windsor 
  7  @contact: mavrin1@uwindsor.ca 
  8  @license: LGPL-3 
  9  """ 
 10   
 11  from copy import copy 
 12   
 13  from .iset import IndexedMember, IndexedSet 
14 15 16 -class FuzzyElement(IndexedMember):
17 """\ 18 Fuzzy element class. 19 """ 20 __slots__ = ['_index', '_mu'] 21
22 - def __init__(self, index, mu=1.0):
23 """\ 24 Constructor. 25 26 @param index: The object for this member. 27 @type index: C{object} 28 @param mu: The membership degree of this member. 29 @type mu: C{float} 30 """ 31 super(FuzzyElement, self).__init__(index) 32 self.mu = mu
33
34 - def __repr__(self):
35 """\ 36 Return the canonical representation of a fuzzy element. 37 38 @return: Canonical representation. 39 @rtype: C{str} 40 """ 41 return 'FuzzyElement(%s, %f)' % (str(self.index), self.mu)
42
43 - def __str__(self):
44 """\ 45 Return the string representation of a fuzzy element. 46 47 @return: String representation. 48 @rtype: C{str} 49 """ 50 return '%s \ %f' % (str(self.index), self.mu)
51 52 @property
53 - def mu(self):
54 """\ 55 The mu value of this fuzzy element. 56 57 @rtype: C{float} 58 """ 59 return self._mu
60 61 @mu.setter
62 - def mu(self, value):
63 """ 64 Set the mu value of this fuzzy element. 65 66 @param value: The value for mu. 67 @type value: C{float} 68 """ 69 if value < 0 or value > 1: 70 raise ValueError('mu value must be in [0, 1]') 71 self._mu = value
72
73 74 -class FuzzySet(IndexedSet):
75 """\ 76 Discrete fuzzy set class. 77 """ 78 NORM_STANDARD = 0 79 NORM_ALGEBRAIC = 1 80 NORM_BOUNDED = 2 81 NORM_DRASTIC = 3 82 83 COMP_STANDARD = 0 84 COMP_YAGER = 1 85 86 _itemcls = FuzzyElement 87
88 - class FuzzySetIterator(object):
89 """\ 90 Discrete fuzzy set iterator class. 91 """
92 - def __init__(self, fuzzyset):
93 self.setiterator = IndexedSet.__iter__(fuzzyset)
94
95 - def __iter__(self):
96 return self
97
98 - def __next__(self):
99 while True: 100 element = next(self.setiterator) 101 if element.mu > 0: 102 return element
103 104 next = __next__
105
106 - def __init__(self, iterable=set()):
107 """\ 108 Construct a fuzzy set from an optional iterable. 109 110 @param iterable: The iterable to construct from (optional). 111 @type iterable: C{object} 112 """ 113 super(FuzzySet, self).__init__(iterable)
114
115 - def __iter__(self):
116 """\ 117 Return an iterator for this fuzzy set. 118 119 @return: Iterator. 120 @rtype: L{FuzzySet.FuzzySetIterator} 121 """ 122 return FuzzySet.FuzzySetIterator(self)
123
124 - def __len__(self):
125 """\ 126 Override the length function. 127 128 @return: Size of this fuzzy set. 129 @rtype: C{int} 130 """ 131 return len([element for element in self])
132
133 - def __contains__(self, element):
134 """\ 135 Override the contents function. 136 137 @return: True if in the set, false otherwise. 138 @rtype: C{bool} 139 """ 140 return set.__contains__(self, element) and self.mu(element) > 0
141
142 - def __getitem__(self, key):
143 """\ 144 Return a set item indexed by key (including those with a membership 145 degree of zero). 146 147 @param key: The index of the item to get. 148 @type key: C{object} 149 @return: The matching item. 150 @rtype: C{object} 151 """ 152 for item in IndexedSet.__iter__(self): 153 if item.index == key: 154 return item 155 raise KeyError(key)
156
157 - def __str__(self):
158 """\ 159 String representation of a fuzzy set. 160 161 @return: String representation. 162 @rtype: C{str} 163 """ 164 return ('%s([' % self.__class__.__name__) \ 165 + ', '.join([str(element) for element in self]) + '])'
166
167 - def keys(self):
168 """\ 169 Return a list of keys in the set (including those with a membership 170 degree of zero). 171 172 @return: List of keys in the set. 173 @rtype: C{list} 174 """ 175 return [element.index for element in IndexedSet.__iter__(self)]
176
177 - def mu(self, key):
178 """\ 179 Return the membership degree of the element specified by key. Returns 180 zero for any non-member element. 181 182 @return: The membership degree of the specified element. 183 @rtype: C{float} 184 """ 185 try: 186 return self[key].mu 187 except KeyError: 188 return 0.0
189 190 @property
191 - def support(self):
192 """\ 193 Support, the crisp set of all elements with non-zero membership in the 194 fuzzy set. 195 196 @rtype: C{set} 197 """ 198 return set([element.index for element in self])
199 200 @property
201 - def kernel(self):
202 """\ 203 Kernel, the crisp set of all elements with membership degree of exactly 204 1. 205 206 @rtype: C{set} 207 """ 208 return self.alpha(1.0)
209 210 @property
211 - def height(self):
212 """\ 213 Height function. Returns the maximum membership degree of any element 214 in the fuzzy set. 215 216 @rtype: C{float} 217 """ 218 return max([element.mu for element in self])
219 220 @property
221 - def cardinality(self):
222 """\ 223 Scalar cardinality, the sum of membership degrees of all elements. 224 225 @rtype: C{float} 226 """ 227 return sum([element.mu for element in self])
228 229 # Binary fuzzy set operations 230
231 - def __or__(self, other):
232 """\ 233 Return the fuzzy union of two fuzzy sets as a new fuzzy set. 234 235 @param other: The other fuzzy set. 236 @type other: L{FuzzySet} 237 @return: The fuzzy union. 238 @rtype: L{FuzzySet} 239 """ 240 return self.efficient_union(other)
241
242 - def __ior__(self, other):
243 """\ 244 In-place fuzzy union. 245 246 @param other: The other fuzzy set. 247 @type other: L{FuzzySet} 248 @return: The fuzzy union (self). 249 @rtype: L{FuzzySet} 250 """ 251 self = self.efficient_union(other) 252 return self
253
254 - def union(self, other, norm=0):
255 """\ 256 Return the fuzzy union of two fuzzy sets as a new fuzzy set. 257 258 t-Conorm Types: 259 0 - Standard Union 260 1 - Algebraic Sum 261 2 - Bounded Sum 262 3 - Drastic Union 263 264 @param other: The other fuzzy set. 265 @type other: L{FuzzySet} 266 @param norm: The t-conorm type to use. 267 @type norm: C{int} 268 @return: The fuzzy union. 269 @rtype: L{FuzzySet} 270 """ 271 if not norm in range(4): 272 raise ValueError('invalid t-conorm type') 273 self._binary_sanity_check(other) 274 result = self.__class__() 275 bothkeys = set(self.keys()) | set(other.keys()) 276 [lambda: result.update([FuzzyElement(key, max(self.mu(key), \ 277 other.mu(key))) for key in bothkeys]), 278 lambda: result.update([FuzzyElement(key, self.mu(key) + other.mu(key) \ 279 - self.mu(key) * other.mu(key)) for key in bothkeys]), 280 lambda: result.update([FuzzyElement(key, min(1.0, self.mu(key) + \ 281 other.mu(key))) for key in bothkeys]), 282 lambda: result.update([FuzzyElement(key, (self.mu(key) == 0.0 and \ 283 other.mu(key)) or (other.mu(key) == 0.0 and self.mu(key)) or \ 284 1.0) for key in bothkeys]) 285 ][norm]() 286 return result
287
288 - def efficient_union(self, other):
289 """\ 290 Optimized version of the standard fuzzy union for large fuzzy sets. 291 292 @param other: The other fuzzy set. 293 @type other: L{FuzzySet} 294 @return: The fuzzy union. 295 @rtype: L{FuzzySet} 296 """ 297 self._binary_sanity_check(other) 298 result = self.copy() 299 keys = result.keys() 300 for element in other: 301 if element.index in keys: 302 result[element.index].mu = max(result[element.index].mu, 303 element.mu) 304 else: 305 set.add(result, copy(element)) 306 return result
307
308 - def __and__(self, other):
309 """\ 310 Return the fuzzy intersection of two fuzzy sets as a new fuzzy set. 311 312 @param other: The other fuzzy set. 313 @type other: L{FuzzySet} 314 @return: The fuzzy intersection. 315 @rtype: L{FuzzySet} 316 """ 317 return self.intersection(other)
318
319 - def __iand__(self, other):
320 """\ 321 In-place fuzzy intersection. 322 323 @param other: The other fuzzy set. 324 @type other: L{FuzzySet} 325 @return: The fuzzy intersection (self). 326 @rtype: L{FuzzySet} 327 """ 328 self = self.intersection(other) 329 return self
330
331 - def intersection(self, other, norm=0):
332 """\ 333 Return the fuzzy intersection of two fuzzy sets as a new fuzzy set. 334 335 t-Norm Types: 336 0 - Standard Intersection 337 1 - Algebraic Product 338 2 - Bounded Difference 339 3 - Drastic Intersection 340 341 @param other: The other fuzzy set. 342 @type other: L{FuzzySet} 343 @param norm: The t-norm type to use. 344 @type norm: C{int} 345 @return: The fuzzy intersection. 346 @rtype: L{FuzzySet} 347 """ 348 if not norm in range(4): 349 raise ValueError('invalid t-norm type') 350 self._binary_sanity_check(other) 351 result = self.__class__() 352 [lambda: result.update([FuzzyElement(key, min(self.mu(key), \ 353 other.mu(key))) for key in self.keys()]), 354 lambda: result.update([FuzzyElement(key, self.mu(key) * \ 355 other.mu(key)) for key in self.keys()]), 356 lambda: result.update([FuzzyElement(key, max(0.0, self.mu(key) + \ 357 other.mu(key) - 1.0)) for key in self.keys()]), 358 lambda: result.update([FuzzyElement(key, (self.mu(key) == 1.0 and \ 359 other.mu(key)) or (other.mu(key) == 1.0 and self.mu(key)) or 0.0) \ 360 for key in self.keys()]) 361 ][norm]() 362 return result
363
364 - def __eq__(self, other):
365 """\ 366 Compare two fuzzy sets for equality. 367 368 @param other: The other fuzzy set. 369 @type other: L{FuzzySet} 370 @return: True if equal, false otherwise. 371 @rtype: C{bool} 372 """ 373 self._binary_sanity_check(other) 374 if len(self) != len(other): 375 return False 376 try: 377 for element in self: 378 if element == other[element.index]: 379 if abs(element.mu - other[element.index].mu) > 1e-10: 380 return False 381 else: 382 return False 383 except KeyError: 384 return False 385 return True
386
387 - def __ne__(self, other):
388 """\ 389 Compare two fuzzy sets for inequality. 390 391 @param other: The other fuzzy set. 392 @type other: L{FuzzySet} 393 @return: True if not equal, false otherwise. 394 @rtype: C{bool} 395 """ 396 return not self == other
397
398 - def isdisjoint(self, other):
399 """\ 400 Report whether two fuzzy sets have a null intersection. 401 402 @param other: The other fuzzy set. 403 @type other: L{FuzzySet} 404 @return: True if null intersection. 405 @rtype: C{bool} 406 """ 407 for element in self: 408 if element in other: 409 return False 410 return True
411
412 - def issubset(self, other):
413 """\ 414 Report whether another fuzzy set contains this fuzzy set. 415 416 @param other: The other fuzzy set. 417 @type other: L{FuzzySet} 418 @return: True if a subset, false otherwise. 419 @rtype: C{bool} 420 """ 421 self._binary_sanity_check(other) 422 if len(self) > len(other): 423 return False 424 try: 425 for element in self: 426 if element.mu > other[element.index].mu: 427 return False 428 except KeyError: 429 return False 430 return True
431
432 - def issuperset(self, other):
433 """\ 434 Report whether this fuzzy set contains another fuzzy set. 435 436 @param other: The other fuzzy set. 437 @type other: L{FuzzySet} 438 @return: True if a superset, false otherwise. 439 @rtype: C{bool} 440 """ 441 self._binary_sanity_check(other) 442 if len(self) < len(other): 443 return False 444 try: 445 for element in other: 446 if element.mu > self[element.index].mu: 447 return False 448 except KeyError: 449 return False 450 return True
451 452 __le__ = issubset 453 __ge__ = issuperset 454
455 - def __lt__(self, other):
456 """\ 457 Report whether another fuzzy set strictly contains this fuzzy set, 458 459 @param other: The other fuzzy set. 460 @type other: L{FuzzySet} 461 @return: True if a strict subset, false otherwise. 462 @rtype: C{bool} 463 """ 464 return self.issubset(other) and self != other
465
466 - def __gt__(self, other):
467 """\ 468 Report whether this fuzzy set strictly contains another fuzzy set. 469 470 @param other: The other fuzzy set. 471 @type other: L{FuzzySet} 472 @return: True if a strict superset, false otherwise. 473 @rtype: C{bool} 474 """ 475 return self.issuperset(other) and self != other
476
477 - def overlap(self, other):
478 """\ 479 Return the degree of overlap of this fuzzy set on another fuzzy set. 480 481 @param other: The other fuzzy set. 482 @type other: L{FuzzySet} 483 @return: The overlap in [0, 1] of this set on the other. 484 @rtype: C{float} 485 """ 486 try: 487 return self.intersection(other).cardinality / other.cardinality 488 except ZeroDivisionError: 489 return 0.0
490 491 @staticmethod
492 - def _binary_sanity_check(other):
493 """\ 494 Check that the other argument to a binary operation is also a fuzzy 495 set, raising a TypeError otherwise. 496 497 @param other: The other argument. 498 @type other: L{FuzzySet} 499 """ 500 if not isinstance(other, FuzzySet): 501 raise TypeError('operation only permitted between fuzzy sets')
502 503 # Unary fuzzy set operations 504
505 - def complement(self, comp=0, **kwargs):
506 """\ 507 Return the complement of this fuzzy set. 508 509 @param comp: The complement type (optional). 510 @type comp: C{int} 511 @return: The complement of this fuzzy set. 512 @rtype: L{FuzzySet} 513 """ 514 if not comp in range(2): 515 raise ValueError('invalid complement type') 516 result = self.__class__() 517 [lambda: result.update([FuzzyElement(key, 1 - self.mu(key)) \ 518 for key in self.keys()]), 519 lambda: result.update([FuzzyElement(key, (1 - self.mu(key) \ 520 ** kwargs['w']) ** (1.0 / kwargs['w'])) for key in self.keys()]) 521 ][comp]() 522 return result
523
524 - def alpha(self, alpha):
525 """\ 526 Alpha cut function. Returns the crisp set of members whose membership 527 degrees meet or exceed the alpha value. 528 529 @param alpha: The alpha value for the cut in (0, 1]. 530 @type alpha: C{float} 531 @return: The crisp set result of the alpha cut. 532 @rtype: C{set} 533 """ 534 return set([element.index for element in self if element.mu >= alpha])
535
536 - def salpha(self, alpha):
537 """\ 538 Strong alpha cut function. Returns the crisp set of members whose 539 membership degrees exceed the alpha value. 540 541 @param alpha: The alpha value for the cut in [0, 1]. 542 @type alpha: C{float} 543 @return: The crisp set result of the strong alpha cut. 544 @rtype: C{set} 545 """ 546 return set([element.index for element in self if element.mu > alpha])
547
548 - def prune(self):
549 """\ 550 Prune the fuzzy set of all elements with zero membership. 551 """ 552 prune = [element.index for element in IndexedSet.__iter__(self) \ 553 if element.mu == 0] 554 for key in prune: 555 self.remove(key)
556
557 - def normalize(self):
558 """\ 559 Normalize the fuzzy set by scaling all membership degrees by a factor 560 such that the height equals 1. 561 """ 562 if self.height > 0: 563 scale = 1.0 / self.height 564 for element in self: 565 element.mu *= scale
566 567 @property
568 - def normal(self):
569 """\ 570 Returns whether the fuzzy set is normal (height = 1). 571 572 @rtype: C{bool} 573 """ 574 return self.height == 1.0
575