def test_fittest_2d(): INSERTION_POINTS = [0, 10, 20] # where we copy the ref chromosome # using numpy as fancy indexing in TF is a pain and perf is not critical. ref_chromosome = np.random.randint(0, 2, (32, 32)) pop1 = np.random.randint(0, 2, (64, 32, 32)) pop2 = np.random.randint(0, 2, (64, 32, 32)) for idx in INSERTION_POINTS: pop1[idx] = ref_chromosome ref_chromosome = B.tensor(ref_chromosome) pop1 = B.tensor(pop1) pop2 = B.tensor(pop2) fitness_function = InvertedCosineSimilarity(ref_chromosome) selector = SelectFittest() selected_pop, fitness_scores, metrics = selector(fitness_function, pop1, pop2) print(selected_pop) assert selected_pop.shape == pop1.shape # check the exact chromosome is the three top choice assert B.tensor_equal(selected_pop[0], ref_chromosome) assert B.tensor_equal(selected_pop[1], ref_chromosome) assert B.tensor_equal(selected_pop[2], ref_chromosome)
def test_sum2d(): t = [[[1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1]]] inputs = B.tensor(t) print(inputs) result = Sum().call(inputs) assert result.shape == (3, ) print(result) expected = B.tensor([9, 9, 9]) assert B.tensor_equal(result, expected)
def test_fittest(): ref = B.tensor([2, 0, 1, 1, 0, 2, 1, 1]) d = B.tensor([2, 1, 1, 0, 1, 1, 1, 1]) pop = B.tensor([ref, d, ref, d, ref, d]) fitness_function = InvertedCosineSimilarity(ref) selector = SelectFittest() selected_pop, fitness_scores, metrics = selector(fitness_function, pop, pop) print(selected_pop) assert selected_pop.shape == pop.shape for chromosome in selected_pop: assert B.tensor_equal(chromosome, ref)
def test_cosine_similarity_1dpopulation(backends): "test the function works on a population and broadcast correctly" reference = [1, 0, 1, 1, 0, 1, 1, 1] different = [1, 1, 1, 0, 1, 1, 1, 1] r = B.tensor(reference) d = B.tensor(different) population = B.tensor([r, d, r]) cs = InvertedCosineSimilarity(r) # similar vector have a distance of 1 distances = cs(population) print("distances", distances) assert B.assert_near(distances[0], 1, absolute_tolerance=0.001) assert not B.assert_near(distances[1], 1, absolute_tolerance=0.001) assert B.assert_near(distances[2], 1, absolute_tolerance=0.001)
def _flatten_population(self, population): """Convert the population to a 1D array as many ops (e.g norm) don't work on Ndimension """ if len(population.shape) < 3: return population num_chromosomes = population.shape[0] flattened_size = int(B.prod(B.tensor(population.shape[1:]))) return B.reshape(population, (num_chromosomes, flattened_size))
def assign(self, chromosomes): """Assign concrete values to the input """ chromosomes = B.tensor(chromosomes) if chromosomes.shape != self.shape: raise ValueError( 'Incompatible input shape expected: %s - got: %s' % (self.shape, chromosomes.shape)) self.chromosomes = chromosomes
def call(self, population): if not self.debug: population = B.shuffle(population) # how many chromosomes to crossover num_reversed_chromosomes = int(population.shape[0] * self.population_fraction) self.print_debug('num chromosomes', num_reversed_chromosomes) # compute the shape needed for the mutation mutations_shape = [num_reversed_chromosomes] for idx, frac in enumerate(self.max_reverse_probability): max_genes = int(population.shape[idx + 1] * frac + 1) # ! not an error: reverse need at least 2 indices to make sense. if max_genes > 2: num_genes = B.randint(2, high=max_genes) else: num_genes = 2 mutations_shape.append(num_genes) self.print_debug(idx, 'num_genes', num_genes, 'max', max_genes) self.print_debug("population_shape:", population.shape) self.print_debug("mutation_shape:", mutations_shape) # compute the fancy indexing dynamlically # ! the start point must be randomized slices = [slice(0, num_reversed_chromosomes)] for idx, crossover_size in enumerate(mutations_shape[1:]): # ! making indexing explicit as its a huge pitfall mutation_dim = idx + 1 max_start = population.shape[mutation_dim] - crossover_size + 1 start = B.randint(0, max_start) # start = random.randint(0, max_start) slices.append(slice(start, crossover_size + start)) slices = tuple(slices) tslices = slices2array(slices) self.print_debug('slices', slices) # revesing reversed_population = population[slices] axis = B.tensor([x for x in range(1, len(reversed_population.shape))]) reversed_population = B.reverse(reversed_population, axis) self.print_debug('reversed population', reversed_population) # assigning population = B.assign(population, reversed_population, tslices) return population
def __init__(self, max_sum_value=None, **kwargs): """Compute the sum of the chromosomes as fitness values. Args: expected_max_sum_value (int, optional): What is the maximum value the sum per chromosome will reach. Used to normalize the fitness function between 0 and 1 if specified. Defaults to None. Note: This fitness function is used to solve the MAXONE problem. """ super(Sum, self).__init__(**kwargs) if max_sum_value: self.max_sum_value = B.tensor(max_sum_value) else: self.max_sum_value = None
def __init__(self, reference_chromosome, **kwargs): """Inverted Cosine similarity function that returns 1 when chromosomes are similar to the reference chromose and [0, 1[ when different For reference implementation see https://github.com/scipy/scipy/blob/v0.14.0/scipy/spatial/distance.py#L267 # noqa Args: reference_chromosome (tensor1D): reference_chromosome. """ super(InvertedCosineSimilarity, self).__init__(**kwargs) # cache ref chromosome flattend self.ref_chromosome = B.flatten(B.tensor(reference_chromosome)) # caching ref pop norm self.ref_norm = B.norm(B.cast(self.ref_chromosome, B.floatx()))
def test_helloworld(): "Solve the MAXONE problem" NUM_EVOLUTIONS = 10 SHAPE = (20, 20) MAX_VAL = 1 population = randint_population(SHAPE, MAX_VAL) inputs = Input(SHAPE) x = RandomMutations1D(max_gene_value=1)(inputs) outputs = UniformCrossover1D()(x) gf = EvoFlow(inputs, outputs, debug=0) fitness_function = Sum(max_sum_value=SHAPE[1]) evolution_strategy = SelectFittest() gf.compile(evolution_strategy, fitness_function) gf.summary() results = gf.evolve(population, generations=NUM_EVOLUTIONS, callbacks=[DummyCallback()], verbose=0) assert results metrics = results.get_metrics_history() # check metrics for metric_grp, data in metrics.items(): for metric_name, vals in data.items(): assert isinstance(vals, list) if metric_grp == 'Fitness function': assert len(vals) == 10 assert isinstance(vals[9], float) assert 'mean' in data assert 'max' in data # assert the engine solved the (Trivial) problem max_fitness = metrics['Fitness function']['max'] assert max(max_fitness) == 1 assert max_fitness[9] == 1 assert min(max_fitness) < 1 # max sure we did improve # check population value population = results.get_populations() assert (population.shape) == SHAPE assert B.tensor_equal(population[0], B.tensor([1] * SHAPE[1]))
def uniform_population(shape, dtype=B.intx()): """Generate a uniform population made of Integers. Uniform means that each chromosome contains only one time each value and each chromosome have them in different order. Args: (set of ints): shape of the population. Its of the form (num_chromosomes, chromosome size) dtype (str): tensor type Returns: Tensor: uniform population. """ population = [] chromosome = B.range(shape[1], dtype=dtype) for i in range(shape[0]): chromosome = B.shuffle(chromosome) population.append(chromosome) return B.tensor(population)
def test_cosine_2d(backends): INSERTION_POINTS = [0, 10, 20] # where we copy the ref chromosome ref_chromosome = B.randint(0, 2, (32, 32)) ref_pop = B.randint(0, 2, (64, 32, 32)) ref_pop = B.as_numpy_array(ref_pop) inserstion = B.as_numpy_array(ref_chromosome) for idx in INSERTION_POINTS: ref_pop[idx] = inserstion ref_pop = B.tensor(ref_pop) cs = InvertedCosineSimilarity(ref_chromosome) distances = cs(ref_pop) print(distances) for idx, dst in enumerate(distances): if idx in INSERTION_POINTS: assert B.assert_near(dst, 1.0, absolute_tolerance=0.001) else: assert dst < 1 assert dst > 0
def test_eager(): "when passing concrete value GF is suppposed to return a concrete value" val = B.tensor(42) assert Dummy(debug=True)(val) == val
def test_box_unbox_tensor(): val = tensor([1, 2, 3]) assert tensor_equal(unbox(box(val)), val)