# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/02_crossover.ipynb (unless otherwise specified).

__all__ = ['cxDictUniform', 'cxDictBlender', 'cxDictBlenderIfCan']

# Cell
import random

def cxDictUniform(ind1, ind2, indpb):
    "Cross over function that changes the corresponding value of two individual at the probability of `indpb`."
    for key in ind1:
        if key in ind2:
            if random.random() < indpb:
                ind1[key], ind2[key] = ind2[key], ind1[key]
    return ind1, ind2

# Cell
def cxDictBlender(ind1, ind2, alpha, fix_invalid=False):
    """Dictionary version of cxBlender.

    Executes a blend crossover that modify in-place the input individuals.
    The blend crossover expects `sequence` individuals of floating point numbers.

    -  alpha: Extent of the interval in which the new values can be drawn for each attribute on both side of the parents' attributes.
    """
    for key in ind1:
        if key in ind2:
            gamma = (1. + 2. * alpha) * random.random() - alpha
            x1, x2 = ind1[key], ind2[key]
            ind1[key] = (1. - gamma) * x1 + gamma * x2
            ind2[key] = gamma * x1 + (1. - gamma) * x2
    if fix_invalid:
        for p in ind1.params:
            ind1[p.name] = p.is_valid(ind1[p.name], fix=True)
        for p in ind2.params:
            ind2[p.name] = p.is_valid(ind2[p.name], fix=True)

    return ind1, ind2

def cxDictBlenderIfCan(ind1, ind2, alpha, indpb, fix_invalid=False):
    """A mixed version of `cxDictUniform` and `cxDictBlender`. For floating numbers, cxBlender will be applied;
    for other types, cxUniform will be applied."""
    for key in ind1:
        if key in ind2:
            x1, x2 = ind1[key], ind2[key]
            try:
                x1*4.2, x2*4.2
                gamma = (1. + 2. * alpha) * random.random() - alpha
                ind1[key] = (1. - gamma) * x1 + gamma * x2
                ind2[key] = gamma * x1 + (1. - gamma) * x2
            except TypeError:
                if random.random() < indpb:
                    ind1[key], ind2[key] = ind2[key], ind1[key]
    if fix_invalid:
        for p in ind1.params:
            ind1[p.name] = p.is_valid(ind1[p.name], fix=True)
        for p in ind2.params:
            ind2[p.name] = p.is_valid(ind2[p.name], fix=True)


    return ind1, ind2