def METHOD_NAME(self, pop_member1, pop_member2):
    """Actual operation that takes `pop_member1` text and `pop_member2`
    text and mixes the two to generate crossover between `pop_member1` and
    `pop_member2`.
    Args:
        pop_member1 (PopulationMember): The first population member.
        pop_member2 (PopulationMember): The second population member.
    Returns:
        Tuple of `AttackedText` and a dictionary of attributes.
    """
    indices_to_replace = []
    words_to_replace = []
    num_replacements_left = np.copy(pop_member1.attributes["num_replacements_left"])
    # To better simulate the reproduction and biological crossover,
    # IGA randomly cut the text from two parents and concat two fragments into a new text
    # rather than randomly choose a word of each position from the two parents.
    crossover_point = np.random.randint(0, pop_member1.num_words)
    for i in range(crossover_point, pop_member1.num_words):
        indices_to_replace.append(i)
        words_to_replace.append(pop_member2.words[i])
        num_replacements_left[i] = pop_member2.attributes["num_replacements_left"][
            i
        ]
    new_text = pop_member1.attacked_text.replace_words_at_indices(
        indices_to_replace, words_to_replace
    )
    return new_text, {"num_replacements_left": num_replacements_left}