def mutate(self) -> MutationReport: mutation_report = MutationReport() if random.random() > self.mutation_chance: return mutation_report range = self.end_range - self.start_range if random.random() < 0.25: # random reset new_current_value = random.uniform(self.start_range, self.end_range) mutation_report += self.name + " changed from " + repr( self.current_value) + " to " + repr(new_current_value) self.current_value = new_current_value else: # random deviation deviation = random.normalvariate(0, range / 8) new_current_value = self.current_value + deviation new_current_value = self.start_range + ( (new_current_value - self.start_range) % range) mutation_report += self.name + " deviated from " + repr( self.current_value) + " to " + repr(new_current_value) self.current_value = new_current_value if mutation_report is None: raise Exception("none mutation report in " + self.name) # print("returning from cont var mutagen: ", mutation_report) return mutation_report
def mutate(self) -> MutationReport: mutation_report = MutationReport() my_weighting = self.probability_weightings[self.options.index(self())] my_relative_weighting = my_weighting / sum(self.probability_weightings) normalised_weighting = len(self.options)*my_relative_weighting effective_mutation_chance = self.mutation_chance * 1.0/ normalised_weighting """ if the probability weightings of an option are not equal, then the mutation rates should be adjusted such that: if the current option value is weighted less - the option is more likely to change, and if the current option value is highly weighted - the option should be less likely to change """ if random.random() < effective_mutation_chance: if len(self.options) < 2: raise Exception("too few options to mutate") new_value = random.choices(self.options, weights=self.probability_weightings)[0] while new_value == self(): new_value = random.choices(self.options, weights=self.probability_weightings)[0] mutation_report += self.name + " changed from " + repr(self.current_value) + " to " + repr(new_value) self.current_value = new_value return mutation_report + self.mutate_sub_mutagens()
def mutate_base_genome(self, genome: Genome, mutation_record: MutationRecords, add_node_chance: float, add_connection_chance: float, allow_disabling_connections: bool = True): """performs base neat genome mutations, as well as node and genome property mutations""" mutation_report = MutationReport() if random.random() < add_node_chance: if self.add_node_mutation(genome, mutation_record): mutation_report.nodes_added += 1 if random.random() < add_connection_chance: if self.add_connection_mutation(genome, mutation_record): mutation_report.connections_created += 1 if allow_disabling_connections: """randomly deactivates and reactivates connections""" for connection in genome.connections.values(): orig_conn = copy.deepcopy(connection) result = connection.mutate( ) # this is the call which enables/disables connections if result.check_mutated(): """the connection was mutated""" # If mutation made the genome invalid then undo it if not genome.validate(): """ disabling the connection lead to a disconnected graph or enabling the connection lead to a cycle - undoing this mutation """ genome.connections[orig_conn.id] = orig_conn else: """mutation is valid""" if connection.enabled(): mutation_report.connections_enabled += 1 else: mutation_report.connections_disabled += 1 for node in genome.nodes.values(): """mutates node properties""" mutation_report += node.mutate() for mutagen in genome.get_all_mutagens(): """mutates the genome level properties""" report = mutagen.mutate() if report is None: raise Exception("none report returned from mutating " + mutagen.name + ", " + repr(mutagen)) mutation_report += report return mutation_report
def forget_da_scheme(self, genome: BlueprintGenome) -> MutationReport: mutation_report = MutationReport() if not config.evolve_da_pop: # blueprint tethered da schemes should not be forgotten by their da return mutation_report if random.random() < config.da_link_forget_chance: genome.da = None genome._da_id = -1 mutation_report += "forgot da scheme link" return mutation_report
def mutate_species_numbers(self, genome) -> MutationReport: mutation_report = MutationReport() import src.main.singleton as Singleton for node in genome.nodes.values(): if type(node) != BlueprintNode: continue if random.random() < config.blueprint_node_species_switch_chance: possible_species_ids = [spc.id for spc in Singleton.instance.module_population.species] new_species_id = random.choice(possible_species_ids) mutation_report+="changed species number of node " + str(node.id) + " from " + str(node.species_id) \ + " to " + str(new_species_id) node.species_id = new_species_id return mutation_report
def forget_module_mappings_mutation(self, genome: BlueprintGenome) -> MutationReport: mutation_report = MutationReport() if config.use_module_retention and random.random()<config.module_map_forget_mutation_chance: choices = list(set([node.species_id for node in genome.get_fully_connected_blueprint_nodes_iter() if node.linked_module_id != -1])) if len(choices) == 0: return mutation_report species_id = random.choice(choices) for node in genome.get_blueprint_nodes_iter(): if node.species_id == species_id: node.linked_module_id = -1 # forget the link. will be sampled fresh next cycle mutation_report += "forgot module mapping for species " + str(species_id) return mutation_report
def mutate(self) -> MutationReport: mutation_report = MutationReport() if random.random() > self.mutation_chance: return mutation_report range = self.end_range - self.start_range if range == 0: raise Exception("zero range on mutagen " + repr(self) + " start = " + repr(self.start_range) + " end = " + repr(self.end_range)) if random.random() < 0.25: # random reset new_current_value = random.randint(self.start_range, self.end_range) change_type = " changed" else: # random deviation deviation = random.normalvariate(0, range / 8) new_current_value = self.current_value + int(deviation) change_type = " deviated" # making sure value changes if new_current_value == self.current_value: new_current_value = self.current_value + random.choice([0, 1]) new_current_value = self.start_range + ( (new_current_value - self.start_range) % range) if new_current_value % 1 != 0: raise Exception("non natural number mutated in int variable") mutation_report += self.name + change_type + " from " + repr( self.current_value) + " to " + repr(new_current_value) self.current_value = new_current_value # print("returning from int var mutagen: ", mutation_report) return mutation_report
def mutate_node_types(self, genome: Genome) -> MutationReport: """ chance to change nodes from blueprint nodes to module nodes and visa versa """ mutation_report = MutationReport() if random.random() < config.blueprint_node_type_switch_chance: """chose 1 node to change type""" node: Node = random.choice(list(genome.nodes.values())) if type(node) == BlueprintNode: """change node to a module node""" module_node = ModuleNode(node.id, node.node_type) genome.nodes[module_node.id] = module_node mutation_report += "swapped blueprint node for a module node" if type(node) == ModuleNode: """change node back to a blueprint node""" blueprint_node = BlueprintNode(node.id, node.node_type) genome.nodes[blueprint_node.id] = blueprint_node mutation_report += "swapped module node for a blueprint node" return mutation_report
def mutate_sub_mutagens(self) -> MutationReport: mutation_report = MutationReport() for sub in self.get_submutagens(): mutation_report += sub.mutate() return mutation_report
def mutate(self) -> MutationReport: report = MutationReport() for mutagen in self.get_all_mutagens(): report += mutagen.mutate() return report