preparing new evaluation method

This commit is contained in:
Jonas Weinz 2020-03-12 19:14:30 +01:00
parent 006bf59eb0
commit 7d6d835a01
2 changed files with 4706 additions and 5275 deletions

File diff suppressed because it is too large Load Diff

View File

@ -659,6 +659,9 @@ class RecipeTreeNode(object):
return r
def serialize_subtree(self):
return [n.serialize() for n in self.traverse()]
def node_score(self):
raise NotImplementedError()
@ -781,8 +784,8 @@ class MixNode(RecipeTreeNode):
#if c_grouped_mix[ia,ib] > 0 or c_grouped_mix[ib,ia] > 0:
# s += 1
ia = m_mix._label_index[grouped_ing_a.to_json()]
ib = m_mix._label_index[grouped_ing_b.to_json()]
ia = m_mix._label_index[ing_a.to_json()]
ib = m_mix._label_index[ing_b.to_json()]
if c_mix[ia,ib] > 0 or c_mix[ib,ia] > 0:
s += 1
@ -1290,10 +1293,10 @@ class Tree(object):
if node.name() in constant_ingredients:
node._constant = True
return Tree(root)
return Tree(root, main_ingredients)
@staticmethod
def from_serialization(s):
def from_serialization(s, main_ingredients = None):
def empty_node(raw_n):
if raw_n['type'] == "MixNode":
node = MixNode(raw_n['constant'])
@ -1317,7 +1320,7 @@ class Tree(object):
for c in childs:
nodes[id].add_child(nodes[c])
return Tree(nodes[s[0]['id']])
return Tree(nodes[s[0]['id']], main_ingredients)
def __init__(self, root, main_ingredients=None):
@ -1354,6 +1357,7 @@ class Tree(object):
def collect_scores(self):
self._mix_scores = []
self._mix_weights = []
self._act_scores = []
self._ing_scores = []
@ -1366,6 +1370,7 @@ class Tree(object):
for n in nodes:
if type(n) == MixNode:
self._mix_scores.append(n.node_score())
self._mix_weights.append(len(n.childs()))
self._n_mix_nodes += 1
if type(n) == ActionNode:
self._act_scores.append(n.node_score())
@ -1385,12 +1390,13 @@ class Tree(object):
seen_ingredients.add(n.name())
self._mix_scores = np.array(self._mix_scores)
self._mix_weights = np.array(self._mix_weights)
self._act_scores = np.array(self._act_scores)
self._ing_scores = np.array(self._ing_scores)
def mix_scores(self):
return self._mix_scores
return self._mix_scores, self._mix_weights
def action_scores(self):
return self._act_scores
@ -1426,11 +1432,11 @@ class Tree(object):
return self._score
self.collect_scores()
s_mix = self.mix_scores()
s_mix, s_mix_weights = self.mix_scores()
s_act = self.action_scores()
s_ing = self.ing_scores()
n = len(s_mix) + len(s_act) + len(s_ing)
#n = len(s_mix) + len(s_act) + len(s_ing)
avg_mix = np.average(s_mix) if len(s_mix) > 0 else 1
avg_act = np.average(s_act) if len(s_act) > 0 else 1
@ -1441,21 +1447,34 @@ class Tree(object):
sum_ing = np.sum(s_ing) if len(s_ing) > 0 else 0
self._touched = False
contains_main_ingred = True
base_main_ings = [i._base_ingredient for i in self.root().traverse_ingredients()]
for ing in self._main_ingredients:
if ing not in base_main_ings:
contains_main_ingred = False
self._score = 0
break
if contains_main_ingred:
# boost creativity
if len(s_act) < 3:
self._score = 0
elif len(s_ing) < 3:
self._score = 0
else:
self._score = (sum_mix + sum_act + sum_ing) / n
weighted_mix_score = np.array([s_mix[i] * s_mix_weights[i] for i in range(len(s_mix))])
#print(weighted_mix_score)
n = len(s_act) + len(s_ing) + np.sum(s_mix_weights)
self._score = (np.sum(weighted_mix_score) + sum_act + sum_ing) / n
#print(self._score)
self._score *= (len(s_ing) - self._n_duplicates) / len(s_ing)
#self._score = 0.95 * self._score + 0.05 * self.main_ingredient_score()
return self._score
def copy(self):
return Tree.from_serialization(self.serialize())
return Tree.from_serialization(self.serialize(), self._main_ingredients)
# ## Population
@ -1494,8 +1513,59 @@ class Population(object):
self.population = new_population
def crossover(self):
# shuffle indices
indices = list(range(len(self.population) // 2))
indices = [i + len(self.population) // 2 for i in indices]
random.shuffle(indices)
# perform crossover for random pairs
for i in range(len(self.population) // 4):
i_a = indices[2*i]
i_b = indices[2*i+1]
self.pairwise_crossover(self.population[i_a], self.population[i_b])
def pairwise_crossover(self, tree_a, tree_b):
# for crossover: find a random subtree in both trees, and switch them
# first, select one random mix node from both
a_nodes = tree_a.root().traverse()
b_nodes = tree_b.root().traverse()
a_mix_nodes = []
b_mix_nodes = []
for n in a_nodes:
if type(n) == MixNode:
a_mix_nodes.append(n)
for n in b_nodes:
if type(n) == MixNode:
b_mix_nodes.append(n)
a_mix_node = np.random.choice(a_mix_nodes)
b_mix_node = np.random.choice(b_mix_nodes)
# now select one random child, we will switch the subtrees there
a_child = np.random.choice(list(a_mix_node.childs()))
b_child = np.random.choice(list(b_mix_node.childs()))
# ...and perform the switch
# manually remove references
a_mix_node.remove_child(a_child)
b_mix_node.remove_child(b_child)
# and add child to other subtree
a_mix_node.add_child(b_child)
b_mix_node.add_child(a_child)
def hold_best(self, n=10):
scores = [tree.score() for tree in self.population]
scores = np.array([tree.score() for tree in self.population])
sorted_indices = np.argsort(-scores)
@ -1505,8 +1575,8 @@ class Population(object):
avg_scores = []
for i in tqdm(range(n), desc="run evolutionary cycles"):
self.mutate()
#self.mutate()
#self.collect_scores()
self.crossover()
self.pairwise_competition()
#self.collect_scores()
@ -1530,17 +1600,13 @@ class Population(object):
# ## Run Evolutionary Algorithm
#p = Population(["noodle"],['noodle'], min_additional=4, max_additional=13, n_population = 50)
p = Population(["noodle"],['noodle'], min_additional=4, max_additional=13, n_population = 50, mutations=1)
#p_ingredient_unprepared(list(p.population[0].root().childs())[0]._name) < 0.2
#p.run(50)
#avg = p.run(10)
#p.plot_population(n_best=20)