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_candidate_transformations = np.copy(
        pop_member1.attributes["num_candidate_transformations"]
    )
    for i in range(pop_member1.num_words):
        if np.random.uniform() < 0.5:
            indices_to_replace.append(i)
            words_to_replace.append(pop_member2.words[i])
            num_candidate_transformations[i] = pop_member2.attributes[
                "num_candidate_transformations"
            ][i]
    new_text = pop_member1.attacked_text.replace_words_at_indices(
        indices_to_replace, words_to_replace
    )
    return (
        new_text,
        {"num_candidate_transformations": num_candidate_transformations},
    )