11
22from 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
429def 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
0 commit comments