def produce_offspring(parent_a, parent_b):
    """
    ______
    Input:
        two parent genomes
    ______
    Output:
        offspring genome
    ______
        The weights of the more fit parent are kept
        To achieve this it is possible to just
        add the genes that are unique to the flop genome to the fit genome
        
    """

    fit_parent, flop_parent = sorted(
        [parent_a, parent_b], key=lambda x: x.fitness,
        reverse=True)  #reverse sort is from highest to lowest
    fit_genome, flop_genome = utils.load_bot_genome(
        fit_parent.name), utils.load_bot_genome(flop_parent.name)

    offspring_genome = fit_genome

    for net in ['play', 'bid', 'stm']:
        fit_connection_genome, flop_connection_genome = fit_genome[
            "{}_connection_genome".format(net)], flop_genome[
                "{}_connection_genome".format(net)]
        fit_node_genome, flop_node_genome = fit_genome["{}_node_genome".format(
            net)], flop_genome["{}_node_genome".format(net)]

        offspring_genome["{}_connection_genome".format(
            net)] = produce_net_connection_offspring(fit_connection_genome,
                                                     flop_connection_genome)
        offspring_genome["{}_node_genome".format(
            net)] = produce_net_node_offspring(fit_node_genome,
                                               flop_node_genome)

    #check for recursive structure in the connection genome
    if utils.genome_check_for_recursion(fit_parent, genome=offspring_genome):
        print("Recursive Offspring Prevented")
        return False

    return offspring_genome
def mutation_step(bot_name, link_thresh, node_thresh, weights_mut_thresh,
                  rand_weight_thresh, pert_rate):
    """
    ______
    Input:
        The name of the bot that is mutated
        The probabilities for mutations to ocurr
    ______
    Output:
        None
        The bots genome is changed
    """
    reset_score_bool = False
    for net in ["stm", "bid", "play"]:
        thresh_list = [link_thresh, node_thresh, weights_mut_thresh]
        mutate_list = [
            chance <= thresh_list[chance_idx]
            for chance_idx, chance in enumerate(np.random.uniform(size=3))
        ]
        link_mut, node_mut, weights_mut = mutate_list

        if any(mutate_list):
            bot_genome = utils.load_bot_genome(bot_name)
            connection_genome, node_genome = bot_genome[
                "{}_connection_genome".format(net)], bot_genome[
                    "{}_node_genome".format(net)]

            if link_mut:
                connection_genome, link_mutation = mutate_link(
                    node_genome, connection_genome, net)

            if node_mut:
                node_genome, connection_genome, in_mutation, out_mutation = mutate_node(
                    node_genome, connection_genome, net)

            if weights_mut:
                connection_genome = mutate_weights(
                    connection_genome,
                    node_genome,
                    rand_weight_thresh,
                    pert_rate=pert_rate
                )  #does not need the net type bc no innovation numbers are incremented

            bot_genome["{}_connection_genome".format(net)] = connection_genome
            bot_genome["{}_node_genome".format(net)] = node_genome

            utils.save_bot_genome(bot_name, bot_genome)

        if sum(thresh_list) > 0:
            reset_score_bool = True

    if reset_score_bool:
        utils.reset_score(bot_name)
    def __init__(self, name, sigmoid_function=expit):

        self.name = name
        self.species = None
        self.fitness = None

        self.round_score = 0
        self.game_score = 0
        self.cards = []  #an empty hand so to speak
        self.current_stm = [0 for _ in range(10)]  #Initiating stm
        self.sigmoid = sigmoid_function

        if os.path.exists(base_path + '\Bots\{}'.format(self.name)):
            genome = utils.load_bot_genome(self.name)

            bid_node_genome, bid_connection_genome = genome[
                "bid_node_genome"], genome["bid_connection_genome"]
            play_node_genome, play_connection_genome = genome[
                "play_node_genome"], genome["play_connection_genome"]
            stm_node_genome, stm_connection_genome = genome[
                "stm_node_genome"], genome["stm_connection_genome"]

            self.bid_net = self.Network(bid_node_genome,
                                        bid_connection_genome,
                                        net_sigmoid_function=self.sigmoid)
            self.play_net = self.Network(play_node_genome,
                                         play_connection_genome,
                                         net_sigmoid_function=self.sigmoid)
            self.stm_net = self.Network(stm_node_genome,
                                        stm_connection_genome,
                                        net_sigmoid_function=self.sigmoid)

            #print('{} loaded'.format(self.name))

        else:
            print("ERROR: Player {} Directory does not exist.".format(name))
def reproduce(bots, bot_species, names, species_size, preservation_rate):
    """
    ______
    Input:
        bots --> a list of bot objects
        bot_species --> a list of bot names
    ______
    Output:
        names of the new set of bots of the same size as the input set
    ______
        makes the species of bots reproduce into a new set of bots
        25% of offspring is not crossover i.e. only mutations
        There is no specification as to who mate with who
        The better the score the more offspring a bot produces
        So the first produces another 37.5% of the offspring each
        the Second produces another 18.75%, third produces 9.375% etc.
    """

    print("Species Size: ", species_size)

    new_generation = []
    bots = [bot for bot in bots
            if bot.name in bot_species]  #from name to object
    bots.sort(key=lambda bot: bot.fitness,
              reverse=True)  #highest score to lowest

    if species_size <= 5:  #in case the species is too small to reproduce
        if len(bots) >= 5:
            new_species_names, kill_list_names = [
                bot.name for bot in bots[:species_size]
            ], [bot for bot in bots[species_size:]]
            return new_species_names, kill_list_names
        else:  #just reproduce the best bot as many times as needed for new generation
            bot_offspring_count = 0
            for bot in range(species_size):
                offspring_genome = utils.load_bot_genome(bots[0].name)
                utils.save_init_genome(
                    (offspring_name := names[bot_offspring_count]),
                    offspring_genome)
                utils.save_init_score(offspring_name)
                new_generation.append(offspring_name)
                bot_offspring_count += 1
            return new_generation, [bot.name for bot in bots]

    kill_list = [
        bot.name
        for bot in bots[int(np.round(preservation_rate * species_size)):]
    ]  #incinerate the bad part of the generation after speciation

    bots = bots[:int(np.round(
        preservation_rate *
        species_size))]  #remove the incinerated botnames from the bot list

    for bot in bots[:int(np.round(species_size * 0.25))]:
        new_generation.append(bot.name)
    """
    This whole bot_count aspect of the reproduction method
    should be gotten rid of
    but I have not the patience for that rn
    """

    species_offspring_count = 0
    while (len(new_generation) < species_size):
        percentage = 0.375
        for bot in bots:
            bot_offspring_count = 0
            while bot_offspring_count / species_size <= percentage and int(
                    np.round(percentage * species_size)) >= 1:
                if (offspring_genome :=
                        produce_offspring(bot, random.choice(bots))):
                    utils.save_init_genome(
                        offspring_name :=
                        names[species_offspring_count + bot_offspring_count],
                        offspring_genome)
                    utils.save_init_score(offspring_name)
                    new_generation.append(offspring_name)
                    bot_offspring_count += 1
            species_offspring_count += bot_offspring_count
            percentage /= 2
Exemple #5
0
def graph(bot_name, net_type, added_only=True):
    """
    TODO
    https://www.geeksforgeeks.org/python-visualize-graphs-generated-in-networkx-using-matplotlib/
    https://networkx.github.io/documentation/stable/_modules/networkx/drawing/nx_pylab.html#draw
    """
    """
    ______
    Input:
        Bot Genome
        Neural Net Type
    ______
    Output:
        None
        Plots graph of each a neural net in the bot genome
    ______
        The layout can be customized with nx.draw(nn_graph,pos = dictionary)
    """

    bot_genome = utils.load_bot_genome(bot_name)
    connection_genome = bot_genome["{}_connection_genome".format(net_type)]
    node_genome = bot_genome["{}_node_genome".format(net_type)]
    init_innovation_number = utils.init_innovation_numbers[net_type]

    nn_graph = nx.DiGraph(
    )  #initializing the graph; adding the nodes and edges
    for node in node_genome:
        nn_graph.add_node(node["INDEX"], bias=node["BIAS"])
    for gene in connection_genome:
        if gene["INNOVATION"] > init_innovation_number or added_only == False:
            nn_graph.add_edge(gene["IN"], gene["OUT"], weight=gene["WEIGHT"])

    try:  #listing the weights and biases st. the graph can be colored
        edges, weights = zip(
            *nx.get_edge_attributes(nn_graph, 'weight').items())
        nodes, biases = zip(*nx.get_node_attributes(nn_graph, 'bias').items())
    except ValueError:
        print(f"No Connections in {bot_name}'s {net_type} net")
        return
    except Exception as exception:
        sys.exit(f"Graphing {bot_name}'s {net_type} net failed: {exception}")

    plt.title(f"{bot_name} Added Graph Structure in {net_type} net"
              )  #only works if called before nx.draw
    cmap = plt.cm.seismic
    sm = plt.cm.ScalarMappable(cmap=cmap,
                               norm=plt.Normalize(vmin=min(weights + biases),
                                                  vmax=max(weights + biases)))
    sm._A = []
    plt.colorbar(sm)  #colorbar legend so the colors are identifiable

    nx.draw(nn_graph,
            arrows=True,
            alpha=0.5,
            pos=node_positions(nn_graph, net_type),
            edge_color=weights,
            node_color=biases,
            edge_cmap=cmap,
            cmap=cmap)
    nx.draw_networkx_labels(nn_graph, pos=node_positions(nn_graph, net_type))

    plt.show()