diff --git a/config.py b/config.py index 4fd3ab2..bfc8c22 100644 --- a/config.py +++ b/config.py @@ -25,14 +25,14 @@ def __init__(self): self.complexity_of_operations = 0.3 self.complexity_of_logic = 0.3 - self.population = 50 + self.population = 100 self.generations = 100 self.tournament_size = 40 self.not_prob = 30 self.evolution_prob = { - 'crossover': 20, - 'mutation': 80 + 'crossover': 50, + 'mutation': 50 } # max 100 diff --git a/evolution.py b/evolution.py index a27aa1b..f538386 100644 --- a/evolution.py +++ b/evolution.py @@ -1,6 +1,7 @@ import random from utils import Utils from gpParser import GpParser +from traverseAdapter import TraverseAdapter class Evolution(Utils): def __init__(self, state, fitness, config): @@ -75,8 +76,95 @@ def _mutation(self, parent_index): return parent_stack_copy + def _get_token_type(self, token): + if self.is_block(token): + return 'block' + elif self.is_operation(token): + return 'operation' + elif self.is_condition(token): + return 'condition' + elif self.is_logic(token): + return 'logic' + elif self.is_not(token): + return 'not' + elif self.is_input(token): + return 'input' + elif self.is_output(token): + return 'output' + elif self.is_true(token): + return 'true' + elif self.is_false(token): + return 'false' + elif self.is_constant(token): + return 'constant' + elif self.is_variable(token): + return 'variable' + else: + return 'unknown' + + def _analyze_stack(self, stack): + stats = { + "block": [], + "operation": [], + "condition": [], + "logic": [], + "not": [], + "input": [], + "output": [], + "true": [], + "false": [], + "constant": [], + "variable": [], + } + + for i in range(len(stack)): + token = stack[i] + type = self._get_token_type(token) + if type == 'unknown': + continue + stats[type].append(i) + + return stats + + def _crossover(self, parent1_index, parent2_index): - pass + parent_copy = self.state.stack[parent1_index][:] + parent2_stack = self.state.stack[parent2_index] + + parent_copy_length = len(parent_copy) + parent2_stack_length = len(parent2_stack) + parent2_stats = self._analyze_stack(parent2_stack) + + # Get random index from parent1 + start_index = random.randint(0, parent_copy_length - 1) + random_index = start_index + qualified = [] + + while len(qualified) == 0: + # Try the neighbour + random_index = (random_index + 1) % parent_copy_length + if random_index == start_index: + # No qualified tokens + return parent_copy + token = parent_copy[random_index] + type = self._get_token_type(token) + if type == 'unknown': + continue + qualified = parent2_stats[type] + + # Get random index from parent2 + random_index2 = random.choice(qualified) + + traverseAdapterParent1 = TraverseAdapter(parent_copy, random_index) + traverseAdapterParent2 = TraverseAdapter(parent2_stack, random_index2) + + s1, e1 = traverseAdapterParent1.shallow_traverse() + s2, e2 = traverseAdapterParent2.shallow_traverse() + + parent_copy = parent_copy[:s1] + parent2_stack[s2:e2] + parent_copy[e1:] + + return parent_copy + def _tournament(self): best = float('-inf') @@ -122,13 +210,15 @@ def problem_solved(self): return 1 def evolve(self): + print("EVOLVE") for g in range(self.config.generations): self.stats(g) if max(self.state.fitness) == 0: return self.problem_solved() + # print(f"Generation {g}") - for i in range(self.config.population): + for _ in range(self.config.population): evolution_type = self.get_random_evolution_type() if evolution_type == 'crossover': @@ -136,8 +226,7 @@ def evolve(self): # TODO: What if parents are the same? parent1_index = self._tournament() parent2_index = self._tournament() - continue - pass + new_indiv = self._crossover(parent1_index, parent2_index) elif evolution_type == 'mutation': indiv_index = self._tournament() diff --git a/gpParser.py b/gpParser.py index 8d9dd35..7c42136 100644 --- a/gpParser.py +++ b/gpParser.py @@ -9,207 +9,120 @@ def __init__(self, data, config=Config()): self.length = len(data) self.result = '' - def increment_iterator(self): + def _increment_iterator(self): self.iterator += 1 - def current_token(self): + def _current_token(self): return self.data[self.iterator] - def next_token(self): - self.increment_iterator() - return self.current_token() + def _next_token(self): + self._increment_iterator() + return self._current_token() - def add_to_result(self, parsed_string): + def _add_to_result(self, parsed_string): self.result += parsed_string - - def _parse_operation(self): - operation_token = self.current_token() - token = self.next_token() - left_side = '' - if self.is_operation(token): - left_side = self._parse_operation() - elif self.is_not(token): - left_side = self._parse_not(token) - else: - left_side = token - - token = self.next_token() - right_side = '' - if self.is_operation(token): - right_side = self._parse_operation() - elif self.is_not(token): - right_side = self._parse_not(token) - else: - right_side = token + def _parse_expression(self): + expression_token = self._current_token() + + self._increment_iterator() + left_side = self.traverse() + self._increment_iterator() + right_side = self.traverse() - parsed = f"({left_side} {operation_token} {right_side})" + parsed = f"({left_side} {expression_token} {right_side})" return parsed + def _parse_operation(self): + return self._parse_expression() + def _parse_condition(self): - condition_token = self.current_token() - token = self.next_token() - - left_side = '' - if self.is_operation(token): - left_side = self._parse_condition() - elif self.is_not(token): - left_side = self._parse_not(token) - else: - left_side = token - - token = self.next_token() - right_side = '' - if self.is_operation(token): - right_side = self._parse_condition() - elif self.is_not(token): - right_side = self._parse_not(token) - else: - right_side = token - - parsed = f"({left_side} {condition_token} {right_side})" - return parsed + return self._parse_expression() def _parse_logic(self): - logic_token = self.current_token() - token = self.next_token() - - left_side = '' - if self.is_logic(token): - left_side = self._parse_logic() - elif self.is_condition(token): - left_side = self._parse_condition() - elif self.is_not(token): - left_side = self._parse_not(token) - else: - left_side = token - - right_side = '' - token = self.next_token() - if self.is_logic(token): - right_side = self._parse_logic() - elif self.is_condition(token): - right_side = self._parse_condition() - elif self.is_not(token): - right_side = self._parse_not(token) - else: - right_side = token - - parsed = f"({left_side} {logic_token} {right_side})" - return parsed + return self._parse_expression() def _parse_equation(self): - left_side = self.next_token() - token = self.next_token() - right_side = '' - - if self.is_input(token) or token.isdigit(): - right_side = token - - elif self.is_operation(token): - right_side = self._parse_operation() - - elif self.is_logic(token): - right_side = self._parse_logic() - - elif self.is_condition(token): - right_side = self._parse_condition() - - elif self.is_not(token): - right_side = self._parse_not(token) - else: - right_side = token + left_side = self._next_token() + self._increment_iterator() + right_side = self.traverse() parsed = f"{left_side} = {right_side};" return parsed def _parse_output(self): - output_token = self.current_token() - variable_token = self.next_token() + output_token = self._current_token() + variable_token = self._next_token() parsed = f"{output_token} {variable_token};" return parsed - def _parse_if(self): - if_token = self.current_token() - token = self.next_token() - condition = '' + def _parse_not(self): + not_token = self._current_token() + self._increment_iterator() + expression = self.traverse() - if self.is_logic(token): - condition = self._parse_logic() - elif self.is_condition(token): - condition = self._parse_condition() - elif self.is_not(token): - condition = self._parse_not(token) - else: - condition = token + parsed = f"({not_token} {expression})" + return parsed + + def _parse_conditional_block(self): + block_token = self._current_token() + self._increment_iterator() + condition = self.traverse() + + self._increment_iterator() + scope = self._traverse_scope() - parsed = f"{if_token} ({condition})" + parsed = f"{block_token} ({condition}) {scope}" return parsed def _parse_while(self): - while_token = self.current_token() - token = self.next_token() - condition = '' + return self._parse_conditional_block() + + def _parse_if(self): + return self._parse_conditional_block() - if self.is_logic(token): - condition = self._parse_logic() - elif self.is_condition(token): - condition = self._parse_condition() - elif self.is_not(token): - condition = self._parse_not(token) - else: - condition = token + + def _traverse_scope(self): + open_scope = self._current_token() + self._increment_iterator() - parsed = f"{while_token} ({condition})" + content = '' + while not self.is_close_scope(self._current_token()): + content += self.traverse() + self._increment_iterator() + + close_scope = self._current_token() + parsed = f"{open_scope} {content} {close_scope}" return parsed - - def _parse_not(self, token): - not_token = self.current_token() - token = self.next_token() - expression = '' - if self.is_logic(token): - expression = self._parse_logic() + def traverse(self): + token = self._current_token() + + if self.is_equation(token): + return self._parse_equation() + elif self.is_output(token): + return self._parse_output() + elif self.is_logic(token): + return self._parse_logic() + elif self.is_if(token): + return self._parse_if() + elif self.is_while(token): + return self._parse_while() + elif self.is_not(token): + return self._parse_not() elif self.is_condition(token): - expression = self._parse_condition() + return self._parse_condition() elif self.is_operation(token): - expression = self._parse_operation() - elif self.is_true(token): - expression = token - elif self.is_false(token): - expression = token - elif self.is_input(token): - expression = token + return self._parse_operation() else: - expression = token - - parsed = f"({not_token} {expression})" - return parsed - + return token def parse(self): while self.iterator < self.length: - token = self.current_token() - parsed = '' - - if self.is_equation(token): - parsed = self._parse_equation() - elif self.is_output(token): - parsed = self._parse_output() - elif self.is_logic(token): - parsed = self._parse_logic() - elif self.is_if(token): - parsed = self._parse_if() - elif self.is_while(token): - parsed = self._parse_while() - elif self.is_open_scope(token): - parsed = token - elif self.is_close_scope(token): - parsed = token - - self.add_to_result(parsed) - self.increment_iterator() + parsed = self.traverse() + self._add_to_result(parsed) + self._increment_iterator() return self.result @@ -219,6 +132,8 @@ def parse(self): # data = f.read() # data = [elem.strip().replace("'", "") for elem in data.strip()[1:-1].split(',')] -# gpParser = GpParser(data) -# res = gpParser.parse() -# print("RES: ", res) \ No newline at end of file +data = ['=', 'var1', '+', '-', '*', '4', '5', '6', '7', 'if', '<', 'var3', '10', '{', '=', 'var3', 'input', 'while', 'not', 'and', 'false', '<', '4', '5', '{', 'output', '5', '}', '}'] +data2 = ['while', '<', '4', '5', '{', 'output', '5', '}'] +gpParser = GpParser(data) +res = gpParser.parse() +print("RES: ", res) \ No newline at end of file diff --git a/traverseAdapter.py b/traverseAdapter.py new file mode 100644 index 0000000..8f35dd4 --- /dev/null +++ b/traverseAdapter.py @@ -0,0 +1,12 @@ +from gpParser import GpParser + + +class TraverseAdapter(GpParser): + def __init__(self, data, start_index): + data = data[start_index:] + super().__init__(data) + self.start_index = start_index + + def shallow_traverse(self): + super().traverse() + return self.start_index, self.start_index + self.iterator + 1 \ No newline at end of file diff --git a/utils.py b/utils.py index 3a46cfb..c62fcad 100644 --- a/utils.py +++ b/utils.py @@ -49,6 +49,15 @@ def is_true(self, token): def is_false(self, token): return token == self.config.syntax['false'] + def is_block(self, token): + if (self.is_if(token) or + self.is_while(token) or + self.is_equation(token) or + self.is_output(token) + ): + return True + return False + def choose_random_operation(self, exclude=None): operations = self.config.syntax['operations'][:] if exclude: