def hierarchical_mutation(original_individual: Individual, strength: float, **kwargs) -> List[Optional[Individual]]: """Choose a node in the graph_manager, choose a parameter inside the node, mutate it. Each parameter has probability: `1/len(nodes) * 1/len(parameters in that node)`. Args: original_individual (Individual): source individual to mutate strength (float): mutation strength Returns: A list with the new mutated individual or None if it is not valid """ check_muation_parameters(original_individual, strength) new_individual = Individual(original_individual.constraints, copy_from=original_individual) new_individual.parents = {original_individual} new_individual.operator = hierarchical_mutation # Do while not (rnd.random() < strength) while True: # Use "while loop" to try choosing a node that doesn't contain only the Information parameter or the mutation # had no effect while True: # Choose a node that contains the parameter to mutate chosen_node = random_generator.choice( new_individual.graph_manager.nodes()) # Create a list of parameters contained into the macro candidate_parameters = list() for parameter_name, parameter in new_individual.graph_manager[ chosen_node]['parameters'].items(): if not isinstance(parameter, Information): candidate_parameters.append(parameter) # If I tried to mutate a macro that contains only an Information parameter -> pick another node to mutate # else -> mutate a random parameter if candidate_parameters: # Choose only one parameter to mutate in the list of all parameters of the chosen macro chosen_parameter = random_generator.choice( candidate_parameters) assert strength chosen_parameter.mutate(strength) break # Stop condition if strength == 1.0 or not (random_generator.random() < strength): break new_individual.finalize() if not new_individual.valid: return [None] else: # print_individual(original_individual, 'ORIGINAL', True) # print_individual(individual, 'MUTATED', True) return [new_individual]
def flat_mutation(original_individual: Individual, strength: float, **kwargs) -> List[Optional[Individual]]: """Build a list of all parameters contained in all nodes then choose one of them and mutate it. Each parameter has probability: `1/len(nodes)`. Args: original_individual (Individual): source individual to mutate strength (float): mutation strength Returns: A list with the new mutated individual or None if it is not valid """ # logging.debug("Flat mutation has been chosen") check_muation_parameters(original_individual, strength) new_individual = Individual(original_individual.constraints, copy_from=original_individual) new_individual.parents = {original_individual} new_individual.operator = flat_mutation while True: # Create a list that contains all parameters candidate_parameters = list() # Iterate for each node in the individual and save a list of parameters for node in new_individual.graph_manager.nodes(): for parameter_name in sorted( new_individual.graph_manager[node]['parameters']): # node parameters need to be in predictable order! parameter = new_individual.graph_manager[node]['parameters'][ parameter_name] if not isinstance(parameter, Information): candidate_parameters.append(parameter) # If the individual contains only Information parameters flat_mutate returns [None] if not candidate_parameters: return [None] # Choose and mutate a parameter chosen_parameter = random_generator.choice(candidate_parameters) chosen_parameter.mutate(strength) # Stop condition if strength == 1.0 or not (random_generator.random() < strength): break new_individual.finalize() if not new_individual.valid: return [None] else: # print_individual(original_individual, 'ORIGINAL', True) # print_individual(individual, 'MUTATED', True) return [new_individual]
def mutate(self, strength: float = 0.5): assert 0 <= strength <= 1, "Invalid strength: " + str( strength) + " (should be 0 <= s <= 1)" if strength == 0: logging.debug("strength == 0") elif strength == 1: bits_list = random_generator.choices([0, 1], k=self.len_) self.value = ''.join(map(str, bits_list)) else: i = random_generator.randint(0, self.len_ - 1) value = list(self._value.strip()) value[i] = str(1 - int(value[i])) self.value = ''.join(map(str, value)) while random_generator.random() < strength: i = random_generator.randint(0, self.len_ - 1) value[i] = str(1 - int(value[i])) self.value = ''.join(map(str, value))
def add_node_mutation(original_individual: Individual, strength: float, **kwargs) -> List[Optional[Individual]]: """Insert a new node in the individual graph_manager. An insertion of a new node could fail because of there are no valid targets for the node that contains a LocalReference. Args: original_individual (Individual): source individual to mutate strength (float): mutation strength Returns: A list with the new mutated individual or None if it is not valid """ # logging.debug("Insert a new node mutation has been chosen") check_muation_parameters(original_individual, strength) new_individual = Individual(original_individual.constraints, copy_from=original_individual) new_individual.parents = {original_individual} new_individual.operator = add_node_mutation while True: frame_count = get_macro_pool_nodes_count(new_individual) expandable_frames = set() # Find the frames in which I can place a new node for frame in frame_count: if frame.section.size[1] >= frame_count[frame] + 1: expandable_frames.add(frame) # If there are not expandable frames return (operator fails) if len(expandable_frames) == 0: return [None] # Choice a valid frame chosen_frame = random_generator.choice(list(expandable_frames)) # Choose randomly a node that will be the parent of the new node candidate_nodes = get_nodes_in_section(individual=new_individual, section=chosen_frame[1]) chosen_parent_node = random_generator.choice(candidate_nodes) frame_path = new_individual.graph_manager[chosen_parent_node][ 'frame_path'] candidate_macros = chosen_frame.section.macro_pool chosen_macro = random_generator.choice(candidate_macros) # Add node to graph_manager node_to_insert = new_individual.add_node( parent_node=chosen_parent_node, macro=chosen_macro, frame_path=frame_path) # Initialize parameters of the inserted node new_individual.initialize_macros(chosen_macro, node_to_insert) assert node_to_insert in new_individual.graph_manager.nodes( ), "Node has not been inserted" if strength == 1.0 or not (random_generator.random() < strength): break assert len(original_individual.graph_manager.nodes()) < len( new_individual.graph_manager.nodes()), "Something wrong!" new_individual.finalize() if not new_individual.valid: return [None] else: # print_individual(individual, 'Mutated individual', True) # print_individual(original_individual, 'Original individual', True) return [new_individual]
def remove_node_mutation(original_individual: Individual, strength: float, **kwargs) -> List[Optional[Individual]]: """Try to remove a node taken from the possible set of nodes in the individual. The removal could fail because of the minimum number of nodes that the individual must contain. This method returns a modified copy of the passed individual leaving it unchanged. Args: original_individual (Individual): source individual to mutate strength (float): mutation strength Returns: A list with the new mutated individual or None if it is not valid """ # logging.debug("Remove a node mutation has been chosen") check_muation_parameters(original_individual, strength) new_individual = Individual(original_individual.constraints, copy_from=original_individual) new_individual.parents = [original_individual] new_individual.operator = remove_node_mutation while True: if len(new_individual.graph_manager.nodes()) <= 2: logging.debug("Individual should have at least two nodes") return [None] frame_count = get_macro_pool_nodes_count(new_individual) shrinkable_frames = set() # Find the frames from which I can remove a node for chosen_frame, count in frame_count.items(): if chosen_frame.section.size[0] <= count - 1: shrinkable_frames.add(chosen_frame) # If there are not shrinkable frames return (operator fails) if len(shrinkable_frames) == 0: return [None] # Otherwise -> removal of a node is always executed # Choice a valid frame chosen_frame = random_generator.choice(list(shrinkable_frames)) # Choose randomly the node that will be removed candidate_nodes = get_nodes_in_section(individual=new_individual, section=chosen_frame[1]) node_to_remove = random_generator.choice(candidate_nodes) chosen_root_frame = new_individual.graph_manager[node_to_remove][ 'frame_path'][1] new_individual.remove_node(node_to_remove) assert node_to_remove not in new_individual.graph_manager.nodes( ), "Node has not been removed" # Example: If the removed NodeID_2 was a destination of a Reference in NodeID_1 and NodeID_6 -> change the value # in parameter of the NodeID_1 and NodeID_6 with a new possible target (mutate(1)) from .parameter import LocalReference for node in new_individual.graph_manager.nodes( frame=chosen_root_frame): for parameter_name, parameter in new_individual.graph_manager[ node]['parameters'].items(): if isinstance( parameter, LocalReference) and parameter.value == node_to_remove: parameter.mutate(1) if strength == 1.0 or not (random_generator.random() < strength): break assert len(original_individual.graph_manager.nodes()) > len( new_individual.graph_manager.nodes()), "Something wrong!" new_individual.finalize() if not new_individual.valid: return [None] else: # print_individual(original_individual, 'Original individual', True) # print_individual(individual, 'Mutated individual', True) return [new_individual]