def test_ND(): "test various tensor size random" TEST_INPUTS = [ [SingleCrossover1D, (2, 4), 0.5], [SingleCrossover2D, (2, 4, 4), (0.5, 0.5)], [SingleCrossover3D, (2, 4, 4, 4), (0.5, 0.5, 0.5)], ] for inputs in TEST_INPUTS: OP = inputs[0] pop_shape = inputs[1] mutations_probability = inputs[2] population_fraction = 1 population = B.randint(0, 1024, pop_shape) # eager RM = OP(population_fraction=population_fraction, mutations_probability=mutations_probability) population = RM(population) assert B.is_tensor(population) assert population.shape == pop_shape # graph RM = OP(population_fraction=population_fraction, mutations_probability=mutations_probability) population = RM._call_from_graph(population) assert B.is_tensor(population) assert population.shape == pop_shape
def test_crossover1D_output_shape(): POPULATION_SHAPE = (8, 6) population = B.randint(0, 1024, POPULATION_SHAPE) population_fraction = 0.5 mutations_probability = 0.2 original_population = copy(population) population = DualCrossover1D(population_fraction, mutations_probability, debug=True)(population) cprint(population, 'cyan') cprint(original_population, 'yellow') assert population.shape == POPULATION_SHAPE # measuring mutation rate diff = B.clip(abs(population - original_population), 0, 1) # row test num_ones_in_row = 0 for col in diff: num_ones_in_row = max(list(col).count(1), num_ones_in_row) max_one_in_row = int(POPULATION_SHAPE[1] * mutations_probability) assert num_ones_in_row == max_one_in_row assert num_ones_in_row
def test_binary_val_default_params(): pop_shape = (6, 4) population = B.randint(0, 2, pop_shape) population = RandomMutations1D(max_gene_value=1, debug=1)(population) print(population) assert B.max(population) == 1 assert not B.min(population)
def test_max_gene_val_2d(): MAX_VAL = 10 t = B.randint(0, MAX_VAL + 1, (10, 10, 10)) max_sum_value = MAX_VAL * 10 * 10 v = Sum(max_sum_value=max_sum_value).call(t) assert v.shape == (10, ) for t in v: assert t < 1
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)) for idx in INSERTION_POINTS: ref_pop[idx] = ref_chromosome cs = InvertedCosineSimilarity(ref_chromosome) distances = cs(ref_pop) for idx, dst in enumerate(distances): if idx in INSERTION_POINTS: assert int(dst) == 1 else: assert dst < 1 assert dst > 0
def call(self, populations): """ Create the mask use to generate mutations Args: population_shape (list): population tensor shape. Returns: tensor: mask Generation works by: 1. creating a slice that contains the mutation 2. Inserting it into the mask 3. Shuffle the mask in every dimension to distribute them """ results = [] for population in populations: affected_population = int(population.shape[0] * self.population_fraction) # Build sub tensors & slices by iterating through tensor dimensions sub_tensor_shape = [affected_population] slices = [slice(0, affected_population)] for idx, pop_size in enumerate(population.shape[1:]): midx = idx - 1 # recall dim1 are genes. tsize = int(pop_size * self.mutations_probability[midx]) sub_tensor_shape.append(tsize) slices.append(slice(0, tsize)) slices = tuple(slices) self.print_debug("sub_tensor_shape", sub_tensor_shape) # drawing mutations mutations = B.randint(self.min_mutation_value, self.max_mutation_value + 1, shape=sub_tensor_shape) # blank mask mask = B.zeros(population.shape) # add mutations mask[slices] = mutations # shuffle mask every axis B.full_shuffle(mask) # mutate population = population + mask # normalize if self.max_gene_value or self.min_gene_value: self.print_debug("min_gen_val", self.min_gene_value) self.print_debug("max_gen_val", self.max_gene_value) population = B.clip(population, min_val=self.min_gene_value, max_val=self.max_gene_value) results.append(population) return results
def test_fittest_2d(): INSERTION_POINTS = [0, 10, 20] # where we copy the ref chromosome ref_chromosome = B.randint(0, 2, (32, 32)) pop1 = B.randint(0, 2, (64, 32, 32)) pop2 = B.randint(0, 2, (64, 32, 32)) for idx in INSERTION_POINTS: pop1[idx] = ref_chromosome fitness_function = InvertedCosineSimilarity(ref_chromosome) selector = SelectFittest() selected_pop, fitness_scores = 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 = B.randint(0, 10, (10, 10, 10)) v = Sum().call(t) result = [] for r in t: result.append(B.sum(r, axis=-1)) result = B.tensor(result) assert v.shape == (10, ) for idx, a in enumerate(v): assert v[idx].all() == result[idx].all()
def genRandIntPopulation(shape, max_value, min_value=0): """Generate a random population of Chromosome made of Integers Args: (set of ints): shape of the population. Its of the form (num_chromosomes, chromosome_dim_1, .... chromesome_dim_n) max_value (int): Maximum value taken by a given gene. min_value (int, optional): Min value a gene can take. Defaults to 0. Returns: Tensor: random population. """ high = max_value + 1 return B.randint(low=min_value, high=high, shape=shape)
def compute(self, population): # mix genomes population_copy = B.copy(population) B.shuffle(population_copy) # how many chromosomes to crossover num_crossover_chromosomes = int(population.shape[0] * self.population_fraction) if self.debug: print('num chromosomes', num_crossover_chromosomes) if not self.has_cache: self.print_debug("Caching fancy indexing") self.has_cache = True # compute the shape needed for the mutation mutations_shape = [num_crossover_chromosomes] for idx, frac in enumerate(self.crossover_probability): num_genes = int(population.shape[idx + 1] * frac) mutations_shape.append(num_genes) self.mutations_shape = mutations_shape self.print_debug("population_shape:", population.shape) self.print_debug("mutation_shape:", self.mutations_shape) else: self.print_debug("Using cached fancy indexing") # compute the fancy indexing dynamlically # ! the start point must be randomized slices = [slice(0, num_crossover_chromosomes)] for idx, crossover_size in enumerate(self.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, 1)[0] slices.append(slice(start, crossover_size + start)) slices = tuple(slices) self.print_debug(slices) # crossover cross_section = population_copy[slices] population[slices] = cross_section return population
def test_ND(): "test various tensor size random" TEST_INPUTS = [ [RandomMutations1D, (2, 4), 0.5], [RandomMutations2D, (2, 4, 4), (0.5, 0.5)], [RandomMutations3D, (2, 4, 4, 4), (0.5, 0.5, 0.5)], ] for inputs in TEST_INPUTS: OP = inputs[0] pop_shape = inputs[1] mutations_probability = inputs[2] max_gene_value = 10 min_gene_value = 0 population_fraction = 1 min_mutation_value = 1 max_mutation_value = 1 population = B.randint(0, max_gene_value, pop_shape) # eager RM = OP(population_fraction=population_fraction, mutations_probability=mutations_probability, min_gene_value=min_gene_value, max_gene_value=max_gene_value, min_mutation_value=min_mutation_value, max_mutation_value=max_mutation_value) population = RM(population) assert B.is_tensor(population) assert population.shape == pop_shape # graph RM = OP(population_fraction=population_fraction, mutations_probability=mutations_probability, min_gene_value=min_gene_value, max_gene_value=max_gene_value, min_mutation_value=min_mutation_value, max_mutation_value=max_mutation_value) population = RM._call_from_graph(population) assert B.is_tensor(population) assert population.shape == pop_shape
def test_direct_2d(): NUM_EVOLUTIONS = 2 POPULATION_SIZE = 32 GENE_SIZE = 10 MAX_VAL = 256 SHAPE = (POPULATION_SIZE, GENE_SIZE, GENE_SIZE) population = B.randint(0, MAX_VAL, SHAPE) inputs = Input(shape=SHAPE) x = RandomMutations2D(max_gene_value=1, min_gene_value=0)(inputs) outputs = UniformCrossover2D()(x) gf = GeneFlow(inputs, outputs) fitness_function = Sum(max_sum_value=GENE_SIZE) evolution_strategy = SelectFittest() gf.compile(evolution_strategy, fitness_function) gf.evolve(population, num_evolutions=NUM_EVOLUTIONS)
def test_uniform_distribution(): """check that every gene of the tensor are going to be flipped equally Note: # ! We need enough iterations and chromosomes to reduce collision # ! and ensure numerical stability """ NUM_ITERATIONS = 1000 GENOME_SHAPE = (20, 4, 4) population = B.randint(0, 1024, GENOME_SHAPE) population_fraction = 1 crossover_probability = (0.5, 0.5) # each gene proba of being mutated 0.5*0.5 > 0.25 # each chromosome proba of being mutated 1 # => gene average hit rate: 1000 / (1/4) ~250 MIN_DIFF_BOUND = 200 MAX_DIFF_BOUND = 300 OP = RandomMutations2D(population_fraction, crossover_probability) # diff matrix previous_population = copy(population) population = OP(population) diff = B.clip(abs(population - previous_population), 0, 1) for _ in range(NUM_ITERATIONS - 1): previous_population = copy(population) population = OP(population) curr_diff = B.clip(abs(population - previous_population), 0, 1) # acumulating diff matrix diff += curr_diff print(curr_diff) for c in diff: print(c) print('mean', B.mean(c), 'min', B.min(c), 'max', B.max(c)) assert B.min(c) > MIN_DIFF_BOUND assert B.max(c) < MAX_DIFF_BOUND assert MIN_DIFF_BOUND < B.mean(c) < MAX_DIFF_BOUND
def test_1D_shape(): POPULATION_SHAPE = (64, 16) population = B.randint(0, 1024, POPULATION_SHAPE) population_fraction = 0.5 crossover_size_fraction = 0.2 original_population = copy(population) population = SingleCrossover1D(population_fraction, crossover_size_fraction, debug=1)(population) cprint(population, 'cyan') cprint(original_population, 'yellow') assert population.shape == POPULATION_SHAPE # measuring mutation rate diff = B.clip(abs(population - original_population), 0, 1) print(diff, 'cyan') # row test num_ones_in_row = 0 for col in diff: num_ones = list(col).count(1) print(num_ones) num_ones_in_row = max(num_ones, num_ones_in_row) max_one_in_row = POPULATION_SHAPE[1] * crossover_size_fraction assert num_ones_in_row <= max_one_in_row assert num_ones_in_row # col diff = diff.T num_ones_in_col = 0 for col in diff: num_ones_in_col = max(list(col).count(1), num_ones_in_col) max_one_in_col = POPULATION_SHAPE[0] * population_fraction assert max_one_in_col - 2 <= num_ones_in_col <= max_one_in_col
def test_mutation2d_graph_mode(): "make sure the boxing / unboxing works in graph mode" pop_shape = (2, 4, 4) max_gene_value = 10 min_gene_value = 0 population_fraction = 1 mutations_probability = (0.5, 0.5) min_mutation_value = 1 max_mutation_value = 1 population = B.randint(0, max_gene_value, pop_shape) RM = RandomMutations2D(population_fraction=population_fraction, mutations_probability=mutations_probability, min_gene_value=min_gene_value, max_gene_value=max_gene_value, min_mutation_value=min_mutation_value, max_mutation_value=max_mutation_value, debug=True) population = RM._call_from_graph(population) assert B.is_tensor(population) assert population.shape == pop_shape
def test_mutation2d_eager(): pop_shape = (2, 4, 4) max_gene_value = 10 min_gene_value = 0 population_fraction = 1 mutations_probability = (0.5, 0.5) min_mutation_value = 1 max_mutation_value = 1 population = B.randint(0, max_gene_value, pop_shape) # save original original_population = copy(population) cprint('[Initial genepool]', 'blue') cprint(original_population, 'blue') RM = RandomMutations2D(population_fraction=population_fraction, mutations_probability=mutations_probability, min_gene_value=min_gene_value, max_gene_value=max_gene_value, min_mutation_value=min_mutation_value, max_mutation_value=max_mutation_value, debug=True) population = RM(population) cprint('\n[Mutated genepool]', 'yellow') cprint(population, 'yellow') cprint('\n[Diff]', 'magenta') diff = population - original_population cprint(diff, 'magenta') assert B.is_tensor(population) assert population.shape == pop_shape assert B.max(diff) <= max_mutation_value for chromosome in diff: assert B.sum(chromosome) == 4
def test_dualcrossover2d_distribution(): """check that every gene of the tensor are going to be flipped equally Note: # ! We need enough iterations and chromosomes to reduce collision # ! and ensure numerical stability """ NUM_ITERATIONS = 1000 GENOME_SHAPE = (100, 4, 4) population = B.randint(0, 1024, GENOME_SHAPE) population_fraction = 1 crossover_probability = (0.5, 0.5) OP = DualCrossover2D(population_fraction, crossover_probability) # diff matrix previous_population = copy(population) population = OP(population) diff = B.clip(abs(population - previous_population), 0, 1) print(diff) for _ in range(NUM_ITERATIONS - 1): previous_population = copy(population) population = OP(population) curr_diff = B.clip(abs(population - previous_population), 0, 1) # acumulating diff matrix diff += curr_diff # print(curr_diff) for c in diff: print(c) print('mean', B.mean(c), 'min', B.min(c), 'max', B.max(c)) assert B.min(c) > 50 assert B.max(c) < NUM_ITERATIONS / 2 assert 200 < B.mean(c) < NUM_ITERATIONS / 2
def test_uniform_2Dcrossover_randomness_shape(): GENOME_SHAPE = (10, 4, 4) population = B.randint(0, 1024, GENOME_SHAPE) population_fraction = 0.5 crossover_probability = (0.5, 0.5) original_population = copy(population) OP = UniformCrossover2D(population_fraction, crossover_probability) population = OP(population) diff = B.clip(abs(population - original_population), 0, 1) print(diff) expected_mutations = original_population.shape[0] * population_fraction mutated_chromosomes = [] for c in diff: if B.max(c): mutated_chromosomes.append(c) num_mutations = len(mutated_chromosomes) # sometime we have a collision so we use a delta assert abs(num_mutations - expected_mutations) < 2 mutated_rows = crossover_probability[0] * GENOME_SHAPE[1] mutated_cells = crossover_probability[0] * GENOME_SHAPE[2] for cidx, c in enumerate(mutated_chromosomes): mr = 0 mc = 0 for r in c: s = B.sum(r) if s: mr += 1 mc += s assert abs(mutated_rows - mr) < 2 assert abs(mutated_cells - (mc // mutated_rows)) < 2
max_mutation_value=max_mutation_value, **kwargs) if __name__ == '__main__': from copy import copy pop_shape = (2, 4, 4) max_gene_value = 10 min_gene_value = 0 population_fraction = 1 mutations_probability = (0.5, 0.5) min_mutation_value = 1 max_mutation_value = 1 population = B.randint(0, max_gene_value, pop_shape) RM = RandomMutations2D(population_fraction=population_fraction, mutations_probability=mutations_probability, min_gene_value=min_gene_value, max_gene_value=max_gene_value, min_mutation_value=min_mutation_value, max_mutation_value=max_mutation_value, debug=True) RM(population) chromosomes_sav = copy(population) cprint('[Initial genepool]', 'blue') cprint(chromosomes_sav, 'blue') population = RM(population) cprint('\n[Mutated genepool]', 'yellow')
def test_call_vs_get(): shape = (128, 64) population = B.randint(1, 10, shape=shape) inputs = Input(shape) inputs.assign(population) assert inputs.get().all() == inputs.call('').all()
def __init__(self, population_fraction=0.9, crossover_probability=(0.2, 0.2, 0.2), **kwargs): if len(crossover_probability) != 3: raise ValueError('crossover_probability must be of form (x, y, z)') super(DualCrossover3D, self).__init__(population_fraction=population_fraction, crossover_probability=crossover_probability, **kwargs) if __name__ == '__main__': from copy import copy print(B.backend()) GENOME_SHAPE = (6, 4, 4) population = B.randint(0, 256, GENOME_SHAPE) population_fraction = 0.5 max_crossover_size_fraction = (0.5, 0.5) print(population.shape) original_population = copy(population) population = DualCrossover2D(population_fraction, max_crossover_size_fraction, debug=True)(population) # diff matrix diff = B.clip(abs(population - original_population), 0, 1) print(diff)
crossover_probability=(0.2, 0.2, 0.2), **kwargs): if len(crossover_probability) != 3: raise ValueError( 'crossover_probability must be of form (x, y, z)') # noqa super(UniformCrossover3D, self).__init__(population_fraction=population_fraction, crossover_probability=crossover_probability, **kwargs) if __name__ == '__main__': from copy import copy print(B.backend()) GENOME_SHAPE = (10, 4, 4) population = B.randint(0, 1024, GENOME_SHAPE) population_fraction = 0.5 crossover_probability = (0.5, 0.5) print(population.shape) # peforming crossover original_population = copy(population) population = UniformCrossover2D(population_fraction, crossover_probability)(population) # diff matrix diff = B.clip(abs(population - original_population), 0, 1) print(diff) # expected mutated chromosomes expected_mutated = population.shape[0] * population_fraction cprint(
def test_2d(): shape = (128, 64, 64) population = B.randint(1, 10, shape=shape) inputs = Input(shape) inputs.assign(population) assert inputs.get().all() == population.all()