def eval_genomes(genomes, config):
     for genome_id, genome in genomes:
         net = create(genome, config, MAP_SIZE)
         #Wrap the network around an agent
         agent = Agent(net, in_proc, out_proc)
         #Evaluate its fitness based on the function given above.
         genome.fitness = evaluation_func(agent)
 def eval_genomes_novelty(genomes, config):
     #Re-evaluate the archive
     evaluator.reevaluate_archive()
     for genome_id, genome in genomes:
         net = create_f(genome, config)
         #Wrap the network around an agent
         agent = Agent(net, in_proc, out_proc)
         #Evaluate its fitness based on the function given above.
         genome.fitness = evaluation_func(genome_id, genome, agent)
def binary_3x3_optimal_genome():
    Config = namedtuple("Config",["genome_config"])
    Genome_config = namedtuple("Genome_config", ["input_keys", "output_keys", "activation_defs", "aggregation_function_defs"])
    from utilities import identity
    from neat.activations import sigmoid_activation
    from neat.aggregations import sum_aggregation, product_aggregation
    activations = {
        "sigmoid": sigmoid_activation,
        "identity": identity
    }
    aggregations = {
        "sum": sum_aggregation,
        "product": product_aggregation
    }
    genome_config = Genome_config(input_keys=[-1,-2], output_keys= [0], activation_defs=activations, aggregation_function_defs=aggregations)
    config = Config(genome_config=genome_config)
    Genome = namedtuple("Genome", ["nodes", "connections"])
    Node = namedtuple("Node", ['bias', 'activation', 'aggregation', 'is_isolated', 'is_switch'])
    Connection = namedtuple("Connection", ["key", "one2one", "extended", "uniform", "weight", "enabled", "is_mod"])

    gatinglayer = Node(bias=1, activation='identity', aggregation='product', is_isolated=False, is_switch=False)
    switchlayer = Node(bias=0, activation='identity', aggregation='sum', is_isolated=False, is_switch=True)
    outputlayer = Node(bias=0, activation='identity', aggregation="sum", is_isolated=True, is_switch=False)

    nodes = {0: outputlayer,
             1: gatinglayer,
             2: switchlayer}

    inpgatconn = Connection(key = (-1,1), one2one=True, extended=False, uniform=True, weight=1, enabled=True, is_mod=False)
    inpswiconn = Connection(key = (-1,2),one2one=True, extended=True, uniform=False, weight=10, enabled=True, is_mod=False)
    gatswiconn = Connection(key = (1,2),one2one=True, extended=False, uniform=True, weight=0.33, enabled=True, is_mod=True)
    rewgatconn = Connection(key = (-2,1),one2one=True, extended=False, uniform=True, weight=1, enabled=True, is_mod=False)
    swioutconn = Connection(key = (2,0),one2one=True, extended=False, uniform=True, weight=1, enabled=True, is_mod=False)

    connections = {
        (-1,1) : inpgatconn,
        (-1,2) : inpswiconn,
        (-2,1) : rewgatconn,
        (1,2) : gatswiconn,
        (2,0) : swioutconn
    }

    genome = Genome(nodes, connections)
    import extended_maps
    network = extended_maps.create(genome, config, 3)
    import render_network
    render_network.draw_net(network,False, "optimal3x3")
    from eval import eval_one_to_one_3x3
    from switch_neuron import Agent
    agent  = Agent(network,reorder_inputs, convert_to_action)
    score = eval_one_to_one_3x3(agent, 1000, 100)
    print(score)
def run(config_file, generations, binary_file, drawfile, progressfile,
        statsfile):

    #Configuring the agent and the evaluation function
    from eval import eval_one_to_one_3x3
    eval_func = eval_one_to_one_3x3
    #Preprocessing for inputs: none
    in_func = reorder_inputs
    from solve import convert_to_action
    out_func = convert_to_action
    #Preprocessing for output - convert float to boolean

    # Load configuration.
    config = neat.Config(GuidedMapGenome, neat.DefaultReproduction,
                         neat.DefaultSpeciesSet, neat.DefaultStagnation,
                         config_file)
    # Create the population, which is the top-level object for a NEAT run.
    p = neat.Population(config)

    # Add a stdout reporter to show progress in the terminal.
    p.add_reporter(neat.StdOutReporter(True))
    stats = Reporters.StatReporterv2()
    p.add_reporter(stats)
    p.add_reporter(Reporters.ProgressTracker(progressfile))
    #p.add_reporter(Reporters.NetRetriever())
    #p.add_reporter(neat.Checkpointer(5))

    # Run for up to 300 generations.
    winner = p.run(make_eval_fun(eval_func, in_func, out_func), generations)

    # Display the winning genome.
    print('\nBest genome:\n{!s}'.format(winner))

    # Show output of the most fit genome against training data.
    print('\nOutput:')
    winner_net = create(winner, config, MAP_SIZE)
    winner_agent = Agent(winner_net, in_func, out_func)
    print("Score in task: {}".format(eval_func(winner_agent)))

    print("Input function: Reorder_inputs")
    print("Output function: convert_to_action")
    render_network.draw_net(winner_net, filename=drawfile)

    #Log the maximum fitness over generations
    from visualize import plot_stats
    plot_stats(stats, False, view=False, filename=statsfile)

    #Uncomment the following if you want to save the network in a binary file
    fp = open(binary_file, 'wb')
    pickle.dump(winner_net, fp)
    fp.close()
def solve_one_to_one_3x3():
    input_keys = [-1, -2, -3, -4]
    output_keys = [0]
    switch_keys = [1, 2, 3]
    node_keys = [4, 5, 6]

    nodes = []

    modulating_nodes_dict = {
        'activation_function': lambda x: clamp(x,-10,10),
        'integration_function': mult,
        'activity': 0,
        'output': 0,
        'bias':1
    }

    node_weights = {4: [(-1, 1), (-4, 1)], 5: [(-2, 1), (-4, 1)], 6: [(-3, 1), (-4, 1)]}
    for i in node_keys:
        node_dict = copy.deepcopy(modulating_nodes_dict)
        node_dict['weights'] = node_weights[i]
        nodes.append(Neuron(i, node_dict))

    switch_std_weights = {
        1: [(-1, 10), (-1, 0), (-1, -10)],
        2: [(-2, 10), (-2, 0), (-2, -10)],
        3: [(-3, 10), (-3, 0), (-3, -10)]
    }
    switch_mod_weights = {
        1: [(4, 1 / 3)],
        2: [(5, 1 / 3)],
        3: [(6, 1 / 3)]
    }
    for key in switch_keys:
        nodes.append(SwitchNeuron(key, switch_std_weights[key], switch_mod_weights[key]))

    node_0_std = {
        'activation_function': lambda x: clamp(x,-10,10),
        'integration_function': sum,
        'activity': 0,
        'output': 0,
        'weights': [(1, 1), (2, 1), (3, 1)],
        'bias' : 0
    }
    nodes.append(Neuron(0, node_0_std))

    net = SwitchNeuronNetwork(input_keys, output_keys, nodes)
    agent = Agent(net,lambda x: x,lambda x: convert_to_action(x))
    return agent
def run(config_file):

    #Configuring the agent and the evaluation function
    from eval import eval_net_xor
    eval_func = eval_net_xor
    #Preprocessing for inputs: none
    in_func = out_func = lambda x: x

    #Preprocessing for outputs: one-hot max encoding.

    # Load configuration.
    config = neat.Config(SwitchGenome, neat.DefaultReproduction,
                         neat.DefaultSpeciesSet, neat.DefaultStagnation,
                         config_file)

    from utilities import heaviside
    config.genome_config.add_activation('heaviside', heaviside)
    # Create the population, which is the top-level object for a NEAT run.
    p = neat.Population(config)

    # Add a stdout reporter to show progress in the terminal.
    p.add_reporter(neat.StdOutReporter(True))
    stats = Reporters.StatReporterv2()
    p.add_reporter(stats)
    #p.add_reporter(Reporters.NetRetriever())
    #p.add_reporter(neat.Checkpointer(5))

    # Run for up to 300 generations.
    winner = p.run(make_eval_fun(eval_func, in_func, out_func), 2000)

    # Display the winning genome.
    print('\nBest genome:\n{!s}'.format(winner))

    # Show output of the most fit genome against training data.
    print('\nOutput:')
    winner_net = create(winner, config)
    winner_agent = Agent(winner_net, in_func, out_func)
    print("Score in task: {}".format(eval_func(winner_agent)))

    #for i, o in (((0,0),0), ((0,1),1), ((1,0),1), ((1,1),0)):
    #    print(f"Input: {i}, Expected: {o}, got {winner_agent.activate(i)}")
    #Uncomment the following if you want to save the network in a binary file
    fp = open('winner_net.bin', 'wb')
    pickle.dump(winner_net, fp)
    fp.close()
    #visualize.draw_net(config, winner, True)
    visualize.plot_stats(stats, ylog=False, view=True)
def solve_tmaze():

    input_keys = [-1,-2,-3,-4,-5]
    output_keys = [0]
    node_keys = [1,2,3]

    nodes = []

    #Aggregating neuron
    params = {
        'activation_function' : lambda x : x,
        'integration_function' : sum,
        'activity': 0,
        'output' : 0,
        'weights' : [(-1,-1), (-5,1)],
        'bias':0
    }
    nodes.append(Neuron(1,params))

    m_params = {
        'activation_function': lambda x: clamp(x, -0.8,0),
        'integration_function': mult,
        'activity': 0,
        'output': 0,
        'weights': [(1, 1), (-4, 1)],
        'bias' : 1
    }
    nodes.append(Neuron(2,m_params))

    std_weights = [(-3,5), (-3,-5)]
    mod_weights = [(2,-1.25*0.5)]
    nodes.append(SwitchNeuron(3,std_weights,mod_weights))

    o_params = {
        'activation_function': tanh,
        'integration_function': sum,
        'activity': 0,
        'output': 0,
        'weights': [(3,1)],
        'bias' : 0
    }
    nodes.append(Neuron(0,o_params))

    net = SwitchNeuronNetwork(input_keys,output_keys,nodes)
    #For input, append the bias to -1 input
    agent = Agent(net, append_bias, convert_to_direction)
    return agent
def main():
    schemes = {
        'switch': switch_neat.create,
        'recurrent': RecurrentNetwork.create,
        'switch_maps': switch_maps.create
    }
    problems = {
        'xor': eval_net_xor,
        'binary_association': eval_one_to_one_3x3,
        'tmaze': TmazeEvaluator().eval_tmaze,
        'double_tmaze': DoubleTmazeEvaluator.eval_double_tmaze,
        'homing_tmaze': HomingTmazeEvaluator().eval_tmaze_homing
    }

    domain_constant = {'tmaze': 2, 'double_tmaze': 4, 'homing_tmaze': 2}

    parser = argparse.ArgumentParser(
        description="Evolve neural networks with neat")
    parser.add_argument(
        '-s',
        '--scheme',
        help=
        f"Choose between the available schemes: {','.join(schemes.keys())}",
        type=str,
        required=True,
        choices=schemes.keys())
    parser.add_argument('-c',
                        '--config',
                        help="The config file",
                        required=True,
                        type=str)
    parser.add_argument('-g',
                        '--generations',
                        help="The number of generations",
                        type=int)
    parser.add_argument(
        '-p',
        '--problem',
        help=f"Available problems: {','.join(problems.keys())}",
        required=True,
        type=str,
        choices=problems.keys())
    parser.add_argument('--map_size',
                        help="Set the map size for the relevant schemes",
                        type=int)
    parser.add_argument('--dump',
                        help="Dump the network in a binary file",
                        type=str)
    parser.add_argument('--num_episodes',
                        help="Number of episodes for tmaze/binary_association",
                        type=int)
    parser.add_argument('--switch_interval',
                        help="Interval of episodes for "
                        "shuffling the associations",
                        type=int)
    parser.add_argument(
        '--novelty',
        help='Use the novelty metric instead of the fitness function',
        action="store_true")
    parser.add_argument('--threshold',
                        help='Threshold for a new genome to enter the archive',
                        type=float,
                        default=1)
    parser.add_argument(
        "--snap_inter",
        help="Snapshot interval for association problem novelty search",
        type=int)
    parser.add_argument(
        "--draw",
        help=
        'Render the network to a picture. Provide the name of the picture.',
        type=str,
        default=None)
    parser.add_argument(
        "--log",
        help="Log the max fitness per generation to text file. (Append)",
        type=str,
        default=None)
    args = parser.parse_args()

    eval_f = problems[args.problem]
    in_f = identity
    out_f = identity
    genome = neat.DefaultGenome
    evaluator = None
    #Configure genome based on the encoding scheme and neurons used
    if args.scheme == 'switch':
        genome = switch_neat.SwitchGenome
    elif args.scheme == 'switch_maps':
        genome = switch_maps.SwitchMapGenome

    #Configure the pre-processing and post-processing functions based on
    #the environment
    if args.problem == 'binary_association':
        out_f = convert_to_action
    elif args.problem in ['tmaze', 'double_tmaze', 'homing_tmaze']:
        out_f = convert_to_direction

    #If we use the map-based encoding scheme add the map size parameter to the function
    #responsible for creating the network from the genotype.
    create_f = None
    if args.map_size is not None and (args.scheme in ['maps', 'switch_maps']):
        create_f = partial(schemes[args.scheme], map_size=args.map_size)
    else:
        create_f = schemes[args.scheme]

    num_episodes = 100
    s_inter = 20
    if args.num_episodes is not None:
        num_episodes = args.num_episodes
    if args.switch_interval is not None:
        s_inter = args.switch_interval
    #If the problem is the t-maze task, use the extra parameters episodes and switch interval
    if args.problem == 'tmaze':
        if args.novelty:
            evaluator = TmazeNovelty(num_episodes,
                                     samples=4,
                                     threshold=args.threshold)
            eval_f = evaluator.eval
        else:
            evaluator = TmazeEvaluator(num_episodes, samples=4)
            eval_f = evaluator.eval_tmaze
    elif args.problem == 'double_tmaze':
        if args.novelty:
            evaluator = DoubleTmazeNovelty(num_episodes,
                                           samples=4,
                                           threshold=args.threshold)
            eval_f = evaluator.eval
        else:
            evaluator = DoubleTmazeEvaluator(num_episodes, samples=4)
            eval_f = evaluator.eval_double_tmaze
    elif args.problem == 'homing_tmaze':
        if args.novelty:
            evaluator = HomingTmazeNovelty(num_episodes,
                                           samples=4,
                                           threshold=args.threshold)
            eval_f = evaluator.eval
        else:
            evaluator = HomingTmazeEvaluator(num_episodes, samples=4)
            eval_f = evaluator.eval_tmaze_homing
    elif args.problem == 'binary_association':
        if args.novelty:
            evaluator = AssociationNovelty(num_episodes,
                                           rand_iter=args.switch_interval,
                                           snapshot_inter=args.snap_inter,
                                           threshold=args.threshold)
            eval_f = evaluator.eval
        else:
            eval_f = partial(eval_one_to_one_3x3,
                             num_episodes=num_episodes,
                             rand_iter=s_inter)

    def make_eval_fun(evaluation_func, in_proc, out_proc, evaluator=None):
        def eval_genomes(genomes, config):
            for genome_id, genome in genomes:
                net = create_f(genome, config)
                #Wrap the network around an agent
                agent = Agent(net, in_proc, out_proc)
                #Evaluate its fitness based on the function given above.
                genome.fitness = evaluation_func(agent)

        def eval_genomes_novelty(genomes, config):
            #Re-evaluate the archive
            evaluator.reevaluate_archive()
            for genome_id, genome in genomes:
                net = create_f(genome, config)
                #Wrap the network around an agent
                agent = Agent(net, in_proc, out_proc)
                #Evaluate its fitness based on the function given above.
                genome.fitness = evaluation_func(genome_id, genome, agent)

        if args.novelty:
            return eval_genomes_novelty
        else:
            return eval_genomes

    config = neat.Config(genome, neat.DefaultReproduction,
                         neat.DefaultSpeciesSet, neat.DefaultStagnation,
                         args.config)
    config.genome_config.add_activation('heaviside', heaviside)

    # Create the population, which is the top-level object for a NEAT run.
    p = neat.Population(config)
    #Add a stdout reporter to show progress in the terminal.
    p.add_reporter(neat.StdOutReporter(True))
    stats = StatisticsReporter()
    p.add_reporter(stats)
    if args.problem in ['double_tmaze', 'tmaze', 'homing_tmaze']:
        if args.novelty:
            mutator = Reporters.EvaluatorMutator(evaluator.evaluator)
        else:
            mutator = Reporters.EvaluatorMutator(evaluator)
        p.add_reporter(mutator)

    # Run for up to ... generations.
    if args.novelty:
        f = make_eval_fun(eval_f, in_f, out_f, evaluator)
    else:
        f = make_eval_fun(eval_f, in_f, out_f)
    winner = p.run(f, args.generations)

    #If we are using the novelty metric get the winner from the archive
    if args.novelty:
        winnerid = evaluator.get_best_id()
        winner = evaluator.archive[winnerid]['genome']
        winner_agent = evaluator.archive[winnerid]['agent']
    else:
        print('\nBest genome:\n{!s}'.format(winner))
        winner_net = create_f(winner, config)
        winner_agent = Agent(winner_net, in_f, out_f)

    if args.novelty:
        if args.problem == 'binary_association':
            score = evaluator.eval_func(winner_agent)[0]
        else:
            score = evaluator.evaluator.eval_func(winner_agent)[0]
    else:
        score = eval_f(winner_agent)
    print("Score in task: {}".format(score))
    if args.draw is not None:
        if args.scheme in ['maps', 'switch_maps']:
            map_size = args.map_size
        else:
            map_size = -1
        render_network.draw_genotype(config,
                                     winner,
                                     filename=args.draw,
                                     map_size=map_size)

    if args.log is not None:
        fp = open(args.log, 'a')
        best_fitness = [str(c.fitness) for c in stats.most_fit_genomes]
        mfs = ' '.join(best_fitness)
        fp.write(mfs)
        fp.write("\n")
        fp.close()

    if args.dump is not None:
        fp = open(args.dump, 'wb')
        pickle.dump(winner, fp)
        fp.close()
        print(f'Agent pre-processing function: {in_f.__name__}')
        print(f'Agent post-processing function: {out_f.__name__}')