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]
Exemple #2
0
 def mutate(self, strength: float = 0.5):
     assert getattr(self, 'alternatives', None), "Illegal or missing alternatives list (not using make_parameter?)"
     assert 0 <= strength <= 1, "Invalid strength: " + str(strength) + " (should be 0 <= s <= 1)"
     if strength == 0:
         logging.debug("strength == 0")
     else:
         self.value = random_generator.choice(self.alternatives,
                                              self.alternatives.index(self._value),
                                              strength=strength)
def unroll_macro_list(individual: Individual, section_name: str, frame_path: Sequence[Frame] = None) \
        -> List[Tuple[Macro, Tuple[Frame]]]:
    """Generate a list of (macro, frame_path) from a given section (=section_name).

    Args:
        individual (Individual): individual that contains the Constraints and the frame_tree.
        section_name (str): name of the section from which to take the macro.
        frame_path (Sequence[Frame]): frame path of the section.

    Returns:
        List of all {Macro, Frame} of the selected section.
    """
    # ...from all possible sections (see __getitem__ in Constraints class)
    section = individual.constraints[section_name]
    if frame_path is None:
        # Take the first frame of the tree
        frame_path = [individual.frame_tree.root.frame]
    frame_path = tuple(frame_path) + (Frame(
        individual.get_unique_frame_name(section), section), )

    nodes = list()
    if isinstance(section, MacroPool):
        # if section.size[0] == section.size[1]:
        #     x = range(section.size[0])
        # else:
        #     x = range(rnd.randint(section.size[0], section.size[1]))
        # for _ in x:
        for i in range(random_generator.randint(*section.size)):
            # Create and append a node that contains of the macros in the list of macros
            #       (returned by section.macro_pool method)
            chosen_macro = random_generator.choice(section.macro_pool)
            # logging.bare(chosen_macro)
            nodes.append((chosen_macro, frame_path))
    elif isinstance(section, SubsectionsSequence):
        # For each section in the SubsectionSequence...
        for seq in section.sub_sections:
            nodes += unroll_macro_list(individual, seq.name, frame_path)
    elif isinstance(section, SubsectionsAlternative):
        seq = random_generator.choice(section.sub_sections)
        nodes += unroll_macro_list(individual, seq.name, frame_path)
    else:
        raise TypeError("Unknown section type")
    return nodes
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]
Exemple #5
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.choice([0, 1]) for _ in range(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))
Exemple #6
0
    def select(self,
               min_arity: int = 0,
               max_arity: int = None,
               k: int = 1) -> List[GenOperator]:
        """Select a set of operators with arity in [min_arity, max_arity]

        Args:
            min_arity (int): minimum arity of the operators that will be returned
            max_arity (int): maximum arity of the operators that will be returned
            k (int): number of genetic operators to return

        Returns:
            list of valid genetic operators
        """
        assert max_arity is None or min_arity <= max_arity, "min_arity must be <= then max_arity"
        assert min_arity >= 0 and (
            max_arity is None
            or max_arity >= 0), "Both min_arity and max_arity must be >= 0"
        assert k > 0, "k must be > 0"
        assert len(self._gen_operators) > 0, "No operators available"

        valid_operators = list()
        for candidate_operator in self._gen_operators:
            if candidate_operator.arity >= min_arity and (
                    max_arity is None
                    or candidate_operator.arity <= max_arity):
                valid_operators.append(candidate_operator)

        assert len(valid_operators
                   ) > 0, "No operators available for the given set of arities"

        selected_operators = list()
        for _ in range(k):
            # TODO: consider statistics in future implementations
            operator = random_generator.choice(valid_operators)
            selected_operators.append(operator)

        return selected_operators
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]
def macro_pool_uniform_crossover(parentA: Individual, parentB: Individual,
                                 **kwargs) -> List[Optional[Individual]]:
    """This crossover builds two lists of MacroPools in parentA and parentB
      belonging to common sections, chooses one element for each list.
      parentA and parentB are cloned and subsequently modified (in individualC
      , individualD); each individual will have the chosen MacroPool swapped
      with that of the other.

      **Examples**:

      - parentA has a MacroPool with nodes: [A, B, C, D, E]

      - parentB has a MacroPool with nodes: [F, G, H, I, L]

      - individualC will have a MacroPool with nodes: [F, G, C, D, E]

      - individualD will have a MacroPool with nodes: [A, B, H, I, L]

      If the chosen MacroPools has a different number of nodes:

      **Examples**:

      - parentA has a MacroPool with nodes: [A, B, C, D, E, F, G]

      - parentB has a MacroPool with nodes: [H, I, L]

      - individualC will have a MacroPool with nodes: [H, I, L]

      - individualD will have a MacroPool with nodes: [A, B, C, D, E, F, G]

      Args:
          parentA (Individual): First individual
          parentB (Individual): Second individual

      Returns:
          A list with the new individuals or None if it is not valid
      """
    assert all([
        parentA, parentB
    ]), f"Arity of crossover_3 is 2, received {len(parents)} instead"

    # Copy the graph_manager structure from the parents and set internal hereditary characteristics_____________________________
    # Initialize first son (individualC) from parentA
    individualC = Individual(parentA.constraints, copy_from=parentA)
    individualC.parents = {parentA, parentB}
    individualC.operator = macro_pool_uniform_crossover
    assert all(individualC.graph_manager[n]['frame_path']
               for n in individualC.graph_manager.nodes()
               ), "Illegal frame_path in individual's node"

    # Initialize second son (individualD) from parentB
    individualD = Individual(parentB.constraints, copy_from=parentB)
    individualD.parents = {parentA, parentB}
    individualD.operator = macro_pool_uniform_crossover
    assert all(individualD.graph_manager[n]['frame_path']
               for n in individualD.graph_manager.nodes()
               ), "Illegal frame_path in individual's node"

    # If the individuals are identical -> return the copies
    if parentA == parentB:
        individualC.finalize()
        individualD.finalize()
        return [individualC, individualD]

    # Find the common frames with the same number of nodes inside
    candidates = list()
    for frame_C in parentA.frame_tree.node_dict:
        if isinstance(frame_C.section, MacroPool):
            for frame_D in parentB.frame_tree.node_dict:
                if frame_C.section == frame_D.section and \
                        len(get_nodes_in_frame(parentA, frame_C)) == len(get_nodes_in_frame(parentB, frame_D)):
                    candidates.append((frame_C, frame_D))

    if not candidates:
        return [None]

    # Choose the frames that will be swapped
    chosen_frame_C, chosen_frame_D = random_generator.choice(candidates)

    nodes_in_frame_C = get_nodes_in_frame(individualC, chosen_frame_C)
    nodes_in_frame_D = get_nodes_in_frame(individualD, chosen_frame_D)
    first_node_id_C = nodes_in_frame_C[0]
    first_node_id_D = nodes_in_frame_D[0]

    # __________________________Create movable nodes and fill parameters____________________________________________
    nodes_to_copy_from_C = nodes_in_frame_C
    nodes_to_copy_from_D = nodes_in_frame_D
    first_movable_node_C = individualC.create_movable_nodes(
        individualD, nodes_to_copy_from_D)
    first_movable_node_D = individualD.create_movable_nodes(
        individualC, nodes_to_copy_from_C)

    # ______________________________________________________________________________________________________________
    # Save the (key:)NodeID of the first node of the 'next-chain' and a tuple containing the parent, the first node
    #   outside the chosen frame and the frame path of the nodes in frame (they will be used in individualC.finalize())
    first_node_outside_C = individualC.get_next(nodes_in_frame_C[-1])
    first_node_outside_D = individualD.get_next(nodes_in_frame_D[-1])
    pred_C = individualC.get_predecessors(first_node_id_C)
    pred_D = individualD.get_predecessors(first_node_id_D)
    pred_C = pred_C[0] if pred_C else None
    pred_D = pred_D[0] if pred_D else None
    frame_path_C = individualC.graph_manager[first_node_id_C]['frame_path']
    frame_path_D = individualD.graph_manager[first_node_id_D]['frame_path']
    individualC._unlinked_nodes[first_movable_node_C] = (pred_C,
                                                         first_node_outside_C,
                                                         frame_path_C)
    individualD._unlinked_nodes[first_movable_node_D] = (pred_D,
                                                         first_node_outside_D,
                                                         frame_path_D)

    individualC.finalize()
    is_valid_C = individualC.valid
    assert individualC.valid is False or all(
        individualC.graph_manager[n]['frame_path']
        for n in individualC.graph_manager.nodes(
        )), "Illegal frame_path in individual's node"

    individualD.finalize()
    is_valid_D = individualD.valid
    assert individualD.valid is False or all(
        individualD.graph_manager[n]['frame_path']
        for n in individualD.graph_manager.nodes(
        )), "Illegal frame_path in individual's node"

    individualC = None if not is_valid_C else individualC
    individualD = None if not is_valid_D else individualD
    # if is_valid_C and is_valid_D:
    #     print_individual(parentA, 'parentA', plot=True)
    #     print_individual(parentB, 'parentB', plot=True)
    #     print_individual(individualC, 'individualC', plot=True)
    #     print_individual(individualD, 'individualD', plot=True)
    return [individualC, individualD]
def macro_pool_one_cut_point_crossover(parentA: Individual,
                                       parentB: Individual,
                                       **kwargs) -> List[Optional[Individual]]:
    """This crossover builds two lists of MacroPools in parentA and parentB
    belonging to common sections, chooses one element for each list and
    chooses one node (called cut_node). parentA and parentB are cloned and
    subsequently modified (in individualC, individualD); each individual will
    have the chosen MacroPool with an half copied from the other individual.

    **Examples**:

    - parentA has a MacroPool with nodes: [A, B, C, D, E]

    - parentB has a MacroPool with nodes: [F, G, H, I, L]

    - the chosen cut_node are B and G

    - individualC will have a MacroPool with nodes: [A, B, H, I, L]

    - individualD will have a MacroPool with nodes: [F, G, C, D, E]

    If the chosen MacroPools has a different number of nodes, then two cut
    point will be chosen:

    **Examples**:

    - parentA has a MacroPool with nodes: [A, B, C, D, E, F, G]

    - parentB has a MacroPool with nodes: [H, I, L]

    - the chosen cut_node are F and H

    - individualC will have a MacroPool with nodes: [A, B, C, D, E, F, I, L]

    - individualD will have a MacroPool with nodes: [H, G]

    Args:
        parentA (Individual): First individual
        parentB (Individual): Second individual

    Returns:
        A list with the new individuals or None if it is not valid
    """
    assert all([parentA,
                parentB]), f"Arity of macro_pool_one_cut_point_crossover is 2"

    assert all(parentA.nodes(
        data='frame_path').values()), "Illegal frame_path in individual's node"
    assert all(parentB.nodes(
        data='frame_path').values()), "Illegal frame_path in individual's node"

    # Copy all the genes from the primary parent and set internal creation characteristics______________________________
    # Initialize first son (individualC)
    individualC = Individual(parentA.constraints, copy_from=parentA)
    # TODO: Merge (operaor, (parents)) in a single field
    individualC.parents = {parentA, parentB}
    individualC.operator = macro_pool_one_cut_point_crossover
    # Initialize second son (individualD)
    individualD = Individual(parentB.constraints, copy_from=parentB)
    individualD.parents = {parentA, parentB}
    individualD.operator = macro_pool_one_cut_point_crossover

    assert all(individualC.nodes(
        data='frame_path').values()), "Illegal frame_path in individual's node"
    assert all(individualD.nodes(
        data='frame_path').values()), "Illegal frame_path in individual's node"

    if parentA == parentB:
        individualC.finalize()
        individualD.finalize()
        return [individualC, individualD]

    # Manipulate sections and frames in order to choose the frame that will be copied into the son______________________
    # common_sections = get_common_sections(parentA, parentB)

    candidates = list()
    for frame_C in parentA.frame_tree.node_dict:
        if isinstance(frame_C.section, MacroPool):
            for frame_D in parentB.frame_tree.node_dict:
                if frame_C.section == frame_D.section \
                        and len(get_nodes_in_frame(parentA, frame_C)) > 1 \
                        and len(get_nodes_in_frame(parentB, frame_D)) > 1:
                    candidates.append((frame_C, frame_D))

    if not candidates:
        return [None, None]

    # Chose the frames that will be swapped
    chosen_frame_C, chosen_frame_D = random_generator.choice(candidates)

    nodes_in_frame_C = get_nodes_in_frame(individualC, chosen_frame_C)
    nodes_in_frame_D = get_nodes_in_frame(individualD, chosen_frame_D)

    # __________________________Choose the cut nodes________________________________________________________________
    # Example:
    #   nodesC = [n4_c, n5_c, n6_c, n7_c, n8_c]
    #   cut_nodeC = n6_c
    #   nodesD = [n7_d, n8_d, n9_d, n10_d, n11_d]
    #   nodesC will be = [n4_c, n5_c, n6_c, n10_d, n11_d]
    #   nodesD will be = [n7_d, n8_d, n9_d, n7_c, n8_c]
    cut_node_C = random_generator.choice(nodes_in_frame_C[:-1])
    cut_index_C = nodes_in_frame_C.index(cut_node_C)
    # If the frames have different size -> pick a new random node from the nodes in D
    if len(nodes_in_frame_C) != len(nodes_in_frame_D):
        cut_node_D = random_generator.choice(nodes_in_frame_D[:-1])
        cut_index_D = nodes_in_frame_D.index(cut_node_D)
    else:
        cut_index_D = cut_index_C
        cut_node_D = nodes_in_frame_D[cut_index_D]
    # __________________________Create movable nodes and fill parameters____________________________________________
    nodes_to_copy_from_C = nodes_in_frame_C[:cut_index_C + 1]
    nodes_to_copy_from_D = nodes_in_frame_D[cut_index_D + 1:]
    first_movable_node_C = individualC.create_movable_nodes(
        individualD, nodes_to_copy_from_D)
    first_movable_node_D = individualD.create_movable_nodes(
        individualC, nodes_to_copy_from_C)

    # ______________________________________________________________________________________________________________
    # Save the (key:)NodeID of the first node of the 'next-chain' and a tuple containing the parent, the first node
    #   outside the chosen frame and the frame path of the nodes in frame (they will be used in individualC.finalize())
    first_outside_C = individualC.get_next(nodes_in_frame_C[-1])
    first_outside_D = individualD.get_next(nodes_in_frame_D[-1])
    frame_path_C = individualC.graph_manager[cut_node_C]['frame_path']
    frame_path_D = individualD.graph_manager[cut_node_D]['frame_path']
    individualC._unlinked_nodes[first_movable_node_C] = (cut_node_C,
                                                         first_outside_C,
                                                         frame_path_C)
    individualD._unlinked_nodes[first_movable_node_D] = (cut_node_D,
                                                         first_outside_D,
                                                         frame_path_D)

    individualC.finalize()
    individualD.finalize()
    is_valid_C = individualC.valid
    is_valid_D = individualD.valid
    individualC = None if not is_valid_C else individualC
    individualD = None if not is_valid_D else individualD
    # if is_valid_C and is_valid_D:
    #     print_individual(parentA, 'parentA', plot=True)
    #     print_individual(parentB, 'parentB', plot=True)
    #     print_individual(individualC, 'individualC', plot=True)
    #     print_individual(individualD, 'individualD', plot=True)
    assert all(individualC.nodes(
        data='frame_path').values()), "Illegal frame_path in individual's node"
    assert all(individualD.nodes(
        data='frame_path').values()), "Illegal frame_path in individual's node"

    return [individualC, individualD]
def switch_proc_crossover(parentA: Individual, parentB: Individual,
                          **kwargs) -> List[Optional[Individual]]:
    """Let's consider a sequence of nodes connected through edges with label=
    'next', we will call this sequence `next-chain`. This operator selects
    the `next-chains` belonging to the common sections between the two parents
    (excluding the `main` section) and link a random node with
    ExternalReference parameter in it, to the selected `next-chain` after
    copying it from the source to the destination individual.
    The word 'proc' in method name refers to the `next-chains` that are not
    'main' sections. The destination individual is a copy of a parent chosen
    randomly and then modified with the new cloned `next-chain`.

    Args:
        parentA (Individual): First individual
        parentB (Individual): Second individual

    Returns:
        A list with the new individual or None if it is not valid
    """
    assert all([parentA, parentB]), f"Arity of switch_proc_crossover is 2"

    # Select the primary parent that will give to the son the greatest number of genes (nodes and their parameters)_____
    primary_parent = random_generator.choice([parentA, parentB])
    source_individual = parentA if primary_parent == parentB else parentB
    # Copy all the genes from the primary parent and set internal creation characteristics______________________________
    individual = Individual(primary_parent.constraints,
                            copy_from=primary_parent)
    individual.parents = {parentA, parentB}
    individual.operator = switch_proc_crossover

    root_frames_in_primary_parent = set(
        f.frame for f in primary_parent.frame_tree.root.children
        if f.frame.section.name != 'main')
    root_sections_frames_in_primary_parent = set(
        (f.section, f) for f in root_frames_in_primary_parent)
    root_frames_in_source_individual = set(
        f.frame for f in source_individual.frame_tree.root.children
        if f.frame.section.name != 'main')
    root_sections_frames_in_source_individual = set(
        (f.section, f) for f in root_frames_in_source_individual)
    candidates = list()
    for section_frame_source_individual in root_sections_frames_in_source_individual:
        if section_frame_source_individual[0] in set(
                t2[0] for t2 in root_sections_frames_in_primary_parent):
            candidates.append(section_frame_source_individual)

    if not candidates:
        return [None]

    chosen_section, chosen_frame = random_generator.choice(candidates)

    from .parameter import ExternalReference

    candidates = list()
    for node_id, parameters in individual.nodes(data='parameters').items():
        candidates = candidates + [
            (node_id, p)
            for p in parameters if isinstance(parameters[p], ExternalReference)
        ]

    chosen_node_id, chosen_parameter_name = random_generator.choice(candidates)
    # Copy the structure of the selected frame and its parameters___________________________________________________
    nodes_to_copy_from = get_nodes_in_frame(source_individual, chosen_frame)
    assert len(nodes_to_copy_from) > 0, "No nodes to copy from"

    # Create movable nodes of the selected proc
    first_movable_node = individual.create_movable_nodes(source_individual,
                                                         nodes_to_copy_from,
                                                         is_new_proc=True)
    assert first_movable_node, "Ops, something went wrong in creation of movable nodes"

    individual.arrange_movable_proc(nodes_to_copy_from, source_individual,
                                    first_movable_node)

    # Change the destination of the external reference of the selected node_____________________________________________
    original_node = individual.nodes[chosen_node_id]
    from microgp.parameter import ExternalReference
    for parameter_name, parameter in original_node['parameters'].items():
        if parameter.name == chosen_parameter_name and isinstance(
                parameter, ExternalReference):
            parameter.value = first_movable_node

    individual.finalize()
    is_valid = individual.valid
    individual = None if not is_valid else individual
    # if is_valid:
    #     print_individual(primary_parent, 'switch_proc crossover: primary parent', plot=True)
    #     print_individual(source_individual, 'switch_proc crossover: source individual', plot=True)
    #     print_individual(individual, 'switch_proc crossover: individual', plot=True)
    return [individual]
Exemple #12
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")
     else:
         self.value = random_generator.choice(self.alternatives)