15
15
import contextlib
16
16
import inspect
17
17
import sys
18
+ from collections .abc import Callable
18
19
from itertools import permutations
19
20
from random import choice , randint
20
21
from statistics import median
22
+ from typing import Any
21
23
22
24
import numpy as np
23
25
36
38
triangular ,
37
39
)
38
40
41
+ type MembershipSetup = Callable [[Any ], Membership ]
42
+
39
43
np .seterr (all = "raise" )
40
44
functions = [step , rectangular ]
41
45
@@ -61,7 +65,7 @@ def normalize(target: Array, output_length: int = 16) -> Array:
61
65
return normalized_array
62
66
63
67
64
- def guess_function (target : Array ) -> Membership :
68
+ def guess_function (target : Array ) -> MembershipSetup :
65
69
normalized = normalize (target )
66
70
return constant if np .all (normalized == 1 ) else singleton
67
71
@@ -77,11 +81,11 @@ def fitness(func: Membership, target: Array, certainty: int | None = None) -> fl
77
81
return result if certainty is None else round (result , certainty )
78
82
79
83
80
- def seed_population (func : Membership , target : Array ) -> dict [tuple , float ]:
84
+ def seed_population (func : MembershipSetup , target : Array ) -> dict [tuple [ float , ...] , float ]:
81
85
# create a random population of parameters
82
86
params = [p for p in inspect .signature (func ).parameters .values () if p .kind == p .POSITIONAL_OR_KEYWORD ]
83
- seed_population = {}
84
- seed_numbers = [
87
+ seed_population : dict [ tuple [ float , ...], float ] = {}
88
+ seed_numbers : list [ float ] = [
85
89
sys .float_info .min ,
86
90
sys .float_info .max ,
87
91
0 ,
@@ -91,7 +95,7 @@ def seed_population(func: Membership, target: Array) -> dict[tuple, float]:
91
95
- 0.5 ,
92
96
min (target ),
93
97
max (target ),
94
- np .argmax (target ),
98
+ float ( np .argmax (target ) ),
95
99
]
96
100
# seed population
97
101
for combination in permutations (seed_numbers , len (params )):
@@ -101,35 +105,41 @@ def seed_population(func: Membership, target: Array) -> dict[tuple, float]:
101
105
return seed_population
102
106
103
107
104
- def reproduce (parent1 : tuple , parent2 : tuple ) -> tuple :
105
- child = []
108
+ def reproduce (parent1 : tuple [ float , ...], parent2 : tuple [ float , ...] ) -> tuple [ float , ...] :
109
+ child : list [ float ] = []
106
110
for p1 , p2 in zip (parent1 , parent2 ):
107
111
# mix the parts of the floats by randomness within the range of the parents
108
112
# adding a random jitter should avoid issues when p1 == p2
109
113
a1 , a2 = np .frexp (p1 )
110
114
b1 , b2 = np .frexp (p2 )
115
+ a1 = float (a1 )
116
+ b1 = float (b1 )
117
+ a2 = float (a2 )
118
+ b2 = float (b2 )
111
119
a1 += randint (- 1 , 1 )
112
120
a2 += randint (- 1 , 1 )
113
121
b1 += randint (- 1 , 1 )
114
122
b2 += randint (- 1 , 1 )
115
- child .append (((a1 + b1 ) / 2 ) * 2 ** np .random .uniform (a2 , b2 ))
123
+ child .append (float ((a1 + b1 ) / 2 ) * 2 ** np .random .uniform (a2 , b2 ))
116
124
return tuple (child )
117
125
118
126
119
127
def guess_parameters (
120
- func : Membership , target : Array , precision : int | None = None , certainty : int | None = None
121
- ) -> tuple :
122
- """Find the best fitting parameters for a function, targetting an array.
128
+ func : MembershipSetup , target : Array , precision : int | None = None , certainty : int | None = None
129
+ ) -> tuple [ float , ...] :
130
+ """Find the best fitting parameters for a function, targeting an array.
123
131
124
132
Args:
125
- func (Callable): A possibly matching membership function, such as `fuzzylogic.functions.triangular`.
126
- array (np.ndarray): The target array to fit the function to.
133
+ func (MembershipSetup): A possibly matching membership function.
134
+ target (Array): The target array to fit the function to.
135
+ precision (int | None): The precision of the parameters.
136
+ certainty (int | None): The certainty of the fitness score.
127
137
128
138
Returns:
129
- tuple: The best fitting parameters for the function.
139
+ tuple[float, ...] : The best fitting parameters for the function.
130
140
"""
131
141
132
- def best () -> tuple :
142
+ def best () -> tuple [ float , ...] :
133
143
return sorted (population .items (), key = lambda x : x [1 ])[0 ][0 ]
134
144
135
145
seed_pop = seed_population (func , target )
@@ -141,9 +151,9 @@ def best() -> tuple:
141
151
last_pop = {}
142
152
for generation in range (12 ):
143
153
# sort the population by fitness
144
- pop : list [tuple [tuple , float ]] = sorted (population . items (), key = lambda x : x [ 1 ], reverse = True )[
145
- : pop_size
146
- ]
154
+ pop : list [tuple [tuple [ float , ...], float ]] = sorted (
155
+ population . items (), key = lambda x : x [ 1 ], reverse = True
156
+ )[: pop_size ]
147
157
if not pop :
148
158
population = last_pop
149
159
return best ()
@@ -153,7 +163,7 @@ def best() -> tuple:
153
163
print ("Lucky!" )
154
164
return best ()
155
165
# the next generation
156
- new_population = {}
166
+ new_population : dict [ tuple [ float , ...], float ] = {}
157
167
killed = 0
158
168
for parent1 in pop :
159
169
while True :
@@ -195,14 +205,14 @@ def best() -> tuple:
195
205
pressure **= 0.999
196
206
population |= seed_pop
197
207
else :
198
- pressure = median ([x [1 ] for x in population .items ()])
208
+ pressure : float = median ([x [1 ] for x in population .items ()])
199
209
return best ()
200
210
201
211
202
- def shave (target : Array , components : dict [Membership , tuple ]) -> Array :
212
+ def shave (target : Array , components : dict [Membership , tuple [ float , ...] ]) -> Array :
203
213
"""Remove the membership functions from the target array."""
204
- result = np .zeros_like (target )
214
+ result : Array = np .zeros_like (target , dtype = float )
205
215
for func , params in components .items ():
206
216
f = func (* params )
207
- result += np .fromiter ([ f (x ) for x in np .arange (* target .shape )], float )
208
- return target - result
217
+ result += np .fromiter (( f (x ) for x in np .arange (* target .shape )), dtype = float ) # type: ignore
218
+ return np . asarray ( target - result , dtype = target . dtype ) # type: ignore
0 commit comments