Skip to content

Commit c6d0026

Browse files
committed
more testing and cleanup
1 parent 349a08c commit c6d0026

File tree

8 files changed

+98
-121
lines changed

8 files changed

+98
-121
lines changed

Showcase.ipynb

Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -13,102 +13,91 @@
1313
]
1414
},
1515
{
16-
"cell_type": "code",
17-
"execution_count": 9,
18-
"metadata": {
19-
"collapsed": false
20-
},
21-
"outputs": [
22-
{
23-
"data": {
24-
"text/plain": [
25-
"1"
26-
]
27-
},
28-
"execution_count": 9,
29-
"metadata": {},
30-
"output_type": "execute_result"
31-
}
32-
],
16+
"cell_type": "markdown",
17+
"metadata": {},
3318
"source": [
34-
"from fuzzy.functions import alpha, singleton, constant\n",
35-
"\n",
19+
"## Usage\n",
3620
"\n",
37-
"a = alpha(0.2, 0.8)\n",
38-
"s = singleton(5)\n",
39-
"\n"
21+
"1. Define a Domain and specify the measurable range & resolution.\n",
22+
"2. Define some fuzzysets (\"adjectives\") as attributes that describe ranges within\n",
23+
"the domain using Set(). Each set is defined by its membership function func(REAL) -> [0,1]\n",
24+
"3. You can combine fuzzysets of the same domain using fuzzy logic operators (~, &, |, +, *) to\n",
25+
"define derived fuzzysets.\n",
26+
"4. Now suppose you have a value x. Call the fuzzyset(x) directly to produce a\n",
27+
"membership value or call the domain with it to get a dictionary of memberships.\n",
28+
"5. To really put these to work, you will want to map membership values of one or\n",
29+
"more domains onto another domain, producing \"real world\" values again that can\n",
30+
"be used to control things. This is called inference (combining domains) and\n",
31+
"defuzzification (turning membership values back into measurables)."
4032
]
4133
},
4234
{
4335
"cell_type": "code",
44-
"execution_count": 3,
36+
"execution_count": null,
4537
"metadata": {
4638
"collapsed": false
4739
},
48-
"outputs": [
49-
{
50-
"ename": "TypeError",
51-
"evalue": "'>=' not supported between instances of 'function' and 'float'",
52-
"output_type": "error",
53-
"traceback": [
54-
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
55-
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
56-
"\u001b[0;32m<ipython-input-3-7ecf006a5990>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mnumbers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mDomain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"numbers\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlow\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhigh\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mclose_to_10\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mSet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malpha\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0.8\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtriangular\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m20\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mclose_to_10\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
57-
"\u001b[0;32m/media/sf_Google_Drive/jupyter/fuzzy/fuzzy/functions.py\u001b[0m in \u001b[0;36mf_value\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 90\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mf_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 92\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0mceiling\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 93\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mceiling\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m<=\u001b[0m \u001b[0mfloor\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
58-
"\u001b[0;31mTypeError\u001b[0m: '>=' not supported between instances of 'function' and 'float'"
59-
]
60-
}
61-
],
40+
"outputs": [],
6241
"source": [
42+
"%matplotlib inline\n",
6343
"from fuzzy.classes import Domain, Set\n",
6444
"from fuzzy.functions import alpha, triangular\n",
45+
"from fuzzy.hedges import plus, minus\n",
6546
"\n",
66-
"numbers = Domain(\"numbers\", low=0, high=20)\n",
67-
"close_to_10 = Set(numbers, alpha(0.2,0.8)(triangular(0, 20)))\n",
68-
"close_to_10.plot()"
47+
"numbers = Domain(\"numbers\", low=0, high=20, res=0.1)\n",
48+
"close_to_10 = Set(numbers, alpha(0.2, 0.8, \n",
49+
" triangular(0, 20)\n",
50+
" ))\n",
51+
"close_to_5 = Set(numbers, triangular(1,10))\n",
52+
"foo = plus(minus(close_to_5))\n",
53+
"bar = close_to_10\n",
54+
"bar.plot()\n",
55+
"foo.plot()\n",
56+
"baz = foo + bar\n",
57+
"baz.plot()"
6958
]
7059
},
7160
{
72-
"cell_type": "code",
73-
"execution_count": null,
74-
"metadata": {
75-
"collapsed": false
76-
},
77-
"outputs": [],
61+
"cell_type": "markdown",
62+
"metadata": {},
7863
"source": [
79-
"%matplotlib inline\n",
80-
"from fuzzy.classes import Domain, Set, Rule\n",
81-
"from fuzzy.functions import S, R\n",
64+
"## Rules\n",
8265
"\n",
83-
"zeit = Domain(\"time\", low=0, high=24, res=0.1)\n",
84-
"morgen = Set(zeit, S(0, 8))\n",
85-
"abend = Set(zeit, R(18, 24))\n",
86-
"mittag = ~morgen & ~abend\n",
87-
"\n",
88-
"morgen.plot()\n",
89-
"abend.plot()\n",
90-
"mittag.plot()\n"
66+
"Rules aka Fuzzy Inference is done when domains and sets are defined to combine sets of different domains. "
9167
]
9268
},
9369
{
9470
"cell_type": "code",
95-
"execution_count": null,
71+
"execution_count": 7,
9672
"metadata": {
9773
"collapsed": false
9874
},
99-
"outputs": [],
75+
"outputs": [
76+
{
77+
"ename": "AttributeError",
78+
"evalue": "'NotImplementedType' object has no attribute 'copy'",
79+
"output_type": "error",
80+
"traceback": [
81+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
82+
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
83+
"\u001b[0;32m<ipython-input-7-4a94e0f36941>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0md1\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtemp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m32\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# {'temperature.hot': 1}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0md2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# {'distance.close': 0}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0md1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# need to merge the results of the Domains\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0md2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# for py3.5: https://www.python.org/dev/peps/pep-0448/\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# min(1, 0)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
84+
"\u001b[0;31mAttributeError\u001b[0m: 'NotImplementedType' object has no attribute 'copy'"
85+
]
86+
}
87+
],
10088
"source": [
10189
"from fuzzy.classes import Domain, Set, Rule\n",
102-
"from fuzzy.functions import S, R\n",
103-
"\n",
104-
"dist = Domain(\"distance\", low=0, high=1000, res=0.1)\n",
105-
"near = Set(dist, S(0, 100))\n",
106-
"\n",
107-
"standing = Domain(\"standing\", low=0, high=100)\n",
108-
"friendly = Set(standing, R(0, 100))\n",
109-
"\n",
110-
"reaction = Domain(\"reaction\", low=0, high=10)\n",
111-
"aggro = IF(near & ~friendly, reaction)"
90+
" \n",
91+
"temp = Domain(\"temperature\", 0, 100)\n",
92+
"temp.hot = Set(temp, lambda x: 1)\n",
93+
"dist = Domain(\"distance\", 0, 300)\n",
94+
"dist.close = Set(dist, lambda x: 0)\n",
95+
"r = Rule(min, [\"distance.close\", \"temperature.hot\"])\n",
96+
"d1 = temp(32) # {'temperature.hot': 1}\n",
97+
"d2 = dist(5) # {'distance.close': 0}\n",
98+
"d = d1.copy() # need to merge the results of the Domains\n",
99+
"d.update(d2) # for py3.5: https://www.python.org/dev/peps/pep-0448/\n",
100+
"r(d) # min(1, 0)"
112101
]
113102
},
114103
{

TODO-fuzzy

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
do: build a testcase suite
1+
do: build a #testcase suite
22
---
3-
do: review functions
3+
do: MUST review #functions
44
---
5-
do: Set.plot should always plot between 0 and 1
5+
do: #Set.plot SHOULD always plot between 0 and 1
6+
---
7+
do: #Set.plot SHOULD always plot the full set range
8+
---
9+
do: MUST fix #Domain._sets (should be either set OR dict)
10+
until: 2017-3-1
11+
---
12+
do: check tests
13+
every: day
14+
---
15+
do: SHOULD find examples for truth functions

fuzzy/classes.py

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,9 @@
11

2-
"""
3-
Fuzzy Logic for Python 3.
4-
5-
Usage:
6-
1) Define a Domain and specify the measurable range & resolution.
7-
2) Define some fuzzysets ("adjectives") as attributes that describe ranges within
8-
the domain using Set().
9-
3) You can combine fuzzysets using fuzzy logic operators (~, &, |, +, *) to
10-
define derived fuzzysets.
11-
4) Now suppose you have a value x. Call the fuzzyset(x) directly to produce a
12-
membership value or
13-
call the domain with it to get a dictionary of memberships.
14-
5) To really put these to work, you will want to map membership values of one or
15-
more domains onto another domain, producing "real world" values again that can
16-
be used to control things. This is called inference (combining domains) and
17-
defuzzification (turning membership values back into measurables).
18-
"""
19-
202
import matplotlib.pyplot as plt
213
from numpy import arange
224

23-
from functions import inv
24-
from combinators import MAX, MIN, product, bounded_sum
5+
from fuzzy.functions import inv
6+
from fuzzy.combinators import MAX, MIN, product, bounded_sum
257

268

279

@@ -125,44 +107,43 @@ class Set:
125107
126108
Sets that are returned from one of the operations are 'derived sets' or
127109
'Superfuzzysets' according to Zadeh.
110+
111+
Note that most checks are merely assertions that can be optimized away.
112+
DO NOT RELY on these checks and use tests to make sure that only valid calls are made.
128113
"""
129114
def __init__(self, domain:Domain, func:callable, ops:dict=None):
130115
self.func = func
131116
assert self.func is not None
132117
self.ops = ops
133-
if not isinstance(domain, Domain):
134-
raise AttributeError
118+
assert isinstance(domain, Domain), "Must be used with a Domain."
135119
self.domain = domain
136120
self.domain._sets.add(self)
137121

138122
def __call__(self, x):
123+
assert self.domain.low <= x <= self.domain.high, "value outside domain"
139124
return self.func(x)
140125

141126
def __invert__(self):
142127
return Set(self.domain, inv(self.func))
143128

144129
def __and__(self, other):
145-
if self.domain is not other.domain:
146-
raise UserWarning
130+
assert self.domain is other.domain, "Cannot combine sets of different domains."
147131
return Set(self.domain, MIN(self.func, other.func))
148132

149133
def __or__(self, other):
150-
if self.domain is not other.domain:
151-
raise UserWarning
134+
assert self.domain is other.domain, "Cannot combine sets of different domains."
152135
return Set(self.domain, MAX(self.func, other.func))
153136

154137
def __mul__(self, other):
155-
if self.domain is not other.domain:
156-
raise UserWarning
138+
assert self.domain is other.domain, "Cannot combine sets of different domains."
157139
return Set(self.domain, product(self.func, other.func))
158140

159-
def __sum__(self, other):
160-
if self.domain is not other.domain:
161-
raise UserWarning
141+
def __add__(self, other):
142+
assert self.domain is other.domain, "Cannot combine sets of different domains."
162143
return Set(self.domain, bounded_sum(self.func, other.func))
163144

164145
def __pow__(self, power):
165-
"""pow is used with lingual hedges as defined in fuzzy.functions"""
146+
"""pow is used with hedges"""
166147
return Set(self.domain, lambda x: pow(self.func(x), power))
167148

168149
def plot(self, low=None, high=None, res=None):
@@ -175,8 +156,6 @@ def plot(self, low=None, high=None, res=None):
175156
res = self.domain.res if res is None else res
176157
R = arange(low, high, res)
177158
V = [self.func(x) for x in R]
178-
#print(R)
179-
#print(V)
180159
plt.plot(R, V)
181160

182161

fuzzy/functions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,8 @@ def bounded_sigmoid(low, high):
399399
>>> round(f(-100000), 2)
400400
0.0
401401
"""
402-
if high < low:
403-
raise ValueError('high must not be less than low.')
402+
assert low < high, 'high must not be less than low'
403+
404404
k = -(4. * log(3)) / (low - high)
405405
o = 9 * exp(low * k)
406406

fuzzy/hedges.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
These work with sets only. Meta-functionality is deprecated.
88
"""
99

10-
from classes import Set
10+
from fuzzy.classes import Set
1111
from warnings import warn
1212

1313
def very(g):

fuzzy/truth.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
TRUTH QUALIFICATION
55
-------------------
66
Functions that transform a given membership value to a truth value.
7+
8+
How this can be useful? Well..
79
"""
810

911

test_fuzzy_functionality.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
"""
23
Functional test of the fuzzylogic lib 'fuzzy'.
34
"""
@@ -35,4 +36,4 @@ def test_temp(self):
3536

3637

3738
if __name__ == '__main__':
38-
unittest.main()
39+
unittest.main()

test_fuzzy_units.py

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

22
from hypothesis import given, strategies as st, assume
3+
from math import isclose
34

45
from fuzzy import functions as fun
5-
6-
6+
77
@given(st.floats(allow_nan=False, allow_infinity=False))
88
def test_noop(x):
99
f = fun.noop()
1010
assert f(x) == x
1111

1212
@given(st.floats(allow_nan=False, allow_infinity=False))
13-
def test_invert(x):
14-
f = fun.inv()
15-
assert f(x) == 1 - x
16-
17-
n = fun.noop()
18-
f = fun.inv(n)
19-
assert f(x) == 1 - x
13+
def test_inv(x):
14+
assume(0 <= x <= 1)
15+
f = fun.inv(fun.noop())
16+
assert isclose(f(f(x)), x, abs_tol=1e-16)
2017

2118
@given(st.floats(allow_nan=False, allow_infinity=False),
2219
st.floats(allow_nan=False, allow_infinity=False))
@@ -30,8 +27,7 @@ def test_constant(c, r):
3027
st.floats(allow_nan=False))
3128
def test_alpha(lower, upper, x):
3229
assume(lower < upper)
33-
34-
f = fun.alpha(lower, upper)
30+
f = fun.alpha(lower, upper, fun.noop())
3531
if x <= lower:
3632
assert f(x) == lower
3733
elif x >= upper:

0 commit comments

Comments
 (0)