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]
Beispiel #3
0
 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]