-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathGrammatical_Evolution_mapper.py
228 lines (182 loc) · 11.3 KB
/
Grammatical_Evolution_mapper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
'''
This file define the Grammar, used to generate chromosomes, as a class that
- define rules of the GRAMMAR as recursive function
- non-terminal rules are represented by functions that calls other functions
- terminal rules are represented by functions that don't calls any other functions
- create the DERIVATION TREE selecting derivation rules through a sequence of integer that
maps the MOD of that integer with a rule.
A node of the tree contains:
- id (unique node identifier)
- parent (parent node, except for the root node)
- label (rule name)
- code (encoded python peace of code)
- create the executable PYTHON CODE
'''
from anytree import Node
import numpy as np
class Parser():
'''
Grammar parser class. Contains all parameters shared between function calls.
Args:
initial_gene_seq (list(int)): the set of genes (integers) of the genotype
root (AnyTree.Node): starting node of the derivation tre, color=self.color, border=self.bordere
environment (environment)
method (str): method used for generate the tree (full or grow)
MAX_DEPTH (int): maximum depth of the generated phenotypes' derivation trees
MAX_WRAP (int): maximum number of time that wrapping operator is applied to genotype
color (str)
colorscheme (str)
'''
def __init__(self, initial_gene_seq, root, environment, method, MAX_DEPTH, MAX_WRAP):
self.wrap_ctr = 0 # global counter that count number of time that wrap func is applied
self.i_gene = -1 # global index that loops through all genes
self.initial_gene_seq = initial_gene_seq # global initial gene_seq (used for wrapping purposes)
self.root = root # starting node
self.environment = environment
self.method = method # full or grow
self.MAX_WRAP = MAX_WRAP # max number of time that wrap func is applied to the sequence of genes
self.MAX_DEPTH = MAX_DEPTH -2 # max depth of the tree
self.color=''
self.border=''
self.extra_id=''
#-------FUNCTION UTILITIES--------#
def start_derivating(self, node_type, tree_depth=0, indent=1, extra_id=''):
''' Starting derivation rule. '''
self.extra_id = extra_id
self.color = self.root.color
self.border = self.root.border
if node_type=='expr':
self.expr(self.initial_gene_seq, tree_depth, self.root, indent)
if node_type=='cond':
self.cond(self.initial_gene_seq, self.root)
return self.root
def wrap(self, gene_seq, is_terminal):
'''
Function called when translation from genes to grammars' rules runs out of genes
'''
if is_terminal:
# if it's translating a terminal, finish the translation wrapping gene_seq by only 1 element
# so the next time (if it's not another terminal and if MAX_WRAP is reached, it will stop wrapping)
gene_seq += self.initial_gene_seq[len(gene_seq) % len(self.initial_gene_seq)],
else:
# if it's translating a non-terminal, increment the counter
self.wrap_ctr+=1
if self.wrap_ctr>self.MAX_WRAP:
# if wrapped too many times, extend gene_seq by a gene that will always lead to a terminal
gene_seq+=[3]
else:
#if can wrap, extend actual gene_seq by the initial one
gene_seq += self.initial_gene_seq
return gene_seq
#-----------DEF GRAMMAR AND TREE/PROGRAM GENERATOR-----------------#
#-----------RULES-----------------#
def expr(self, gene_seq, tree_depth, node, indent):
'''
<expr>: "if" <cond> ":" _NL _INDENT <expr> _NL # 0
| "if" <cond> ":" _NL _INDENT <expr> _NL "else:"_NL _INDENT <expr> _NL # 1
| <expr> _NL _INDENT <expr> # 2
| "action =" ACTION # 3
'''
self.i_gene+=1
if self.i_gene >= len(gene_seq): # if translation from genes to grammrs' rules runned out of genes (self.i_gene is the index of genes)
gene_seq = self.wrap(gene_seq, False)
if tree_depth<self.MAX_DEPTH: # if tree max depth it hasn't yet been reached
if self.method == 'full': # and method is FULL
idx = gene_seq[self.i_gene] % 3 # skip terminal ACTION so we have always tree of max_depth
else: # method is GROW
if node.name.rsplit(')')[1].rsplit('_id')[0]=='expr_a' or node.name.rsplit(')')[1].rsplit('_id')[0]=='expr_b':
idx = gene_seq[self.i_gene] % 3 # in order to not have two ACTION terminals that aren't a consequence of if/else in case <expr><expr> was chosen
else:
idx = gene_seq[self.i_gene] % 4
i_gene = self.i_gene
if idx == 0: # 0
child1 = Node('('+str(i_gene)+')cond'+'_id_'+str(id(node)), parent=node, label='cond', code="if ", color=self.color, border=self.border)
self.cond(gene_seq, child1)
child2 = Node('('+str(i_gene)+')expr_i'+'_id_'+str(id(node)), parent=node, label='expr', code=":\n{tab1}".format(tab1='\t'*(indent+1)), indent=indent+1, color=self.color, border=self.border)
self.expr(gene_seq, tree_depth+1, child2, indent+1)
if idx == 1: # 1
child1 = Node('('+str(i_gene)+')cond'+'_id_'+str(id(node)), parent=node, label='cond', code="if ", color=self.color, border=self.border)
self.cond(gene_seq, child1)
child2 = Node('('+str(i_gene)+')expr_i'+'_id_'+str(id(node)), parent=node, label='expr', code=":\n{tab1}".format(tab1='\t'*(indent+1)), indent=indent+1, color=self.color, border=self.border)
self.expr(gene_seq, tree_depth+1, child2, indent+1)
child3 = Node('('+str(i_gene)+')expr_e'+'_id_'+str(id(node)), parent=node, label='expr', code="\n{tab2}else:\n{tab3}".format(tab2='\t'*(indent), tab3='\t'*(indent+1)), indent=indent+1, color=self.color, border=self.border)
self.expr(gene_seq, tree_depth+1, child3, indent+1)
if idx == 2: # 2
child1 = Node('('+str(i_gene)+')expr_a'+'_id_'+str(id(node)), parent=node, label='expr', code="", indent=indent, color=self.color, border=self.border)
self.expr(gene_seq, tree_depth+1, child1, indent)
child2 = Node('('+str(i_gene)+')expr_b'+'_id_'+str(id(node)), parent=node, label='expr', code="\n{tab1}".format(tab1='\t'*(indent)), indent=indent, color=self.color, border=self.border)
self.expr(gene_seq, tree_depth+1, child2, indent)
if idx == 3: # 3
child = Node('('+str(i_gene)+')ACTION'+'_id_'+str(id(node)), parent=node, label='ACT', code="action = ", color=self.color, border=self.border)
self.ACTION(gene_seq, child)
else:
child = Node('('+str(self.i_gene)+')ACTION'+'_id_'+str(id(node)), parent=node, label='ACT', code="action = ", color=self.color, border=self.border)
self.ACTION(gene_seq, child)
def cond(self, gene_seq, node):
'''
<cond>: "observation[" N_OBS "]" COMP "all_obs[" N_OBS "][" SPLT_PT "]"
'''
if self.i_gene >= len(gene_seq):
gene_seq = self.wrap(gene_seq, True)
i_gene=self.i_gene
child1 = Node('('+str(i_gene)+')N_OBS_obser'+'_id_'+str(id(node)), parent=node, label='N_OBS', code="observation[", color=self.color, border=self.border)
self.N_OBS(gene_seq, child1, True, '_obser')
child2 = Node('('+str(i_gene)+')COMP'+'_id_'+str(id(node)), parent=node, label='COMP', code='] ', color=self.color, border=self.border)
self.COMP(gene_seq, child2)
child3 = Node('('+str(i_gene)+')N_OBS_state'+'_id_'+str(id(node)), parent=node, label='N_OBS', code=' all_obs[', color=self.color, border=self.border)
n_obs = self.N_OBS(gene_seq, child3, False, '_state')
child4 = Node('('+str(i_gene)+')SPT_PT'+'_id_'+str(id(node)), parent=node, label='SPLT_PT', code='][', color=self.color, border=self.border)
self.SPLT_PT(gene_seq, child4, n_obs)
#-----------TERMINALS-----------------#
def COMP(self, gene_seq, node):
'''
COMP: "<=" | ">"
'''
self.i_gene+=1
if self.i_gene >= len(gene_seq):
gene_seq = self.wrap(gene_seq, True)
idx = gene_seq[self.i_gene] % 2
if idx == 0:
Node('('+str(self.i_gene)+')less'+'_id_'+str(id(node)), parent=node, label='<= ', code="<=", color=self.color, border=self.border)
if idx == 1:
Node('('+str(self.i_gene)+')great'+'_id_'+str(id(node)), parent=node, label='> ', code=">", color=self.color, border=self.border)
def N_OBS(self, gene_seq, node, incr, arr):
'''
N_OBS: /[0-3]/
'''
# must compare a state of the observaion with the same state of all possible observations
# so the two N_OBS must have the same i_seq
if incr:
self.i_gene+=1
i_gene_same= self.i_gene
else:
i_gene_same = self.i_gene -1
if i_gene_same >= len(gene_seq):
gene_seq = self.wrap(gene_seq, True)
idx = gene_seq[i_gene_same] % len(self.environment.all_obs)
for obs in range(len(self.environment.all_obs)):
if idx == obs:
Node('('+str(self.i_gene)+')idx'+'_id_'+str(id(node)), parent=node, label=str(idx), code=str(idx), color=self.color, border=self.border)
return idx
def SPLT_PT(self, gene_seq, node, n_obs):
'''
SPLIT_PT: /[n*]/
'''
self.i_gene+=1
if self.i_gene >= len(gene_seq):
gene_seq = self.wrap(gene_seq, True)
idx = gene_seq[self.i_gene] % self.environment.bins[n_obs]
for n_of_splt in range(self.environment.bins[n_obs]):
if idx == n_of_splt:
Node('('+str(self.i_gene)+')splt'+'_id_'+str(id(node)), parent=node, label=str(idx), code=str(idx)+"]", color=self.color, border=self.border)
def ACTION(self, gene_seq, node):
'''
ACTION: /[0-1]/
'''
self.i_gene+=1
if self.i_gene >= len(gene_seq):
gene_seq = self.wrap(gene_seq, True)
idx = gene_seq[self.i_gene] % len(self.environment.actions)
for action in self.environment.actions:
if idx == action:
Node('('+str(self.i_gene)+')act'+'_id_'+str(id(node)), parent=node, label=str(idx), code=str(idx)+"\n", color=self.color, border=self.border)