Skip to content

Commit f7f9db5

Browse files
committed
added set ops
1 parent 042b6fd commit f7f9db5

File tree

4 files changed

+183
-39
lines changed

4 files changed

+183
-39
lines changed

Showcase.ipynb

Lines changed: 104 additions & 29 deletions
Large diffs are not rendered by default.

fuzzy/classes.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11

22
import matplotlib.pyplot as plt
3-
from numpy import arange, fromiter, array_equal
3+
from numpy import arange, fromiter, array_equal, less_equal, greater_equal, less, greater
4+
import numpy as np
45
from logging import warn
56
import pickle
67

7-
from fuzzy.functions import inv
8+
from fuzzy.functions import inv, normalize
89
from fuzzy.combinators import MAX, MIN, product, bounded_sum
910

1011
class FuzzyWarning(UserWarning):
@@ -179,7 +180,7 @@ def __pow__(self, power):
179180
return Set(lambda x: pow(self.func(x), power))
180181

181182
def __eq__(self, other):
182-
"""A set is equal with another if it returns exactly the same values."""
183+
"""A set is equal with another if both return the same values over the same range."""
183184
if self.domain is None or other.domain is None:
184185
# It would require complete AST analysis to check whether both Sets
185186
# represent the same recursive functions -
@@ -190,7 +191,37 @@ def __eq__(self, other):
190191
# however, if domains ARE assigned (whether or not it's the same domain),
191192
# we simply can check if they map to the same values
192193
return array_equal(self.array(), other.array())
194+
195+
def __le__(self, other):
196+
"""If this is less than other, it means this is a subset of the other."""
197+
if self.domain is None or other.domain is None:
198+
raise FuzzyWarning("Can't compare without Domains.")
199+
return all(less_equal(self.array(), other.array()))
200+
201+
def __lt__(self, other):
202+
"""If this is less than other, it means this is a subset of the other."""
203+
if self.domain is None or other.domain is None:
204+
raise FuzzyWarning("Can't compare without Domains.")
205+
return all(less(self.array(), other.array()))
206+
207+
def __ge__(self, other):
208+
"""If this is greater than other, it means this is a superset of the other."""
209+
if self.domain is None or other.domain is None:
210+
raise FuzzyWarning("Can't compare without Domains.")
211+
return all(greater_equal(self.array(), other.array()))
193212

213+
def __gt__(self, other):
214+
"""If this is less than other, it means this is a subset of the other."""
215+
if self.domain is None or other.domain is None:
216+
raise FuzzyWarning("Can't compare without Domains.")
217+
return all(greater(self.array(), other.array()))
218+
219+
def __len__(self):
220+
if self.domain is None:
221+
raise FuzzyWarning("No domain, can't determine length.")
222+
return len(self.array())
223+
224+
194225
def plot(self, low=None, high=None, res=None):
195226
"""Graph the set.
196227
Use the bounds and resolution of the domain to display the set
@@ -219,7 +250,9 @@ def __repr__(self):
219250
Return a string representation of the Set that reconstructs the set with eval().
220251
221252
*******
222-
this is harder than expected since all functions are (recursive!) closures which
253+
Current implementation does NOT work correctly.
254+
255+
This is harder than expected since all functions are (recursive!) closures which
223256
can't simply be pickled. If this functionality really is needed, all functions
224257
would have to be peppered with closure-returning overhead such as
225258
@@ -232,15 +265,21 @@ def create_function_closure():
232265
func = types.FunctionType(*args[:-1] + [closure])
233266
return func
234267
"""
235-
return NotImplemented
236-
#return f"Set({}, domain={self.domain}, name={self.name})"
268+
return f"Set({self.func}, domain={self.domain}, name={self.name})"
237269

238270
def __str__(self):
239271
if self.name is None and self.domain is None:
240272
return f"dangling Set({self.func})"
241273
else:
242274
return f"{self.domain}.{self.name}"
243-
275+
276+
def normalized(self):
277+
if self.domain is None:
278+
raise FuzzyWarning("Can't normalize without domain.")
279+
else:
280+
return Set(normalize(max(self.array()), self.func),
281+
domain=self.domain,
282+
name=f"normalized_{self.name}")
244283

245284
class Rule:
246285
"""

fuzzy/functions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
2323
The intervals with non-zero m are called 'support', short s_m
2424
The intervals with m == 1 are called 'core', short c_m
25+
The intervals with max(m) are called "height"
2526
The intervals m != 1 and m != 0 are called 'boundary'.
2627
The intervals with m == 0 are called 'unsupported', short no_m
2728
@@ -83,6 +84,13 @@ def f(x):
8384
return func(x)
8485
return f
8586

87+
def normalize(height, func):
88+
assert 0 < height <= 1
89+
90+
def f(x):
91+
return func(x) / height
92+
return f
93+
8694
########################
8795
# MEMBERSHIP FUNCTIONS #
8896
########################

test_fuzzy_units.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ def test_constant(self, x, c):
2929

3030
@given(st.floats(allow_nan=False),
3131
st.floats(min_value=0, max_value=1),
32-
st.floats(min_value=0, max_value=1),
33-
)
32+
st.floats(min_value=0, max_value=1))
3433
def test_alpha(self, x, lower, upper):
3534
assume(lower < upper)
3635
f = fun.alpha(lower, upper, fun.noop())
@@ -40,6 +39,13 @@ def test_alpha(self, x, lower, upper):
4039
assert f(x) == upper
4140
else:
4241
assert f(x) == x
42+
43+
@given(st.floats(allow_nan=False),
44+
st.floats(min_value=0, max_value=1))
45+
def test_normalize(self, x, height):
46+
assume(0 < height)
47+
f = fun.normalize(height, fun.alpha(0, height, fun.R(0,100)))
48+
assert (0 <= f(x) <= 1)
4349

4450
@given(st.floats(),
4551
st.floats(),
@@ -291,4 +297,20 @@ def test_eq(self, low, high, res):
291297
D1.s1 = Set(fun.bounded_linear(low, high))
292298
D2 = Domain("2", low, high, res=res)
293299
D2.s2 = Set(fun.bounded_linear(low, high))
294-
assert(D1.s1 == D2.s2)
300+
assert(D1.s1 == D2.s2)
301+
302+
def test_normalized(self):
303+
D = Domain("d", 0, 10, res=0.1)
304+
D.s = Set(fun.bounded_linear(3, 12))
305+
x = D.s.normalized()
306+
y = x.normalized()
307+
308+
assert (set(D._sets.keys()) == set(["s", "normalized_s", "normalized_normalized_s"]))
309+
assert x == y
310+
311+
def test_sub_super_set(self):
312+
D = Domain("d", 0, 10, res=0.1)
313+
D.s = Set(fun.bounded_linear(3, 12))
314+
x = D.s.normalized()
315+
assert (x >= D.s)
316+
assert (D.s <= x)

0 commit comments

Comments
 (0)