Skip to content

Commit 4124e52

Browse files
committed
fixed rule.scale
1 parent cf22ef7 commit 4124e52

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

fuzzy/rules.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,73 @@
11

22
from math import isinf
3+
from fuzzy.classes import Domain, Set
4+
5+
def round_partial(value, res):
6+
"""
7+
>>> round_partial(0.405, 0.02)
8+
0.4
9+
>>> round_partial(0.412, 0.02)
10+
0.42
11+
>>> round_partial(1.38, 0.25)
12+
1.5
13+
>>> round_partial(1.12, 0.25)
14+
1.0
15+
>>> round_partial(9.24, 0.25)
16+
9.25
17+
>>> round_partial(7.76, 0.25)
18+
7.75
19+
>>> round_partial(987654321, 100)
20+
987654300
21+
>>> round_partial(3.14, 0)
22+
3.14
23+
"""
24+
# backed up by wolframalpha
25+
if res == 0 or isinf(res):
26+
return value
27+
return round(value / res) * res
328

429
def scale(OUT_min, OUT_max, *, IN_min=0, IN_max=1):
530
"""Scale from one domain to another.
631
7-
Works best for [0,1] -> R. For R -> R additional testing is required.
32+
Works best for [0,1] -> R.
33+
34+
For R -> R additional testing is required,
35+
but it should work in general out of the box.
36+
37+
Originally used the naive algo from SO
38+
(OUT_max - OUT_min)*(x - IN_min) / (IN_max - IN_min) + OUT_min
39+
but there are too many edge cases thanks to over/underflows.
40+
Current factorized algo was proposed as equivalent by wolframalpha,
41+
which seems more stable.
842
"""
943
assert IN_min < IN_max
10-
# this is not arbitrary. lim x -> inf x*0+x -> inf
11-
if isinf(OUT_max - OUT_min):
12-
return lambda x: OUT_max
13-
44+
1445
def f(x):
15-
return (OUT_max - OUT_min)*(x - IN_min) / (IN_max - IN_min) + OUT_min
46+
if x == IN_min:
47+
return OUT_min
48+
elif x == IN_max:
49+
return OUT_max
50+
return (OUT_min * IN_min - OUT_min * x -
51+
2 * OUT_max * IN_min + OUT_max * IN_max + IN_max * x) / (
52+
IN_max - IN_min)
53+
54+
return f
55+
56+
57+
def weighted_sum(*, weights:dict, target:Domain) -> float:
58+
"""Ordinarily used for weighted decision trees and such.
59+
Parametrize with dict of factorname -> weight and domain of results.
60+
Call with a dict of factorname -> [0, 1]
61+
62+
There SHOULD be the same number of items (with the same names!)
63+
of weights and factors, but it doesn't have to be - however
64+
set(factors.names) <= set(weights.names) - in other words:
65+
there MUST be at least as many items in weights as factors.
66+
"""
67+
assert sum(w for w in weights.values()) == 1
68+
S = scale(target.low, target.high)
69+
70+
def f(factors):
71+
RES = sum(r * weights[n] for n, r in factors.items())
72+
return S(round_partial(RES, target.res))
1673
return f

test_fuzzy_units.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,9 @@ def test_scaling(self, x, IN_min, IN_max, OUT_min, OUT_max):
358358
assume(IN_min <= x <= IN_max)
359359
assume(OUT_min < OUT_max)
360360
f = ru.scale(OUT_min, OUT_max, IN_min=IN_min, IN_max=IN_max)
361-
assert (OUT_min <= f(x) <= OUT_max)
361+
assert (OUT_min <= f(x) <= OUT_max)
362+
363+
@given(st.floats(allow_nan=False),
364+
st.floats(allow_nan=False))
365+
def round_partial(self, x, res):
366+
assert(isclose(x, ru.round_partial(x, res), res=res))

0 commit comments

Comments
 (0)