|
@@ -8,10 +8,30 @@ import numpy
|
8
|
8
|
|
9
|
9
|
import pyrpn
|
10
|
10
|
|
|
11
|
+class RpnSym(object):
|
|
12
|
+ """ @brief Abstract class representing RpnExpr symbols """
|
|
13
|
+
|
|
14
|
+ def __init__(self):
|
|
15
|
+ raise NotImplementedError('Abstract class')
|
|
16
|
+
|
|
17
|
+ def __sub__(self, b):
|
|
18
|
+ """ @return the difference between two symbols : 2 if types differs,
|
|
19
|
+ 1 if same type but different value
|
|
20
|
+ """
|
|
21
|
+ if type(b) != type(self):
|
|
22
|
+ return 2
|
|
23
|
+ elif str(b) != str(self):
|
|
24
|
+ return 1
|
|
25
|
+ return 0
|
11
|
26
|
|
12
|
|
-class RpnOp(object):
|
|
27
|
+class RpnOp(RpnSym):
|
|
28
|
+ """ @brief an RPN operation symbol """
|
13
|
29
|
|
14
|
30
|
def __init__(self, op=None):
|
|
31
|
+ """ @brief instanciate an RPN symbol
|
|
32
|
+ @param op if None a random op is choosen else a value from
|
|
33
|
+ pyrpn.get_ops().values() is expected
|
|
34
|
+ """
|
15
|
35
|
if op is None:
|
16
|
36
|
op = self.random()
|
17
|
37
|
elif op not in pyrpn.get_ops().values():
|
|
@@ -24,25 +44,25 @@ class RpnOp(object):
|
24
|
44
|
def __copy__(self):
|
25
|
45
|
return type(self)(self.__op)
|
26
|
46
|
|
27
|
|
- def __sub__(self, b):
|
28
|
|
- if type(b) != type(self):
|
29
|
|
- return 2
|
30
|
|
- elif str(b) != str(self):
|
31
|
|
- return 1
|
32
|
|
- return 0
|
33
|
|
-
|
34
|
47
|
def to_bytes(self):
|
|
48
|
+ """ @return a 9 bytes representation of the symbol """
|
35
|
49
|
brepr = self.__op.encode('utf8')
|
36
|
50
|
return bytes([1] + ([0]*(8-len(brepr)))) + brepr
|
37
|
51
|
|
38
|
52
|
@staticmethod
|
39
|
53
|
def random():
|
|
54
|
+ """ @return a random RpnOp instance """
|
40
|
55
|
return random.choice(list(pyrpn.get_ops().values()))
|
41
|
56
|
|
42
|
57
|
|
43
|
|
-class RpnConst(object):
|
|
58
|
+class RpnConst(RpnSym):
|
|
59
|
+ """ @brief an RPN numeric constant symbol """
|
44
|
60
|
|
45
|
61
|
def __init__(self, value=None):
|
|
62
|
+ """ @brief instanciate an RPN numeric constant symbol
|
|
63
|
+ @param value a 64bit unsigned integer (a random one is choosen
|
|
64
|
+ if None given)
|
|
65
|
+ """
|
46
|
66
|
if value is None:
|
47
|
67
|
value = random.randint(0, (1<<64)-1)
|
48
|
68
|
elif value < 0 or value > (1<<64)-1:
|
|
@@ -55,20 +75,23 @@ class RpnConst(object):
|
55
|
75
|
def __str__(self):
|
56
|
76
|
return '0x%X' % self.__val
|
57
|
77
|
|
58
|
|
- def __sub__(self, b):
|
59
|
|
- if type(b) != type(self):
|
60
|
|
- return 2
|
61
|
|
- elif str(b) != str(self):
|
62
|
|
- return 1
|
63
|
|
- return 0
|
64
|
|
-
|
65
|
78
|
def to_bytes(self):
|
|
79
|
+ """ @return a 9 bytes representation of the symbol """
|
66
|
80
|
return bytes([0]) + self.__val.to_bytes(8, 'little')
|
67
|
81
|
|
68
|
82
|
|
69
|
|
-class RpnVar(object):
|
|
83
|
+class RpnVar(RpnSym):
|
|
84
|
+ """ @brief an RPN variable symbol
|
|
85
|
+
|
|
86
|
+ Variables are indexed with integer, string representation are : A0, A1
|
|
87
|
+ etc.
|
|
88
|
+ """
|
70
|
89
|
|
71
|
90
|
def __init__(self, varnum=None, nvar=1):
|
|
91
|
+ """ @brief instanciate an RPN variable symbol
|
|
92
|
+ @param varnum the variable number (random one choose if None given)
|
|
93
|
+ @param nvar the maximum variable number
|
|
94
|
+ """
|
72
|
95
|
if varnum is None:
|
73
|
96
|
if nvar == 1:
|
74
|
97
|
varnum = 0
|
|
@@ -81,6 +104,7 @@ class RpnVar(object):
|
81
|
104
|
|
82
|
105
|
@property
|
83
|
106
|
def nvar(self):
|
|
107
|
+ """ @return the maximum variable number """
|
84
|
108
|
return self.__nvar
|
85
|
109
|
|
86
|
110
|
def __copy__(self):
|
|
@@ -89,21 +113,26 @@ class RpnVar(object):
|
89
|
113
|
def __str__(self):
|
90
|
114
|
return 'A%d' % self.__val
|
91
|
115
|
|
92
|
|
- def __sub__(self, b):
|
93
|
|
- if type(b) != type(self):
|
94
|
|
- return 2
|
95
|
|
- elif str(b) != str(self):
|
96
|
|
- return 1
|
97
|
|
- return 0
|
98
|
|
-
|
99
|
116
|
def to_bytes(self):
|
|
117
|
+ """ @return a 9 bytes representation of the symbol """
|
100
|
118
|
brepr = str(self).encode('utf8')
|
101
|
119
|
return bytes([1] + ([0]*(8-len(brepr)))) + brepr
|
102
|
120
|
|
103
|
121
|
|
104
|
122
|
class RpnExpr(object):
|
|
123
|
+ """ @brief Wrapper on pyrpnifs.RPNExpr
|
|
124
|
+
|
|
125
|
+ Allow using pyrpnifs.RPNExpr while keeping a high level representation
|
|
126
|
+ of the expression (using @ref RpnSym child classes).
|
|
127
|
+ """
|
105
|
128
|
|
106
|
129
|
def __init__(self, sz=0, w_op=4, w_const=1, w_var=2, nvar=1):
|
|
130
|
+ """ @param sz The expression size for random generation
|
|
131
|
+ @param w_op RpnOp weight on random generation
|
|
132
|
+ @param w_const RpnConst weight on random generation
|
|
133
|
+ @param w_var RpnVar weight on random generation
|
|
134
|
+ @param n_var the variable count
|
|
135
|
+ """
|
107
|
136
|
self.sz = 0
|
108
|
137
|
self._weight = (w_op, w_const, w_var)
|
109
|
138
|
choices = (RpnOp, RpnConst, lambda: RpnVar(nvar=nvar))
|
|
@@ -114,6 +143,9 @@ class RpnExpr(object):
|
114
|
143
|
self.refresh_expr()
|
115
|
144
|
|
116
|
145
|
def refresh_expr(self):
|
|
146
|
+ """ @brief Refresh the internal pyrpn.RPNExpr using the
|
|
147
|
+ @ref RpnSym representation
|
|
148
|
+ """
|
117
|
149
|
self.pyrpn = pyrpn.RPNExpr(str(self), self.__nvar)
|
118
|
150
|
|
119
|
151
|
def __str__(self):
|
|
@@ -130,16 +162,33 @@ class RpnExpr(object):
|
130
|
162
|
return ret
|
131
|
163
|
|
132
|
164
|
def to_bytes(self):
|
|
165
|
+ """ @return a bytes representation with 9 bytes for each symbols """
|
133
|
166
|
return b''.join([elt.to_bytes() for elt in self.expr])
|
134
|
167
|
|
135
|
168
|
def __sub__(self, b):
|
136
|
169
|
return self.levenshtein(b)
|
137
|
170
|
|
138
|
171
|
def eval(self, *args):
|
|
172
|
+ """ @brief Evaluate the expression with given arguments
|
|
173
|
+ @param args* an array of argument (with len = nvar)
|
|
174
|
+ """
|
139
|
175
|
return self.pyrpn.eval(*args)
|
140
|
176
|
|
141
|
177
|
def mutation(self, n=1, min_len=3, w_add=1.25, w_del=1, w_mut=2,
|
142
|
178
|
w_mut_soft=4, w_add_elt=(1,1,1), w_mut_elt=(1,1,1)):
|
|
179
|
+ """ @brief Expression mutation
|
|
180
|
+ @param n mutation count
|
|
181
|
+ @param min_len the minimum length for an expression
|
|
182
|
+ @param w_add weight of adding a symbol
|
|
183
|
+ @param w_del weight of deleting a symbol
|
|
184
|
+ @param w_mut weight of mutating a symbol
|
|
185
|
+ @param w_mut_soft weight of mutationg a symbol without type change
|
|
186
|
+ @param w_add_elt weights for each types (op, const, var) when
|
|
187
|
+ adding a symbol
|
|
188
|
+ @param w_mut_elt weight for each types (op, const, var) when
|
|
189
|
+ mutating a symbol
|
|
190
|
+ @note The internal pyrpn.RPNExpr is refreshed after mutations
|
|
191
|
+ """
|
143
|
192
|
for _ in range(n):
|
144
|
193
|
if len(self.expr) <= min_len:
|
145
|
194
|
self.add(*w_add_elt)
|
|
@@ -156,6 +205,7 @@ class RpnExpr(object):
|
156
|
205
|
pass
|
157
|
206
|
|
158
|
207
|
def add(self, w_op=1, w_const=1, w_var=1):
|
|
208
|
+ """ @brief Mutate by adding a symbol """
|
159
|
209
|
idx = random.randint(0,len(self.expr))
|
160
|
210
|
new = random.choices((RpnOp, RpnConst, RpnVar), self._weight)[0]
|
161
|
211
|
new = new(nvar=self.__nvar) if new == RpnVar else new()
|
|
@@ -163,11 +213,13 @@ class RpnExpr(object):
|
163
|
213
|
pass
|
164
|
214
|
|
165
|
215
|
def delete(self, *args):
|
|
216
|
+ """ @brief Mutate by deleting a symbol """
|
166
|
217
|
idx = random.randint(0, len(self.expr)-1)
|
167
|
218
|
self.expr.pop(idx)
|
168
|
219
|
pass
|
169
|
220
|
|
170
|
221
|
def mutate(self, w_op=1, w_const=1, w_var=1):
|
|
222
|
+ """ @brief Mutate changing a symbol into another """
|
171
|
223
|
idx = random.randint(0, len(self.expr)-1)
|
172
|
224
|
new = random.choices((RpnOp, RpnConst, RpnVar), self._weight)[0]
|
173
|
225
|
new = new(nvar=self.__nvar) if new == RpnVar else new()
|
|
@@ -175,6 +227,7 @@ class RpnExpr(object):
|
175
|
227
|
pass
|
176
|
228
|
|
177
|
229
|
def mutate_soft(self):
|
|
230
|
+ """ @brief Mutate changing a symbol into another of the same type """
|
178
|
231
|
idx = random.randint(0, len(self.expr)-1)
|
179
|
232
|
cur = self.expr[idx]
|
180
|
233
|
cls = type(cur)
|
|
@@ -185,8 +238,16 @@ class RpnExpr(object):
|
185
|
238
|
self.expr[idx] = new
|
186
|
239
|
pass
|
187
|
240
|
|
188
|
|
- def levenshtein(self, b, custom_weight=True):
|
189
|
|
- a = self
|
|
241
|
+ def levenshtein(a, b, custom_weight=True):
|
|
242
|
+ """ @brief Calculate a levenshtein distance between two expression
|
|
243
|
+ @param a expression A
|
|
244
|
+ @param b expression B
|
|
245
|
+ @param custom_weight If True a different symbol of same type weight
|
|
246
|
+ 1 and a different symbol of different type weight 2. Else no
|
|
247
|
+ special weight is applied.
|
|
248
|
+ @return an integer
|
|
249
|
+ """
|
|
250
|
+
|
190
|
251
|
len_a = len(a.expr)
|
191
|
252
|
len_b = len(b.expr)
|
192
|
253
|
|
|
@@ -209,6 +270,11 @@ class RpnExpr(object):
|
209
|
270
|
|
210
|
271
|
@classmethod
|
211
|
272
|
def from_string(cls, expr, nvar=1):
|
|
273
|
+ """ @brief Return an RpnExpr instance given a string representing
|
|
274
|
+ an expression
|
|
275
|
+ @param epxr the string representation
|
|
276
|
+ @param nvar the variable count
|
|
277
|
+ """
|
212
|
278
|
res = RpnExpr(sz=0)
|
213
|
279
|
|
214
|
280
|
for spl in expr.split(' '):
|