From d06837b352516fdcf6e1d4f7948d2b49ce146519 Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sun, 29 Jun 2025 13:54:09 -0400 Subject: [PATCH 01/25] Generate constrained random values --- example.py | 28 +++ pygad/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 227 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 0 -> 82308 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 255 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 0 -> 16272 bytes pygad/helper/unique.py | 3 +- pygad/pygad.py | 30 +-- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 354 bytes .../__pycache__/crossover.cpython-310.pyc | Bin 0 -> 6073 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 0 -> 24993 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 0 -> 7299 bytes .../parent_selection.cpython-310.pyc | Bin 0 -> 14577 bytes pygad/utils/mutation.py | 198 +++++++++++++++--- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 259 bytes .../__pycache__/plot.cpython-310.pyc | Bin 0 -> 13852 bytes 15 files changed, 213 insertions(+), 46 deletions(-) create mode 100644 example.py create mode 100644 pygad/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/__pycache__/pygad.cpython-310.pyc create mode 100644 pygad/helper/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/helper/__pycache__/unique.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/crossover.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/mutation.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/nsga2.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/parent_selection.cpython-310.pyc create mode 100644 pygad/visualize/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/visualize/__pycache__/plot.cpython-310.pyc diff --git a/example.py b/example.py new file mode 100644 index 0000000..1299a46 --- /dev/null +++ b/example.py @@ -0,0 +1,28 @@ +import pygad +import numpy + +function_inputs = [4,-2,3.5,5,-11,-4.7] +desired_output = 44 + +def fitness_func(ga_instance, solution, solution_idx): + output = numpy.sum(solution*function_inputs) + fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001) + return fitness + +num_genes = len(function_inputs) + +ga_instance = pygad.GA(num_generations=100, + num_parents_mating=10, + sol_per_pop=20, + num_genes=num_genes, + mutation_num_genes=6, + fitness_func=fitness_func, + # suppress_warnings=True, + random_mutation_min_val=4, + random_mutation_max_val=10, + mutation_by_replacement=True, + gene_type=int, + # mutation_probability=0.4, + gene_constraint=[lambda x: x[0]>=8,None,None,None,None,None]) + +ga_instance.run() diff --git a/pygad/__pycache__/__init__.cpython-310.pyc b/pygad/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38c074e3eb591001514bf5fd8106cdef041639fa GIT binary patch literal 227 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 literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/__init__.cpython-310.pyc b/pygad/helper/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc306e5f101ca37b1c45133fc964cb603d422952 GIT binary patch literal 255 zcmYjKOA5j;6in&|(Yo^h-FDGpcOoL5z>OOzl~9|IM%pB%sZej^jig&wUcr^Gir~PT z$GkVpsOfZ!0Qdc#FJM19@ec>Y2Bw+;QABZpN*rU1s1rN1w?lsa2_NL+c34YsuGw`E zg;6-)`n}lI4Nqy9vqCdtx~O(S$c33oC`rf-nLgY;^g7UG0%kApxUhMB^t4eYmf0%# zge06vW<{13Tp2Kwy0N*E!Pgw5nT5!o_gk0{BBZc{gt}=*t5VUrU~3GGRafhxk@E&Q CxJ6R{ literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad216d8e6c08a8dfe5d8f31b4c0fd979a06d7ce4 GIT binary patch literal 16272 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 literal 0 HcmV?d00001 diff --git a/pygad/helper/unique.py b/pygad/helper/unique.py index a74a9c0..94ea8d5 100644 --- a/pygad/helper/unique.py +++ b/pygad/helper/unique.py @@ -316,7 +316,6 @@ def unique_genes_by_space(self, # Update the list of duplicate indices after each iteration. _, unique_gene_indices = numpy.unique(new_solution, return_index=True) not_unique_indices = set(range(len(new_solution))) - set(unique_gene_indices) - # self.logger.info("not_unique_indices INSIDE", not_unique_indices) return new_solution, not_unique_indices, num_unsolved_duplicates @@ -551,7 +550,7 @@ def find_two_duplicates(self, # This means there is no way to solve the duplicates between the genes. # Because the space of the duplicates genes only has a single value and there is no alternatives. return None, gene - + def unpack_gene_space(self, range_min, range_max, diff --git a/pygad/pygad.py b/pygad/pygad.py index 1a34b9a..2cc5c01 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -564,24 +564,30 @@ def __init__(self, # Validate that gene_constraint is a list or tuple and every element inside it is either None or callable. if gene_constraint: if type(gene_constraint) in [list, tuple]: - for constraint_idx, item in enumerate(gene_constraint): - # Check whether the element is None or a callable. - if item and callable(item): - if item.__code__.co_argcount == 1: - # Every callable is valid if it receives a single argument. - # This argument represents the solution. + if len(gene_constraint) == self.num_genes: + for constraint_idx, item in enumerate(gene_constraint): + # Check whether the element is None or a callable. + if item is None: pass + elif item and callable(item): + if item.__code__.co_argcount == 1: + # Every callable is valid if it receives a single argument. + # This argument represents the solution. + pass + else: + self.valid_parameters = False + raise ValueError(f"Every callable inside the gene_constraint parameter must accept a single argument representing the solution/chromosome. But the callable at index {constraint_idx} named '{item.__code__.co_name}' accepts {item.__code__.co_argcount} argument(s).") else: self.valid_parameters = False - raise ValueError(f"Every callable inside the gene_constraint parameter must accept a single argument representing the solution/chromosome. But the callable at index {constraint_idx} named '{item.__code__.co_name}' accepts {item.__code__.co_argcount} argument(s).") - else: - self.valid_parameters = False - raise TypeError(f"The expected type of an element in the 'gene_constraint' parameter is None or a callable (e.g. function). But {item} at index {constraint_idx} of type {type(item)} found.") + raise TypeError(f"The expected type of an element in the 'gene_constraint' parameter is None or a callable (e.g. function). But {item} at index {constraint_idx} of type {type(item)} found.") + else: + self.valid_parameters = False + raise ValueError(f"The number of constrains ({len(gene_constraint)}) in the 'gene_constraint' parameter must be equal to the number of genes ({self.num_genes}).") else: self.valid_parameters = False - raise TypeError(f"The expected type of the 'gene_constraint' parameter is either list or tuple. But the value {gene_constraint} of type {type(gene_constraint)} found.") + raise TypeError(f"The expected type of the 'gene_constraint' parameter is either a list or tuple. But the value {gene_constraint} of type {type(gene_constraint)} found.") else: - # It is None. + # gene_constraint is None and not used. pass self.gene_constraint = gene_constraint diff --git a/pygad/utils/__pycache__/__init__.cpython-310.pyc b/pygad/utils/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b5a31185773287ac709257526cea352409186d5 GIT binary patch literal 354 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 vPNsMo)umnTfQ^}WfAJv- zpY$%uo6aOJx(0AikTQm%qGCZXG$IdU>?0JTkBHb4cFFC44+J))VW)%7eO&FpejKms zGD*If#-9D~u3P-q-SJrOI~{C^J&2gv*`FcnC#x&EE~^dnv3buw$pY|E5DD_S!C3xf ziMfhIo|!1rOp1c$yw*VFo}Qm1VMn9!*mc<8#oi=SM7@FN3LS8qW|Wh|728oPL&b_R zO@}B72jpU9J%B$ast4p1)Fc71P@*PvdpE$S?)f_jDh!)}cPjwpB5D<*hyY%x72bH^ zoEBNwdG$zaHYb%wPf@Wz9ke<9kXPBRB1~t5jRLkJp{33^FIxc BmEiyY literal 0 HcmV?d00001 diff --git a/pygad/utils/__pycache__/parent_selection.cpython-310.pyc b/pygad/utils/__pycache__/parent_selection.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..491ba741c1657f376ed2ec7c603513cac8318feb GIT binary patch literal 14577 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 literal 0 HcmV?d00001 diff --git a/pygad/utils/mutation.py b/pygad/utils/mutation.py index 4309269..cb58039 100644 --- a/pygad/utils/mutation.py +++ b/pygad/utils/mutation.py @@ -8,6 +8,8 @@ import pygad import concurrent.futures +import warnings + class Mutation: def __init__(self): @@ -43,7 +45,7 @@ def random_mutation(self, offspring): return offspring - def get_mutation_range(self, gene_index): + def get_random_mutation_range(self, gene_index): """ Returns the minimum and maximum values of the mutation range. @@ -75,7 +77,7 @@ def mutation_by_space(self, offspring): mutation_indices = numpy.array(random.sample(range(0, self.num_genes), self.mutation_num_genes)) for gene_idx in mutation_indices: - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) if self.gene_space_nested: # Returning the current gene space from the 'gene_space' attribute. @@ -214,7 +216,7 @@ def mutation_probs_by_space(self, offspring): probs = numpy.random.random(size=offspring.shape[1]) for gene_idx in range(offspring.shape[1]): - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) if probs[gene_idx] <= self.mutation_probability: if self.gene_space_nested: @@ -301,12 +303,17 @@ def mutation_probs_by_space(self, offspring): return offspring - def change_random_mutation_value_dtype(self, random_value, gene_index): + def change_random_mutation_value_dtype(self, + random_value, + gene_index, + gene_value): + # TODO Instead of passing offspring and offspring_idx, only pass offspring[offspring_idx, gene_index] """ Change the data type of the random value used to apply mutation. It accepts 2 parameters: -random_value: The random value to change its data type. -gene_index: The index of the target gene. + -gene_value: The gene value before mutation. It returns the new value after changing the data type. """ @@ -321,9 +328,9 @@ def change_random_mutation_value_dtype(self, random_value, gene_index): # If the mutation_by_replacement attribute is False, then the random value is added to the gene value. else: if self.gene_type_single == True: - random_value = self.gene_type[0](offspring[offspring_idx, gene_index] + random_value) + random_value = self.gene_type[0](gene_value + random_value) else: - random_value = self.gene_type[gene_index][0](offspring[offspring_idx, gene_index] + random_value) + random_value = self.gene_type[gene_index][0](gene_value + random_value) if type(random_value) is numpy.ndarray: random_value = random_value[0] return random_value @@ -346,6 +353,132 @@ def round_random_mutation_value(self, random_value, gene_index): random_value = numpy.round(random_value, self.gene_type[gene_index][1]) return random_value + def mutation_generate_random_value(self, + range_min, + range_max, + gene_value, + gene_idx, + num_values=1): + """ + Randomly generate values to use for applying mutation. + It accepts: + -range_min: The minimum value in the range from which a value is selected. + -range_max: The maximum value in the range from which a value is selected. + -gene_value: The original gene value before applying mutation. + -gene_idx: The index of the gene in the solution. + -num_values: The number of random valus to generate. + If num_values=1, it returns a single numeric value. If num_values>1, it returns an array with number of values equal to num_values. + """ + + # Generating a random value. + random_value = numpy.random.uniform(low=range_min, + high=range_max, + size=num_values) + + # Change the random mutation value data type. + for idx, val in enumerate(random_value): + random_value[idx] = self.change_random_mutation_value_dtype(random_value[idx], + gene_idx, + gene_value) + + # Round the gene. + random_value[idx] = self.round_random_mutation_value(random_value[idx], gene_idx) + + # Rounding different values could return the same value multiple times. + # For example, 2.8 and 2.7 will be 3.0. + # Use the unique() function to avoid any duplicates. + random_value = numpy.unique(random_value) + + if num_values == 1: + random_value = random_value[0] + + return random_value + + def mutation_filter_values_by_constraint(self, + random_values, + solution, + gene_idx): + + """ + Filter the random values generated for mutation based on whether they meet the gene constraint in the gene_constraint parameter. + It accepts: + -random_values: The random values to filter. + -solution: The solution containing the target gene. + -gene_idx: The index of the gene in the solution. + It returns None if no values satisfy the constraint. Otherwise, an array of values that satisfy the constraint is returned. + """ + + # A list of the indices where the random values satisfy the constraint. + filtered_values_indices = [] + # A temporary solution to avoid changing the original solution. + solution_tmp = solution.copy() + # Loop through the random values to filter the ones satisfying the constraint. + for value_idx, random_value in enumerate(random_values): + solution_tmp[gene_idx] = random_value + # Check if the constraint is satisfied. + if self.gene_constraint[gene_idx](solution_tmp): + # The current value satisfies the constraint. + filtered_values_indices.append(value_idx) + + # After going through all the values, check if any value satisfies the constraint. + if len(filtered_values_indices) > 0: + # At least one value was found that meets the gene constraint. + pass + else: + # No value found for the current gene that satisfies the constraint. + if not self.suppress_warnings: + warnings.warn(f"No value found for the gene at index {gene_idx} at generation {self.generations_completed+1} that satisfies its gene constraint.") + return None + + filtered_values = random_values[filtered_values_indices] + + return filtered_values + + def mutation_process_random_value(self, + range_min, + range_max, + solution, + gene_idx): + + """ + Randomly generate constrained values to use for applying mutation. + It accepts: + -range_min: The minimum value in the range from which a value is selected. + -range_max: The maximum value in the range from which a value is selected. + -solution: The solution where the target gene exists. + -gene_idx: The index of the gene in the solution. + It returns either a single numeric value or multiple values based on whether a gene constraint exists in the gene_constraint parameter. + """ + + # Check if the gene has a constraint. + if self.gene_constraint and self.gene_constraint[gene_idx]: + # Generate random values to use for mutation. + random_values = self.mutation_generate_random_value(range_min=range_min, + range_max=range_max, + gene_value=solution[gene_idx], + gene_idx=gene_idx, + num_values=100) + # Filter the values that satisfy the constraint. + random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, + solution=solution, + gene_idx=gene_idx) + if random_values_filtered is None: + # No value found that satisfy the constraint. + # Keep the old value. + random_value = solution[gene_idx] + else: + # Select a value randomly from the list of values satisfying the constraint. + random_value = numpy.random.choice(random_values_filtered, size=1)[0] + # The gene does not have a constraint. + else: + random_value = self.mutation_generate_random_value(range_min=range_min, + range_max=range_max, + gene_value=solution[gene_idx], + gene_idx=gene_idx, + num_values=1) + # Even that its name is singular, it might have a multiple values. + return random_value + def mutation_randomly(self, offspring): """ @@ -357,20 +490,18 @@ def mutation_randomly(self, offspring): # Random mutation changes one or more genes in each offspring randomly. for offspring_idx in range(offspring.shape[0]): - mutation_indices = numpy.array(random.sample(range(0, self.num_genes), self.mutation_num_genes)) + # Return the indices of the genes to mutate. + mutation_indices = numpy.array(random.sample(range(0, self.num_genes), + self.mutation_num_genes)) for gene_idx in mutation_indices: - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) - # Generating a random value. - random_value = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - # Change the random mutation value data type. - random_value = self.change_random_mutation_value_dtype(random_value, gene_idx) - - # Round the gene. - random_value = self.round_random_mutation_value(random_value, gene_idx) + # Generate one or more random values that meet the gene constraint if exists. + random_value = self.mutation_process_random_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -395,21 +526,20 @@ def mutation_probs_randomly(self, offspring): # Random mutation changes one or more genes in each offspring randomly. for offspring_idx in range(offspring.shape[0]): + # The mutation probabilities for the current offspring. probs = numpy.random.random(size=offspring.shape[1]) for gene_idx in range(offspring.shape[1]): - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) + # A gene is mutated only if its mutation probability is less than or equal to the threshold. if probs[gene_idx] <= self.mutation_probability: - # Generating a random value. - random_value = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - # Change the random mutation value data type. - random_value = self.change_random_mutation_value_dtype(random_value, gene_idx) - # Round the gene. - random_value = self.round_random_mutation_value(random_value, gene_idx) + # Generate one or more random values that meet the gene constraint if exists. + random_value = self.mutation_process_random_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -707,7 +837,7 @@ def adaptive_mutation_by_space(self, offspring): mutation_indices = numpy.array(random.sample(range(0, self.num_genes), adaptive_mutation_num_genes)) for gene_idx in mutation_indices: - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) if self.gene_space_nested: # Returning the current gene space from the 'gene_space' attribute. @@ -845,14 +975,16 @@ def adaptive_mutation_randomly(self, offspring): mutation_indices = numpy.array(random.sample(range(0, self.num_genes), adaptive_mutation_num_genes)) for gene_idx in mutation_indices: - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) # Generating a random value. random_value = numpy.random.uniform(low=range_min, high=range_max, size=1)[0] # Change the random mutation value data type. - random_value = self.change_random_mutation_value_dtype(random_value, gene_idx) + random_value = self.change_random_mutation_value_dtype(random_value, + gene_idx, + offspring[offspring_idx, gene_idx]) # Round the gene. random_value = self.round_random_mutation_value(random_value, gene_idx) @@ -914,7 +1046,7 @@ def adaptive_mutation_probs_by_space(self, offspring): probs = numpy.random.random(size=offspring.shape[1]) for gene_idx in range(offspring.shape[1]): - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) if probs[gene_idx] <= adaptive_mutation_probability: if self.gene_space_nested: @@ -1052,7 +1184,7 @@ def adaptive_mutation_probs_randomly(self, offspring): probs = numpy.random.random(size=offspring.shape[1]) for gene_idx in range(offspring.shape[1]): - range_min, range_max = self.get_mutation_range(gene_idx) + range_min, range_max = self.get_random_mutation_range(gene_idx) if probs[gene_idx] <= adaptive_mutation_probability: # Generating a random value. @@ -1060,7 +1192,9 @@ def adaptive_mutation_probs_randomly(self, offspring): high=range_max, size=1)[0] # Change the random mutation value data type. - random_value = self.change_random_mutation_value_dtype(random_value, gene_idx) + random_value = self.change_random_mutation_value_dtype(random_value, + gene_idx, + offspring[offspring_idx, gene_idx]) # Round the gene. random_value = self.round_random_mutation_value(random_value, gene_idx) diff --git a/pygad/visualize/__pycache__/__init__.cpython-310.pyc b/pygad/visualize/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c367b5cc1c47805adcca3da71281298d99ffd67 GIT binary patch literal 259 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 Date: Sun, 29 Jun 2025 13:59:55 -0400 Subject: [PATCH 02/25] Generate constrained random values --- pygad/utils/mutation.py | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/pygad/utils/mutation.py b/pygad/utils/mutation.py index cb58039..eae575b 100644 --- a/pygad/utils/mutation.py +++ b/pygad/utils/mutation.py @@ -447,7 +447,7 @@ def mutation_process_random_value(self, -range_max: The maximum value in the range from which a value is selected. -solution: The solution where the target gene exists. -gene_idx: The index of the gene in the solution. - It returns either a single numeric value or multiple values based on whether a gene constraint exists in the gene_constraint parameter. + It returns a single numeric value the satisfies the gene constraint if exists in the gene_constraint parameter. """ # Check if the gene has a constraint. @@ -497,7 +497,7 @@ def mutation_randomly(self, offspring): range_min, range_max = self.get_random_mutation_range(gene_idx) - # Generate one or more random values that meet the gene constraint if exists. + # Generate a random value fpr mutation that meet the gene constraint if exists. random_value = self.mutation_process_random_value(range_min=range_min, range_max=range_max, solution=offspring[offspring_idx], @@ -535,7 +535,7 @@ def mutation_probs_randomly(self, offspring): # A gene is mutated only if its mutation probability is less than or equal to the threshold. if probs[gene_idx] <= self.mutation_probability: - # Generate one or more random values that meet the gene constraint if exists. + # Generate a random value fpr mutation that meet the gene constraint if exists. random_value = self.mutation_process_random_value(range_min=range_min, range_max=range_max, solution=offspring[offspring_idx], @@ -977,17 +977,11 @@ def adaptive_mutation_randomly(self, offspring): range_min, range_max = self.get_random_mutation_range(gene_idx) - # Generating a random value. - random_value = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - # Change the random mutation value data type. - random_value = self.change_random_mutation_value_dtype(random_value, - gene_idx, - offspring[offspring_idx, gene_idx]) - - # Round the gene. - random_value = self.round_random_mutation_value(random_value, gene_idx) + # Generate a random value fpr mutation that meet the gene constraint if exists. + random_value = self.mutation_process_random_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -1187,17 +1181,11 @@ def adaptive_mutation_probs_randomly(self, offspring): range_min, range_max = self.get_random_mutation_range(gene_idx) if probs[gene_idx] <= adaptive_mutation_probability: - # Generating a random value. - random_value = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - # Change the random mutation value data type. - random_value = self.change_random_mutation_value_dtype(random_value, - gene_idx, - offspring[offspring_idx, gene_idx]) - - # Round the gene. - random_value = self.round_random_mutation_value(random_value, gene_idx) + # Generate a random value fpr mutation that meet the gene constraint if exists. + random_value = self.mutation_process_random_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value From c2dc95a0d121c67993e08c12d11ec18770258ce6 Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sun, 29 Jun 2025 14:01:47 -0400 Subject: [PATCH 03/25] Use Python 3.13 and remove Python 3.7 workflow --- .github/workflows/{main_py37.yml => main_py313.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{main_py37.yml => main_py313.yml} (89%) diff --git a/.github/workflows/main_py37.yml b/.github/workflows/main_py313.yml similarity index 89% rename from .github/workflows/main_py37.yml rename to .github/workflows/main_py313.yml index 427aaec..f2d96cc 100644 --- a/.github/workflows/main_py37.yml +++ b/.github/workflows/main_py313.yml @@ -1,4 +1,4 @@ -name: PyGAD PyTest / Python 3.7 +name: PyGAD PyTest / Python 3.13 on: push: @@ -15,10 +15,10 @@ jobs: - name: Checkout Pre-Built Action uses: actions/checkout@v3 - - name: Setup Python 3.7 + - name: Setup Python 3.13 uses: actions/setup-python@v4 with: - python-version: '3.7' + python-version: '3.13' - name: Install Dependencies run: | From a6d8456eaaaa60b0d354dc27c13bccbc3a27147e Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sun, 29 Jun 2025 14:09:25 -0400 Subject: [PATCH 04/25] Allow manually triggering the workflows --- .github/workflows/main_py310.yml | 2 ++ .github/workflows/main_py311.yml | 2 ++ .github/workflows/main_py312.yml | 11 ++++++----- .github/workflows/main_py313.yml | 2 ++ .github/workflows/main_py38.yml | 2 ++ .github/workflows/main_py39.yml | 2 ++ 6 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main_py310.yml b/.github/workflows/main_py310.yml index 9602f17..9e701d1 100644 --- a/.github/workflows/main_py310.yml +++ b/.github/workflows/main_py310.yml @@ -5,6 +5,8 @@ on: branches: - github-actions # - master + # Manually trigger the workflow. + workflow_dispatch: jobs: job_id_1: diff --git a/.github/workflows/main_py311.yml b/.github/workflows/main_py311.yml index d468243..0ca059b 100644 --- a/.github/workflows/main_py311.yml +++ b/.github/workflows/main_py311.yml @@ -5,6 +5,8 @@ on: branches: - github-actions # - master + # Manually trigger the workflow. + workflow_dispatch: jobs: job_id_1: diff --git a/.github/workflows/main_py312.yml b/.github/workflows/main_py312.yml index 87bd648..41b0531 100644 --- a/.github/workflows/main_py312.yml +++ b/.github/workflows/main_py312.yml @@ -7,12 +7,13 @@ name: PyGAD PyTest / Python 3.12 # But Python 3.12 does not support distutils. # Let's wait until setuptools changes its dependencies. -# on: -# push: -# branches: - # - github-actions +on: + push: + branches: + - github-actions # - master -on: workflow_dispatch + # Manually trigger the workflow. + workflow_dispatch: jobs: job_id_1: diff --git a/.github/workflows/main_py313.yml b/.github/workflows/main_py313.yml index f2d96cc..0e0301c 100644 --- a/.github/workflows/main_py313.yml +++ b/.github/workflows/main_py313.yml @@ -5,6 +5,8 @@ on: branches: - github-actions # - master + # Manually trigger the workflow. + workflow_dispatch: jobs: job_id_1: diff --git a/.github/workflows/main_py38.yml b/.github/workflows/main_py38.yml index 602f917..7838c64 100644 --- a/.github/workflows/main_py38.yml +++ b/.github/workflows/main_py38.yml @@ -5,6 +5,8 @@ on: branches: - github-actions # - master + # Manually trigger the workflow. + workflow_dispatch: jobs: job_id_1: diff --git a/.github/workflows/main_py39.yml b/.github/workflows/main_py39.yml index c6b61fc..be32ab4 100644 --- a/.github/workflows/main_py39.yml +++ b/.github/workflows/main_py39.yml @@ -5,6 +5,8 @@ on: branches: - github-actions # - master + # Manually trigger the workflow. + workflow_dispatch: jobs: job_id_1: From e346488f37c0bf5537a7d0f8a4cae5ef6f012dc3 Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sun, 29 Jun 2025 14:12:46 -0400 Subject: [PATCH 05/25] Change Python 3.12 version --- .github/workflows/main_py312.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main_py312.yml b/.github/workflows/main_py312.yml index 41b0531..3527711 100644 --- a/.github/workflows/main_py312.yml +++ b/.github/workflows/main_py312.yml @@ -27,7 +27,7 @@ jobs: - name: Setup Python 3.12 uses: actions/setup-python@v4 with: - python-version: '3.12.0-beta.2' + python-version: '3.12' - name: Install Dependencies run: | From 01a614b02fb58eba5a044b93e552813c6993a76c Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sun, 29 Jun 2025 14:17:15 -0400 Subject: [PATCH 06/25] Remove comments --- .github/workflows/main_py312.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/main_py312.yml b/.github/workflows/main_py312.yml index 3527711..91cc504 100644 --- a/.github/workflows/main_py312.yml +++ b/.github/workflows/main_py312.yml @@ -1,12 +1,5 @@ name: PyGAD PyTest / Python 3.12 -# Cannot install packages in Python 3.12. -# The reason is that we use pip for installing packages. -# pip uses setuptools for the installation. -# setuptools depends on distutils. -# But Python 3.12 does not support distutils. -# Let's wait until setuptools changes its dependencies. - on: push: branches: From 13055365d272e943239cbeb863d29f001aaeaa8a Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sun, 29 Jun 2025 16:13:27 -0400 Subject: [PATCH 07/25] Apply constraints in initial population --- example.py | 4 +- pygad/__pycache__/pygad.cpython-310.pyc | Bin 82308 -> 82822 bytes pygad/helper/__init__.py | 3 +- .../__pycache__/__init__.cpython-310.pyc | Bin 255 -> 282 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 0 -> 4934 bytes pygad/helper/misc.py | 122 ++++++++++++++++++ pygad/pygad.py | 71 +++++----- .../__pycache__/mutation.cpython-310.pyc | Bin 24993 -> 23567 bytes pygad/utils/mutation.py | 93 +++---------- 9 files changed, 187 insertions(+), 106 deletions(-) create mode 100644 pygad/helper/__pycache__/misc.cpython-310.pyc create mode 100644 pygad/helper/misc.py diff --git a/example.py b/example.py index 1299a46..362c5be 100644 --- a/example.py +++ b/example.py @@ -17,6 +17,8 @@ def fitness_func(ga_instance, solution, solution_idx): num_genes=num_genes, mutation_num_genes=6, fitness_func=fitness_func, + init_range_low=4, + init_range_high=10, # suppress_warnings=True, random_mutation_min_val=4, random_mutation_max_val=10, @@ -25,4 +27,4 @@ def fitness_func(ga_instance, solution, solution_idx): # mutation_probability=0.4, gene_constraint=[lambda x: x[0]>=8,None,None,None,None,None]) -ga_instance.run() +# ga_instance.run() diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index 7601436849897624340125710ba38120fab2889f..2980c4a9d5177a6b87596a24482887043d8938bb 100644 GIT binary patch delta 11186 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 H0|yfTb+;L5 diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93bfc50a82ee733f7e59e3c39fefa46328137788 GIT binary patch literal 4934 zcmeHLOOG5y60TQQS2r`p@*-d`Ldj#b>RrzO5<>gaB$;zyXjEIbg{JyNtcX!7Y zuIbl&@vp0v^*s$XFBgM%@n61!y9%M(ccOsVg)(bI`hH?#fxYGc1&zt?WIkd2V|ByNS); zuq|uM+UrxPE0vW@BQ9y2#c7o?o(Y!n6Vv~aCzUK&K5HZ@&2^k-Oz~_k4|**(G~-bu z3tckK$~c=Nlm%BDVUy~5FGe=^DY9Xl33+mzQ8rAsGe9qy&JljiVrXmZu)k-?l2xS? zEN&bza+EBXD`XVUkgD{YbG=dcrheBVjqdZx)+?QOwWpVbte=8wB2U9kFhul1M62E_ ztD?x2mIABP;Vj9y4rxmHALj5An)4l$mNJ>u<8^TAky$&b?YsQsJ4@}N3V!^jxPF8p zX8UX&>qRwXh01TrNS8;ZxQ|GWjw7DTa|MC4SfYNj{YH47N0h`)wkh$WG%llqVp;DY z_Tl#8j4wM>gqP4<9{aX$2j2Mp*beMd))el+11CPPq!sNOl;ns1eMzEbb<7i1;`u@acxuY2X(+Sxvy{ZvP`Hk1 zqw6`Ea2d_%iyJ9_kn}wy)ry8_x{Dy5#)M|&DH;u=!oPFVUD-e{U)XnD?Fr|ub2ik2 z)$q3cq4oLIBj+qQ8v)h))o>N;Su1blt_H%r>(Tfv;N3UF?#;JO^X%3YZG980hTqy} zL+JIsaMYWtp%?%aAq%|q`Ns%VpOH_^ZU)NG&QV-TmIH}$1!7Q=wJ12ln1GUG){w9U zzb&L|;*Sm709^;KMj2qws4_Bart*{>FXCtcKx^NFh-88-EydcD0nk=0Z}IaSQa^-R z13kue<1T<>9%tBD^rCfYDiH$7cu^!vpnMOxTOJx!v3MZBw`(u+q#_{Nn%yJnbQvmH zBs`L-%=C5k9-Ae6PJ{tu(`5g60bZ6!QV0FhCBVL)!s3$2j878~2U3^i`oK_*lDq_o z;Vq~pEt6goi8s0omf$&y)A z>#u}cnrRiosA1K%wmDl*Z4|PqAPL?RKa{S`9kBN+B4i2~InR@0zAV|if?X})?Nmk_ z{A_}wbB(IGU?2tLXfqoNm04cR7nBqVXX}0IG#*CCkcQ8qDiX?dFVZ->E|?peUcUY6 zHS)1z`~D;XSIk+?Oh!aD=e)BuCu3YGzGTN>&5LW0w<;_RaoPw?4qt_Om+ADySVBHbzp^#p)W5mrt{^#F>&!9!jeH)_8LO+Bo#7+Wu`{d`J1Uyr0& z6+%S>DkqPBYinj&Ji?jqu$u_2y6qXROr4CS~W6A%rE$3F|q!8us@E21D5 zlrsWRxcsYMCknIdl5LU$s+wz70x-(i(v&#uY^^v>gd4@%pPC;L2jN@yZSaUVK#E%^ z@o>b^F>ON|-G~kg&7q?^)G&Dxml~hBCLL|FzuxMPXs3zEISTn-(~iGJZ1p0rO}#`7 zp|tuV8qm`2Cf1g9&=r7@oH%L%vHGQ+pJs7_qe*+xpgLTG*9DH)ws@^GtNubtpFC$d zY&1);O$|6-X|flK2F=$7TQ5F`vhry(z9EQ)7W|)lpL+g%&prK9PYb`U>63|V7#BAp zg2}KB!Z5=x6=7J9!?5`s2>qQfB(iVbsK+6y{y+`IO1(@CeTZ)~^!cRdw;Cdwl170A z%DUn_J^s;o?b5%^yNO>9!%*Z=7^-I=QU}LeWjI!r>RB2N%;&oi-B4{Tna{J}t;WH3 T=)6!8Nqbh{1^5aN>?_VMgTM3= literal 0 HcmV?d00001 diff --git a/pygad/helper/misc.py b/pygad/helper/misc.py new file mode 100644 index 0000000..1052c3f --- /dev/null +++ b/pygad/helper/misc.py @@ -0,0 +1,122 @@ +""" +The pygad.helper.helper module has some generic helper methods. +""" + +import numpy +import warnings +import random +import pygad + +class Helper: + + def get_random_mutation_range(self, gene_index): + + """ + Returns the minimum and maximum values of the mutation range. + It accepts a single parameter: + -gene_index: The index of the gene to get its range. Only used if the gene has a specific mutation range + It returns the minimum and maximum values of the gene mutation range. + """ + + # We can use either random_mutation_min_val or random_mutation_max_val. + if type(self.random_mutation_min_val) in self.supported_int_float_types: + range_min = self.random_mutation_min_val + range_max = self.random_mutation_max_val + else: + range_min = self.random_mutation_min_val[gene_index] + range_max = self.random_mutation_max_val[gene_index] + return range_min, range_max + + def get_initial_population_range(self, gene_index): + + """ + Returns the minimum and maximum values of the initial population range. + It accepts a single parameter: + -gene_index: The index of the gene to get its range. Only used if the gene has a specific range + It returns the minimum and maximum values of the gene initial population range. + """ + + # We can use either init_range_low or init_range_high. + if type(self.init_range_low) in self.supported_int_float_types: + range_min = self.init_range_low + range_max = self.init_range_high + else: + range_min = self.init_range_low[gene_index] + range_max = self.init_range_high[gene_index] + return range_min, range_max + + def generate_gene_random_value(self, + range_min, + range_max, + gene_value, + gene_idx, + mutation_by_replacement, + num_values=1): + """ + Randomly generate one or more values for the gene. + It accepts: + -range_min: The minimum value in the range from which a value is selected. + -range_max: The maximum value in the range from which a value is selected. + -gene_value: The original gene value before applying mutation. + -gene_idx: The index of the gene in the solution. + -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. + If num_values=1, it returns a single numeric value. If num_values>1, it returns an array with number of values equal to num_values. + """ + + # Generating a random value. + random_value = numpy.asarray(numpy.random.uniform(low=range_min, + high=range_max, + size=num_values), + dtype=object) + + # Change the random mutation value data type. + for idx, val in enumerate(random_value): + random_value[idx] = self.change_random_mutation_value_dtype(random_value[idx], + gene_idx, + gene_value, + mutation_by_replacement=mutation_by_replacement) + + # Round the gene. + random_value[idx] = self.round_random_mutation_value(random_value[idx], gene_idx) + + # Rounding different values could return the same value multiple times. + # For example, 2.8 and 2.7 will be 3.0. + # Use the unique() function to avoid any duplicates. + random_value = numpy.unique(random_value) + + if num_values == 1: + random_value = random_value[0] + + return random_value + + def get_valid_gene_constraint_values(self, + range_min, + range_max, + gene_value, + gene_idx, + mutation_by_replacement, + solution, + num_values=100): + """ + Randomly generate values for the gene that satisfy the constraint. + It accepts: + -range_min: The minimum value in the range from which a value is selected. + -range_max: The maximum value in the range from which a value is selected. + -gene_value: The original gene value before applying mutation. + -gene_idx: The index of the gene in the solution. + -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + -solution: The solution in which the gene exists. + -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. + If num_values=1, it returns a single numeric value. If num_values>1, it returns an array with number of values equal to num_values. + """ + random_values = self.generate_gene_random_value(range_min=range_min, + range_max=range_max, + gene_value=gene_value, + gene_idx=gene_idx, + mutation_by_replacement=mutation_by_replacement, + num_values=num_values) + random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, + solution=solution, + gene_idx=gene_idx) + return random_values_filtered diff --git a/pygad/pygad.py b/pygad/pygad.py index 2cc5c01..b163960 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -15,6 +15,7 @@ class GA(utils.parent_selection.ParentSelection, utils.mutation.Mutation, utils.nsga2.NSGA2, helper.unique.Unique, + helper.misc.Helper, visualize.plot.Plot): supported_int_types = [int, numpy.int8, numpy.int16, numpy.int32, numpy.int64, @@ -435,7 +436,8 @@ def __init__(self, high=self.init_range_high, allow_duplicate_genes=allow_duplicate_genes, mutation_by_replacement=True, - gene_type=self.gene_type) + gene_type=self.gene_type, + gene_constraint=gene_constraint) else: self.valid_parameters = False raise TypeError(f"The expected type of both the sol_per_pop and num_genes parameters is int but {type(sol_per_pop)} and {type(num_genes)} found.") @@ -1377,12 +1379,18 @@ def initialize_population(self, high, allow_duplicate_genes, mutation_by_replacement, - gene_type): + gene_type, + gene_constraint): """ Creates an initial population randomly as a NumPy array. The array is saved in the instance attribute named 'population'. - low: The lower value of the random range from which the gene values in the initial population are selected. It defaults to -4. Available in PyGAD 1.0.20 and higher. - high: The upper value of the random range from which the gene values in the initial population are selected. It defaults to -4. Available in PyGAD 1.0.20. + It accepts: + -low: The lower value of the random range from which the gene values in the initial population are selected. It defaults to -4. Available in PyGAD 1.0.20 and higher. + -high: The upper value of the random range from which the gene values in the initial population are selected. It defaults to -4. Available in PyGAD 1.0.20. + -allow_duplicate_genes: Whether duplicate genes are allowed or not. + -mutation_by_replacement: Whether mutation by replacement is enabled or not. + -gene_type: The data type of the genes. + -gene_constraint: The constraints of the genes. This method assigns the values of the following 3 instance attributes: 1. pop_size: Size of the population. @@ -1397,10 +1405,11 @@ def initialize_population(self, if self.gene_space is None: # Creating the initial population randomly. if self.gene_type_single == True: + # A NumPy array holding the initial population. self.population = numpy.asarray(numpy.random.uniform(low=low, high=high, size=self.pop_size), - dtype=self.gene_type[0]) # A NumPy array holding the initial population. + dtype=self.gene_type[0]) else: # Create an empty population of dtype=object to support storing mixed data types within the same array. self.population = numpy.zeros( @@ -1408,12 +1417,7 @@ def initialize_population(self, # Loop through the genes, randomly generate the values of a single gene across the entire population, and add the values of each gene to the population. for gene_idx in range(self.num_genes): - if type(self.init_range_low) in self.supported_int_float_types: - range_min = self.init_range_low - range_max = self.init_range_high - else: - range_min = self.init_range_low[gene_idx] - range_max = self.init_range_high[gene_idx] + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) # A vector of all values of this single gene across all solutions in the population. gene_values = numpy.asarray(numpy.random.uniform(low=range_min, @@ -1423,6 +1427,30 @@ def initialize_population(self, # Adding the current gene values to the population. self.population[:, gene_idx] = gene_values + # Enforce the gene constraints as much as possible. + if gene_constraint is None: + pass + else: + # Note that gene_constraint is not validated yet. + # We have to set it as a propery of the pygad.GA instance to retrieve without passing it as an additional parameter. + self.gene_constraint = gene_constraint + for solution in self.population: + for gene_idx in range(self.num_genes): + # Check that a constraint is available for the gene and that the current value does not satisfy that constraint + if self.gene_constraint[gene_idx]: + print(gene_idx, solution[gene_idx]) + if not self.gene_constraint[gene_idx](solution): + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) + # While initializing the population, we follow a mutation by replacement approach. So, the gene value is not needed. + random_values_filtered = self.get_valid_gene_constraint_values(range_min=range_min, + range_max=range_max, + gene_value=None, + gene_idx=gene_idx, + mutation_by_replacement=True, + solution=solution, + num_values=100) + print(gene_idx, random_values_filtered) + if allow_duplicate_genes == False: for solution_idx in range(self.population.shape[0]): # self.logger.info("Before", self.population[solution_idx]) @@ -1444,12 +1472,7 @@ def initialize_population(self, for sol_idx in range(self.sol_per_pop): for gene_idx in range(self.num_genes): - if type(self.init_range_low) in self.supported_int_float_types: - range_min = self.init_range_low - range_max = self.init_range_high - else: - range_min = self.init_range_low[gene_idx] - range_max = self.init_range_high[gene_idx] + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) if self.gene_space[gene_idx] is None: @@ -1524,12 +1547,7 @@ def initialize_population(self, for sol_idx in range(self.sol_per_pop): for gene_idx in range(self.num_genes): - if type(self.init_range_low) in self.supported_int_float_types: - range_min = self.init_range_low - range_max = self.init_range_high - else: - range_min = self.init_range_low[gene_idx] - range_max = self.init_range_high[gene_idx] + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) if type(self.gene_space[gene_idx]) in [numpy.ndarray, list, tuple, range]: # Convert to list because tuple and range do not have copy(). @@ -1584,12 +1602,7 @@ def initialize_population(self, # Replace all the None values with random values using the init_range_low, init_range_high, and gene_type attributes. for gene_idx, curr_gene_space in enumerate(self.gene_space): - if type(self.init_range_low) in self.supported_int_float_types: - range_min = self.init_range_low - range_max = self.init_range_high - else: - range_min = self.init_range_low[gene_idx] - range_max = self.init_range_high[gene_idx] + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) if curr_gene_space is None: self.gene_space[gene_idx] = numpy.asarray(numpy.random.uniform(low=range_min, diff --git a/pygad/utils/__pycache__/mutation.cpython-310.pyc b/pygad/utils/__pycache__/mutation.cpython-310.pyc index 107cdc8275ecb5294e25c929e5d214c9960973c7..16be5abd1b90d84bae82148d5a5b5611dc2bfb79 100644 GIT binary patch delta 4277 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 Tc@_Z&mjjKzeX)i=V?_T4&DpS4 delta 5318 zcmbtYYiu0V6`nhW1ZG1DLz}p1;Ij=wa&Y@DciH^`_ z+VUhh6``#({-ks&O512VJY%$jcEYoPcF}HlHqsv23(qFHf%d_(nfB9-@NA(b9e`&m z9i*G!8K;}+7I?PNAvz4tcDj{X@a&-5=yrH^(jBvsHR>DF1-E>O{8A~YAdU=whfu5u z09W#)Me+mzlpm2;E4~$FK&7a7vZtivSuw*S-BQ38RCUZE$q^z+5wr3;Zo37Rb4+)^ zHnZtmI$Ow^$s9Ga$z`DXvgRYnOu=@{d@7hyaFcF2pEFr9H_yjr$9Zo@-=TRsXPa)` z%;Km$Y97mFE|>+!re-==CMGN-9W!YkDyH8SK&ORV>w4 z-3v>$-wDFed^RD(ffEvNu>{h4&QfyDPULLIwJB=>K0g%QqOmwqPef0XA^vW3 zIl?;NopmBJ8yg`$e<3zbCd+?}{Y^XFjYER~Gpq+mJc?BXVH@G~#9eF?ayJ858`xH) zEre|dI}lJLW+32wg(NJ@FHQB4I6oHeD(yw?K7{=U2N3Q+7(zIRFoAF~+45Q+={H^#~B0^w>_@PO0<{|iVz6Jns zA+ceM5lYU)i1SIamn(gD%wnz^$rZAg1R$9OGvWQL%`mYAdK8%s z5@FuNJsBplYHB%}GHc5Z?Al?b`IP~)ZPdJO;0`fBBJt9rX?FovR1OG^HSP0-WCjnY z?NvJ>2vrffU^K}J)rI1%!0^L1EE96E8F**Mkf|4P>GK8K3J12UL{bTcr*RI3mmjGe z$I!UGlBUbP0z$0`E5E;*F;`WPsP{37-h{c#XqAaf^lQyp1fDW6u61Y;&5#YE$`^;H z3lo(zc=PS%FkDxE=u#p06E5`_Pxa1>w?UL?&TX!KB}AdJy&zEqI0jWaC7`vhYAb|D z6dVHlP6g*MJOi8~RDQQNF1I(CNq5zdqR60T`3HSlqhWRnknDbZ-`3wl_VXkCL$~?u zl`F$!Coz}LE@f;;z!k;%4fT3(*cRMDD3^bszpK}eIr&Th>te#qF8N_$?*wR>-N)bP zKT0n1@Wu_KpAT)EO4Ke1d%%4pwi^*|ASlR6SjX|*Qz>Mh%UV%`84y{>r3ywDgo6s% zX(*Ui40)kh$yF91XYt=}9Bif#Hn2>vEh9Nx*3EB{SsR=Z96o6}>D+wAMlZA(6k;JQ z1P)Z>jGKmguh#stNi5+|>>pjQD@##H<{YTVWyy5T<$oEty`y2(Bg|tP$rGzW(+&o3j z*2+`$9zpedSr<3+LzVslh~zTnm>_?PIyel{oNAd|`?H$rYc4-G)fW}@mZAr3KODK894zk|eV+u%tL-i`6Q{tPBr9^mlNiHE zc#!C!{E)LyNTo71b8uZ~{ZnDck75Wqn3v~m<^qR*Wo(e#Uw&hZ=}mZ1i-=Cnl36#O z7(dl<3-V-N9vy>2@)=A^FGIih%J>lZMfvSBgqe zkEPNsoaZD`h1^^qFXR3DCMS2}sB$sAJ0upJgp_4N;w$!^NETD*d+h^)b!+f zL4O3rxC`OtZSyNIWrI>K-W?(2R(|lLNuK5ppWMiQb9AwExEO|RtkDav5a`$<#i*+- zMi-mBh!>?YRWBUV-&WqErQA5FMrCyU|^_s5ABm#6yi^&l=Xtq?M!g%w?@?-KCJl0 z`~REIKJ&AvRD1x|R4Tk)WdFd`@&3Cc{>)To@Kt#9e|7jZ>h=2|!sFaO5xrOF_+C6{ zP4W1wUjIW_dlgmwYySA%-TdeS|0FZQ`JV^pf39?oaQtY^@jb%}L-VDdBkV;8g2-a5 z5(HXZ5HzeC1dTTcf+ng%5WEO3-+WyVzz3A=N_%bZJ07cZn8LJ*2t9X2~8Y2<8O>QN78f6Fp=-NJ`@4v71 zT(tOir+fK#CK@9%Q1wLd>*8;o+Id=dXawbrA=E;>)@V{3h$3LC!OkNT5S9@{UHB1F zm@^qhEki$IWq#=NKnc}pNbtd<82v`8R5D_QZJ2Ur1, it returns an array with number of values equal to num_values. - """ - - # Generating a random value. - random_value = numpy.random.uniform(low=range_min, - high=range_max, - size=num_values) - - # Change the random mutation value data type. - for idx, val in enumerate(random_value): - random_value[idx] = self.change_random_mutation_value_dtype(random_value[idx], - gene_idx, - gene_value) - - # Round the gene. - random_value[idx] = self.round_random_mutation_value(random_value[idx], gene_idx) - - # Rounding different values could return the same value multiple times. - # For example, 2.8 and 2.7 will be 3.0. - # Use the unique() function to avoid any duplicates. - random_value = numpy.unique(random_value) - - if num_values == 1: - random_value = random_value[0] - - return random_value - def mutation_filter_values_by_constraint(self, random_values, solution, @@ -427,7 +368,7 @@ def mutation_filter_values_by_constraint(self, else: # No value found for the current gene that satisfies the constraint. if not self.suppress_warnings: - warnings.warn(f"No value found for the gene at index {gene_idx} at generation {self.generations_completed+1} that satisfies its gene constraint.") + warnings.warn(f"No value found for the gene at index {gene_idx} that satisfies its gene constraint.") return None filtered_values = random_values[filtered_values_indices] @@ -453,11 +394,12 @@ def mutation_process_random_value(self, # Check if the gene has a constraint. if self.gene_constraint and self.gene_constraint[gene_idx]: # Generate random values to use for mutation. - random_values = self.mutation_generate_random_value(range_min=range_min, - range_max=range_max, - gene_value=solution[gene_idx], - gene_idx=gene_idx, - num_values=100) + random_values = self.generate_gene_random_value(range_min=range_min, + range_max=range_max, + gene_value=solution[gene_idx], + gene_idx=gene_idx, + mutation_by_replacement=self.mutation_by_replacement, + num_values=100) # Filter the values that satisfy the constraint. random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, solution=solution, @@ -471,11 +413,12 @@ def mutation_process_random_value(self, random_value = numpy.random.choice(random_values_filtered, size=1)[0] # The gene does not have a constraint. else: - random_value = self.mutation_generate_random_value(range_min=range_min, - range_max=range_max, - gene_value=solution[gene_idx], - gene_idx=gene_idx, - num_values=1) + random_value = self.generate_gene_random_value(range_min=range_min, + range_max=range_max, + gene_value=solution[gene_idx], + gene_idx=gene_idx, + mutation_by_replacement=self.mutation_by_replacement, + num_values=1) # Even that its name is singular, it might have a multiple values. return random_value From c5d35dceda521427c2531b8feb041b25bfae1dc3 Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Tue, 1 Jul 2025 17:10:50 -0400 Subject: [PATCH 08/25] Apply constraints with duplicates --- example.py | 13 +- pygad/__pycache__/pygad.cpython-310.pyc | Bin 82822 -> 83006 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 4934 -> 6193 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 16272 -> 16314 bytes pygad/helper/misc.py | 72 +- pygad/helper/unique.py | 115 ++-- pygad/pygad.py | 19 +- .../__pycache__/crossover.cpython-310.pyc | Bin 6073 -> 6100 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 23567 -> 23864 bytes pygad/utils/crossover.py | 9 +- pygad/utils/mutation.py | 16 +- test_gene_constraint.py | 164 +++++ tests/test_gene_constraint.py | 635 ++++++++++++++++++ 13 files changed, 965 insertions(+), 78 deletions(-) create mode 100644 test_gene_constraint.py create mode 100644 tests/test_gene_constraint.py diff --git a/example.py b/example.py index 362c5be..405a273 100644 --- a/example.py +++ b/example.py @@ -17,14 +17,17 @@ def fitness_func(ga_instance, solution, solution_idx): num_genes=num_genes, mutation_num_genes=6, fitness_func=fitness_func, - init_range_low=4, - init_range_high=10, + init_range_low=1, + init_range_high=100, # suppress_warnings=True, - random_mutation_min_val=4, - random_mutation_max_val=10, + random_mutation_min_val=1, + random_mutation_max_val=100, mutation_by_replacement=True, gene_type=int, + allow_duplicate_genes=False, # mutation_probability=0.4, - gene_constraint=[lambda x: x[0]>=8,None,None,None,None,None]) + # gene_constraint=[lambda x: x[0]>=8,None,None,None,None,None], + gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], + ) # ga_instance.run() diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index 2980c4a9d5177a6b87596a24482887043d8938bb..f45cda16f87b541fb82f89ff8cda156ec09927ca 100644 GIT binary patch delta 7556 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 KTf&;zw*LpMDt18t delta 7615 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 z zVFbDg(NR|-ufWyI5T+qaM<~%|&SUvAFyq{bj7xH+8|)sbhEFwR9=k6g9aZx+b0d3W z;dfwe)O3Bo?{%@L1->slio!?USS@gp+PtmXT06b&wvfMdVxPkEaD`Sep9QB?!;;&j z=`LMmX3UnDL3DWLY0u4PIjN*h`I}g6BpY|YJHwSZCXe)ddkDDvVFbG3A3=(4H@d{=;-Cv*00s1Zq9a9lqW-uJ z1%E_Xi*O5oD@X5-*dy>18x1^Nb1q=hp%pJ%z>2ccP>_L;hcE-7NK+TE)ht_kZ2`M+ z5xU_@;`Jz*G?yrFA;i_Lq%^gj4yL&>OPHZ7=x8AWMkHMY`bSj?$_zNy3sf{6Uq~U2Z?l@VkP_n1@K2-4oe0-x^A@uOSyVlh eIESjI3aJ|HzQwFD3zr_VvtFxObTMmX%Krkxbc(kC diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc index 93bfc50a82ee733f7e59e3c39fefa46328137788..be22fbdc0f82cd32d6d079863eccce5f0f7e54ae 100644 GIT binary patch literal 6193 zcmeHLTaVku73NJ8#a*wrP8)meqM5pFS^-|QL2tk!P3RVcV;N+vc3Qb3KXa%XvE>nxy+n1=ljU~(xuSC z=gmKe@zoz$)<3AT{BhB_jbHIAG{V}qge{!Z-naF?v+wACci+|j-oE#NC0yY>wuJY{ z+3yJF&sM*4dMEsNEZOvA#KmAN)2UR=ADiT&N+lcff))8hvXRWBN@8Yav573lxhMwV z6PUN|#ndi;TK8__uC(BmWn6k}!6og;z9%}uN59kS_PSzA1fuuY-uHVUdY8q9*hH@@ zF2T|5+W)=wE_RlTkPYg;hhK3M&CEK)Hau`th%5WdnPHK1HM4gt)9X9DXIoatn$JhF ztW;L861yOH$ys?al`J3X8?^>&q1zWsdDFwVl?90sB*yp0KKrf+a&Hc|6uJ&=Dbz@}q3w@``8b zWs6$O{Qcu;*X?MeN;2`mkL#O3HC{ zz@{oclyO>xfw`ZSz$K=Ieb>>Q%c#xQ<$rrcLDRojjvXL6jCziU>B#U+Ep1Ct; z<_=fW2KRLWwPSso?>#SxCP|h|stMznU=x0VMdz3=;Rrn_INi?4rL2-obs&|TN zI?Yun1x`*G4bz;L5s4Jfkm1K9^SP%w2(I$c)IGguQrBMcW1X+2>CvPeN}6>KwBQX4 zf3byTJM?Ye4!rPbXb1KwTgtX)3O+aS)CRO6ao+#>iG$246P~hZKCROK-4xOr*j7Mf z_18$8moK&NDJlo`7EPrfj%1YPM~Z@0UtYR7PDW$3jl1j7|1L$pF%&KK)acqJfp4QZ z{lQYmpEvsnf@;N74d);7t&5+AroZC?HO88G!aldpf>|J(9jmktokJJ(=jWw&*ul@A z*{D8U;hwu@hVagvv);@Xoyt--vo~gb*`4(s*$+CZpIw7QaM+vCxG%cr?pZI}YBU9h zq1NS#;M^@YjGl6nWZ$>`dgH!x7M^Xuqf3wuci`8|opnU-+@t>6@b%NJ`N(IEx%cpQ zrT6LOS?@DjdlUj>vssVUfqoAVy}=*g__cBWNFQtf7!?A=OMoh>O|HNzlw?gs9HKw3 zM9(UOYd~tEj5BR;bY6tCGN7tfrRT7r$|vk-oB%VqF~$<`7?}c>q*$2J_}r-F$Bo+M zs{IYrYTVMk8NEQiQIg^O>e6VOK9JY~$@p}do`6KO<#)|Pttt_XgBSTjv-ToSD?*kv z>9)8YoJ2}aQy$BS%*s7>hYeFcB0d6mF3JAU7_?3zNOOz=`&km9O_UiwNWn=Ex-2gT zn$al93ji8M!DJ@q^O{6vVv?<0yGC}sk9B)hZIm~iFqM!wH`&Q zi(n!LvceIVEkV6B?iS;`N(DQREC#hmgjDeNE>DXkwAGQ%stF+nz}bWd{R8L#P$MUR zOFDJ*q8fiC+$veA5*XE_t;uc97NSNWtEM!Awhl`9|KO(~I>yka7xdQ*CBh7@351wCok)oM|CFsl|YtIBm|kR|f2TIpkFek_?yMT4vHmV4zx5 z(=m>m#eRAbl!r~C+tekd}F1WjLr_%~!%u{FVrai#bP(|%kC zseD)=((rkqhPeNt_s(03-Rj$#>L|jVz73j=jl(@coqKmd>pj}4y4xII5P28)SbG`| zuN#@G^K-s-s?;?a-&Vx6>yD=Jwe!K9`n6S7X82eK?t!a!d7+n9`*;ij8pE8-YTu+{ z?Z@LhiQ!I_C5Yan_VWXJNT_=f;u;|8oAFq~xDi=!LcO_>egdd(sJzO=1=6Oc)IO9y ztYm-F5W#Q3dv%4DU>;t~q<|79LNQg}(?g5RRXZd=PYd;C*;+* z{Q}eKAGUBmOj3~LhWpZ~TiEVztfA++*JRJN4-WuSE->LRD0CWbN*68D68oGBC(}{-WdcIfctWA#kmF`|CjSe z$KLvdA35hyr2qf` delta 1132 zcmb_c%TLrm81M8k?Y>vQeF#cL&=d$lBr%F|kqeQ7uM-iQ%9N$Jba7gQC9@&%aqz%_ zOya>r0v8j#8REfs_u$QoCcS&|FHkRjQ&?Cy`!)ULd(Ag9zhBc2!(Yy4Yne<^0(bN>-NV&`Ew%xreUsZRIB)mP>p~ zebpAJfm@x6k`>o?ZON|;M_*>aVWxl1AQJSx7cMJvgw2(6a1Fr72<2)Ku zAroai-wSQ(_!ZZ#)>bGM$y`fv-mA=01F|%809pP(Ka0vhRh>t#*eMxKQbfzLkIIMB~pON zNEOV1Lf)Edp-3ZPeMvY)o=+8fX$KHP5rCD5G}vva9_dctQ0mkp404d#BLAM9yPTv6 z(U}sE2G|!nK38Py2~SpOIW8UEi#KxRRI6Y+x$e|3GzC2X=??xYH#sF#ZJJO`#Ib7q z;D8cRtd{~xfJ&emZWxe7%wU)a{S?qE0VFHVYjyW&KCj+B!qfla_CgD{_>c*D4o*pX z1&G_zK5iAd4G~*1wfL<<|G;tbjxQOE*9jQl?+aZ=2>Vv(E6+eFC;{LO88sjL7h`^+ zcqxZs5iLXoU6#&gihb)jF|1vHCE%ifLm6T(D{?Y`pga(fAsMpKCYO?b4?K7XX8-^I diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc index ad216d8e6c08a8dfe5d8f31b4c0fd979a06d7ce4..fde934ea73a4094bf2a032d6a4a9e47f81e65091 100644 GIT binary patch delta 4436 zcmbtXYiwLc6~1%d_wLNv}`w?n`Afk-Ob$V zJj&f}D+eK!Mv63lfVSXBAT?E0gjPa4iUf#CJSq@>FBSZY6cvG>0+gf_&YA1j8^@F% z*xIvaX3m^BbIy0poV{BAVLeqzCT$7+#t+al$`8JqQb=93G}=pbYFs9hs%6qXYEc__ z%_1~Q6Vw4-r%9T+EKM3#ES;t;Fl$T zV&Ecws^rmJF*hr270d9MhjRR>+(CYOz~&!n|LiA`jav#J>R!CAN2Xtyo()PBkD&v& zZR~o`0!EA&x_~8+I7pH}BHNptDZ~byHEyI*r`@0+G}7Qwl^4w~XT|;%wz4#rtuK;| z{CVq3ZDXLz%(TfpM3o(45{K}NeO$YV`x;f-RCjC!F0ZjQ@Fh-W>wt@o4_K)LqWLJJ8cp+V&< zX3Y#uN@gWC0AYvK!;&R=DcBoBXK{y+R{%h3O$!fDLK8Ot{*;;(>Qn>8)*J!e0#NIX z;g%VtelTxF7GUMHTbL<%i)``YLi9!aA~Qw;y%>jAcnXmpsea&A^9I`i12%$WCz4%A z1QJk+?FMpPi4sCfjL6vAMje+4E~`;mEKgNDKVZd@7eoNr00C5@F>aQYVp#c67Pla% zV}Tg+@nY2OyVLGeP>4YuPv&j56l#QVeI?`yd!uBtHa|+m>RcLYS_S~t56WJGkB|3n z$>@qqbRsYQ>$gnH1OV%5^SplBkxjh=#q^IHjX&S9t&T83{|cy7fe}#2EkXsk`=A0q zArN>3iVQ&MZvsjZjq?IdrZ6%agIzU4!zREYZ^fr!l^8dS{d|kKsxgNj%aTr;I z)4{y{KaliEa0uH2Pl5Oo4E?!0Xag|!d-(XB$t|cB$No83(f_ z)L5NbK$8@$uoq6TmUh?KyMa>fEO!CzzGsV2N)uNU>d@pB8828fygv!)o@Pl{Pk~gt z{$7y53s48H{{cveSuiQ<|1Id9@1yBZ1~j(3rd%UL^4V6_1E2kF>< zS4o{cj>8q5i1X-1{9fir@2Pv$uos&=xqRP({`>qnf`0ET_lN_U4nn7Lb7(l-fED!3 zTd{BW_|RY?itzoQB360nXvkP%mj~{A1`P zc^}g;As{WKBMf^5hS?cEJ<4?DLZqH4ojwC)Gfr+?!>1u`vhYD&=(PAx^X&s-j9Hr} z@FbU>bFVAxF_d{6$-%~r`~X?o34CODw8&WTlBhP37R*+sU533Z>qXMX*KZimv&cTc z4{bQUAKx{Gbuvd58>nW=~bv%u2OO6t$?EKRMRDCem1C z)}#5BriXZW3F50auA5wsM=afV={Dl7`|xb*Zb!Opd13zyEc;mTbRcGXmUPIr+M<}m zgcg#&zHujh-Pm}9j}1>YULF1s5iQ$6Xd*@ZMNhHqc!U-^3kx!YLR4IgN7OEQ6~9`X za-qS1ud^^%3sa6sH51dDyNjG(n3+u$N&Q8MR|$UI;E! z3KRlUngUUOg#UH(ZoXwpmYitp-!e@)-vGo(^Tsu)rUq@HI!EBw#{2x;t%Ib&|Gc&L z=(htI`@BGbiuQh>A`ibX%u&hv>cWaCvU>vbbV0#LLOr!Z8C*X@6KyNAF*2r`{ zTCG64ctqqg69IE&%k|&Y$AQ7*x2>f9jLQ2#nHi_)#kvt6~ zGN((Ph3&DW8rZx7b}r{I@%LHT)GC@xScIYM40@PmOG}#ih|<5g#8JOe_M64fEX`F${>PCn*-`kNk+H^}otIQSR>R*O z&8-rw0I5AXUGjse%1m|aCaC=YK2neL+&2DVx{E(MX7av0o9Y)~vMDPs!4PUwq5{ni zYwn;?BdYzmKbJ zak4fU;71Bb2_y3|3sF-191$jc0fxWesWG>8GjJ0Cs~AxeY%~AL*!ub=6n_ZGVufUn z;t(4dhV_RD+byc3?(8^Syzyx#$@gPy0MKwqD_nphVhgbavKF097 delta 4359 zcma)9eQX>@72nz2+uhsSJD=mP`*3QhL?Px?UuO4^cxa$G#?_%3&Q zm)UjFl)WAzMIb7Rh@lb%RXs~pQKVJ^o%p9ghzhkxs6r|d2(6H)R0I@Mfdq=wk0iV| zYsVKiRM@(=Z|A+4dGmW8^Y+!|nG0=N9aD(S}(a4Uu{UmAc@8qX*&V_VO3Wp@VhbJS08-aKrMY zvPQ{SnN9eSM%+>Th;OB(M%<56B`tY+*(gWPOYAV{nZE8@zU(VLXi=39$>|-DMZJ;m z6TS{QXZ;w}2Bby|bS<~+#ajsqGkz#h!zZ{Za8X{8kE&)dqw-y`?d?Cymdxwck<{#b z(K%Cc&J26=_V5|ov4_tWD+{*E2Xe!Pc=s;W`MwK4`_km)+!5LspbSG zsuW5NwV&W`X?u3z@tJ`niUbeOVn{3?f$1z%3mz*KEAG1l20oZPQY=+$%3tdr;BOB` z_?PsbC6i)F*wrq$U|5BZ8Q;u^b?>kwOYmPAUnN`k!RR;o55ST}dYzmis%#O1m_#Fn z%!BA-?Omp!W)zU;B5V{M!X7q;TmlF$B>FQw_=cWieMtI|3?NDI?Z{+mQ9wgp*1w(@IB=`T0%j49l|KYSgP#bA)4=#z!|z`M{{LJg^`gmS7ZDh)G~_~s?j&`sZj7**S; zWL0T0#QX|jIeaKVOaL9aJ3JT~no|V^fnIcrj1?~iT9HAB*g=nLSL|7@uppwnAm-&_ zY!#OO>2}ri>^jTYuzJ3$Sg8~`HVzWuvNjD@3?U401bwz1$Rhk)^p%=1Rhhv54NcLm ztGY}q891We)D#@oZdfW$^i4JqWptH5`8+TyD3=AslmNDhz&7D%o!A0)kbP8L0F(e{ z1%9f)I+B)_WTCJ zr0UIgDVPeLQ7W|yfjVD0GasmK=}DVm7#$PtW1j*xr-eS!z%#T~4Rr9yT#Z$O2&*kP zlXCWbL?-HfNUB2-=?M8XOO6)@sQPDq~C(tUM_ zgl{>e{oBl2gq-AWj*s&<1``nxeSCOJYM;S&z`9|!rjQ##GK^#clKnseZFas^nzaEp z5w4#`L0GEz-u2nBJDm~XyAD4W>q9DT$;tLp>t9gs!D)f0jD-??BCI2#44|HH6iXu4 zjbt4^GBVO!W6>Q;3}~-7YZPaS8gr{Yg@tAl3N|AdN3s#g-ALBBXB&+5L9V;Qz^Ezy z*O5o9djNd^T)Iry4*tOC@jWm2aQ!Su?CUTZqcU7XQ@+wL;3876&@_xN*gd$0$`)KQ zr+rmu9p|r)ZtBBSP#3j^ewjF>Wl5EjjkyybflCUnCzL(l5nsDRydK!;Z?|^3{E0hV zrJAp`dY2^F^+#Z(#?Ni+YwBr8Vrl@5Q`|keyhE}>`wyN{?ko2L9RS_c8L$Q9{99jz9=a)==eJdSSy$DSr~hzvg0py`}iGw*vHc&?o% zR=fkMyO&X2qrZYIFOKE9-hkZ^p4UQn_;X_$V(*1f1lIl$;wZ!aG!{GD;SHD}q7>xU zK6d;V*g+P^Wtnw(N^2;LTwlLLfQ#`Bo3f<4y?4{M<++n#4aEzX#gXhmvKPq=ket4H z4|X&#LJ6-Fk(E%NBd%9u9@J*9Ru7lJ0-YmlA1Y0^_m2;e2Qke9-J#+Z6-6qDcnkH4 z%|xS-LDekkq}_#jy~aGa0!xlpn5)!^UIF_bm!(l@kYCyISQ9TohJ`m!X=xTVKWkrf zMRj9XRM~DM_anJe6~tyH7=X6zyj`GRL}|9@*=~W_cD-_u9RO)A*>RO0#&tvqS}AS( zTU+n`GByq!xZJ`m#NZG}^U;;oaB^)`Is)PYSe5#L7(E%%E1POkF`z|TZq)RNDQ%g( zf8wMnnzv^s@`7pqbK)uy?bI=7;Y4pH>gaY@HHd^5pxbM>HC4y*O(_+X9cX zHqEOzn74!o8dta$T!!b(4?uT%+Gd4f#j}|M_=FvayLyl;!wWCynS*%+??SBu%60tE z_Io4jK9KXLwr9wb?dP^vNZ(b6TdAclNe$Io=c|_pH2)v*%;W}gl}}G*?N>b6k3y0w zP#^!{L8}2VtNO;Y1jQhf7@|}hgLN!wKqdMRxJ1KrBKY(ngQ4LwX%s#gddj`!{_|K^ z#wav|tteJdcXV+TAU*4G`ukzpT93qMKwVgz6-kxggC^}1JpMeK^ z08v!{{=l#uTCYJGENALY6pQ4NS5?_G&dsuiQTQN|hmeR~T{HtCGCqrABGpbJ*L(yi zW=UXFN{$GMFuzWrKmTK2!k)V@$7)qb`MH8nWqFvLPkDB=9yVaN$88v+{OInj zrbt7CGtV2lH*C87_7A$lNbgu4Zq z$q6|@Ovp!5=G&)ckZD(^o>aAPeg59`aKB(h4w%!GlIzXZs`Z&)^U=LuXwD-FtEQ9? z3KGO>@LkC|TYH?%szBZWtC)jonmBsst1qHhIPojM1rhOSnTxI_b*PJ7P`+E_aJWPF z@^{QE&Zj8lv6*xh4a&)^f}=GgV*5vdyTbR)*eM0LBT%@*5Ie%~9bWI_Kb;vrFo6>$ zk=!c2qN`@xa184i6Xu>l4%h5Be~1*{urF0ODiiY~`H=a(yw`l+xK|#8_YdVANTsIn oc{yudx0(#l03-svP@uKhLLvMR9mYlXBN5SuIv?NT`};=z3tE@f(*OVf diff --git a/pygad/helper/misc.py b/pygad/helper/misc.py index 1052c3f..1437e27 100644 --- a/pygad/helper/misc.py +++ b/pygad/helper/misc.py @@ -9,12 +9,27 @@ class Helper: + def get_gene_dtype(self, gene_index): + + """ + Returns the data type of the gene by its index. + It accepts a single parameter: + -gene_index: The index of the gene to get its data type. Only used if each gene has its own data type. + It returns the data type of the gene. + """ + + if self.gene_type_single == True: + dtype = self.gene_type + else: + dtype = self.gene_type[gene_index] + return dtype + def get_random_mutation_range(self, gene_index): """ Returns the minimum and maximum values of the mutation range. It accepts a single parameter: - -gene_index: The index of the gene to get its range. Only used if the gene has a specific mutation range + -gene_index: The index of the gene to get its range. Only used if the gene has a specific mutation range. It returns the minimum and maximum values of the gene mutation range. """ @@ -51,7 +66,8 @@ def generate_gene_random_value(self, gene_value, gene_idx, mutation_by_replacement, - num_values=1): + num_values=1, + step=1): """ Randomly generate one or more values for the gene. It accepts: @@ -60,15 +76,39 @@ def generate_gene_random_value(self, -gene_value: The original gene value before applying mutation. -gene_idx: The index of the gene in the solution. -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. - -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. - If num_values=1, it returns a single numeric value. If num_values>1, it returns an array with number of values equal to num_values. + -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For flaot data types, a None value returns only a single value. + -step (int, optional): The step size for generating candidate values. Defaults to 1. Only used with genes of an integer data type. + + It returns, + -A single numeric value if num_values=1. Or + -An array with number of values equal to num_values if num_values>1. """ - # Generating a random value. - random_value = numpy.asarray(numpy.random.uniform(low=range_min, - high=range_max, - size=num_values), - dtype=object) + gene_type = self.get_gene_dtype(gene_index=gene_idx) + if gene_type[0] in pygad.GA.supported_int_types: + random_value = numpy.asarray(numpy.arange(range_min, + range_max, + step=step), + dtype=gene_type[0]) + if num_values is None: + # Keep all the values. + pass + else: + if num_values >= len(random_value): + # Number of values is larger than or equal to the number of elements in random_value. + # Makes no sense to create a larger sample out of the population because it just creates redundant values. + pass + else: + # Set replace=True to avoid selecting the same value more than once. + random_value = numpy.random.choice(random_value, + size=num_values, + replace=False) + else: + # Generating a random value. + random_value = numpy.asarray(numpy.random.uniform(low=range_min, + high=range_max, + size=num_values), + dtype=object) # Change the random mutation value data type. for idx, val in enumerate(random_value): @@ -97,7 +137,8 @@ def get_valid_gene_constraint_values(self, gene_idx, mutation_by_replacement, solution, - num_values=100): + num_values=100, + step=1): """ Randomly generate values for the gene that satisfy the constraint. It accepts: @@ -108,14 +149,21 @@ def get_valid_gene_constraint_values(self, -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. -solution: The solution in which the gene exists. -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. - If num_values=1, it returns a single numeric value. If num_values>1, it returns an array with number of values equal to num_values. + -step (int, optional): The step size for generating candidate values. Defaults to 1. Only used with genes of an integer data type. + + It returns, + -A single numeric value if num_values=1. Or + -An array with number of values equal to num_values if num_values>1. Or + -None if no value found that satisfies the constraint. """ random_values = self.generate_gene_random_value(range_min=range_min, range_max=range_max, gene_value=gene_value, gene_idx=gene_idx, mutation_by_replacement=mutation_by_replacement, - num_values=num_values) + num_values=num_values, + step=step) + # It returns None if no value found that satisfies the constraint. random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, solution=solution, gene_idx=gene_idx) diff --git a/pygad/helper/unique.py b/pygad/helper/unique.py index 94ea8d5..f074d06 100644 --- a/pygad/helper/unique.py +++ b/pygad/helper/unique.py @@ -15,7 +15,7 @@ def solve_duplicate_genes_randomly(self, max_val, mutation_by_replacement, gene_type, - num_trials=10): + num_values=100): """ Resolves duplicates in a solution by randomly selecting new values for the duplicate genes. @@ -25,7 +25,7 @@ def solve_duplicate_genes_randomly(self, max_val (int): The maximum value of the range to sample a number randomly. mutation_by_replacement (bool): Indicates if mutation is performed by replacement. gene_type (type): The data type of the gene (e.g., int, float). - num_trials (int): The maximum number of attempts to resolve duplicates by changing the gene values. Only works for floating-point gene types. + num_values (int): The maximum number of random values to generate to find a unique value. Returns: tuple: @@ -42,10 +42,7 @@ def solve_duplicate_genes_randomly(self, num_unsolved_duplicates = 0 if len(not_unique_indices) > 0: for duplicate_index in not_unique_indices: - if self.gene_type_single == True: - dtype = gene_type - else: - dtype = gene_type[duplicate_index] + dtype = self.get_gene_dtype(gene_index=duplicate_index) if dtype[0] in pygad.GA.supported_int_types: temp_val = self.unique_int_gene_from_range(solution=new_solution, @@ -61,7 +58,7 @@ def solve_duplicate_genes_randomly(self, max_val=max_val, mutation_by_replacement=mutation_by_replacement, gene_type=gene_type, - num_trials=num_trials) + num_values=num_values) if temp_val in new_solution: num_unsolved_duplicates = num_unsolved_duplicates + 1 @@ -163,9 +160,34 @@ def unique_int_gene_from_range(self, int: The new integer value of the gene. If no unique value can be found, the original gene value is returned. """ - # The gene_type is of the form [type, precision] - dtype = gene_type + if self.gene_constraint and self.gene_constraint[gene_index]: + # A unique value is created out of the values that satisfy the constraint. + # num_values=None to return all the values. + random_values = self.get_valid_gene_constraint_values(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + solution=solution, + num_values=None, + step=step) + # If there is no value satisfying the constraint, then return the current gene value. + if random_values is None: + return solution[gene_index] + else: + pass + else: + # There is no constraint for the current gene. Return the same range. + # num_values=None to return all the values. + random_values = self.generate_gene_random_value(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + num_values=None, + step=step) + """ # For non-integer steps, the numpy.arange() function returns zeros if the dtype parameter is set to an integer data type. So, this returns zeros if step is non-integer and dtype is set to an int data type: numpy.arange(min_val, max_val, step, dtype=gene_type[0]) # To solve this issue, the data type casting will not be handled inside numpy.arange(). The range is generated by numpy.arange() and then the data type is converted using the numpy.asarray() function. all_gene_values = numpy.asarray(numpy.arange(min_val, @@ -183,12 +205,14 @@ def unique_int_gene_from_range(self, # After adding solution[gene_index] to the list, we have to change the data type again. all_gene_values = numpy.asarray(all_gene_values, dtype[0]) + """ - selected_value = self.select_unique_value(gene_values=all_gene_values, + selected_value = self.select_unique_value(gene_values=random_values, solution=solution, gene_index=gene_index) - selected_value = dtype[0](selected_value) + # The gene_type is of the form [type, precision] + selected_value = gene_type[0](selected_value) return selected_value @@ -199,7 +223,7 @@ def unique_float_gene_from_range(self, max_val, mutation_by_replacement, gene_type, - num_trials=10): + num_values=100): """ Finds a unique floating-point value for a specific gene in a solution. @@ -211,20 +235,44 @@ def unique_float_gene_from_range(self, max_val (int): The maximum value of the range to sample a floating-point number randomly. mutation_by_replacement (bool): Indicates if mutation is performed by replacement. gene_type (type): The data type of the gene (e.g., float, float16, float32, etc). - num_trials (int): The maximum number of attempts to resolve duplicates by changing the gene values. + num_values (int): The maximum number of random values to generate to find a unique value. Returns: int: The new floating-point value of the gene. If no unique value can be found, the original gene value is returned. """ + if self.gene_constraint and self.gene_constraint[gene_index]: + # A unique value is created out of the values that satisfy the constraint. + random_values = self.get_valid_gene_constraint_values(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + solution=solution, + num_values=num_values) + # If there is no value satisfying the constraint, then return the current gene value. + if random_values is None: + return solution[gene_index] + else: + pass + else: + # There is no constraint for the current gene. Return the same range. + random_values = self.generate_gene_random_value(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + num_values=num_values) + + """ # The gene_type is of the form [type, precision] dtype = gene_type # We cannot have a list of all values out of a continous range. - # Solution is to create a subset (e.g. 100) of all the values. + # Solution is to create a subset (e.g. 100) of some random values out of the range. some_gene_values = numpy.random.uniform(low=min_val, high=max_val, - size=100) + size=num_values) # If mutation is by replacement, do not add the current gene value into the list. # This is to avoid replacing the value by itself again. We are doing nothing in this case. @@ -244,8 +292,9 @@ def unique_float_gene_from_range(self, # Just convert the data type. some_gene_values = numpy.asarray(some_gene_values, dtype[0]) + """ - selected_value = self.select_unique_value(gene_values=some_gene_values, + selected_value = self.select_unique_value(gene_values=random_values, solution=solution, gene_index=gene_index) return selected_value @@ -267,7 +316,7 @@ def select_unique_value(self, gene_values, solution, gene_index): if len(values_to_select_from) == 0: # If there are no values, then keep the current gene value. - if not self.suppress_warnings: warnings.warn("You set 'allow_duplicate_genes=False' but cannot find a value to prevent duplicates.") + if not self.suppress_warnings: warnings.warn(f"'allow_duplicate_genes=False' but cannot find a unique value for the gene at index {gene_index}.") selected_value = solution[gene_index] else: selected_value = random.choice(values_to_select_from) @@ -352,10 +401,7 @@ def unique_gene_by_space(self, value_from_space = curr_gene_space # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. elif curr_gene_space is None: - if self.gene_type_single == True: - dtype = gene_type - else: - dtype = gene_type[gene_idx] + dtype = self.get_gene_dtype(gene_index=gene_idx) if dtype[0] in pygad.GA.supported_int_types: if build_initial_pop == True: @@ -393,14 +439,11 @@ def unique_gene_by_space(self, max_val=high, mutation_by_replacement=True, gene_type=dtype, - num_trials=num_trials) + num_values=num_trials) elif type(curr_gene_space) is dict: - if self.gene_type_single == True: - dtype = gene_type - else: - dtype = gene_type[gene_idx] + dtype = self.get_gene_dtype(gene_index=gene_idx) # Use index 0 to return the type from the list (e.g. [int, None] or [float, 2]). if dtype[0] in pygad.GA.supported_int_types: @@ -459,10 +502,7 @@ def unique_gene_by_space(self, else: # Selecting a value randomly from the global gene space in the 'gene_space' attribute. if type(self.gene_space) is dict: - if self.gene_type_single == True: - dtype = gene_type - else: - dtype = gene_type[gene_idx] + dtype = self.get_gene_dtype(gene_index=gene_idx) if dtype[0] in pygad.GA.supported_int_types: if 'step' in self.gene_space.keys(): @@ -514,10 +554,7 @@ def unique_gene_by_space(self, # Similar to the round_genes() method in the pygad module, # Create a round_gene() method to round a single gene. - if self.gene_type_single == True: - dtype = gene_type - else: - dtype = gene_type[gene_idx] + dtype = self.get_gene_dtype(gene_index=gene_idx) if not dtype[1] is None: value_from_space = numpy.round(dtype[0](value_from_space), @@ -625,10 +662,7 @@ def unpack_gene_space(self, elif type(space) is dict: # Create a list of values using the dict range. # Use numpy.linspace() - if self.gene_type_single == True: - dtype = self.gene_type - else: - dtype = self.gene_type[space_idx] + dtype = self.get_gene_dtype(gene_index=gene_idx) if dtype[0] in pygad.GA.supported_int_types: if 'step' in space.keys(): @@ -663,10 +697,7 @@ def unpack_gene_space(self, size=1)[0] gene_space_unpacked[space_idx][idx] = random_value - if self.gene_type_single == True: - dtype = self.gene_type - else: - dtype = self.gene_type[space_idx] + dtype = self.get_gene_dtype(gene_index=space_idx) # Change the data type. gene_space_unpacked[space_idx] = numpy.array(gene_space_unpacked[space_idx], diff --git a/pygad/pygad.py b/pygad/pygad.py index b163960..6765169 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -491,7 +491,7 @@ def __init__(self, max_val=self.init_range_high, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) else: self.initial_population[initial_solution_idx], _, _ = self.solve_duplicate_genes_by_space(solution=initial_solution, gene_type=self.gene_type, @@ -1427,6 +1427,9 @@ def initialize_population(self, # Adding the current gene values to the population. self.population[:, gene_idx] = gene_values + # Round the randomly generated values. + self.population = self.round_genes(self.population) + # Enforce the gene constraints as much as possible. if gene_constraint is None: pass @@ -1434,14 +1437,13 @@ def initialize_population(self, # Note that gene_constraint is not validated yet. # We have to set it as a propery of the pygad.GA instance to retrieve without passing it as an additional parameter. self.gene_constraint = gene_constraint - for solution in self.population: + for sol_idx, solution in enumerate(self.population): for gene_idx in range(self.num_genes): # Check that a constraint is available for the gene and that the current value does not satisfy that constraint if self.gene_constraint[gene_idx]: - print(gene_idx, solution[gene_idx]) if not self.gene_constraint[gene_idx](solution): range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) - # While initializing the population, we follow a mutation by replacement approach. So, the gene value is not needed. + # While initializing the population, we follow a mutation by replacement approach. So, the original gene value is not needed. random_values_filtered = self.get_valid_gene_constraint_values(range_min=range_min, range_max=range_max, gene_value=None, @@ -1449,7 +1451,11 @@ def initialize_population(self, mutation_by_replacement=True, solution=solution, num_values=100) - print(gene_idx, random_values_filtered) + if random_values_filtered is None: + if not self.suppress_warnings: + warnings.warn(f"No value satisfied the constraint for the gene at index {gene_idx} while creating the initial population.") + else: + self.population[sol_idx, gene_idx] = random.choice(random_values_filtered) if allow_duplicate_genes == False: for solution_idx in range(self.population.shape[0]): @@ -1459,7 +1465,7 @@ def initialize_population(self, max_val=high, mutation_by_replacement=True, gene_type=gene_type, - num_trials=10) + num_values=100) # self.logger.info("After", self.population[solution_idx]) elif self.gene_space_nested: @@ -2010,7 +2016,6 @@ def run(self): if self.save_best_solutions: self.best_solutions.append(list(best_solution)) - # Note: Any code that has loop-dependant statements (e.g. continue, break, etc) must be kept inside the loop of the 'run()' method. It can be moved to another method to clean the run() method. # If the on_generation attribute is not None, then cal the callback function after the generation. if not (self.on_generation is None): diff --git a/pygad/utils/__pycache__/crossover.cpython-310.pyc b/pygad/utils/__pycache__/crossover.cpython-310.pyc index d2b64d32f7e0d65392c4128076b1f0e3053c69b6..339bfd606ceb0b92b7110057f508de92953b19f0 100644 GIT binary patch delta 598 zcmdm~e?^}+pO=@50SIhuQ!@HC@=j!C=1Jk5ypUOxk#F;M=HHBrDU&y|dMlSD=9H!u zzXT~=$y_7|q>4m=#4WD8(%krxqRhmc;?1ILoJ@=?o8>vOnIwUR^fJdV*D%+x*)Y^{ z*K!y7)G!A#X!31d%(a-&u}A`_SyQq|3S^Kph>!shAR~)pL4*R3xW!tPT9jX0BnJ|Z z2MMqwr)K8dV$RGfncTx;tfmc;&;b!5AVL>NXz~>4f!O*W!T>}VPQJ~fGg**Vgv|`Z zHJ_}=tHBt!*^4(G*_EtNSE@l>d5C{4qZ!zhE5&fT67I4hMUdG_AVOjCDnT6!;+zIl ze~ULgH7~U|zBs?MC^@xwvH-8B0LY=YI6&g@nJE=uCks!u5fWmYvDsh93fh#45J!jtEVO923Jetl>F delta 611 zcmcbjzf+$#pO=@50SInHM5Om@AaRSeDzzxTxJU*hAPW*;Nlwkoxy78B zS5hPo5>x;ZlPB{S%ZPw@+CV~+r$`6H)&&uIAVPog8y+1g0}#^?L>PexV-R5iB1|V6 z@oF#zZI0%RXOc!Zm=)?^HHd=~D}er&*!-4%GNZ{#G2G6EI~CzRuvb8i494e3pi^$~ zrl;nm7RMLomlh?b78l`i@+}UKy76o7Zwi;zM6N=WY|JLc$Gv=ggUN z&YU?jv+wVaoi9j*R$gA}f-g1BQahn56%R_?Fp9#zzIo-*_U z(Y)ETT z-nHV+OL=$R!-_WCYu>zY;w>6AeY zo>rf-Qe{tQAt5zaF;1ab6(_Ub(2#qzMZfs>UGM@)l53B7?tYY zEvmZMJy|cdI|+J%|S#L;y55&!ab-*5l&be zY9|p3h&gc&vP_9b!3gnN4V^|2nHQHsJ!C=L2|Ys=#c!ck0?xIGp*vpl0*X=d0k*8g z?(;=uOQ25hd9=PJe(vq^EutDh#6?xO{pcjB&cadDoMH}4Tn;YZT1pto`OA9P|2-j0 zYz?#zs9?7}aW$Rc2}ez2(yP!GXOoqfH;G6g(&F!d=ZIt-8QjJ2&f)$u7|t^YjONb* zY_Fa&6N!w_d6c1zX1ST+Mzti~`CVsW@lQo_wzGA-jh+rJ9 z<=4?n5gmv=#3u;n7TrRv2XR|`GaY>DL?2H#H*ez9r-(6xg}8&bD~4to556^9;G%;I7L>HnLvG*ZB+s^J&J^u_H3x%V0*k`g0&L_moyeSdq z>4~rABe7}BFoSS@5S-=lp>ljw{B?we;0Wgi+(Zp;3&*43*c`qs#uqwTkAf<@7%Q6o7Yj*XwO;Cvg(z$8PMzc49mHq$xiMv~HbNO&kgiQPK)IDaOs%%?8`4 zX6?dZ143=3ULsoN00*ih5NHc72li50fdrMfRF!(@$(4!_r(O^sQKa_5dmE)HD3Q97 zzxU?Ndoyp|%=4A2qHnO;HR&hyR$z}8))MxAzeq^ zvL6)z+1YAW4#4JY#JtuVv@1!}$IRcnRf(keS%14P?bH_vm9n-_ z%9M-8Yuip_3zb|(qwlgLzwSR@KWl@KjZfuz;CYP_DlRFDHba<~44WYtGL@F3Ma594 zToG6lEgiz5-LOvzWqIB0})X~Yzk0KtY4uORj!b^>a3rAjel<*&yiHi`Dm4UZIaqv}NvK+Mik{8XsN za~Lf_L>Tb`&Z+!|ke5vIyP@qQ$sdMx5w|%QE{nv+tGy8t;J169Bhy@syy3SBI)ahX zBVfgt18~WFFY=D)oB?%&`OtcVlfJNH8r5BhxA=~}z|K)rt#c$$v(C2V!Y**RX@1ZL z>HKBhfBH5dS$^Z$z%xg|UXy2YT7k`4YQCTyhjPidWCH^0$8v}~Z|i@F$mT@ z;HiTc&NKu@vljt1yFORR=LzzeX&0o_7a;z6c8oE3gQdIDgtkW xVO7}LM@QPiOav9f^c?_IP(@Wz{lfdIQ>ALR+N4I+etuylrJTl)3I1><{x9J{R*wJx diff --git a/pygad/utils/crossover.py b/pygad/utils/crossover.py index d7cd86e..a09edc3 100644 --- a/pygad/utils/crossover.py +++ b/pygad/utils/crossover.py @@ -70,12 +70,11 @@ def single_point_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, num_trials=10) - return offspring @@ -143,7 +142,7 @@ def two_points_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, @@ -210,7 +209,7 @@ def uniform_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, @@ -274,7 +273,7 @@ def scattered_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, diff --git a/pygad/utils/mutation.py b/pygad/utils/mutation.py index 676d0fb..8535902 100644 --- a/pygad/utils/mutation.py +++ b/pygad/utils/mutation.py @@ -379,7 +379,8 @@ def mutation_process_random_value(self, range_min, range_max, solution, - gene_idx): + gene_idx, + num_values=100): """ Randomly generate constrained values to use for applying mutation. @@ -388,6 +389,7 @@ def mutation_process_random_value(self, -range_max: The maximum value in the range from which a value is selected. -solution: The solution where the target gene exists. -gene_idx: The index of the gene in the solution. + -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. It returns a single numeric value the satisfies the gene constraint if exists in the gene_constraint parameter. """ @@ -399,7 +401,7 @@ def mutation_process_random_value(self, gene_value=solution[gene_idx], gene_idx=gene_idx, mutation_by_replacement=self.mutation_by_replacement, - num_values=100) + num_values=num_values) # Filter the values that satisfy the constraint. random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, solution=solution, @@ -440,7 +442,7 @@ def mutation_randomly(self, offspring): range_min, range_max = self.get_random_mutation_range(gene_idx) - # Generate a random value fpr mutation that meet the gene constraint if exists. + # Generate a random value for mutation that meet the gene constraint if exists. random_value = self.mutation_process_random_value(range_min=range_min, range_max=range_max, solution=offspring[offspring_idx], @@ -454,7 +456,7 @@ def mutation_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) return offspring @@ -492,7 +494,7 @@ def mutation_probs_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) return offspring def swap_mutation(self, offspring): @@ -934,7 +936,7 @@ def adaptive_mutation_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) return offspring def adaptive_mutation_probs_by_space(self, offspring): @@ -1138,5 +1140,5 @@ def adaptive_mutation_probs_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_trials=10) + num_values=100) return offspring diff --git a/test_gene_constraint.py b/test_gene_constraint.py new file mode 100644 index 0000000..28e290a --- /dev/null +++ b/test_gene_constraint.py @@ -0,0 +1,164 @@ +import pygad +import random +import numpy + +num_generations = 1 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +def population_gene_constraint(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + random_seed=123, + crossover_type='single_point', + initial_population=None, + parent_selection_type='sss', + multi_objective=False, + gene_constraint=None, + allow_duplicate_genes=True): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + initial_population=initial_population, + parent_selection_type=parent_selection_type, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=allow_duplicate_genes, + mutation_by_replacement=mutation_by_replacement, + random_seed=random_seed, + crossover_type=crossover_type, + gene_constraint=gene_constraint, + save_solutions=True, + suppress_warnings=True) + + ga_instance.run() + + return ga_instance + +#### Single-Objective +def test_initial_population_int_by_replacement(): + gene_constraint=[lambda x: x[0]>=8,lambda x: x[1]>=8,lambda x: 5>=x[2]>=1,lambda x: 5>x[3]>3,lambda x: x[4]<2] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=0, + init_range_high=10, + num_genes=5, + gene_type=int, + mutation_by_replacement=True) + initial_population = ga_instance.initial_population + # print(initial_population) + + assert numpy.all(initial_population[:, 0] >= 8), "Not all values in column 0 are >= 98" + + assert numpy.all(initial_population[:, 1] >= 8), "Not all values in column 1 are >= 98" + + assert numpy.all(initial_population[:, 2] >= 1), "Not all values in column 2 are >= 1" + assert numpy.all((initial_population[:, 2] >= 1) & (initial_population[:, 2] <= 5)), "Not all values in column 2 between 1 and 5 (inclusive)" + + assert numpy.all(initial_population[:, 3] == 4), "Not all values in column 3 between 3 and 5 (exclusive)" + + assert numpy.all(initial_population[:, 4] < 2), "Not all values in column 4 < 2" + +def test_initial_population_int_by_replacement_no_duplicates(): + gene_constraint=[lambda x: x[0]>=5,lambda x: x[1]>=5,lambda x: x[2]>=5,lambda x: x[3]>=5,lambda x: x[4]>=5] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=1, + init_range_high=10, + random_mutation_min_val=1, + random_mutation_max_val=10, + gene_type=int, + num_genes=5, + mutation_by_replacement=True, + allow_duplicate_genes=False) + + num_duplicates = 0 + for idx, solution in enumerate(ga_instance.solutions): + num = len(solution) - len(set(solution)) + if num != 0: + print(solution, idx) + num_duplicates += num + + assert num_duplicates == 0 + +def test_initial_population_float_by_replacement_no_duplicates(): + gene_constraint=[lambda x: x[0]>=5,lambda x: x[1]>=5,lambda x: x[2]>=5,lambda x: x[3]>=5,lambda x: x[4]>=5] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=1, + init_range_high=10, + gene_type=[float, 1], + num_genes=5, + crossover_type=None, + mutation_by_replacement=False, + allow_duplicate_genes=False) + + num_duplicates = 0 + for idx, solution in enumerate(ga_instance.solutions): + num = len(solution) - len(set(solution)) + if num != 0: + print(solution, idx) + num_duplicates += num + + assert num_duplicates == 0 + +def test_initial_population_float_by_replacement_no_duplicates2(): + gene_constraint=[lambda x: x[0]>=1,lambda x: x[1]>=1,lambda x: x[2]>=1,lambda x: x[3]>=1,lambda x: x[4]>=1] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=1, + init_range_high=2, + gene_type=[float, 1], + num_genes=5, + crossover_type=None, + mutation_by_replacement=False, + allow_duplicate_genes=False) + + num_duplicates = 0 + for idx, solution in enumerate(ga_instance.solutions): + num = len(solution) - len(set(solution)) + if num != 0: + print(solution, idx) + num_duplicates += num + + assert num_duplicates == 0 + +if __name__ == "__main__": + #### Single-objective + print() + test_initial_population_int_by_replacement() + print() + test_initial_population_int_by_replacement_no_duplicates() + print() + test_initial_population_float_by_replacement_no_duplicates() + print() + test_initial_population_float_by_replacement_no_duplicates2() + print() diff --git a/tests/test_gene_constraint.py b/tests/test_gene_constraint.py new file mode 100644 index 0000000..34a18b9 --- /dev/null +++ b/tests/test_gene_constraint.py @@ -0,0 +1,635 @@ +import pygad +import random +import numpy + +num_generations = 1 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +def population_gene_constraint(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + random_seed=123, + initial_population=None, + parent_selection_type='sss', + multi_objective=False, + gene_constraint=None): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + initial_population=initial_population, + parent_selection_type=parent_selection_type, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=False, + mutation_by_replacement=mutation_by_replacement, + random_seed=random_seed, + gene_constraint=gene_constraint, + save_solutions=True, + suppress_warnings=True) + + ga_instance.run() + + return ga_instance + +#### Single-Objective +def test_number_duplicates_default(): + gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,None,None,None,None,None,None,None,None] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=98, + init_range_high=98) + initial_population = ga_instance.initial_population + assert initial_population[:, 0] >= 98 + assert initial_population[:, 1] >= 98 + +def test_number_duplicates_default_initial_population(): + num_duplicates = population_gene_constraint(initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type_initial_population(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + initial_population=initial_population, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_initial_population(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement_initial_population(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space(): + num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space_initial_population(): + num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=range(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space_initial_population(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=range(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space_initial_population(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space(): + num_duplicates = population_gene_constraint(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_initial_population(): + num_duplicates = population_gene_constraint(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + + +# def test_number_duplicates_nested_gene_space_nested_gene_type(): + """ + This example causes duplicate genes that can only be solved by changing the values of a chain of genes. + Let's explain it using this solution: [0, 2, 3, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 6 at indices 5 and 6. + According to the gene space, none of these genes can has a different value that solves the duplicates. + -If the value of the gene at index 5 is changed from 6 to 5, then it causes another duplicate with the gene at index 4. + -If the value of the gene at index 6 is changed from 6 to 7, then it causes another duplicate with the gene at index 7. + The solution is to change a chain of genes that make a room to solve the duplicates between the 2 genes. + 1) Change the second gene from 2 to 1. + 2) Change the third gene from 3 to 2. + 3) Change the fourth gene from 4 to 3. + 4) Change the fifth gene from 5 to 4. + 5) Change the sixth gene from 6 to 5. This solves the duplicates. + But this is NOT SUPPORTED yet. + We support changing only a single gene that makes a room to solve the duplicates. + + Let's explain it using this solution: [1, 2, 2, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 2 at indices 1 and 2. + This is how the duplicates are solved: + 1) Change the first gene from 1 to 0. + 2) Change the second gene from 2 to 1. This solves the duplicates. + The result is [0, 1, 2, 4, 5, 6, 6, 7, 8, 9] + """ + # num_duplicates = population_gene_constraint(gene_space=[[0, 1], + # [1, 2], + # [2, 3], + # [3, 4], + # [4, 5], + # [5, 6], + # [6, 7], + # [7, 8], + # [8, 9], + # [9, 10]], + # gene_type=[int, int, int, int, int, int, int, int, int, int], + # num_genes=10) + + # assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_nested_gene_type_initial_population(): + num_duplicates = population_gene_constraint(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=[int, int, int, int, int, int, int, int, int, int], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +#### Multi-Objective +def test_number_duplicates_default_multi_objective(): + num_duplicates = population_gene_constraint() + + assert num_duplicates == 0 + +def test_number_duplicates_default_initial_population_multi_objective(): + num_duplicates = population_gene_constraint(initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type_multi_objective(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type_initial_population_multi_objective(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + initial_population=initial_population, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_initial_population_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement_initial_population_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = population_gene_constraint(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space_multi_objective(): + num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space_initial_population_multi_objective(): + num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space_multi_objective(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=range(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space_initial_population_multi_objective(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=range(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space_multi_objective(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space_initial_population_multi_objective(): + num_genes = 10 + num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_multi_objective(): + num_duplicates = population_gene_constraint(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_initial_population_multi_objective(): + num_duplicates = population_gene_constraint(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + + +# def test_number_duplicates_nested_gene_space_nested_gene_type_multi_objective(): + """ + This example causes duplicate genes that can only be solved by changing the values of a chain of genes. + Let's explain it using this solution: [0, 2, 3, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 6 at indices 5 and 6. + According to the gene space, none of these genes can has a different value that solves the duplicates. + -If the value of the gene at index 5 is changed from 6 to 5, then it causes another duplicate with the gene at index 4. + -If the value of the gene at index 6 is changed from 6 to 7, then it causes another duplicate with the gene at index 7. + The solution is to change a chain of genes that make a room to solve the duplicates between the 2 genes. + 1) Change the second gene from 2 to 1. + 2) Change the third gene from 3 to 2. + 3) Change the fourth gene from 4 to 3. + 4) Change the fifth gene from 5 to 4. + 5) Change the sixth gene from 6 to 5. This solves the duplicates. + But this is NOT SUPPORTED yet. + We support changing only a single gene that makes a room to solve the duplicates. + + Let's explain it using this solution: [1, 2, 2, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 2 at indices 1 and 2. + This is how the duplicates are solved: + 1) Change the first gene from 1 to 0. + 2) Change the second gene from 2 to 1. This solves the duplicates. + The result is [0, 1, 2, 4, 5, 6, 6, 7, 8, 9] + """ + # num_duplicates = population_gene_constraint(gene_space=[[0, 1], + # [1, 2], + # [2, 3], + # [3, 4], + # [4, 5], + # [5, 6], + # [6, 7], + # [7, 8], + # [8, 9], + # [9, 10]], + # gene_type=[int, int, int, int, int, int, int, int, int, int], + # num_genes=10) + + # assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_nested_gene_type_initial_population_multi_objective(): + num_duplicates = population_gene_constraint(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=[int, int, int, int, int, int, int, int, int, int], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +if __name__ == "__main__": + #### Single-objective + print() + test_number_duplicates_default() + print() + + """ + test_number_duplicates_default_initial_population() + print() + + test_number_duplicates_float_gene_type() + print() + test_number_duplicates_float_gene_type_initial_population() + print() + + test_number_duplicates_int_gene_type() + print() + test_number_duplicates_int_gene_type_initial_population() + print() + + test_number_duplicates_int_gene_type_replacement() + print() + test_number_duplicates_int_gene_type_replacement_initial_population() + print() + + test_number_duplicates_single_gene_space() + print() + test_number_duplicates_single_gene_space_initial_population() + print() + + test_number_duplicates_single_range_gene_space() + print() + test_number_duplicates_single_range_gene_space_initial_population() + print() + + test_number_duplicates_single_numpy_range_gene_space() + print() + test_number_duplicates_single_numpy_range_gene_space_initial_population() + print() + + test_number_duplicates_nested_gene_space() + print() + test_number_duplicates_nested_gene_space_initial_population() + print() + + # This example causes duplicates that can only be solved by changing a chain of genes. + # test_number_duplicates_nested_gene_space_nested_gene_type() + # print() + test_number_duplicates_nested_gene_space_nested_gene_type_initial_population() + print() + + #### Multi-objective + print() + test_number_duplicates_default_initial_population_multi_objective() + print() + + test_number_duplicates_float_gene_type_multi_objective() + print() + test_number_duplicates_float_gene_type_initial_population_multi_objective() + print() + + test_number_duplicates_int_gene_type_multi_objective() + print() + test_number_duplicates_int_gene_type_initial_population_multi_objective() + print() + + test_number_duplicates_int_gene_type_replacement_multi_objective() + print() + test_number_duplicates_int_gene_type_replacement_initial_population_multi_objective() + print() + + test_number_duplicates_single_gene_space_multi_objective() + print() + test_number_duplicates_single_gene_space_initial_population_multi_objective() + print() + + test_number_duplicates_single_range_gene_space_multi_objective() + print() + test_number_duplicates_single_range_gene_space_initial_population_multi_objective() + print() + + test_number_duplicates_single_numpy_range_gene_space_multi_objective() + print() + test_number_duplicates_single_numpy_range_gene_space_initial_population_multi_objective() + print() + + test_number_duplicates_nested_gene_space_multi_objective() + print() + test_number_duplicates_nested_gene_space_initial_population_multi_objective() + print() + + # This example causes duplicates that can only be solved by changing a chain of genes. + # test_number_duplicates_nested_gene_space_nested_gene_type_multi_objective() + # print() + test_number_duplicates_nested_gene_space_nested_gene_type_initial_population_multi_objective() + print() + """ + + From 6a3b58b58348093561f806d35e931fa3e161238a Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Wed, 2 Jul 2025 22:48:44 -0400 Subject: [PATCH 09/25] Apply constraints on gene space --- .DS_Store | Bin 0 -> 8196 bytes example.py | 12 +- pygad/.DS_Store | Bin 0 -> 6148 bytes pygad/__init__.py | 2 +- pygad/__pycache__/__init__.cpython-310.pyc | Bin 227 -> 205 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 83006 -> 82985 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 282 -> 260 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 6193 -> 13628 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 16314 -> 16294 bytes pygad/helper/misc.py | 337 +++- pygad/helper/unique.py | 110 +- pygad/pygad.py | 6 +- pygad/utils/.DS_Store | Bin 0 -> 6148 bytes pygad/utils/__init__.py | 2 +- .../__pycache__/__init__.cpython-310.pyc | Bin 354 -> 332 bytes .../__pycache__/crossover.cpython-310.pyc | Bin 6100 -> 6079 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 23864 -> 17721 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 7299 -> 7277 bytes .../parent_selection.cpython-310.pyc | Bin 14577 -> 14555 bytes pygad/utils/crossover.py | 8 +- pygad/utils/mutation.py | 516 +----- .../__pycache__/__init__.cpython-310.pyc | Bin 259 -> 237 bytes .../__pycache__/plot.cpython-310.pyc | Bin 13852 -> 13830 bytes setup.py | 2 +- test_gene_constraint.py | 75 +- test_gene_space.py | 1605 +++++++++++++++++ tests/test_gene_constraint.py | 658 ++----- tests/test_gene_space.py | 2 +- 28 files changed, 2214 insertions(+), 1121 deletions(-) create mode 100644 .DS_Store create mode 100644 pygad/.DS_Store create mode 100644 pygad/utils/.DS_Store create mode 100644 test_gene_space.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ccee05d899388080d4c4fe26cc7300ddc72623e8 GIT binary patch literal 8196 zcmeHMPj4GV6n~Q@vE9&S>$*uOsOu6zJK0C)HbTsR;E*WNkt0g&J~GwXD{Nz_v*YGj&8NW3Vv1-44 zlW2j6@~DzpGiXjJa$eRGWg;0Vf->k62@2MI;kJCyIw5Ta3<3rLgMdN6AYc&qKOlfR zTbBG8_r9JrtwF#b@LD1u&j%Y-(n>;ynyRA%jY}$4+P^p+ilTDs1ZP zfhDReNimpK$Gk1YK`RLzYO2+VX?0?WXO?6rOnHZ%t;C5{G&QY3z#woE0hzl`(+V9> zOpi19y9}j|QE&gFic+4WHl$pNQhC0V`z<4Po;dB3LtW~T4}KB!;G_;~rflakIvF2q zC*vbWcWE7KU!zZ`Mr-5N+0%^llw1LIu%`C7$8y?3g6!pkr#U+*m)2E_4Sx{*MnUF=X*eW zqbH}LZ5~FTO)I0**m1(p2|B!b@X&W!+}{YQB92A3;khEv4>7CEqPrP#X|nwOUmDeN z<)aa^s2PjOn!&H>(uhkas|Oq$E4-|0Iy^USFK@CL5*Yq8IPe0I)^fUcVf70J< znw8i!c7rXmJM2Stmwn2%S%>+o%l5Q^iP5XRHtvZH&`-VUy;>OfBIsaLYFt$NzegeB z_X|Xti-=!D6!UsD>%&>PL{Eg zXzJKn<_uD2McQvl@>Mx2MnFcH4P;RruS0H_5KuZe)@|BMZ>RBBDmT#>sN=iU^je@?Do?tis0U-9N&YL+Ms literal 0 HcmV?d00001 diff --git a/example.py b/example.py index 405a273..752575c 100644 --- a/example.py +++ b/example.py @@ -23,11 +23,15 @@ def fitness_func(ga_instance, solution, solution_idx): random_mutation_min_val=1, random_mutation_max_val=100, mutation_by_replacement=True, - gene_type=int, + gene_type=[float, 1], + save_solutions=True, allow_duplicate_genes=False, - # mutation_probability=0.4, - # gene_constraint=[lambda x: x[0]>=8,None,None,None,None,None], + # gene_space=numpy.unique(numpy.random.uniform(1, 100, size=100)), + gene_space=[range(10), {"low": 1, "high": 5}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], ) -# ga_instance.run() +ga_instance.run() + +print(ga_instance.gene_space_unpacked) +print(ga_instance.population) diff --git a/pygad/.DS_Store b/pygad/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..98be63892770443c647aa222a56a6882b24d65b7 GIT binary patch literal 6148 zcmeHK&x_MQ6n@hmo5mvapy)B+b-rD* zS9lV<`lsmk-b^8BtVfY$UYYsk$M+^P-^*k=M5Ow&(E(A1h+0UDja$h6VBF8CVl~^s z&Pv8eXi7u;9@4nNb`4Ger@;TF0Po$l?Ae4eD(>IK^HiTjsgA&4)(fQLUt%b2yvDSk zGYXL|DCTjtyuRbpFf4f0!OSSDvPxq&>0HLFFxA_wSu})M<^9~^?aBO7e3xRHl%HOU z|Hh5x`Pi!PH5#W$KJ9jYs9L?z-19u&YkMCC7kWOJhSOQz4=3-q_DbtC+LXiSbrR2q zojcETHVu<3o`^#-ia~k*Cdo#6-q*8il#B1cbohSDA9n67mj_4P{owHFYCl*WJ?7$* z!_~@f-F@)r_~r02{gmlzOdqE32^PC;aRXmaC@ueLFv(J#oui$7Ji262Ab8GVmc@~< ze=7vAwj+E+P75+GBrqd63btM)31X4&;n>ia0l!3*87K~Ak=>*j_z9(ym**;h+`+sV zVJs|tD~LW_L6RKOgb*g7&lH!%;Xsylq6t2GoB~A!_;3i27<(4i2Dypb z;@TiuVD68A$lx-kz&};sH;9NK_5c6? literal 0 HcmV?d00001 diff --git a/pygad/__init__.py b/pygad/__init__.py index 17959b7..53ccec2 100644 --- a/pygad/__init__.py +++ b/pygad/__init__.py @@ -1,3 +1,3 @@ from .pygad import * # Relative import. -__version__ = "3.3.1" +__version__ = "3.5.0" diff --git a/pygad/__pycache__/__init__.cpython-310.pyc b/pygad/__pycache__/__init__.cpython-310.pyc index 38c074e3eb591001514bf5fd8106cdef041639fa..a939de9e2e698a7f89fc506e58abc4558a6692ed 100644 GIT binary patch delta 86 zcmaFNc$SejpO=@50SLA`r)Jns0hl9}w7lb&CcS(1?(P+5|ZpQm3?2~sripb`LN#vJSb delta 108 zcmX@h_?VG5pO=@50SInHM5H@SOD)MvPRvQqF9IsaEvPKX$j{S-DAr9(F3HT#gQ=akS_uGMvnEae diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index f45cda16f87b541fb82f89ff8cda156ec09927ca..7b6c592df0cde0e632c0bc20361189d07465f9c7 100644 GIT binary patch delta 4726 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 Nnq$$Pj-#&|zXqir^|Al} delta 1447 zcmZ`(&yU+w5RRS1akBa0F70NMY`bpCR&^m^m;P8()RvVHg;EM?FBGk0dF>a+ZDNP9 z!)_2#gn&Pwyes04;J_-!f_g$i6{I3Pe`;ShYEf$Ier|PIq@nh*7rC7pU%~^2DxGOl7+ZjU>%ByTJ zVlaE5d|To_<&IB{tjL9I&=Vz>`ONfuhwX{H6HP|!iwvkJ^5-4r(za_kdPjG?s6XD( zM-bd)b`)-pCWvoSV&vJ@zzqNi!_g$_2Yx3ZcPu;d0{?GrKbGGfdZFDNO+>ZkvdD~; zW6ARD`^qCp6*Z4uV~%5?D)teb4QKhG=qjtWxCK;z^}L;8DK0 zcqadggzMS2ElHB@%^t4o%O!t2G~crZV;1rsmsm5$8W zmp8ilo1pXIGZ%YvRQG!Ne0OD|Thb36Ljr~BpaKjDM;5UWVg=mZe8rFv@ZM*l0Bkg{ zY=%S4;w?okzEv~M;aBsGIxN##19FeZ$Kw<7wJrW(``A?EQ5t7butE#Knh3>;`-Mj_ zUnsbCf}v3Ce&E?CHTJz;00;y-WCl@Wn9jh8m}uMmxY0?9IUGk8c_3+&84p^tAQ<}& zO$1LR$~46h3ZDM%m>Fe=Cw~OXg+;14acSIfp(c)X_TWVPYTh|$P(qH-@_|BIcOz+sD^OLu>c0<{I!DNsMq6qWR~ zLZODiU`&&V>4psFktUu#oLA2qL<|o6ZSG7=8$aX-EYFXOh16tTr_nsAyGhugI9#No zm;Rw|Cg(FJJq-T&xwVB)kQ|b2l_r0DZmV?@Ya{0-_Lbqn;acTa&aX|$&hQk4It980 zT%s`lStK7cVKN3v2#;b=GpelC)J1t&mP&ufZ^%;h_n2WQqF|a%V4Ehiktoz%3w+Oa XLpY6FSfxNmp$7g;Qc6YMywLs&K_jS2 diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc index fde934ea73a4094bf2a032d6a4a9e47f81e65091..71e7049cad614f07d97c66d4d7636bed3a4f7a2a 100644 GIT binary patch delta 700 zcmZuuOK1~O6z!cfpC)Zc>BO|@C;d>Qd|dID}^pZ5f|c4;>P!)NI(bXygPFa=bn4ttLBrYUmqIs z_R(jvh2-MlOaEhz`|Q9gz6ANmc{raX)eIGhmU0{6?+zXxj21{|Fb%ln+8^s>zW_Dq zES;AW6_xRu6Cr#&;UrhxUm>$0Y+1;(1WB%;az!c&>&U9wx*_C|hHRyjQ!Bcu*mCXS zo-ON|VC*rGj8p@e0(m&_)axIqP^l@|NMr@R%x^#exA;TQ@P*(t{5%+f77ho-$#L-h zKcUIN=p~50ckN1Q16k%84Qj^!!Xm386h6?`N?suLE- z(d+{UM6iDH5loYj=zKrSko4(Oz;^?SmLZjp*wE-vrCTY+Yc%$|_!Mm6Z}B4-AnipT z7coU@S?Fhz6$TlKidI%iG-{d=*qt~B<77RN;z%N#= z%of5uw&kI6G-nZa2GH*q@?in3|U7=^-ccNpE4Y1==`uQF0)B=)UN^EUgu n#h`l)*J=HB|bc{m_#rR75d#*=DE7+GKZ~-Po3@ z0S_MZpz@vs@uEl(56YbMs0T&-1q&j05P~O9DqfsPwSMg3*_rowX5Qz0m`Al2wKh8z zi#CaG?zmA3Jb%{qkiaL-$vBBv)rxjWFIOq*t$n!BaT4RhNxT({;)j%qqp{C;KXkJt zx-E10SXdzo=Zr)7qHd(rRMGTG)uoif>y}Mbjz?SIHAmgkJpr^;9xefNFq&mJCTHY zd^7R*zZv*O$1w=A88I!J#dn?EFoN~YbdZEYyqD`uMkjy+KIv-obr$y!@x zc11I-Wtl^fGDBY2;n9k-mCnB&e)Y@d#)NG%Hv=d5liX4>)N$wJnfRIvB(n|cxH{QW zxFYr>Ac(7Ckq$K)Fhz<=5=JFh0{mt%6J1M3SidYqBuFzXEy?!A)V5cmZ0jw#j7id! zBz~PtDVOE8CP9^OUceh1oO;#LXtUa=p);Mt_;i2!0_i3Y{Xq^$Y^v>;$GPdHp9}fl AIRF3v diff --git a/pygad/helper/misc.py b/pygad/helper/misc.py index 1437e27..42a68bf 100644 --- a/pygad/helper/misc.py +++ b/pygad/helper/misc.py @@ -9,6 +9,97 @@ class Helper: + def change_gene_value_dtype(self, + random_value, + gene_index, + gene_value, + mutation_by_replacement): + """ + Change the data type of the random value used to apply mutation. + It accepts 2 parameters: + -random_value: The random value to change its data type. + -gene_index: The index of the target gene. + -gene_value: The gene value before mutation. Only used if mutation_by_replacement=False and gene_type_single=False. + -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + It returns the new value after changing the data type. + """ + + # If the mutation_by_replacement attribute is True, then the random value replaces the current gene value. + if mutation_by_replacement: + if self.gene_type_single == True: + random_value = self.gene_type[0](random_value) + else: + random_value = self.gene_type[gene_index][0](random_value) + if type(random_value) is numpy.ndarray: + random_value = random_value[0] + # If the mutation_by_replacement attribute is False, then the random value is added to the gene value. + else: + if self.gene_type_single == True: + random_value = self.gene_type[0](gene_value + random_value) + else: + random_value = self.gene_type[gene_index][0](gene_value + random_value) + if type(random_value) is numpy.ndarray: + random_value = random_value[0] + return random_value + + def round_gene_value(self, random_value, gene_index): + """ + Round the random value used to apply mutation. + It accepts 2 parameters: + -random_value: The random value to round its value. + -gene_index: The index of the target gene. Only used if nested gene_type is used. + It returns the new value after being rounded. + """ + + # Round the gene + if self.gene_type_single == True: + if not self.gene_type[1] is None: + random_value = numpy.round(random_value, self.gene_type[1]) + else: + if not self.gene_type[gene_index][1] is None: + random_value = numpy.round(random_value, self.gene_type[gene_index][1]) + return random_value + + def filter_gene_values_by_constraint(self, + values, + solution, + gene_idx): + + """ + Filter the random values generated for mutation based on whether they meet the gene constraint in the gene_constraint parameter. + It accepts: + -values: The values to filter. + -solution: The solution containing the target gene. + -gene_idx: The index of the gene in the solution. + It returns None if no values satisfy the constraint. Otherwise, an array of values that satisfy the constraint is returned. + """ + + # A list of the indices where the random values satisfy the constraint. + filtered_values_indices = [] + # A temporary solution to avoid changing the original solution. + solution_tmp = solution.copy() + # Loop through the random values to filter the ones satisfying the constraint. + for value_idx, value in enumerate(values): + solution_tmp[gene_idx] = value + # Check if the constraint is satisfied. + if self.gene_constraint[gene_idx](solution_tmp): + # The current value satisfies the constraint. + filtered_values_indices.append(value_idx) + + # After going through all the values, check if any value satisfies the constraint. + if len(filtered_values_indices) > 0: + # At least one value was found that meets the gene constraint. + pass + else: + # No value found for the current gene that satisfies the constraint. + if not self.suppress_warnings: + warnings.warn(f"No value found for the gene at index {gene_idx} that satisfies its gene constraint.") + return None + + filtered_values = values[filtered_values_indices] + + return filtered_values + def get_gene_dtype(self, gene_index): """ @@ -60,14 +151,146 @@ def get_initial_population_range(self, gene_index): range_max = self.init_range_high[gene_index] return range_min, range_max - def generate_gene_random_value(self, - range_min, - range_max, - gene_value, - gene_idx, - mutation_by_replacement, - num_values=1, - step=1): + def generate_gene_value_from_space(self, + gene_value, + gene_idx, + mutation_by_replacement, + sample_size=1): + """ + Generate/select one or more values for the gene from the gene space. + It accepts: + -gene_value: The original gene value before applying mutation. + -gene_idx: The index of the gene in the solution. + -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + -sample_size: The number of random values to generate. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For float data types, a None value returns only a single value. + -step (int, optional): The step size for generating candidate values. Defaults to 1. Only used with genes of an integer data type. + + It returns, + -A single numeric value if sample_size=1. Or + -An array with number of maximum number of values equal to sample_size if sample_size>1. + """ + + range_min, range_max = self.get_random_mutation_range(gene_idx) + + if self.gene_space_nested: + # Returning the current gene space from the 'gene_space' attribute. + # It is used to determine the way of selecting the next gene value: + # 1) List/NumPy Array: Whether it has only one value, multiple values, or one of its values is None. + # 2) Fixed Numeric Value + # 3) None + # 4) Dict: Whether the dict has the key `step` or not. + if type(self.gene_space[gene_idx]) in [numpy.ndarray, list]: + # Get the gene space from the `gene_space_unpacked` property because it undergoes data type change and rounded. + curr_gene_space = self.gene_space_unpacked[gene_idx].copy() + elif type(self.gene_space[gene_idx]) in pygad.GA.supported_int_float_types: + # Get the gene space from the `gene_space_unpacked` property because it undergoes data type change and rounded. + curr_gene_space = self.gene_space_unpacked[gene_idx] + else: + curr_gene_space = self.gene_space[gene_idx] + + if type(curr_gene_space) in pygad.GA.supported_int_float_types: + # If the gene space is simply a single numeric value (e.g. 5), use it as the new gene value. + value_from_space = curr_gene_space + elif curr_gene_space is None: + # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. + rand_val = numpy.random.uniform(low=range_min, + high=range_max, + size=sample_size) + if mutation_by_replacement: + value_from_space = rand_val + else: + value_from_space = gene_value + rand_val + elif type(curr_gene_space) is dict: + # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. + # The gene's space of type dict specifies the lower and upper limits of a gene. + if 'step' in curr_gene_space.keys(): + # When the `size` parameter is used, the numpy.random.choice() and numpy.random.uniform() functions return a NumPy array as the output even if the array has a single value (i.e. size=1). + # We have to return the output at index 0 to force a numeric value to be returned not an object of type numpy.ndarray. + # If numpy.ndarray is returned, then it will cause an issue later while using the set() function. + # Randomly select a value from a discrete range. + value_from_space = numpy.random.choice(numpy.arange(start=curr_gene_space['low'], + stop=curr_gene_space['high'], + step=curr_gene_space['step']), + size=sample_size) + else: + value_from_space = numpy.random.uniform(low=curr_gene_space['low'], + high=curr_gene_space['high'], + size=sample_size) + else: + # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. + # If the gene space has only 1 value, then select it. The old and new values of the gene are identical. + if len(curr_gene_space) == 1: + value_from_space = curr_gene_space + else: + # If the gene space has more than 1 value, then select a new one that is different from the current value. + # To avoid selecting the current gene value again, remove it from the current gene space and do the selection. + value_from_space = list(set(curr_gene_space) - set([gene_value])) + + """ + if len(values_to_select_from) == 0: + # After removing the current gene value from the space, there are no more values. + # Then keep the current gene value. + value_from_space = gene_value + else: + value_from_space = random.choice(values_to_select_from) + """ + else: + # Selecting a value randomly from the global gene space in the 'gene_space' attribute. + # The gene's space of type dict specifies the lower and upper limits of a gene. + if type(self.gene_space) is dict: + # When the gene_space is assigned a dict object, then it specifies the lower and upper limits of all genes in the space. + if 'step' in self.gene_space.keys(): + value_from_space = numpy.random.choice(numpy.arange(start=self.gene_space['low'], + stop=self.gene_space['high'], + step=self.gene_space['step']), + size=sample_size) + else: + value_from_space = numpy.random.uniform(low=self.gene_space['low'], + high=self.gene_space['high'], + size=sample_size) + else: + # If the space type is not of type dict, then a value is randomly selected from the gene_space attribute. + # To avoid selecting the current gene value again, remove it from the current gene space and do the selection. + value_from_space = list(set(self.gene_space) - set([gene_value])) + + """ + if len(values_to_select_from) == 0: + # After removing the current gene value from the space, there are no more values. + # Then keep the current gene value. + value_from_space = gene_value + else: + value_from_space = random.choice(values_to_select_from) + """ + + if len(value_from_space) == 0: + # After removing the current gene value from the space, there are no more values. + # Then keep the current gene value. + value_from_space = gene_value + elif sample_size == 1: + value_from_space = random.choice(value_from_space) + + # The gene space might be [None, 1, 7]. + # It might happen that the value None is selected. + # In this case, generate a random value out of the mutation range. + if value_from_space is None: + # TODO: Return index 0. + # TODO: Check if this if statement is necessary. + value_from_space = numpy.random.uniform(low=range_min, + high=range_max, + size=sample_size) + else: + value_from_space = numpy.array(value_from_space) + + return value_from_space + + def generate_gene_value_randomly(self, + range_min, + range_max, + gene_value, + gene_idx, + mutation_by_replacement, + sample_size=1, + step=1): """ Randomly generate one or more values for the gene. It accepts: @@ -76,12 +299,12 @@ def generate_gene_random_value(self, -gene_value: The original gene value before applying mutation. -gene_idx: The index of the gene in the solution. -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. - -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For flaot data types, a None value returns only a single value. + -sample_size: The number of random values to generate. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For float data types, a None value returns only a single value. -step (int, optional): The step size for generating candidate values. Defaults to 1. Only used with genes of an integer data type. It returns, - -A single numeric value if num_values=1. Or - -An array with number of values equal to num_values if num_values>1. + -A single numeric value if sample_size=1. Or + -An array with number of values equal to sample_size if sample_size>1. """ gene_type = self.get_gene_dtype(gene_index=gene_idx) @@ -90,46 +313,84 @@ def generate_gene_random_value(self, range_max, step=step), dtype=gene_type[0]) - if num_values is None: + if sample_size is None: # Keep all the values. pass else: - if num_values >= len(random_value): + if sample_size >= len(random_value): # Number of values is larger than or equal to the number of elements in random_value. # Makes no sense to create a larger sample out of the population because it just creates redundant values. pass else: # Set replace=True to avoid selecting the same value more than once. random_value = numpy.random.choice(random_value, - size=num_values, + size=sample_size, replace=False) else: # Generating a random value. random_value = numpy.asarray(numpy.random.uniform(low=range_min, high=range_max, - size=num_values), + size=sample_size), dtype=object) # Change the random mutation value data type. for idx, val in enumerate(random_value): - random_value[idx] = self.change_random_mutation_value_dtype(random_value[idx], - gene_idx, - gene_value, - mutation_by_replacement=mutation_by_replacement) + random_value[idx] = self.change_gene_value_dtype(random_value[idx], + gene_idx, + gene_value, + mutation_by_replacement=mutation_by_replacement) # Round the gene. - random_value[idx] = self.round_random_mutation_value(random_value[idx], gene_idx) + random_value[idx] = self.round_gene_value(random_value[idx], gene_idx) # Rounding different values could return the same value multiple times. # For example, 2.8 and 2.7 will be 3.0. # Use the unique() function to avoid any duplicates. random_value = numpy.unique(random_value) - if num_values == 1: + if sample_size == 1: random_value = random_value[0] return random_value + def generate_gene_value(self, + gene_value, + gene_idx, + mutation_by_replacement, + range_min=None, + range_max=None, + sample_size=1, + step=1): + """ + Generate one or more values for the gene either randomly or from the gene space. It acts as a router. + It accepts: + -gene_value: The original gene value before applying mutation. + -gene_idx: The index of the gene in the solution. + -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + -range_min (int, optional): The minimum value in the range from which a value is selected. It must be passed for generating the gene value randomly because we cannot decide whether it is the range for the initial population (init_range_low and init_range_high) or mutation (random_mutation_min_val and random_mutation_max_val). + -range_max (int, optional): The maximum value in the range from which a value is selected. It must be passed for generating the gene value randomly because we cannot decide whether it is the range for the initial population (init_range_low and init_range_high) or mutation (random_mutation_min_val and random_mutation_max_val). + -sample_size: The number of random values to generate/select and return. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For float data types, a None value returns only a single value. + -step (int, optional): The step size for generating candidate values. Defaults to 1. Only used with genes of an integer data type. + + It returns, + -A single numeric value if sample_size=1. Or + -An array with number of values equal to sample_size if sample_size>1. + """ + if self.gene_space is None: + output = self.generate_gene_value_randomly(range_min=range_min, + range_max=range_max, + gene_value=gene_value, + gene_idx=gene_idx, + mutation_by_replacement=mutation_by_replacement, + sample_size=sample_size, + step=step) + else: + output = self.generate_gene_value_from_space(gene_value=gene_value, + gene_idx=gene_idx, + mutation_by_replacement=mutation_by_replacement, + sample_size=sample_size) + return output + def get_valid_gene_constraint_values(self, range_min, range_max, @@ -137,10 +398,11 @@ def get_valid_gene_constraint_values(self, gene_idx, mutation_by_replacement, solution, - num_values=100, + sample_size=100, step=1): """ - Randomly generate values for the gene that satisfy the constraint. + Generate/select values for the gene that satisfy the constraint. The values could be generated randomly or from the gene space. + The number of returned values is at its maximum equal to the sample_size parameter. It accepts: -range_min: The minimum value in the range from which a value is selected. -range_max: The maximum value in the range from which a value is selected. @@ -148,23 +410,24 @@ def get_valid_gene_constraint_values(self, -gene_idx: The index of the gene in the solution. -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. -solution: The solution in which the gene exists. - -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. + -sample_size: The number of values to generate or select. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. -step (int, optional): The step size for generating candidate values. Defaults to 1. Only used with genes of an integer data type. It returns, - -A single numeric value if num_values=1. Or - -An array with number of values equal to num_values if num_values>1. Or + -A single numeric value if sample_size=1. Or + -An array with number of values equal to sample_size if sample_size>1. Or -None if no value found that satisfies the constraint. """ - random_values = self.generate_gene_random_value(range_min=range_min, - range_max=range_max, - gene_value=gene_value, - gene_idx=gene_idx, - mutation_by_replacement=mutation_by_replacement, - num_values=num_values, - step=step) + # Either generate the values randomly or from the gene space. + values = self.generate_gene_value(range_min=range_min, + range_max=range_max, + gene_value=gene_value, + gene_idx=gene_idx, + mutation_by_replacement=mutation_by_replacement, + sample_size=sample_size, + step=step) # It returns None if no value found that satisfies the constraint. - random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, - solution=solution, - gene_idx=gene_idx) - return random_values_filtered + values_filtered = self.filter_gene_values_by_constraint(values=values, + solution=solution, + gene_idx=gene_idx) + return values_filtered diff --git a/pygad/helper/unique.py b/pygad/helper/unique.py index f074d06..4b3edb1 100644 --- a/pygad/helper/unique.py +++ b/pygad/helper/unique.py @@ -15,7 +15,7 @@ def solve_duplicate_genes_randomly(self, max_val, mutation_by_replacement, gene_type, - num_values=100): + sample_size=100): """ Resolves duplicates in a solution by randomly selecting new values for the duplicate genes. @@ -25,7 +25,7 @@ def solve_duplicate_genes_randomly(self, max_val (int): The maximum value of the range to sample a number randomly. mutation_by_replacement (bool): Indicates if mutation is performed by replacement. gene_type (type): The data type of the gene (e.g., int, float). - num_values (int): The maximum number of random values to generate to find a unique value. + sample_size (int): The maximum number of random values to generate to find a unique value. Returns: tuple: @@ -58,7 +58,7 @@ def solve_duplicate_genes_randomly(self, max_val=max_val, mutation_by_replacement=mutation_by_replacement, gene_type=gene_type, - num_values=num_values) + sample_size=sample_size) if temp_val in new_solution: num_unsolved_duplicates = num_unsolved_duplicates + 1 @@ -162,14 +162,14 @@ def unique_int_gene_from_range(self, if self.gene_constraint and self.gene_constraint[gene_index]: # A unique value is created out of the values that satisfy the constraint. - # num_values=None to return all the values. + # sample_size=None to return all the values. random_values = self.get_valid_gene_constraint_values(range_min=min_val, range_max=max_val, gene_value=solution[gene_index], gene_idx=gene_index, mutation_by_replacement=mutation_by_replacement, solution=solution, - num_values=None, + sample_size=None, step=step) # If there is no value satisfying the constraint, then return the current gene value. if random_values is None: @@ -178,14 +178,14 @@ def unique_int_gene_from_range(self, pass else: # There is no constraint for the current gene. Return the same range. - # num_values=None to return all the values. - random_values = self.generate_gene_random_value(range_min=min_val, - range_max=max_val, - gene_value=solution[gene_index], - gene_idx=gene_index, - mutation_by_replacement=mutation_by_replacement, - num_values=None, - step=step) + # sample_size=None to return all the values. + random_values = self.generate_gene_value(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + sample_size=None, + step=step) """ # For non-integer steps, the numpy.arange() function returns zeros if the dtype parameter is set to an integer data type. So, this returns zeros if step is non-integer and dtype is set to an int data type: numpy.arange(min_val, max_val, step, dtype=gene_type[0]) @@ -223,7 +223,7 @@ def unique_float_gene_from_range(self, max_val, mutation_by_replacement, gene_type, - num_values=100): + sample_size=100): """ Finds a unique floating-point value for a specific gene in a solution. @@ -235,7 +235,7 @@ def unique_float_gene_from_range(self, max_val (int): The maximum value of the range to sample a floating-point number randomly. mutation_by_replacement (bool): Indicates if mutation is performed by replacement. gene_type (type): The data type of the gene (e.g., float, float16, float32, etc). - num_values (int): The maximum number of random values to generate to find a unique value. + sample_size (int): The maximum number of random values to generate to find a unique value. Returns: int: The new floating-point value of the gene. If no unique value can be found, the original gene value is returned. @@ -243,58 +243,28 @@ def unique_float_gene_from_range(self, if self.gene_constraint and self.gene_constraint[gene_index]: # A unique value is created out of the values that satisfy the constraint. - random_values = self.get_valid_gene_constraint_values(range_min=min_val, - range_max=max_val, - gene_value=solution[gene_index], - gene_idx=gene_index, - mutation_by_replacement=mutation_by_replacement, - solution=solution, - num_values=num_values) + values = self.get_valid_gene_constraint_values(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + solution=solution, + sample_size=sample_size) # If there is no value satisfying the constraint, then return the current gene value. - if random_values is None: + if values is None: return solution[gene_index] else: pass else: # There is no constraint for the current gene. Return the same range. - random_values = self.generate_gene_random_value(range_min=min_val, - range_max=max_val, - gene_value=solution[gene_index], - gene_idx=gene_index, - mutation_by_replacement=mutation_by_replacement, - num_values=num_values) - - """ - # The gene_type is of the form [type, precision] - dtype = gene_type - - # We cannot have a list of all values out of a continous range. - # Solution is to create a subset (e.g. 100) of some random values out of the range. - some_gene_values = numpy.random.uniform(low=min_val, - high=max_val, - size=num_values) - - # If mutation is by replacement, do not add the current gene value into the list. - # This is to avoid replacing the value by itself again. We are doing nothing in this case. - if mutation_by_replacement: - pass - else: - some_gene_values = some_gene_values + solution[gene_index] - - if not dtype[1] is None: - # Precision is available and we have to round the number. - # Convert the data type and round the number. - some_gene_values = numpy.round(numpy.asarray(some_gene_values, - dtype[0]), - dtype[1]) - else: - # There is no precision and rounding the number is not needed. The type is [type, None] - # Just convert the data type. - some_gene_values = numpy.asarray(some_gene_values, - dtype[0]) - """ - - selected_value = self.select_unique_value(gene_values=random_values, + values = self.generate_gene_value(range_min=min_val, + range_max=max_val, + gene_value=solution[gene_index], + gene_idx=gene_index, + mutation_by_replacement=mutation_by_replacement, + sample_size=sample_size) + + selected_value = self.select_unique_value(gene_values=values, solution=solution, gene_index=gene_index) return selected_value @@ -439,7 +409,7 @@ def unique_gene_by_space(self, max_val=high, mutation_by_replacement=True, gene_type=dtype, - num_values=num_trials) + sample_size=num_trials) elif type(curr_gene_space) is dict: @@ -532,8 +502,10 @@ def unique_gene_by_space(self, # If the space type is not of type dict, then a value is randomly selected from the gene_space attribute. # Remove all the genes in the current solution from the gene_space. # This only leaves the unique values that could be selected for the gene. - values_to_select_from = list(set(self.gene_space) - set(solution)) - + + # Before using the gene_space, use gene_space_unpacked instead of gene_space to make sure the numbers has the right data type and its values are rounded. + values_to_select_from = list(set(self.gene_space_unpacked) - set(solution)) + if len(values_to_select_from) == 0: if not self.suppress_warnings: warnings.warn("You set 'allow_duplicate_genes=False' but the gene space does not have enough values to prevent duplicates.") value_from_space = solution[gene_idx] @@ -591,14 +563,14 @@ def find_two_duplicates(self, def unpack_gene_space(self, range_min, range_max, - num_values_from_inf_range=100): + sample_size_from_inf_range=100): """ Unpacks the gene space for selecting a value to resolve duplicates by converting ranges into lists of values. Args: range_min (float or int): The minimum value of the range. range_max (float or int): The maximum value of the range. - num_values_from_inf_range (int): The number of values to generate for an infinite range of float values using `numpy.linspace()`. + sample_size_from_inf_range (int): The number of values to generate for an infinite range of float values using `numpy.linspace()`. Returns: list: A list representing the unpacked gene space. @@ -621,7 +593,7 @@ def unpack_gene_space(self, else: gene_space_unpacked = numpy.linspace(start=self.gene_space['low'], stop=self.gene_space['high'], - num=num_values_from_inf_range, + num=sample_size_from_inf_range, endpoint=False) if self.gene_type_single == True: @@ -662,7 +634,7 @@ def unpack_gene_space(self, elif type(space) is dict: # Create a list of values using the dict range. # Use numpy.linspace() - dtype = self.get_gene_dtype(gene_index=gene_idx) + dtype = self.get_gene_dtype(gene_index=space_idx) if dtype[0] in pygad.GA.supported_int_types: if 'step' in space.keys(): @@ -681,7 +653,7 @@ def unpack_gene_space(self, else: gene_space_unpacked[space_idx] = numpy.linspace(start=space['low'], stop=space['high'], - num=num_values_from_inf_range, + num=sample_size_from_inf_range, endpoint=False) elif type(space) in [numpy.ndarray, list, tuple]: # list/tuple/numpy.ndarray diff --git a/pygad/pygad.py b/pygad/pygad.py index 6765169..12afb2c 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -491,7 +491,7 @@ def __init__(self, max_val=self.init_range_high, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) else: self.initial_population[initial_solution_idx], _, _ = self.solve_duplicate_genes_by_space(solution=initial_solution, gene_type=self.gene_type, @@ -1450,7 +1450,7 @@ def initialize_population(self, gene_idx=gene_idx, mutation_by_replacement=True, solution=solution, - num_values=100) + sample_size=100) if random_values_filtered is None: if not self.suppress_warnings: warnings.warn(f"No value satisfied the constraint for the gene at index {gene_idx} while creating the initial population.") @@ -1465,7 +1465,7 @@ def initialize_population(self, max_val=high, mutation_by_replacement=True, gene_type=gene_type, - num_values=100) + sample_size=100) # self.logger.info("After", self.population[solution_idx]) elif self.gene_space_nested: diff --git a/pygad/utils/.DS_Store b/pygad/utils/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a364f0c0bd6445a262c5f5901cffdae298ec52af GIT binary patch literal 6148 zcmeHKu};G<5Pc3s3JOCd7Dj&o609syRR#toCMIY@gd&9o5sJi?f8+!B6nN*eDw+gF zgsQvf{Orp;J9)O^7=W9t?@xg#fRrwn9IzNMxi3DjBu4g$F7#311-HCT@HEJ_z<*Rg z<}M3!R$+zj^Y?aHH^s7UmdudlaYaA>48|I8hyu@O(DG{Vijpmv_df5v$II}DHP^|4 zUXi&PW4W6iD`j4b2eQsa^FMm|M|_e6pQt2H)?~|CO)q{MH7R?&uxVxrm;$E24l2Ms zTP)2KtuzHp0aIY3fP5c3T`-T>DTYr6i*N)WP8bfxy8J8($4AT~c8a`1b4n#D)x{%* zQ##{5=H(GPMWw^V!-tC_yLduzVRZKILpofpXr(D&3T!K|r_HJC{}(@>|F=ojG6hV5 zol?L}=GXHXUn%abgO`)N*3<9kVv^S>ZYZpTt(dv86(7@sG47>8%p-P+jL_^yAjn{a JDe$KXd;wTAS1$kn literal 0 HcmV?d00001 diff --git a/pygad/utils/__init__.py b/pygad/utils/__init__.py index b39eda1..7d30650 100644 --- a/pygad/utils/__init__.py +++ b/pygad/utils/__init__.py @@ -3,4 +3,4 @@ from pygad.utils import mutation from pygad.utils import nsga2 -__version__ = "1.2.1" \ No newline at end of file +__version__ = "1.3.0" \ No newline at end of file diff --git a/pygad/utils/__pycache__/__init__.cpython-310.pyc b/pygad/utils/__pycache__/__init__.cpython-310.pyc index 8b5a31185773287ac709257526cea352409186d5..d759c90d10eaed53530be5207e3626eadf84f7ae 100644 GIT binary patch delta 93 zcmaFFbcTsHpO=@50SMkZr)Dge$UBSKSkGYMDmxoD{m|mnqGJ8TjNH_e^u!c>m(=3y tlKcXF_td=9lFVesob>#n%#w`UfXb4L{5<`FN|2(`lFXc9{mE908UUY*AUyy8 delta 115 zcmX@Z^oWT!pO=@50SInHM5HgB$UBSKNY8NMD!ZVV6swr@#1sWxh4jpljM5~9f};Ga z)Z~)ln1V{6cuaa~UTR5Za$-(;ei2YXZb4;9Mt+_yM6qsSa!F=>9!za%NoG!Q%w!ct F4FKilDVG2M diff --git a/pygad/utils/__pycache__/crossover.cpython-310.pyc b/pygad/utils/__pycache__/crossover.cpython-310.pyc index 339bfd606ceb0b92b7110057f508de92953b19f0..c8c3626066c9e541fac87f78cc0a88154c574e2b 100644 GIT binary patch delta 107 zcmcbjzh9p(pO=@50SH>^Q!^@UHu4!U+PLe77N-^!>nCR9rlzDPrs%t*7H5~_7wEgE z=B1WoCOhV&=NDy`WaI`^mSp7T=@(Rj6qS}_<`nC1p2V2W!oyvhm|KvO8eg1QmAaXQ H?SdcxbH61W delta 128 zcmdn5e?^}!pO=@50SIhuQ!@H&H}V-V2F0dW#iS>uDCjDrXO?7?CMgsYDKbrX|IGV}9bYD-HpbBbd&*D$8DaC7CA S=Ej#L=9H!uZ+_2uK@b4qLoS&B diff --git a/pygad/utils/__pycache__/mutation.cpython-310.pyc b/pygad/utils/__pycache__/mutation.cpython-310.pyc index 006918879918afe8d76fca9a509e262d43824423..20719fc735f915a36ed7f9729ebc6a9c79e768a4 100644 GIT binary patch delta 4794 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 diff --git a/pygad/utils/__pycache__/nsga2.cpython-310.pyc b/pygad/utils/__pycache__/nsga2.cpython-310.pyc index 0f95b902c7b3663fe0d142179bd5b50b851402a2..02233250a6afff8ddaf1b670402d0548dbcac56a 100644 GIT binary patch delta 592 zcmX|;O-~b16o$QZ`e7+hXIjL5sFg+-i427dfute9YMQVhE_5?7Aq{iWnb^+IJEOwd zJqbB07n+dp54tsm#H}k=WWt`6Kfs;$y@bx5*_gw20k3PiVq1EL9kNtz4K|d6J>$6gp%hsP-T82f|B z8n;>ZZq?Cz_o(G)Q8;Kh1wYz%!=WGaV(vQhU28CBe4$;YnLhrS53;ODIsMxsQ_hfN z`G6035DQI4H%LP*(=Xj-i}X+b%nTYCb>oFho2fm)CWR|>W*GTZ8UIGEML&(sls&PR zs8CvB%Vd{Ui)a>$6H9gOqVp2Dmc&~#CD!FT$qN2SUIsU3#XXp~D^Ue%z+>PEFa;EV zI{hiFWSWo-%9Q);IR)h?vw=i&d|f`!Zq1P~PhMr=VFQ+0S#66+a&j@b!+B-RTa%%A zUzAa#PML#J(>#EB22_BjKu03U@S~UqZjx3%CrNd8#5~L`K#gCA+LSPJq6pof?dn`z lePRJI7D(8>c!@v__nJo4lcJ7Tor#mgF3r}qS(o-|_J8xGfW-g+ delta 616 zcmZ{hyH5f^5XN0O!6zaQ5j^#Lfrn!7fkDyA_=t%X#sZbZAY6_!a7Xq|jg5EW#_$e*7IC90iCSwl~kuHZZ^OR8ij*-Sy!C>V;RDFkAqqQB50r^YZpECMQ_U zE0ZWo`E>g-9)v6JzAV>Bt^|{T2|WILzjk;6xZiDXJ{E_p*hdG@Wc3W7vStgS5)ng) zNyH|r1^owuC|42I=mM%FKy0Tj)Y$UKrE3NFHu%Gn`tbD%AvECZCOM%S7+AAyv*W>A Xmk(pBmf+*R>RgHy diff --git a/pygad/utils/__pycache__/parent_selection.cpython-310.pyc b/pygad/utils/__pycache__/parent_selection.cpython-310.pyc index 491ba741c1657f376ed2ec7c603513cac8318feb..487dcd97223a51570e8593debde5f8d75e678df2 100644 GIT binary patch delta 89 zcmexZc)O4nCR9rlzDPrs%t*7H5~_7wEgE p=B1WoCOhV&=NDy`WaI`^mSp7T=@(Rj6qS}_<`nC1KE|kR4gf|VADRFF delta 111 zcmcaz__2^LpO=@50SInHM5NCs+Q^s27?hG?6_cKrqM)mgo>`Jnnxs%rl%JKFTv8lU zPze-|Nl(p7Ey+wy%t_BL0xHNYs4U6I&(nn{)=f+<$;{7#sVyzZ%qfo9yn#{M900{E BDaZf- diff --git a/pygad/utils/crossover.py b/pygad/utils/crossover.py index a09edc3..8789135 100644 --- a/pygad/utils/crossover.py +++ b/pygad/utils/crossover.py @@ -70,7 +70,7 @@ def single_point_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, @@ -142,7 +142,7 @@ def two_points_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, @@ -209,7 +209,7 @@ def uniform_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, @@ -273,7 +273,7 @@ def scattered_crossover(self, parents, offspring_size): max_val=self.random_mutation_max_val, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, diff --git a/pygad/utils/mutation.py b/pygad/utils/mutation.py index 8535902..dbb4a6a 100644 --- a/pygad/utils/mutation.py +++ b/pygad/utils/mutation.py @@ -59,111 +59,10 @@ def mutation_by_space(self, offspring): mutation_indices = numpy.array(random.sample(range(0, self.num_genes), self.mutation_num_genes)) for gene_idx in mutation_indices: - range_min, range_max = self.get_random_mutation_range(gene_idx) - - if self.gene_space_nested: - # Returning the current gene space from the 'gene_space' attribute. - if type(self.gene_space[gene_idx]) in [numpy.ndarray, list]: - curr_gene_space = self.gene_space[gene_idx].copy() - else: - curr_gene_space = self.gene_space[gene_idx] - - # If the gene space has only a single value, use it as the new gene value. - if type(curr_gene_space) in pygad.GA.supported_int_float_types: - value_from_space = curr_gene_space - # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. - elif curr_gene_space is None: - rand_val = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - if self.mutation_by_replacement: - value_from_space = rand_val - else: - value_from_space = offspring[offspring_idx, gene_idx] + rand_val - elif type(curr_gene_space) is dict: - # The gene's space of type dict specifies the lower and upper limits of a gene. - if 'step' in curr_gene_space.keys(): - # The numpy.random.choice() and numpy.random.uniform() functions return a NumPy array as the output even if the array has a single value. - # We have to return the output at index 0 to force a numeric value to be returned not an object of type numpy.ndarray. - # If numpy.ndarray is returned, then it will cause an issue later while using the set() function. - # Randomly select a value from a discrete range. - value_from_space = numpy.random.choice(numpy.arange(start=curr_gene_space['low'], - stop=curr_gene_space['high'], - step=curr_gene_space['step']), - size=1)[0] - else: - # Return the current gene value. - value_from_space = offspring[offspring_idx, gene_idx] - # Generate a random value to be added to the current gene value. - rand_val = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - # The objective is to have a new gene value that respects the gene_space boundaries. - # The next if-else block checks if adding the random value keeps the new gene value within the gene_space boundaries. - temp_val = value_from_space + rand_val - if temp_val < curr_gene_space['low']: - # Restrict the new value to be > curr_gene_space['low'] - # If subtracting the random value makes the new gene value outside the boundaries [low, high), then use the lower boundary the gene value. - if curr_gene_space['low'] <= value_from_space - rand_val < curr_gene_space['high']: - # Because subtracting the random value keeps the new gene value within the boundaries [low, high), then use such a value as the gene value. - temp_val = value_from_space - rand_val - else: - # Because subtracting the random value makes the new gene value outside the boundaries [low, high), then use the lower boundary as the gene value. - temp_val = curr_gene_space['low'] - elif temp_val >= curr_gene_space['high']: - # Restrict the new value to be < curr_gene_space['high'] - # If subtracting the random value makes the new gene value outside the boundaries [low, high), then use such a value as the gene value. - if curr_gene_space['low'] <= value_from_space - rand_val < curr_gene_space['high']: - # Because subtracting the random value keeps the new value within the boundaries [low, high), then use such a value as the gene value. - temp_val = value_from_space - rand_val - else: - # Because subtracting the random value makes the new gene value outside the boundaries [low, high), then use the lower boundary as the gene value. - temp_val = curr_gene_space['low'] - value_from_space = temp_val - else: - # Selecting a value randomly based on the current gene's space in the 'gene_space' attribute. - # If the gene space has only 1 value, then select it. The old and new values of the gene are identical. - if len(curr_gene_space) == 1: - value_from_space = curr_gene_space[0] - # If the gene space has more than 1 value, then select a new one that is different from the current value. - else: - values_to_select_from = list(set(curr_gene_space) - set([offspring[offspring_idx, gene_idx]])) - - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) - else: - # Selecting a value randomly from the global gene space in the 'gene_space' attribute. - if type(self.gene_space) is dict: - # When the gene_space is assigned a dict object, then it specifies the lower and upper limits of all genes in the space. - if 'step' in self.gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=1)[0] - else: - # If the space type is not of type dict, then a value is randomly selected from the gene_space attribute. - values_to_select_from = list(set(self.gene_space) - set([offspring[offspring_idx, gene_idx]])) + value_from_space = self.mutation_process_gene_value(solution=offspring[offspring_idx], + gene_idx=gene_idx) - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) - # value_from_space = random.choice(self.gene_space) - - if value_from_space is None: - # TODO: Return index 0. - # TODO: Check if this if statement is necessary. - value_from_space = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - - # Assinging the selected value from the space to the gene. + # Before assigning the selected value from the space to the gene, change its data type and round it. if self.gene_type_single == True: if not self.gene_type[1] is None: offspring[offspring_idx, gene_idx] = numpy.round(self.gene_type[0](value_from_space), @@ -174,7 +73,6 @@ def mutation_by_space(self, offspring): if not self.gene_type[gene_idx][1] is None: offspring[offspring_idx, gene_idx] = numpy.round(self.gene_type[gene_idx][0](value_from_space), self.gene_type[gene_idx][1]) - else: offspring[offspring_idx, gene_idx] = self.gene_type[gene_idx][0](value_from_space) @@ -198,71 +96,9 @@ def mutation_probs_by_space(self, offspring): probs = numpy.random.random(size=offspring.shape[1]) for gene_idx in range(offspring.shape[1]): - range_min, range_max = self.get_random_mutation_range(gene_idx) - if probs[gene_idx] <= self.mutation_probability: - if self.gene_space_nested: - # Returning the current gene space from the 'gene_space' attribute. - if type(self.gene_space[gene_idx]) in [numpy.ndarray, list]: - curr_gene_space = self.gene_space[gene_idx].copy() - else: - curr_gene_space = self.gene_space[gene_idx] - - # If the gene space has only a single value, use it as the new gene value. - if type(curr_gene_space) in pygad.GA.supported_int_float_types: - value_from_space = curr_gene_space - # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. - elif curr_gene_space is None: - rand_val = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - if self.mutation_by_replacement: - value_from_space = rand_val - else: - value_from_space = offspring[offspring_idx, gene_idx] + rand_val - elif type(curr_gene_space) is dict: - # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. - if 'step' in curr_gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=curr_gene_space['low'], - stop=curr_gene_space['high'], - step=curr_gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=curr_gene_space['low'], - high=curr_gene_space['high'], - size=1)[0] - else: - # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. - # If the gene space has only 1 value, then select it. The old and new values of the gene are identical. - if len(curr_gene_space) == 1: - value_from_space = curr_gene_space[0] - # If the gene space has more than 1 value, then select a new one that is different from the current value. - else: - values_to_select_from = list(set(curr_gene_space) - set([offspring[offspring_idx, gene_idx]])) - - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) - else: - # Selecting a value randomly from the global gene space in the 'gene_space' attribute. - if type(self.gene_space) is dict: - if 'step' in self.gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=1)[0] - else: - values_to_select_from = list(set(self.gene_space) - set([offspring[offspring_idx, gene_idx]])) - - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) + value_from_space = self.mutation_process_gene_value(solution=offspring[offspring_idx], + gene_idx=gene_idx) # Assigning the selected value from the space to the gene. if self.gene_type_single == True: @@ -284,145 +120,53 @@ def mutation_probs_by_space(self, offspring): num_trials=10) return offspring - def change_random_mutation_value_dtype(self, - random_value, - gene_index, - gene_value, - mutation_by_replacement): - """ - Change the data type of the random value used to apply mutation. - It accepts 2 parameters: - -random_value: The random value to change its data type. - -gene_index: The index of the target gene. - -gene_value: The gene value before mutation. Only used if mutation_by_replacement=False and gene_type_single=False. - -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. - It returns the new value after changing the data type. - """ + def mutation_process_gene_value(self, + solution, + gene_idx, + range_min=None, + range_max=None, + sample_size=100): - # If the mutation_by_replacement attribute is True, then the random value replaces the current gene value. - if mutation_by_replacement: - if self.gene_type_single == True: - random_value = self.gene_type[0](random_value) - else: - random_value = self.gene_type[gene_index][0](random_value) - if type(random_value) is numpy.ndarray: - random_value = random_value[0] - # If the mutation_by_replacement attribute is False, then the random value is added to the gene value. - else: - if self.gene_type_single == True: - random_value = self.gene_type[0](gene_value + random_value) - else: - random_value = self.gene_type[gene_index][0](gene_value + random_value) - if type(random_value) is numpy.ndarray: - random_value = random_value[0] - return random_value - - def round_random_mutation_value(self, random_value, gene_index): - """ - Round the random value used to apply mutation. - It accepts 2 parameters: - -random_value: The random value to round its value. - -gene_index: The index of the target gene. Only used if nested gene_type is used. - It returns the new value after being rounded. """ - - # Round the gene - if self.gene_type_single == True: - if not self.gene_type[1] is None: - random_value = numpy.round(random_value, self.gene_type[1]) - else: - if not self.gene_type[gene_index][1] is None: - random_value = numpy.round(random_value, self.gene_type[gene_index][1]) - return random_value - - def mutation_filter_values_by_constraint(self, - random_values, - solution, - gene_idx): - - """ - Filter the random values generated for mutation based on whether they meet the gene constraint in the gene_constraint parameter. - It accepts: - -random_values: The random values to filter. - -solution: The solution containing the target gene. - -gene_idx: The index of the gene in the solution. - It returns None if no values satisfy the constraint. Otherwise, an array of values that satisfy the constraint is returned. - """ - - # A list of the indices where the random values satisfy the constraint. - filtered_values_indices = [] - # A temporary solution to avoid changing the original solution. - solution_tmp = solution.copy() - # Loop through the random values to filter the ones satisfying the constraint. - for value_idx, random_value in enumerate(random_values): - solution_tmp[gene_idx] = random_value - # Check if the constraint is satisfied. - if self.gene_constraint[gene_idx](solution_tmp): - # The current value satisfies the constraint. - filtered_values_indices.append(value_idx) - - # After going through all the values, check if any value satisfies the constraint. - if len(filtered_values_indices) > 0: - # At least one value was found that meets the gene constraint. - pass - else: - # No value found for the current gene that satisfies the constraint. - if not self.suppress_warnings: - warnings.warn(f"No value found for the gene at index {gene_idx} that satisfies its gene constraint.") - return None - - filtered_values = random_values[filtered_values_indices] - - return filtered_values - - def mutation_process_random_value(self, - range_min, - range_max, - solution, - gene_idx, - num_values=100): - - """ - Randomly generate constrained values to use for applying mutation. + Generate/select values for the gene that satisfy the constraint. The values could be generated randomly or from the gene space. It accepts: -range_min: The minimum value in the range from which a value is selected. -range_max: The maximum value in the range from which a value is selected. -solution: The solution where the target gene exists. -gene_idx: The index of the gene in the solution. - -num_values: The number of random valus to generate. It tries to generate a number of values up to a maximum of num_values. But it is not always guranteed because the total number of values might not be enough or the random generator creates duplicate random values. + -sample_size: The number of random values to generate from which a value is selected. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. It returns a single numeric value the satisfies the gene constraint if exists in the gene_constraint parameter. """ # Check if the gene has a constraint. if self.gene_constraint and self.gene_constraint[gene_idx]: - # Generate random values to use for mutation. - random_values = self.generate_gene_random_value(range_min=range_min, - range_max=range_max, - gene_value=solution[gene_idx], - gene_idx=gene_idx, - mutation_by_replacement=self.mutation_by_replacement, - num_values=num_values) - # Filter the values that satisfy the constraint. - random_values_filtered = self.mutation_filter_values_by_constraint(random_values=random_values, - solution=solution, - gene_idx=gene_idx) - if random_values_filtered is None: + # Generate values that meet the gene constraint. Select more than 1 value. + # This method: 1) generates or selects the values 2) filters the values according to the constraint. + values = self.get_valid_gene_constraint_values(range_min=range_min, + range_max=range_max, + gene_value=solution[gene_idx], + gene_idx=gene_idx, + mutation_by_replacement=self.mutation_by_replacement, + solution=solution, + sample_size=sample_size) + if values is None: # No value found that satisfy the constraint. # Keep the old value. - random_value = solution[gene_idx] + value_selected = solution[gene_idx] else: # Select a value randomly from the list of values satisfying the constraint. - random_value = numpy.random.choice(random_values_filtered, size=1)[0] - # The gene does not have a constraint. + # If size is used with numpy.random.choice(), it returns an array even if it has a single value. To return a numeric value, not an array, then return index 0. + value_selected = numpy.random.choice(values, size=1)[0] else: - random_value = self.generate_gene_random_value(range_min=range_min, - range_max=range_max, - gene_value=solution[gene_idx], - gene_idx=gene_idx, - mutation_by_replacement=self.mutation_by_replacement, - num_values=1) + # The gene does not have a constraint. Just select a single value. + value_selected = self.generate_gene_value(range_min=range_min, + range_max=range_max, + gene_value=solution[gene_idx], + gene_idx=gene_idx, + mutation_by_replacement=self.mutation_by_replacement, + sample_size=1) # Even that its name is singular, it might have a multiple values. - return random_value + return value_selected def mutation_randomly(self, offspring): @@ -443,10 +187,10 @@ def mutation_randomly(self, offspring): range_min, range_max = self.get_random_mutation_range(gene_idx) # Generate a random value for mutation that meet the gene constraint if exists. - random_value = self.mutation_process_random_value(range_min=range_min, - range_max=range_max, - solution=offspring[offspring_idx], - gene_idx=gene_idx) + random_value = self.mutation_process_gene_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -456,7 +200,7 @@ def mutation_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) return offspring @@ -481,10 +225,10 @@ def mutation_probs_randomly(self, offspring): if probs[gene_idx] <= self.mutation_probability: # Generate a random value fpr mutation that meet the gene constraint if exists. - random_value = self.mutation_process_random_value(range_min=range_min, - range_max=range_max, - solution=offspring[offspring_idx], - gene_idx=gene_idx) + random_value = self.mutation_process_gene_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -494,7 +238,7 @@ def mutation_probs_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) return offspring def swap_mutation(self, offspring): @@ -692,8 +436,6 @@ def adaptive_mutation_population_fitness(self, offspring): else: raise ValueError(f"The fitness function should return a number or an iterable (list, tuple, or numpy.ndarray) but the value ({sol_fitness}) of type {type(sol_fitness)} found.") - - if len(fitness.shape) > 1: # TODO This is a multi-objective optimization problem. # Calculate the average of each objective's fitness across all solutions in the population. @@ -782,79 +524,8 @@ def adaptive_mutation_by_space(self, offspring): mutation_indices = numpy.array(random.sample(range(0, self.num_genes), adaptive_mutation_num_genes)) for gene_idx in mutation_indices: - range_min, range_max = self.get_random_mutation_range(gene_idx) - - if self.gene_space_nested: - # Returning the current gene space from the 'gene_space' attribute. - if type(self.gene_space[gene_idx]) in [numpy.ndarray, list]: - curr_gene_space = self.gene_space[gene_idx].copy() - else: - curr_gene_space = self.gene_space[gene_idx] - - # If the gene space has only a single value, use it as the new gene value. - if type(curr_gene_space) in pygad.GA.supported_int_float_types: - value_from_space = curr_gene_space - # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. - elif curr_gene_space is None: - rand_val = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - if self.mutation_by_replacement: - value_from_space = rand_val - else: - value_from_space = offspring[offspring_idx, gene_idx] + rand_val - elif type(curr_gene_space) is dict: - # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. - if 'step' in curr_gene_space.keys(): - # The numpy.random.choice() and numpy.random.uniform() functions return a NumPy array as the output even if the array has a single value. - # We have to return the output at index 0 to force a numeric value to be returned not an object of type numpy.ndarray. - # If numpy.ndarray is returned, then it will cause an issue later while using the set() function. - value_from_space = numpy.random.choice(numpy.arange(start=curr_gene_space['low'], - stop=curr_gene_space['high'], - step=curr_gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=curr_gene_space['low'], - high=curr_gene_space['high'], - size=1)[0] - else: - # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. - # If the gene space has only 1 value, then select it. The old and new values of the gene are identical. - if len(curr_gene_space) == 1: - value_from_space = curr_gene_space[0] - # If the gene space has more than 1 value, then select a new one that is different from the current value. - else: - values_to_select_from = list(set(curr_gene_space) - set([offspring[offspring_idx, gene_idx]])) - - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) - else: - # Selecting a value randomly from the global gene space in the 'gene_space' attribute. - if type(self.gene_space) is dict: - if 'step' in self.gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=1)[0] - else: - values_to_select_from = list(set(self.gene_space) - set([offspring[offspring_idx, gene_idx]])) - - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) - - - if value_from_space is None: - value_from_space = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] + value_from_space = self.mutation_process_gene_value(solution=offspring[offspring_idx], + gene_idx=gene_idx) # Assigning the selected value from the space to the gene. if self.gene_type_single == True: @@ -923,10 +594,10 @@ def adaptive_mutation_randomly(self, offspring): range_min, range_max = self.get_random_mutation_range(gene_idx) # Generate a random value fpr mutation that meet the gene constraint if exists. - random_value = self.mutation_process_random_value(range_min=range_min, - range_max=range_max, - solution=offspring[offspring_idx], - gene_idx=gene_idx) + random_value = self.mutation_process_gene_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -936,7 +607,7 @@ def adaptive_mutation_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) return offspring def adaptive_mutation_probs_by_space(self, offspring): @@ -985,81 +656,12 @@ def adaptive_mutation_probs_by_space(self, offspring): probs = numpy.random.random(size=offspring.shape[1]) for gene_idx in range(offspring.shape[1]): - range_min, range_max = self.get_random_mutation_range(gene_idx) - if probs[gene_idx] <= adaptive_mutation_probability: - if self.gene_space_nested: - # Returning the current gene space from the 'gene_space' attribute. - if type(self.gene_space[gene_idx]) in [numpy.ndarray, list]: - curr_gene_space = self.gene_space[gene_idx].copy() - else: - curr_gene_space = self.gene_space[gene_idx] - - # If the gene space has only a single value, use it as the new gene value. - if type(curr_gene_space) in pygad.GA.supported_int_float_types: - value_from_space = curr_gene_space - # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. - elif curr_gene_space is None: - rand_val = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - if self.mutation_by_replacement: - value_from_space = rand_val - else: - value_from_space = offspring[offspring_idx, gene_idx] + rand_val - elif type(curr_gene_space) is dict: - # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. - if 'step' in curr_gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=curr_gene_space['low'], - stop=curr_gene_space['high'], - step=curr_gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=curr_gene_space['low'], - high=curr_gene_space['high'], - size=1)[0] - else: - # Selecting a value randomly from the current gene's space in the 'gene_space' attribute. - # If the gene space has only 1 value, then select it. The old and new values of the gene are identical. - if len(curr_gene_space) == 1: - value_from_space = curr_gene_space[0] - # If the gene space has more than 1 value, then select a new one that is different from the current value. - else: - values_to_select_from = list(set(curr_gene_space) - set([offspring[offspring_idx, gene_idx]])) - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) - else: - # Selecting a value randomly from the global gene space in the 'gene_space' attribute. - if type(self.gene_space) is dict: - if 'step' in self.gene_space.keys(): - # The numpy.random.choice() and numpy.random.uniform() functions return a NumPy array as the output even if the array has a single value. - # We have to return the output at index 0 to force a numeric value to be returned not an object of type numpy.ndarray. - # If numpy.ndarray is returned, then it will cause an issue later while using the set() function. - value_from_space = numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=1)[0] - else: - values_to_select_from = list(set(self.gene_space) - set([offspring[offspring_idx, gene_idx]])) - - if len(values_to_select_from) == 0: - value_from_space = offspring[offspring_idx, gene_idx] - else: - value_from_space = random.choice(values_to_select_from) + value_from_space = self.mutation_process_gene_value(solution=offspring[offspring_idx], + gene_idx=gene_idx) - if value_from_space is None: - value_from_space = numpy.random.uniform(low=range_min, - high=range_max, - size=1)[0] - - # Assinging the selected value from the space to the gene. + # Assigning the selected value from the space to the gene. if self.gene_type_single == True: if not self.gene_type[1] is None: offspring[offspring_idx, gene_idx] = numpy.round(self.gene_type[0](value_from_space), @@ -1127,10 +729,10 @@ def adaptive_mutation_probs_randomly(self, offspring): if probs[gene_idx] <= adaptive_mutation_probability: # Generate a random value fpr mutation that meet the gene constraint if exists. - random_value = self.mutation_process_random_value(range_min=range_min, - range_max=range_max, - solution=offspring[offspring_idx], - gene_idx=gene_idx) + random_value = self.mutation_process_gene_value(range_min=range_min, + range_max=range_max, + solution=offspring[offspring_idx], + gene_idx=gene_idx) offspring[offspring_idx, gene_idx] = random_value @@ -1140,5 +742,5 @@ def adaptive_mutation_probs_randomly(self, offspring): max_val=range_max, mutation_by_replacement=self.mutation_by_replacement, gene_type=self.gene_type, - num_values=100) + sample_size=100) return offspring diff --git a/pygad/visualize/__pycache__/__init__.cpython-310.pyc b/pygad/visualize/__pycache__/__init__.cpython-310.pyc index 4c367b5cc1c47805adcca3da71281298d99ffd67..473b3f831e39a308d92cef5e00424e43e5c2b1cb 100644 GIT binary patch delta 89 zcmZo>ddtY0&&$ij00cJ1F&X9)dFvd!^h1kNi;DFVGjdZ?(i2nkT~dp)OY#f!-Ba^Y pOEQxkbJFvRGD|XY11d{0^7HfyDnW|MGK)(Sb26(^^(Q`50sz%>9`OJG delta 111 zcmaFM*v!P6&&$ij00cK8BGN4<^45hVq*%qIC#ERqDx_zYWRxZ;6cpuWr6!jY#}rfo z#beS_^HNJPlM{2&^NWBAatkU;GV=3uA&PYqlS?x5^I&SrGK)(Sb26(^V7%Q6lNW-VXDp!OM^Ua1jq?WppKfihJ&6|1eee=F? zK6X0XbUN{b{FY|lv50xlaW6|UW$Js5>pS-5lvjg94jyD*ZIfPqt>!nl$#|YxWwThK z#r!KK-}YTEf6=U%zFm09DY?w{t@4{S-*PK?uLh?bn^&o0@0)pHXk)rABNYxZ{oz8H`93I5^^WmQ;-J$rvXn(2s+eB+;ytH?N)fu#!a6+1M|4Z_6%>0z$r#O&#V*~ zO2GyJNAkh)SUNM%%}U5+*<1^|D$+a*&jM853*u(aSEnLaKL|nA@N24 z-7cvsko4ioGVPM(vvKJi=-i^7iIARe`;JLcK8%J#Yi`w7Jtaw6EE{#Q2D2m0J?H_nt`^Dn;z-86{6&al^0Kfr~c}PjacMbKd zFF;g@X8nmQ~`T{&HG{v8oh^d@h~OWdo1( z45gYwYDyfN4HsbD1T<;V{}`GeLzM|hE80n}`xTS(FwPBFV=C_jaernj77h{d_Uu|X zI8|w*;_mFNdK|td04OQLQ)VfEx@w1%*4A=y_B>R|Z3$8(-}6KN0d3 ziNnH_LMe|{4Hp3t2vj5%S$oGHz_JTAh#LuZ2sRN5R$UH@vDC-!Kiw2&_D@O_z#*vR4gCt}_`1Pm+9Kxz0ba*DSw6Y-`8Z zV5`cg5@oKke;TXMCa872_2;N^ik*+28|y(pufU$dV@1~qe35}i0?RA~d#yZDbV%9Y zjKx<(#vkLi6Q?lYG|fdlJ7)idNOG!1{uC)W(UU{Tu6 zeoL$+8XHvW&nGWOlwa6woP3FPP?%!dWEe)<^f$AJtm#D3`+P|v~C z*k3(EwJ~9Gm3W@v6lfIV!@!Y);Iru*3m97z(9-mY6a4bto`mBm05?dN1lGGMj`c>x z(0$jg$nvnSdTWF2@IMV4Iji{Oee7PkuYNH-n^L;jTSK?hoEY7ClkEPkJQCmvlk{%MW;D)7>_FdQqg&TEl(Gn#8tBmVectK981- zf<+vkv+Eq9QL-Z!K5^{A;g|QCg*62{3!DL_ffe>( z=9` z8$2mBtJ|dNRsvaKo*;#Sez+!my(Xts$+qS5c9U8K`W!~Ww4{3GkdnF+iES0CS!Rfmb z-N^nSa2vP_gl<^70{0bReB9E)mS+ap!E|Qi19&iHua({re_XMS&117O~ePq9A>Jszt`anY=1OK5Ac^sT%JtY+< zWF9z%?}vHhIaPV?CHBMY6*cS}?800wY?JaLRrbZ)H%gqj^Zm8(Ep>^6xttcG=dqgv zungd3(@sF{UxsN3`VvN723`T0qW%q)ZG@sF<+LPy8=*IVcN&vqCw>>-H-YyAUZa;_ z-eL#yXQw(~J_D=**MYAAska7HjK$cC3s>i3!qg%>s_O+euj^sSCg7KKf&18O_Ul6C EZvX_gFaQ7m diff --git a/setup.py b/setup.py index d9ec309..2d9478c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="pygad", - version="3.4.0", + version="3.5.0", author="Ahmed Fawzy Gad", install_requires=["numpy", "matplotlib", "cloudpickle",], author_email="ahmed.f.gad@gmail.com", diff --git a/test_gene_constraint.py b/test_gene_constraint.py index 28e290a..43cf38a 100644 --- a/test_gene_constraint.py +++ b/test_gene_constraint.py @@ -61,7 +61,7 @@ def fitness_func_no_batch_multi(ga, solution, idx): crossover_type=crossover_type, gene_constraint=gene_constraint, save_solutions=True, - suppress_warnings=True) + suppress_warnings=False) ga_instance.run() @@ -73,21 +73,19 @@ def test_initial_population_int_by_replacement(): ga_instance = population_gene_constraint(gene_constraint=gene_constraint, init_range_low=0, init_range_high=10, + random_mutation_min_val=0, + random_mutation_max_val=10, num_genes=5, gene_type=int, mutation_by_replacement=True) initial_population = ga_instance.initial_population # print(initial_population) - assert numpy.all(initial_population[:, 0] >= 8), "Not all values in column 0 are >= 98" - - assert numpy.all(initial_population[:, 1] >= 8), "Not all values in column 1 are >= 98" - + assert numpy.all(initial_population[:, 0] >= 8), "Not all values in column 0 are >= 8" + assert numpy.all(initial_population[:, 1] >= 8), "Not all values in column 1 are >= 8" assert numpy.all(initial_population[:, 2] >= 1), "Not all values in column 2 are >= 1" assert numpy.all((initial_population[:, 2] >= 1) & (initial_population[:, 2] <= 5)), "Not all values in column 2 between 1 and 5 (inclusive)" - assert numpy.all(initial_population[:, 3] == 4), "Not all values in column 3 between 3 and 5 (exclusive)" - assert numpy.all(initial_population[:, 4] < 2), "Not all values in column 4 < 2" def test_initial_population_int_by_replacement_no_duplicates(): @@ -111,6 +109,47 @@ def test_initial_population_int_by_replacement_no_duplicates(): assert num_duplicates == 0 + initial_population = ga_instance.initial_population + # print(initial_population) + + assert numpy.all(initial_population[:, 0] >= 5), "Not all values in column 0 >= 5" + assert numpy.all(initial_population[:, 1] >= 5), "Not all values in column 1 >= 5" + assert numpy.all(initial_population[:, 2] >= 5), "Not all values in column 2 >= 5" + assert numpy.all(initial_population[:, 3] >= 5), "Not all values in column 3 >= 5" + assert numpy.all(initial_population[:, 4] >= 5), "Not all values in column 4 >= 5" + +def test_initial_population_int_by_replacement_no_duplicates2(): + gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,lambda x: 20= 98), "Not all values in column 0 are >= 98" + assert numpy.all(initial_population[:, 1] >= 98), "Not all values in column 1 are >= 98" + assert numpy.all((initial_population[:, 2] > 20) & (initial_population[:, 2] < 40)), "Not all values in column 2 between 20 and 40 (exclusive)" + assert numpy.all(initial_population[:, 3] < 40), "Not all values in column 3 < 40" + assert numpy.all(initial_population[:, 4] < 50), "Not all values in column 4 < 50" + assert numpy.all(initial_population[:, 5] < 100), "Not all values in column 4 < 100" + def test_initial_population_float_by_replacement_no_duplicates(): gene_constraint=[lambda x: x[0]>=5,lambda x: x[1]>=5,lambda x: x[2]>=5,lambda x: x[3]>=5,lambda x: x[4]>=5] ga_instance = population_gene_constraint(gene_constraint=gene_constraint, @@ -131,6 +170,15 @@ def test_initial_population_float_by_replacement_no_duplicates(): assert num_duplicates == 0 + initial_population = ga_instance.initial_population + # print(initial_population) + + assert numpy.all(initial_population[:, 0] >= 5), "Not all values in column 0 >= 5" + assert numpy.all(initial_population[:, 1] >= 5), "Not all values in column 1 >= 5" + assert numpy.all(initial_population[:, 2] >= 5), "Not all values in column 2 >= 5" + assert numpy.all(initial_population[:, 3] >= 5), "Not all values in column 3 >= 5" + assert numpy.all(initial_population[:, 4] >= 5), "Not all values in column 4 >= 5" + def test_initial_population_float_by_replacement_no_duplicates2(): gene_constraint=[lambda x: x[0]>=1,lambda x: x[1]>=1,lambda x: x[2]>=1,lambda x: x[3]>=1,lambda x: x[4]>=1] ga_instance = population_gene_constraint(gene_constraint=gene_constraint, @@ -151,13 +199,24 @@ def test_initial_population_float_by_replacement_no_duplicates2(): assert num_duplicates == 0 + initial_population = ga_instance.initial_population + # print(initial_population) + + assert numpy.all(initial_population[:, 0] >= 1), "Not all values in column 0 >= 1" + assert numpy.all(initial_population[:, 1] >= 1), "Not all values in column 1 >= 1" + assert numpy.all(initial_population[:, 2] >= 1), "Not all values in column 2 >= 1" + assert numpy.all(initial_population[:, 3] >= 1), "Not all values in column 3 >= 1" + assert numpy.all(initial_population[:, 4] >= 1), "Not all values in column 4 >= 1" + if __name__ == "__main__": #### Single-objective print() test_initial_population_int_by_replacement() - print() + print() test_initial_population_int_by_replacement_no_duplicates() print() + test_initial_population_int_by_replacement_no_duplicates2() + print() test_initial_population_float_by_replacement_no_duplicates() print() test_initial_population_float_by_replacement_no_duplicates2() diff --git a/test_gene_space.py b/test_gene_space.py new file mode 100644 index 0000000..2e83bf6 --- /dev/null +++ b/test_gene_space.py @@ -0,0 +1,1605 @@ +""" +This script is identical to the test_gene_space_allow_duplicate_genes.py script except for: + Setting allow_duplicate_genes=True instead of False. +""" + +import pygad +import random +import numpy + +num_generations = 100 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +# Test single gene space with nested gene type. + +def number_respect_gene_space(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + mutation_type="random", + mutation_percent_genes="default", + mutation_probability=None, + initial_population=None, + parent_selection_type='sss', + multi_objective=False): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + initial_population=initial_population, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=True, + mutation_by_replacement=mutation_by_replacement, + parent_selection_type=parent_selection_type, + save_solutions=True, + mutation_type=mutation_type, + mutation_percent_genes=mutation_percent_genes, + mutation_probability=mutation_probability, + suppress_warnings=True, + random_seed=2) + + ga_instance.run() + ga_instance.solutions = numpy.array(ga_instance.solutions, + dtype=object) + + # gene_space_unpacked = ga_instance.unpack_gene_space(num_values_from_inf_range=100) + num_outside = 0 + if ga_instance.gene_space_nested == True: + for gene_idx in range(ga_instance.num_genes): + + if type(ga_instance.init_range_low) in ga_instance.supported_int_float_types: + range_min_init = ga_instance.init_range_low + range_max_init = ga_instance.init_range_high + else: + range_min_init = ga_instance.init_range_low[gene_idx] + range_max_init = ga_instance.init_range_high[gene_idx] + if type(ga_instance.random_mutation_min_val) in ga_instance.supported_int_float_types: + range_min_mutation = ga_instance.random_mutation_min_val + range_max_mutation = ga_instance.random_mutation_max_val + else: + range_min_mutation = ga_instance.random_mutation_min_val[gene_idx] + range_max_mutation = ga_instance.random_mutation_max_val[gene_idx] + + all_gene_values = ga_instance.solutions[:, gene_idx] + if type(ga_instance.gene_space[gene_idx]) in [list, tuple, range, numpy.ndarray]: + current_gene_space = list(ga_instance.gene_space[gene_idx]) + # print("current_gene_space", current_gene_space) + for val_idx, val in enumerate(all_gene_values): + if None in current_gene_space: + if (val in current_gene_space) or (val >= range_min_init and val < range_max_init) or (val >= range_min_mutation and val < range_max_mutation): + pass + else: + # print("###########") + # print(gene_idx, val) + # print(current_gene_space) + # print(range_min_mutation, range_max_mutation) + # print("\n\n") + num_outside += 1 + elif val in current_gene_space: + # print("val, current_gene_space", val, current_gene_space) + pass + else: + # print(gene_idx, val, current_gene_space) + num_outside += 1 + elif type(ga_instance.gene_space[gene_idx]) is dict: + if not "step" in ga_instance.gene_space[gene_idx].keys(): + for val in all_gene_values: + if val >= ga_instance.gene_space[gene_idx]["low"] and val < ga_instance.gene_space[gene_idx]["high"]: + pass + else: + print(gene_idx, val, current_gene_space) + num_outside += 1 + else: + gene_space_values = numpy.arange(ga_instance.gene_space[gene_idx]["low"], + ga_instance.gene_space[gene_idx]["high"], + ga_instance.gene_space[gene_idx]["step"]) + for val in all_gene_values: + if val in gene_space_values: + pass + else: + num_outside += 1 + elif type(ga_instance.gene_space[gene_idx]) in ga_instance.supported_int_float_types: + for val in all_gene_values: + if val == ga_instance.gene_space[gene_idx]: + pass + else: + num_outside += 1 + elif ga_instance.gene_space[gene_idx] is None: + for val in all_gene_values: + # print(val) + if (val >= range_min_init and val < range_max_init) or (val >= range_min_mutation and val < range_max_mutation): + pass + else: + # print("###########") + # print(gene_idx, val) + # print(ga_instance.gene_space[gene_idx]) + # print(range_min_init, range_max_init) + # print(range_min_mutation, range_max_mutation) + # print("\n\n") + num_outside += 1 + else: + for gene_idx in range(ga_instance.num_genes): + + if type(ga_instance.init_range_low) in ga_instance.supported_int_float_types: + range_min_init = ga_instance.init_range_low + range_max_init = ga_instance.init_range_high + else: + range_min_init = ga_instance.init_range_low[gene_idx] + range_max_init = ga_instance.init_range_high[gene_idx] + if type(ga_instance.random_mutation_min_val) in ga_instance.supported_int_float_types: + range_min_mutation = ga_instance.random_mutation_min_val + range_max_mutation = ga_instance.random_mutation_max_val + else: + range_min_mutation = ga_instance.random_mutation_min_val[gene_idx] + range_max_mutation = ga_instance.random_mutation_max_val[gene_idx] + + all_gene_values = ga_instance.solutions[:, gene_idx] + # print("all_gene_values", gene_idx, all_gene_values) + if type(ga_instance.gene_space) in [list, tuple, range, numpy.ndarray]: + current_gene_space = list(ga_instance.gene_space) + for val in all_gene_values: + if None in current_gene_space: + if (val in current_gene_space) or (val >= range_min_init and val < range_max_init) or (val >= range_min_mutation and val < range_max_mutation): + pass + else: + # print("###########") + # print(gene_idx, val) + # print(current_gene_space) + # print(range_min_mutation, range_max_mutation) + # print("\n\n") + num_outside += 1 + elif val in current_gene_space: + pass + else: + num_outside += 1 + elif type(ga_instance.gene_space) is dict: + if not "step" in ga_instance.gene_space.keys(): + for val in all_gene_values: + if val >= ga_instance.gene_space["low"] and val < ga_instance.gene_space["high"]: + pass + else: + num_outside += 1 + else: + gene_space_values = numpy.arange(ga_instance.gene_space["low"], + ga_instance.gene_space["high"], + ga_instance.gene_space["step"]) + for val in all_gene_values: + if val in gene_space_values: + pass + else: + num_outside += 1 + + print(f"Number of values outside the range is: {num_outside}.") + return num_outside, ga_instance + +#### Single-Objective +def test_gene_space_range(): + num_outside, _ = number_respect_gene_space(gene_space=range(10)) + + assert num_outside == 0 + +def test_gene_space_numpy_arange(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10)) + + assert num_outside == 0 + +def test_gene_space_list(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10))) + + assert num_outside == 0 + +def test_gene_space_list_None(): + num_outside, _ = number_respect_gene_space(gene_space=[30, None, 40, 50, None, 60, 70, None, None, None]) + + assert num_outside == 0 + +def test_gene_space_numpy(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10)))) + + assert num_outside == 0 + +def test_gene_space_dict_without_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}) + + assert num_outside == 0 + +def test_gene_space_dict_with_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}) + + assert num_outside == 0 + +def test_gene_space_list_single_value(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5]) + + assert num_outside == 0 + +def test_gene_space_range_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_list_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_numpy_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_dict_without_step_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_dict_with_step_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_list_single_value_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_nested_gene_space_range(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[range(0, 10), + range(10, 20), + range(20, 30), + range(30, 40), + range(40, 50), + range(50, 60), + range(60, 70), + range(70, 80), + range(80, 90), + range(90, 100)]) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}]) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_float_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + gene_type=[float, 3]) + + assert num_outside == 0 + +def test_nested_gene_space_dict_with_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10, "step": 1}, + {"low": 10, "high": 20, "step": 1.5}, + {"low": 20, "high": 30, "step": 2}, + {"low": 30, "high": 40, "step": 2.5}, + {"low": 40, "high": 50, "step": 3}, + {"low": 50, "high": 60, "step": 3.5}, + {"low": 60, "high": 70, "step": 4}, + {"low": 70, "high": 80, "step": 4.5}, + {"low": 80, "high": 90, "step": 5}, + {"low": 90, "high": 100, "step": 5.5}]) + + assert num_outside == 0 + + +def test_nested_gene_space_numpy_arange(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[numpy.arange(0, 10), + numpy.arange(10, 20), + numpy.arange(20, 30), + numpy.arange(30, 40), + numpy.arange(40, 50), + numpy.arange(50, 60), + numpy.arange(60, 70), + numpy.arange(70, 80), + numpy.arange(80, 90), + numpy.arange(90, 100)]) + + assert num_outside == 0 + +def test_nested_gene_space_list(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [-10, 10, 20, 30, 40, 50, 60, 70, 80, 90], + [-11, 11, 22, 33, 44, 55, 66, 77, 88, 99], + [-100, 100, 200, 300, 400, 500, 600, 700, 800, 900], + [-4.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], + [-5.1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9], + [-10.5, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], + [-15, 15, 25, 35, 45, 55, 65, 75, 85, 95], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]]) + + assert num_outside == 0 + +def test_nested_gene_space_list2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]]) + + assert num_outside == 0 + +def test_nested_gene_space_list3_None(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, None], + [1, 2], + [2, None], + [3, 4], + [None, 5], + None, + [None, 7], + [None, None], + [8, 9], + None], + mutation_by_replacement=True) + + assert num_outside == 0 + +def test_nested_gene_space_list4_None_custom_mutation_range(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, None], + [1, 2], + [2, None], + [3, 4], + [None, 5], + None, + [None, 7], + [None, None], + [8, 9], + None], + random_mutation_min_val=20, + random_mutation_max_val=40, + mutation_by_replacement=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=int, + mutation_by_replacement=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], int, numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 00, "high": 10, "step": 1}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_single_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[float, 4]) + + assert num_outside == 0 + +def test_nested_gene_space_single_gene_type_adaptive_mutation(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + # Due to rounding the genes, a gene at index 4 will have a value of 10 (outside the dict range) if [float, 2] is used. + gene_type=[float, 4], + mutation_percent_genes=[70, 50], + mutation_type="adaptive") + + assert num_outside == 0 + +def test_nested_gene_space_single_gene_type_adaptive_mutation_probability(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + # Due to rounding the genes, a gene at index 4 will have a value of 10 (outside the dict range) if [float, 2] is used. + gene_type=[float, 4], + mutation_probability=[0.7, 0.5], + mutation_type="adaptive") + + assert num_outside == 0 + +def test_nested_gene_space_nested_gene_type_adaptive_mutation(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_percent_genes=[70, 50], + mutation_type="adaptive") + + assert num_outside == 0 + +def test_nested_gene_space_nested_gene_type_adaptive_mutation_probability(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.7, 0.5], + mutation_type="adaptive") + assert num_outside == 0 + +#### Multi-Objective +def test_gene_space_range_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_None_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=[30, None, 40, 50, None, 60, 70, None, None, None], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_without_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_with_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_single_value_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_range_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_without_step_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_with_step_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_single_value_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_range_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[range(0, 10), + range(10, 20), + range(20, 30), + range(30, 40), + range(40, 50), + range(50, 60), + range(60, 70), + range(70, 80), + range(80, 90), + range(90, 100)], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_float_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + gene_type=[float, 3], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_dict_with_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10, "step": 1}, + {"low": 10, "high": 20, "step": 1.5}, + {"low": 20, "high": 30, "step": 2}, + {"low": 30, "high": 40, "step": 2.5}, + {"low": 40, "high": 50, "step": 3}, + {"low": 50, "high": 60, "step": 3.5}, + {"low": 60, "high": 70, "step": 4}, + {"low": 70, "high": 80, "step": 4.5}, + {"low": 80, "high": 90, "step": 5}, + {"low": 90, "high": 100, "step": 5.5}], + multi_objective=True) + + assert num_outside == 0 + + +def test_nested_gene_space_numpy_arange_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[numpy.arange(0, 10), + numpy.arange(10, 20), + numpy.arange(20, 30), + numpy.arange(30, 40), + numpy.arange(40, 50), + numpy.arange(50, 60), + numpy.arange(60, 70), + numpy.arange(70, 80), + numpy.arange(80, 90), + numpy.arange(90, 100)], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_list_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [-10, 10, 20, 30, 40, 50, 60, 70, 80, 90], + [-11, 11, 22, 33, 44, 55, 66, 77, 88, 99], + [-100, 100, 200, 300, 400, 500, 600, 700, 800, 900], + [-4.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], + [-5.1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9], + [-10.5, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], + [-15, 15, 25, 35, 45, 55, 65, 75, 85, 95], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_list2_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_list3_None_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, None], + [1, 2], + [2, None], + [3, 4], + [None, 5], + None, + [None, 7], + [None, None], + [8, 9], + None], + mutation_by_replacement=True, + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_list4_None_custom_mutation_range_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, None], + [1, 2], + [2, None], + [3, 4], + [None, 5], + None, + [None, 7], + [None, None], + [8, 9], + None], + random_mutation_min_val=20, + random_mutation_max_val=40, + mutation_by_replacement=True, + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=int, + mutation_by_replacement=True, + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], int, numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 00, "high": 10, "step": 1}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[float, 4], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_single_gene_type_adaptive_mutation_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + # Due to rounding the genes, a gene at index 4 will have a value of 10 (outside the dict range) if [float, 2] is used. + gene_type=[float, 4], + mutation_percent_genes=[70, 50], + mutation_type="adaptive", + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_single_gene_type_adaptive_mutation_probability_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + # Due to rounding the genes, a gene at index 4 will have a value of 10 (outside the dict range) if [float, 2] is used. + gene_type=[float, 4], + mutation_probability=[0.7, 0.5], + mutation_type="adaptive", + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_nested_gene_type_adaptive_mutation_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_percent_genes=[70, 50], + mutation_type="adaptive", + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_nested_gene_type_adaptive_mutation_probability_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.7, 0.5], + mutation_type="adaptive", + multi_objective=True) + assert num_outside == 0 + +#### Multi-Objective NSGA-II Parent Selection +def test_gene_space_range_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_arange_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_None_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=[30, None, 40, 50, None, 60, 70, None, None, None], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_without_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_with_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_single_value_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_range_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_arange_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_without_step_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_with_step_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_single_value_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_range_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[range(0, 10), + range(10, 20), + range(20, 30), + range(30, 40), + range(40, 50), + range(50, 60), + range(60, 70), + range(70, 80), + range(80, 90), + range(90, 100)], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_float_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + gene_type=[float, 3], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_dict_with_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10, "step": 1}, + {"low": 10, "high": 20, "step": 1.5}, + {"low": 20, "high": 30, "step": 2}, + {"low": 30, "high": 40, "step": 2.5}, + {"low": 40, "high": 50, "step": 3}, + {"low": 50, "high": 60, "step": 3.5}, + {"low": 60, "high": 70, "step": 4}, + {"low": 70, "high": 80, "step": 4.5}, + {"low": 80, "high": 90, "step": 5}, + {"low": 90, "high": 100, "step": 5.5}], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + + +def test_nested_gene_space_numpy_arange_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[numpy.arange(0, 10), + numpy.arange(10, 20), + numpy.arange(20, 30), + numpy.arange(30, 40), + numpy.arange(40, 50), + numpy.arange(50, 60), + numpy.arange(60, 70), + numpy.arange(70, 80), + numpy.arange(80, 90), + numpy.arange(90, 100)], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_list_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [-10, 10, 20, 30, 40, 50, 60, 70, 80, 90], + [-11, 11, 22, 33, 44, 55, 66, 77, 88, 99], + [-100, 100, 200, 300, 400, 500, 600, 700, 800, 900], + [-4.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], + [-5.1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9], + [-10.5, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], + [-15, 15, 25, 35, 45, 55, 65, 75, 85, 95], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_list2_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_list3_None_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, None], + [1, 2], + [2, None], + [3, 4], + [None, 5], + None, + [None, 7], + [None, None], + [8, 9], + None], + mutation_by_replacement=True, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_list4_None_custom_mutation_range_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, None], + [1, 2], + [2, None], + [3, 4], + [None, 5], + None, + [None, 7], + [None, None], + [8, 9], + None], + random_mutation_min_val=20, + random_mutation_max_val=40, + mutation_by_replacement=True, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=int, + mutation_by_replacement=True, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], int, numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 00, "high": 10, "step": 1}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[float, 4], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_single_gene_type_adaptive_mutation_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + # Due to rounding the genes, a gene at index 4 will have a value of 10 (outside the dict range) if [float, 2] is used. + gene_type=[float, 4], + mutation_percent_genes=[70, 50], + mutation_type="adaptive", + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_single_gene_type_adaptive_mutation_probability_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + # Due to rounding the genes, a gene at index 4 will have a value of 10 (outside the dict range) if [float, 2] is used. + gene_type=[float, 4], + mutation_probability=[0.7, 0.5], + mutation_type="adaptive", + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_nested_gene_type_adaptive_mutation_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_percent_genes=[70, 50], + mutation_type="adaptive", + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_nested_gene_type_adaptive_mutation_probability_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.7, 0.5], + mutation_type="adaptive", + multi_objective=True, + parent_selection_type='nsga2') + assert num_outside == 0 + + + + +if __name__ == "__main__": + #### Single-objective + print() + test_gene_space_range() + print() + test_gene_space_range_nested_gene_type() + print() + + test_gene_space_numpy_arange() + print() + test_gene_space_numpy_arange_nested_gene_type() + print() + + test_gene_space_list() + print() + test_gene_space_list_None() + print() + test_gene_space_list_nested_gene_type() + print() + + test_gene_space_list_single_value() + print() + test_gene_space_list_single_value_nested_gene_type() + print() + + test_gene_space_numpy() + print() + test_gene_space_numpy_nested_gene_type() + print() + + test_gene_space_dict_without_step() + print() + test_gene_space_dict_without_step_nested_gene_type() + print() + + test_gene_space_dict_with_step() + print() + test_gene_space_dict_with_step_nested_gene_type() + print() + + test_nested_gene_space_range() + print() + + test_nested_gene_space_dict_without_step() + print() + + test_nested_gene_space_dict_without_step_float_gene_type() + print() + + test_nested_gene_space_dict_with_step() + print() + + test_nested_gene_space_numpy_arange() + print() + + test_nested_gene_space_list() + print() + + test_nested_gene_space_list2() + print() + + test_nested_gene_space_list3_None() + print() + + test_nested_gene_space_list4_None_custom_mutation_range() + print() + + test_nested_gene_space_mix() + print() + + test_nested_gene_space_mix_nested_gene_type() + print() + + test_nested_gene_space_mix_initial_population() + print() + + test_nested_gene_space_mix_initial_population_single_gene_type() + print() + + test_nested_gene_space_single_gene_type_adaptive_mutation() + print() + test_nested_gene_space_single_gene_type_adaptive_mutation_probability() + print() + + test_nested_gene_space_nested_gene_type_adaptive_mutation() + print() + test_nested_gene_space_nested_gene_type_adaptive_mutation_probability() + print() + + #### Multi-objective + print() + test_gene_space_range_multi_objective() + print() + test_gene_space_range_nested_gene_type_multi_objective() + print() + + test_gene_space_numpy_arange_multi_objective() + print() + test_gene_space_numpy_arange_nested_gene_type_multi_objective() + print() + + test_gene_space_list_multi_objective() + print() + test_gene_space_list_None_multi_objective() + print() + test_gene_space_list_nested_gene_type_multi_objective() + print() + + test_gene_space_list_single_value_multi_objective() + print() + test_gene_space_list_single_value_nested_gene_type_multi_objective() + print() + + test_gene_space_numpy_multi_objective() + print() + test_gene_space_numpy_nested_gene_type_multi_objective() + print() + + test_gene_space_dict_without_step_multi_objective() + print() + test_gene_space_dict_without_step_nested_gene_type_multi_objective() + print() + + test_gene_space_dict_with_step_multi_objective() + print() + test_gene_space_dict_with_step_nested_gene_type_multi_objective() + print() + + test_nested_gene_space_range_multi_objective() + print() + + test_nested_gene_space_dict_without_step_multi_objective() + print() + + test_nested_gene_space_dict_without_step_float_gene_type_multi_objective() + print() + + test_nested_gene_space_dict_with_step_multi_objective() + print() + + test_nested_gene_space_numpy_arange_multi_objective() + print() + + test_nested_gene_space_list_multi_objective() + print() + + test_nested_gene_space_list2_multi_objective() + print() + + test_nested_gene_space_list3_None_multi_objective() + print() + + test_nested_gene_space_list4_None_custom_mutation_range_multi_objective() + print() + + test_nested_gene_space_mix_multi_objective() + print() + + test_nested_gene_space_mix_nested_gene_type_multi_objective() + print() + + test_nested_gene_space_mix_initial_population_multi_objective() + print() + + test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective() + print() + + test_nested_gene_space_single_gene_type_adaptive_mutation_multi_objective() + print() + test_nested_gene_space_single_gene_type_adaptive_mutation_probability_multi_objective() + print() + + test_nested_gene_space_nested_gene_type_adaptive_mutation_multi_objective() + print() + test_nested_gene_space_nested_gene_type_adaptive_mutation_probability_multi_objective() + print() + + + #### Multi-objective NSGA-II Parent Selection + print() + test_gene_space_range_multi_objective_nsga2() + print() + test_gene_space_range_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_numpy_arange_multi_objective_nsga2() + print() + test_gene_space_numpy_arange_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_list_multi_objective_nsga2() + print() + test_gene_space_list_None_multi_objective_nsga2() + print() + test_gene_space_list_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_list_single_value_multi_objective_nsga2() + print() + test_gene_space_list_single_value_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_numpy_multi_objective_nsga2() + print() + test_gene_space_numpy_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_dict_without_step_multi_objective_nsga2() + print() + test_gene_space_dict_without_step_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_dict_with_step_multi_objective_nsga2() + print() + test_gene_space_dict_with_step_nested_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_range_multi_objective_nsga2() + print() + + test_nested_gene_space_dict_without_step_multi_objective_nsga2() + print() + + test_nested_gene_space_dict_without_step_float_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_dict_with_step_multi_objective_nsga2() + print() + + test_nested_gene_space_numpy_arange_multi_objective_nsga2() + print() + + test_nested_gene_space_list_multi_objective_nsga2() + print() + + test_nested_gene_space_list2_multi_objective_nsga2() + print() + + test_nested_gene_space_list3_None_multi_objective_nsga2() + print() + + test_nested_gene_space_list4_None_custom_mutation_range_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_nested_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_initial_population_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_single_gene_type_adaptive_mutation_multi_objective_nsga2() + print() + test_nested_gene_space_single_gene_type_adaptive_mutation_probability_multi_objective_nsga2() + print() + + test_nested_gene_space_nested_gene_type_adaptive_mutation_multi_objective_nsga2() + print() + test_nested_gene_space_nested_gene_type_adaptive_mutation_probability_multi_objective_nsga2() + print() diff --git a/tests/test_gene_constraint.py b/tests/test_gene_constraint.py index 34a18b9..43cf38a 100644 --- a/tests/test_gene_constraint.py +++ b/tests/test_gene_constraint.py @@ -24,10 +24,12 @@ def population_gene_constraint(gene_space=None, init_range_low=-4, init_range_high=4, random_seed=123, + crossover_type='single_point', initial_population=None, parent_selection_type='sss', multi_objective=False, - gene_constraint=None): + gene_constraint=None, + allow_duplicate_genes=True): def fitness_func_no_batch_single(ga, solution, idx): return random.random() @@ -53,583 +55,169 @@ def fitness_func_no_batch_multi(ga, solution, idx): init_range_high=init_range_high, random_mutation_min_val=random_mutation_min_val, random_mutation_max_val=random_mutation_max_val, - allow_duplicate_genes=False, + allow_duplicate_genes=allow_duplicate_genes, mutation_by_replacement=mutation_by_replacement, random_seed=random_seed, + crossover_type=crossover_type, gene_constraint=gene_constraint, save_solutions=True, - suppress_warnings=True) + suppress_warnings=False) ga_instance.run() return ga_instance #### Single-Objective -def test_number_duplicates_default(): - gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,None,None,None,None,None,None,None,None] +def test_initial_population_int_by_replacement(): + gene_constraint=[lambda x: x[0]>=8,lambda x: x[1]>=8,lambda x: 5>=x[2]>=1,lambda x: 5>x[3]>3,lambda x: x[4]<2] ga_instance = population_gene_constraint(gene_constraint=gene_constraint, - init_range_low=98, - init_range_high=98) + init_range_low=0, + init_range_high=10, + random_mutation_min_val=0, + random_mutation_max_val=10, + num_genes=5, + gene_type=int, + mutation_by_replacement=True) initial_population = ga_instance.initial_population - assert initial_population[:, 0] >= 98 - assert initial_population[:, 1] >= 98 + # print(initial_population) -def test_number_duplicates_default_initial_population(): - num_duplicates = population_gene_constraint(initial_population=initial_population) - - assert num_duplicates == 0 - -def test_number_duplicates_float_gene_type(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_type=float, - num_genes=num_genes, - init_range_low=0, - init_range_high=1, - random_mutation_min_val=0, - random_mutation_max_val=1) - - assert num_duplicates == 0 - -def test_number_duplicates_float_gene_type_initial_population(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_type=float, - num_genes=num_genes, - init_range_low=0, - init_range_high=1, - initial_population=initial_population, - random_mutation_min_val=0, - random_mutation_max_val=1) - - assert num_duplicates == 0 - -def test_number_duplicates_int_gene_type(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=False, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) - - assert num_duplicates == 0 - -def test_number_duplicates_int_gene_type_initial_population(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=False, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - initial_population=initial_population, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) - - assert num_duplicates == 0 - -def test_number_duplicates_int_gene_type_replacement(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=True, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) - - assert num_duplicates == 0 - -def test_number_duplicates_int_gene_type_replacement_initial_population(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=True, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - initial_population=initial_population, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) + assert numpy.all(initial_population[:, 0] >= 8), "Not all values in column 0 are >= 8" + assert numpy.all(initial_population[:, 1] >= 8), "Not all values in column 1 are >= 8" + assert numpy.all(initial_population[:, 2] >= 1), "Not all values in column 2 are >= 1" + assert numpy.all((initial_population[:, 2] >= 1) & (initial_population[:, 2] <= 5)), "Not all values in column 2 between 1 and 5 (inclusive)" + assert numpy.all(initial_population[:, 3] == 4), "Not all values in column 3 between 3 and 5 (exclusive)" + assert numpy.all(initial_population[:, 4] < 2), "Not all values in column 4 < 2" - assert num_duplicates == 0 - -def test_number_duplicates_single_gene_space(): - num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - num_genes=10) - - assert num_duplicates == 0 - -def test_number_duplicates_single_gene_space_initial_population(): - num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - num_genes=10, - initial_population=initial_population) - - assert num_duplicates == 0 - -def test_number_duplicates_single_range_gene_space(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_space=range(num_genes), - num_genes=num_genes) - - assert num_duplicates == 0 - -def test_number_duplicates_single_range_gene_space_initial_population(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_space=range(num_genes), - num_genes=num_genes, - initial_population=initial_population) - - assert num_duplicates == 0 - -def test_number_duplicates_single_numpy_range_gene_space(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), - num_genes=num_genes) - - assert num_duplicates == 0 - -def test_number_duplicates_single_numpy_range_gene_space_initial_population(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), - num_genes=num_genes, - initial_population=initial_population) - - assert num_duplicates == 0 - -def test_number_duplicates_nested_gene_space(): - num_duplicates = population_gene_constraint(gene_space=[[0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], - gene_type=int, - num_genes=10) - - assert num_duplicates == 0 - -def test_number_duplicates_nested_gene_space_initial_population(): - num_duplicates = population_gene_constraint(gene_space=[[0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], - gene_type=int, - num_genes=10, - initial_population=initial_population) - - assert num_duplicates == 0 - - -# def test_number_duplicates_nested_gene_space_nested_gene_type(): - """ - This example causes duplicate genes that can only be solved by changing the values of a chain of genes. - Let's explain it using this solution: [0, 2, 3, 4, 5, 6, 6, 7, 8, 9] - It has 2 genes with the value 6 at indices 5 and 6. - According to the gene space, none of these genes can has a different value that solves the duplicates. - -If the value of the gene at index 5 is changed from 6 to 5, then it causes another duplicate with the gene at index 4. - -If the value of the gene at index 6 is changed from 6 to 7, then it causes another duplicate with the gene at index 7. - The solution is to change a chain of genes that make a room to solve the duplicates between the 2 genes. - 1) Change the second gene from 2 to 1. - 2) Change the third gene from 3 to 2. - 3) Change the fourth gene from 4 to 3. - 4) Change the fifth gene from 5 to 4. - 5) Change the sixth gene from 6 to 5. This solves the duplicates. - But this is NOT SUPPORTED yet. - We support changing only a single gene that makes a room to solve the duplicates. - - Let's explain it using this solution: [1, 2, 2, 4, 5, 6, 6, 7, 8, 9] - It has 2 genes with the value 2 at indices 1 and 2. - This is how the duplicates are solved: - 1) Change the first gene from 1 to 0. - 2) Change the second gene from 2 to 1. This solves the duplicates. - The result is [0, 1, 2, 4, 5, 6, 6, 7, 8, 9] - """ - # num_duplicates = population_gene_constraint(gene_space=[[0, 1], - # [1, 2], - # [2, 3], - # [3, 4], - # [4, 5], - # [5, 6], - # [6, 7], - # [7, 8], - # [8, 9], - # [9, 10]], - # gene_type=[int, int, int, int, int, int, int, int, int, int], - # num_genes=10) - - # assert num_duplicates == 0 - -def test_number_duplicates_nested_gene_space_nested_gene_type_initial_population(): - num_duplicates = population_gene_constraint(gene_space=[[0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], - gene_type=[int, int, int, int, int, int, int, int, int, int], - num_genes=10, - initial_population=initial_population) - - assert num_duplicates == 0 - -#### Multi-Objective -def test_number_duplicates_default_multi_objective(): - num_duplicates = population_gene_constraint() - - assert num_duplicates == 0 - -def test_number_duplicates_default_initial_population_multi_objective(): - num_duplicates = population_gene_constraint(initial_population=initial_population) - - assert num_duplicates == 0 - -def test_number_duplicates_float_gene_type_multi_objective(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_type=float, - num_genes=num_genes, - init_range_low=0, - init_range_high=1, - random_mutation_min_val=0, - random_mutation_max_val=1) - - assert num_duplicates == 0 - -def test_number_duplicates_float_gene_type_initial_population_multi_objective(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_type=float, - num_genes=num_genes, - init_range_low=0, - init_range_high=1, - initial_population=initial_population, - random_mutation_min_val=0, - random_mutation_max_val=1) - - assert num_duplicates == 0 - -def test_number_duplicates_int_gene_type_multi_objective(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=False, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) - - assert num_duplicates == 0 - -def test_number_duplicates_int_gene_type_initial_population_multi_objective(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=False, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - initial_population=initial_population, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) - - assert num_duplicates == 0 +def test_initial_population_int_by_replacement_no_duplicates(): + gene_constraint=[lambda x: x[0]>=5,lambda x: x[1]>=5,lambda x: x[2]>=5,lambda x: x[3]>=5,lambda x: x[4]>=5] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=1, + init_range_high=10, + random_mutation_min_val=1, + random_mutation_max_val=10, + gene_type=int, + num_genes=5, + mutation_by_replacement=True, + allow_duplicate_genes=False) -def test_number_duplicates_int_gene_type_replacement_multi_objective(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=True, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) + num_duplicates = 0 + for idx, solution in enumerate(ga_instance.solutions): + num = len(solution) - len(set(solution)) + if num != 0: + print(solution, idx) + num_duplicates += num assert num_duplicates == 0 -def test_number_duplicates_int_gene_type_replacement_initial_population_multi_objective(): - num_genes = 10 - init_range_low = 0 - init_range_high = init_range_low + num_genes - random_mutation_min_val = 0 - random_mutation_max_val = random_mutation_min_val + num_genes - num_duplicates = population_gene_constraint(gene_type=int, - mutation_by_replacement=True, - num_genes=num_genes, - init_range_low=init_range_low, - init_range_high=init_range_high, - initial_population=initial_population, - random_mutation_min_val=random_mutation_min_val, - random_mutation_max_val=random_mutation_max_val) - - assert num_duplicates == 0 + initial_population = ga_instance.initial_population + # print(initial_population) -def test_number_duplicates_single_gene_space_multi_objective(): - num_duplicates = population_gene_constraint(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - num_genes=10) + assert numpy.all(initial_population[:, 0] >= 5), "Not all values in column 0 >= 5" + assert numpy.all(initial_population[:, 1] >= 5), "Not all values in column 1 >= 5" + assert numpy.all(initial_population[:, 2] >= 5), "Not all values in column 2 >= 5" + assert numpy.all(initial_population[:, 3] >= 5), "Not all values in column 3 >= 5" + assert numpy.all(initial_population[:, 4] >= 5), "Not all values in column 4 >= 5" - assert num_duplicates == 0 +def test_initial_population_int_by_replacement_no_duplicates2(): + gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,lambda x: 20= 98), "Not all values in column 0 are >= 98" + assert numpy.all(initial_population[:, 1] >= 98), "Not all values in column 1 are >= 98" + assert numpy.all((initial_population[:, 2] > 20) & (initial_population[:, 2] < 40)), "Not all values in column 2 between 20 and 40 (exclusive)" + assert numpy.all(initial_population[:, 3] < 40), "Not all values in column 3 < 40" + assert numpy.all(initial_population[:, 4] < 50), "Not all values in column 4 < 50" + assert numpy.all(initial_population[:, 5] < 100), "Not all values in column 4 < 100" - assert num_duplicates == 0 +def test_initial_population_float_by_replacement_no_duplicates(): + gene_constraint=[lambda x: x[0]>=5,lambda x: x[1]>=5,lambda x: x[2]>=5,lambda x: x[3]>=5,lambda x: x[4]>=5] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=1, + init_range_high=10, + gene_type=[float, 1], + num_genes=5, + crossover_type=None, + mutation_by_replacement=False, + allow_duplicate_genes=False) -def test_number_duplicates_single_numpy_range_gene_space_multi_objective(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), - num_genes=num_genes) + num_duplicates = 0 + for idx, solution in enumerate(ga_instance.solutions): + num = len(solution) - len(set(solution)) + if num != 0: + print(solution, idx) + num_duplicates += num assert num_duplicates == 0 -def test_number_duplicates_single_numpy_range_gene_space_initial_population_multi_objective(): - num_genes = 10 - num_duplicates = population_gene_constraint(gene_space=numpy.arange(num_genes), - num_genes=num_genes, - initial_population=initial_population) - - assert num_duplicates == 0 + initial_population = ga_instance.initial_population + # print(initial_population) -def test_number_duplicates_nested_gene_space_multi_objective(): - num_duplicates = population_gene_constraint(gene_space=[[0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], - gene_type=int, - num_genes=10) + assert numpy.all(initial_population[:, 0] >= 5), "Not all values in column 0 >= 5" + assert numpy.all(initial_population[:, 1] >= 5), "Not all values in column 1 >= 5" + assert numpy.all(initial_population[:, 2] >= 5), "Not all values in column 2 >= 5" + assert numpy.all(initial_population[:, 3] >= 5), "Not all values in column 3 >= 5" + assert numpy.all(initial_population[:, 4] >= 5), "Not all values in column 4 >= 5" - assert num_duplicates == 0 +def test_initial_population_float_by_replacement_no_duplicates2(): + gene_constraint=[lambda x: x[0]>=1,lambda x: x[1]>=1,lambda x: x[2]>=1,lambda x: x[3]>=1,lambda x: x[4]>=1] + ga_instance = population_gene_constraint(gene_constraint=gene_constraint, + init_range_low=1, + init_range_high=2, + gene_type=[float, 1], + num_genes=5, + crossover_type=None, + mutation_by_replacement=False, + allow_duplicate_genes=False) -def test_number_duplicates_nested_gene_space_initial_population_multi_objective(): - num_duplicates = population_gene_constraint(gene_space=[[0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], - gene_type=int, - num_genes=10, - initial_population=initial_population) + num_duplicates = 0 + for idx, solution in enumerate(ga_instance.solutions): + num = len(solution) - len(set(solution)) + if num != 0: + print(solution, idx) + num_duplicates += num assert num_duplicates == 0 + initial_population = ga_instance.initial_population + # print(initial_population) -# def test_number_duplicates_nested_gene_space_nested_gene_type_multi_objective(): - """ - This example causes duplicate genes that can only be solved by changing the values of a chain of genes. - Let's explain it using this solution: [0, 2, 3, 4, 5, 6, 6, 7, 8, 9] - It has 2 genes with the value 6 at indices 5 and 6. - According to the gene space, none of these genes can has a different value that solves the duplicates. - -If the value of the gene at index 5 is changed from 6 to 5, then it causes another duplicate with the gene at index 4. - -If the value of the gene at index 6 is changed from 6 to 7, then it causes another duplicate with the gene at index 7. - The solution is to change a chain of genes that make a room to solve the duplicates between the 2 genes. - 1) Change the second gene from 2 to 1. - 2) Change the third gene from 3 to 2. - 3) Change the fourth gene from 4 to 3. - 4) Change the fifth gene from 5 to 4. - 5) Change the sixth gene from 6 to 5. This solves the duplicates. - But this is NOT SUPPORTED yet. - We support changing only a single gene that makes a room to solve the duplicates. - - Let's explain it using this solution: [1, 2, 2, 4, 5, 6, 6, 7, 8, 9] - It has 2 genes with the value 2 at indices 1 and 2. - This is how the duplicates are solved: - 1) Change the first gene from 1 to 0. - 2) Change the second gene from 2 to 1. This solves the duplicates. - The result is [0, 1, 2, 4, 5, 6, 6, 7, 8, 9] - """ - # num_duplicates = population_gene_constraint(gene_space=[[0, 1], - # [1, 2], - # [2, 3], - # [3, 4], - # [4, 5], - # [5, 6], - # [6, 7], - # [7, 8], - # [8, 9], - # [9, 10]], - # gene_type=[int, int, int, int, int, int, int, int, int, int], - # num_genes=10) - - # assert num_duplicates == 0 - -def test_number_duplicates_nested_gene_space_nested_gene_type_initial_population_multi_objective(): - num_duplicates = population_gene_constraint(gene_space=[[0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], - gene_type=[int, int, int, int, int, int, int, int, int, int], - num_genes=10, - initial_population=initial_population) - - assert num_duplicates == 0 + assert numpy.all(initial_population[:, 0] >= 1), "Not all values in column 0 >= 1" + assert numpy.all(initial_population[:, 1] >= 1), "Not all values in column 1 >= 1" + assert numpy.all(initial_population[:, 2] >= 1), "Not all values in column 2 >= 1" + assert numpy.all(initial_population[:, 3] >= 1), "Not all values in column 3 >= 1" + assert numpy.all(initial_population[:, 4] >= 1), "Not all values in column 4 >= 1" if __name__ == "__main__": #### Single-objective print() - test_number_duplicates_default() - print() - - """ - test_number_duplicates_default_initial_population() - print() - - test_number_duplicates_float_gene_type() - print() - test_number_duplicates_float_gene_type_initial_population() - print() - - test_number_duplicates_int_gene_type() - print() - test_number_duplicates_int_gene_type_initial_population() - print() - - test_number_duplicates_int_gene_type_replacement() - print() - test_number_duplicates_int_gene_type_replacement_initial_population() - print() - - test_number_duplicates_single_gene_space() - print() - test_number_duplicates_single_gene_space_initial_population() - print() - - test_number_duplicates_single_range_gene_space() - print() - test_number_duplicates_single_range_gene_space_initial_population() + test_initial_population_int_by_replacement() print() - - test_number_duplicates_single_numpy_range_gene_space() - print() - test_number_duplicates_single_numpy_range_gene_space_initial_population() - print() - - test_number_duplicates_nested_gene_space() - print() - test_number_duplicates_nested_gene_space_initial_population() - print() - - # This example causes duplicates that can only be solved by changing a chain of genes. - # test_number_duplicates_nested_gene_space_nested_gene_type() - # print() - test_number_duplicates_nested_gene_space_nested_gene_type_initial_population() - print() - - #### Multi-objective - print() - test_number_duplicates_default_initial_population_multi_objective() - print() - - test_number_duplicates_float_gene_type_multi_objective() - print() - test_number_duplicates_float_gene_type_initial_population_multi_objective() - print() - - test_number_duplicates_int_gene_type_multi_objective() - print() - test_number_duplicates_int_gene_type_initial_population_multi_objective() + test_initial_population_int_by_replacement_no_duplicates() print() - - test_number_duplicates_int_gene_type_replacement_multi_objective() - print() - test_number_duplicates_int_gene_type_replacement_initial_population_multi_objective() - print() - - test_number_duplicates_single_gene_space_multi_objective() - print() - test_number_duplicates_single_gene_space_initial_population_multi_objective() - print() - - test_number_duplicates_single_range_gene_space_multi_objective() - print() - test_number_duplicates_single_range_gene_space_initial_population_multi_objective() + test_initial_population_int_by_replacement_no_duplicates2() print() - - test_number_duplicates_single_numpy_range_gene_space_multi_objective() - print() - test_number_duplicates_single_numpy_range_gene_space_initial_population_multi_objective() - print() - - test_number_duplicates_nested_gene_space_multi_objective() + test_initial_population_float_by_replacement_no_duplicates() print() - test_number_duplicates_nested_gene_space_initial_population_multi_objective() + test_initial_population_float_by_replacement_no_duplicates2() print() - - # This example causes duplicates that can only be solved by changing a chain of genes. - # test_number_duplicates_nested_gene_space_nested_gene_type_multi_objective() - # print() - test_number_duplicates_nested_gene_space_nested_gene_type_initial_population_multi_objective() - print() - """ - - diff --git a/tests/test_gene_space.py b/tests/test_gene_space.py index b633880..2e83bf6 100644 --- a/tests/test_gene_space.py +++ b/tests/test_gene_space.py @@ -201,7 +201,7 @@ def fitness_func_no_batch_multi(ga, solution, idx): else: num_outside += 1 - print(f"Number of outside range is {num_outside}.") + print(f"Number of values outside the range is: {num_outside}.") return num_outside, ga_instance #### Single-Objective From 4073ffc9bae60183072b94289ff38a2e5c284421 Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Thu, 3 Jul 2025 19:39:23 -0400 Subject: [PATCH 10/25] Clean repo --- .DS_Store | Bin 8196 -> 0 bytes pygad/.DS_Store | Bin 6148 -> 0 bytes pygad/__pycache__/__init__.cpython-310.pyc | Bin 205 -> 0 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 82985 -> 0 bytes .../helper/__pycache__/__init__.cpython-310.pyc | Bin 260 -> 0 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 13628 -> 0 bytes pygad/helper/__pycache__/unique.cpython-310.pyc | Bin 16294 -> 0 bytes pygad/utils/.DS_Store | Bin 6148 -> 0 bytes .../utils/__pycache__/__init__.cpython-310.pyc | Bin 332 -> 0 bytes .../utils/__pycache__/crossover.cpython-310.pyc | Bin 6079 -> 0 bytes .../utils/__pycache__/mutation.cpython-310.pyc | Bin 17721 -> 0 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 7277 -> 0 bytes .../parent_selection.cpython-310.pyc | Bin 14555 -> 0 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 237 -> 0 bytes .../visualize/__pycache__/plot.cpython-310.pyc | Bin 13830 -> 0 bytes 15 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store delete mode 100644 pygad/.DS_Store delete mode 100644 pygad/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/__pycache__/pygad.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/misc.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/unique.cpython-310.pyc delete mode 100644 pygad/utils/.DS_Store delete mode 100644 pygad/utils/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/crossover.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/mutation.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/nsga2.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/parent_selection.cpython-310.pyc delete mode 100644 pygad/visualize/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/visualize/__pycache__/plot.cpython-310.pyc diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index ccee05d899388080d4c4fe26cc7300ddc72623e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMPj4GV6n~Q@vE9&S>$*uOsOu6zJK0C)HbTsR;E*WNkt0g&J~GwXD{Nz_v*YGj&8NW3Vv1-44 zlW2j6@~DzpGiXjJa$eRGWg;0Vf->k62@2MI;kJCyIw5Ta3<3rLgMdN6AYc&qKOlfR zTbBG8_r9JrtwF#b@LD1u&j%Y-(n>;ynyRA%jY}$4+P^p+ilTDs1ZP zfhDReNimpK$Gk1YK`RLzYO2+VX?0?WXO?6rOnHZ%t;C5{G&QY3z#woE0hzl`(+V9> zOpi19y9}j|QE&gFic+4WHl$pNQhC0V`z<4Po;dB3LtW~T4}KB!;G_;~rflakIvF2q zC*vbWcWE7KU!zZ`Mr-5N+0%^llw1LIu%`C7$8y?3g6!pkr#U+*m)2E_4Sx{*MnUF=X*eW zqbH}LZ5~FTO)I0**m1(p2|B!b@X&W!+}{YQB92A3;khEv4>7CEqPrP#X|nwOUmDeN z<)aa^s2PjOn!&H>(uhkas|Oq$E4-|0Iy^USFK@CL5*Yq8IPe0I)^fUcVf70J< znw8i!c7rXmJM2Stmwn2%S%>+o%l5Q^iP5XRHtvZH&`-VUy;>OfBIsaLYFt$NzegeB z_X|Xti-=!D6!UsD>%&>PL{Eg zXzJKn<_uD2McQvl@>Mx2MnFcH4P;RruS0H_5KuZe)@|BMZ>RBBDmT#>sN=iU^je@?Do?tis0U-9N&YL+Ms diff --git a/pygad/.DS_Store b/pygad/.DS_Store deleted file mode 100644 index 98be63892770443c647aa222a56a6882b24d65b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK&x_MQ6n@hmo5mvapy)B+b-rD* zS9lV<`lsmk-b^8BtVfY$UYYsk$M+^P-^*k=M5Ow&(E(A1h+0UDja$h6VBF8CVl~^s z&Pv8eXi7u;9@4nNb`4Ger@;TF0Po$l?Ae4eD(>IK^HiTjsgA&4)(fQLUt%b2yvDSk zGYXL|DCTjtyuRbpFf4f0!OSSDvPxq&>0HLFFxA_wSu})M<^9~^?aBO7e3xRHl%HOU z|Hh5x`Pi!PH5#W$KJ9jYs9L?z-19u&YkMCC7kWOJhSOQz4=3-q_DbtC+LXiSbrR2q zojcETHVu<3o`^#-ia~k*Cdo#6-q*8il#B1cbohSDA9n67mj_4P{owHFYCl*WJ?7$* z!_~@f-F@)r_~r02{gmlzOdqE32^PC;aRXmaC@ueLFv(J#oui$7Ji262Ab8GVmc@~< ze=7vAwj+E+P75+GBrqd63btM)31X4&;n>ia0l!3*87K~Ak=>*j_z9(ym**;h+`+sV zVJs|tD~LW_L6RKOgb*g7&lH!%;Xsylq6t2GoB~A!_;3i27<(4i2Dypb z;@TiuVD68A$lx-kz&};sH;9NK_5c6? diff --git a/pygad/__pycache__/__init__.cpython-310.pyc b/pygad/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index a939de9e2e698a7f89fc506e58abc4558a6692ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 205 zcmd1j<>g`kg6+F^GcG?{L(7F4DurrhF=k1tCtD$dN$i;rK)P{ayU2_}9y=!X^qCG-lIYq X;;_lhPbtkwwF6mS%mO4h7&w>!VCOT1 diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc deleted file mode 100644 index 7b6c592df0cde0e632c0bc20361189d07465f9c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82985 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 diff --git a/pygad/helper/__pycache__/__init__.cpython-310.pyc b/pygad/helper/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index b92603d2b45f76f85219da6837cef6d3d8aed1e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmYjJK~BRk5VW17sv!A8uerp)6@*#|skaIVxa4A0^(wZA?O;1dffw0_n=;J|0Z(@hGvZuR!EXa+7m-tN|R=&S<6soZ6@;bJyJL>bxz7soTp&K|g@^sorNu^R*D4;DdHnh#%sM6eK!s`CGt% z@09_GulxzZdvpWef`zCLo4#{iM`fO;sGQ~h{rud|h0s<;M1& diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc deleted file mode 100644 index 9cc35c6db4750e286f51fbde72bf842208382105..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13628 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 Nnq$$Pj-#&|zXqir^|Al} diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc deleted file mode 100644 index 71e7049cad614f07d97c66d4d7636bed3a4f7a2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16294 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 M$-r(--$~_v0TMZc82|tP diff --git a/pygad/utils/.DS_Store b/pygad/utils/.DS_Store deleted file mode 100644 index a364f0c0bd6445a262c5f5901cffdae298ec52af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKu};G<5Pc3s3JOCd7Dj&o609syRR#toCMIY@gd&9o5sJi?f8+!B6nN*eDw+gF zgsQvf{Orp;J9)O^7=W9t?@xg#fRrwn9IzNMxi3DjBu4g$F7#311-HCT@HEJ_z<*Rg z<}M3!R$+zj^Y?aHH^s7UmdudlaYaA>48|I8hyu@O(DG{Vijpmv_df5v$II}DHP^|4 zUXi&PW4W6iD`j4b2eQsa^FMm|M|_e6pQt2H)?~|CO)q{MH7R?&uxVxrm;$E24l2Ms zTP)2KtuzHp0aIY3fP5c3T`-T>DTYr6i*N)WP8bfxy8J8($4AT~c8a`1b4n#D)x{%* zQ##{5=H(GPMWw^V!-tC_yLduzVRZKILpofpXr(D&3T!K|r_HJC{}(@>|F=ojG6hV5 zol?L}=GXHXUn%abgO`)N*3<9kVv^S>ZYZpTt(dv86(7@sG47>8%p-P+jL_^yAjn{a JDe$KXd;wTAS1$kn diff --git a/pygad/utils/__pycache__/__init__.cpython-310.pyc b/pygad/utils/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index d759c90d10eaed53530be5207e3626eadf84f7ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 332 zcmYk2F;B!G6vu(Kch$Q~{2E=#sf&X#8jWto#N5I{NFgneLIc#Kei~Q5QdcJ@cPAf> zgOBj%_udQtM{v8{F@pHMRoy$BpDFnt1d?mm;Q~Y=i4E#8!#FVkCX6hc0cR{*I0w#I z4qUK8uAWh~{({@6!t==>yt0u8WmFTjvrs!~ybHm-Di2B7k5T+J3L9E+_EwzM=k@-v zTCvSAwL;co)F$}!GM^>MlN2EHhI6n9;LG_3^6S({@^eoQ0UA_vy^`QT?^Jk+ZlDj! xs;HYA(>kwX*FR0MbCxcH=rV|MuC0!o*TXcIm%WptQCApTL;AWc+TyfI@(W3FR22XK diff --git a/pygad/utils/__pycache__/crossover.cpython-310.pyc b/pygad/utils/__pycache__/crossover.cpython-310.pyc deleted file mode 100644 index c8c3626066c9e541fac87f78cc0a88154c574e2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6079 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 diff --git a/pygad/utils/__pycache__/mutation.cpython-310.pyc b/pygad/utils/__pycache__/mutation.cpython-310.pyc deleted file mode 100644 index 20719fc735f915a36ed7f9729ebc6a9c79e768a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17721 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 diff --git a/pygad/utils/__pycache__/parent_selection.cpython-310.pyc b/pygad/utils/__pycache__/parent_selection.cpython-310.pyc deleted file mode 100644 index 487dcd97223a51570e8593debde5f8d75e678df2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14555 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 diff --git a/pygad/visualize/__pycache__/__init__.cpython-310.pyc b/pygad/visualize/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 473b3f831e39a308d92cef5e00424e43e5c2b1cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 237 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 m)~eND+nysX=wjf-qj>by=Yx^Hh@~Sqbkrz From 47b33dd230e8a62e01fcd9377beea032cac0df97 Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Thu, 3 Jul 2025 19:46:28 -0400 Subject: [PATCH 11/25] Initialize population with gene constraints --- .DS_Store | Bin 8196 -> 8196 bytes example.py | 24 +- pygad/__pycache__/pygad.cpython-310.pyc | Bin 82985 -> 82172 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 13628 -> 15042 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 16294 -> 16386 bytes pygad/helper/misc.py | 209 +++++++++++------- pygad/helper/unique.py | 17 +- pygad/pygad.py | 105 +++------ .../__pycache__/crossover.cpython-310.pyc | Bin 6079 -> 6079 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 17721 -> 17721 bytes 10 files changed, 196 insertions(+), 159 deletions(-) diff --git a/.DS_Store b/.DS_Store index ccee05d899388080d4c4fe26cc7300ddc72623e8..e558d3d00f9ff8bdc903a1b088a6bd7b4b7c1fb6 100644 GIT binary patch delta 680 zcmZp1XmOa}&nUVvU^hRb=w==PQC3+Yh7yKUhGHNY&yWseYW&Z87je6q%!0(=re>d1QtzX$N;OTPXW3sks$@F5Tqd+s5l>Jg+7BjP%+58B@CGi z$qbG_L(_pWML=E&SaSf-S`aHAs7jv%hx= 90 + ga_instance = pygad.GA(num_generations=100, num_parents_mating=10, sol_per_pop=20, @@ -23,15 +29,21 @@ def fitness_func(ga_instance, solution, solution_idx): random_mutation_min_val=1, random_mutation_max_val=100, mutation_by_replacement=True, - gene_type=[float, 1], - save_solutions=True, - allow_duplicate_genes=False, + # gene_type=[float, 1], + gene_type=[float, [float, 5], [float, 4], [float, 3], [float, 2], [float, 1]], + # save_solutions=True, + # allow_duplicate_genes=False, # gene_space=numpy.unique(numpy.random.uniform(1, 100, size=100)), - gene_space=[range(10), {"low": 1, "high": 5}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], - gene_constraint=[lambda x: x[0]>=98,lambda x: x[1]>=98,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], + gene_space=[range(100), {"low": 1, "high": 100}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], + # gene_space=numpy.linspace(1, 100, 200), + gene_constraint=[const,lambda x: x[1]>=90,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], ) +# print(ga_instance.initial_population) + ga_instance.run() -print(ga_instance.gene_space_unpacked) +# print(ga_instance.gene_space_unpacked) print(ga_instance.population) + +print(calls) \ No newline at end of file diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index 7b6c592df0cde0e632c0bc20361189d07465f9c7..056b0f92c11a60af8a17c549a1b6b8bdec5360e5 100644 GIT binary patch delta 14024 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 Tm>lKw!>}9EPpuTK;@tlQScTWP delta 14488 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 tcWT{12_W30+sV$@US~bC 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 GZTS!7NiI_W delta 2752 zcmai0U2GiH6`nhPduMiccD=TD?bxx$j+1yC<;RF0*|8J|C6pF9kmRKSv06Nn^=>z_ z%bnQ}r{gY&meL2JqAMX(Dq%lHt@`3e5PuR9iclLQp5~#-TSX-hNKmyEaeL04-Hn|F zG1eZP``vTTJ@-53p6d_W@6@ekHfu`oo!s&6_w*;e%TR1B@>tSmIsZv@~9M%mpWvfMC7c5Py|pH1~immnuJPSZA)w>5)1maiKF}b zj+BU$q?hjIt3R{L-{U=h6gPL2%E!EHOz~$1Qv9m%0Kch^Z+&K*lDTP`^c*8_0!QO} z)QLybaINLVn%}+@+Ls2d6QT~%@)432qmgv{-;1xt$0g} z$d!pC0Y|>a%?c)>_o0r-5s5huN+fl;~n6za_Wq4$!qR>Ian%Kfo z7)8iHf!Bg%`@#Y(&I_|0IXMhyfK#Sv5#V1fY!``2u9Uj$@3xoQ-qwFJAIN&Ft~6U~ z{G;4y8#jz~ap)kXL9@{gYfV3uc8zs~O`C$|2W-y}=@L-Yd2m}5=U z$(^BFRWD2e7t__PmkZZPiecL050l?ns&xIR8$-FCDfBE z<$Z@#9!;#Q9K~cE1K}vK7K9ZRLYoVlEp`uLgw5l89))O-!^{Ca9uV~$v??oJIk~Dy z=_TDz;Nm8-6j<{&CdUT`ahcXyt5)1te)J(W!#|pwUo0W21YQ_xNs~2`E1L$K?L~nN zGEN7Ti>-!N4r*6Cb`arNe!`yLRn)(*_oaUiQ^7AmScCkUy>zmus?sBYvU12SVox@$uA{H>+w-)J4tc?5Z+N2(1WE}wE_ z!ET&lUaJ92vLgTk<#=R4xszv8s4t*c1Q8Dg?N+PFpf$pi6PB?v21(0HRRX~vdk~G0 zS9To5i4Mvo5V78Tw(hNjYzUDYih_XKe(kxo=VTJ<6MAuWcM=#rC*lA}qs6WoD>e6W ztU$Zl*#vN691Db1La)q!Yp40Uvs9l34uX^Xli3B`yj9dqjT_D(vb=S#GfX=_yeoA1 zy95NS@_vkK8O13Oj&ZjN>J|+e^^XC3w8K_A#L#KCi&^}}{0Ldy`h5O9y>lAfDQpZ^ zjibPVgrq%*`XuVTf7KozEqYL-|6j5qivX?_>_%yb%AlsPTgfBjhT7aN+SI zBq;}Ir?@o=M_pdtF_PJt{!Es?y|BO2&##CysUy6)4Bt4xRKl4!%vr>4ze+;GXOKbP zmau+h}EiTQ8&CwOawc339XznIKd7jq82#cULXC`D1m N=<)xy;v0+8{{w`Ua@YU> diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc index 71e7049cad614f07d97c66d4d7636bed3a4f7a2a..7b9d5415fb61338c565519f031ec4ab21335c548 100644 GIT binary patch delta 1929 zcmZ{kT})g>6o7Xw_ul>gYiVJD1>8S+`3GB8=$5qR@-c)>t^po7M9)Z?1e%z z3yt)_7n+)hzEqQvs7Z|v;@VdqY_y3cKAD)_G(4ETG^T2ziEqR?vxWtvBz$}3%(>^x z`I-4_`Q2;Cm3KOACib^@Z*uC>^>x>K$mL~A(4Aoa#a{Xe8eLu@wo%hWqU0c^X_+`w zaTS%Ex|s+oh-obICLAR~$wg{P?wo}P#Nji+ivPNaQ?2otbnCQj+RiM`vM(IOOdP~T z+(aajDy_;TPY!%Lx9f?l%B$v*hj7d;@cO z(i#iTi#JT`{oy@5!?f0w-}JAG9$pDvc3#|mS{E3wZ}U$t1X{+qyKN1E$km4M;&*_q z3s7y%u6z!&-5&aF;BB-?gTVy4L6?F_bct>R<7k*RhcslT*F%Hoclu4J1#QrcP#3yS z9pUrNTl#_92Z?J9mCU&hXdzrg7wMz$2{cF#MS^{8x}^KroVO6cXIeo_Mk}bENhO!= zBd!&s*31Yu5x&c88g8QEmvkZ0W`9UTRpj~~>F1GC=%4c6k)H(C{(DN8@v-6$#ULFI zgM!#C$XwO#)ZlT@y$pDb;p#YDZ||lT+8>PJMz%I=a3_+*LMpA{1E4VH)D*_4MGP%8 zBz>-s)o?A7u^-S3XaY0 z%KzGVt+Q>U6TXC33flqI6nKKCm@Z*0=CgBpCZFryJ%ul^?YHRT&WEU>{C(G-sP+g` z7*ZuEm`26SX84Dm06I#a^!P(Nn{Mo%%;eOhekGq&S?ZZ|O4o{ka(D07QWK9}JSynyKUYQ>yMzv@-uq#3DT#j{nhChat#t)Uhh_*>`#g0CR zwI1Xmf0S&oY^JEE^RtEFpQ-!U#B$Z!Wr&XqdtRT?Fg&4iTBR58A)aG+A~0bd%w(Jd zC7&Y|B%bs%lMIQ~TU$`9l|=ZX#BDp+X2Q$Ib6W)Z+p%yXoIw$=4vww>c*ILgD$|Z( z%?mH|n1U)-K^Vh*bZxkOq!%osfK~v^D~5*!!#&3^HkcnR4-z=rHd&Q|P-nazjnhbc UD8wH#KHBk$sRZKPG1wucmVD;;y!-CE zzc;?{68UIdD*62$2m4ICr=EZE^J~&;L~3L|Q9I7Qng}0~(e(wxGwPTftN0AZf?)U* zeuGr}hF@t2ICR&7xZqxO&=I!gFrk3ph3%aGCQG$=u}Doum;$Vg@v^!q_eGvSf+6 zx?Ik$&Mj>@XfJ&1F6|j+fs28?Tsz@89}$VqJmgXNz5PsvmB0{!dGSNq_!wZQs{&)43tF4CgKB>5h06!!& z@)(@zI!-e1c2~@u(4Bgi&1eK(PK4YhW5Fv#`wV{RN_s7WSNKKl3ZdUXI5|%KFrQA| z;8>s2-3f;0y2Fty2HnI!CKj(1o>#XT=s0Q*B21a@bo+>4@%ma>g{_{@5Vpf|>MP|X zl{TYE2%!VjfVNUDm#D5P`9e|8Kebev)AP7dqd_zZ!_PfuI&hvS_SfQ-!n|(ri|U$2 zPoO#mv%SHVPNcOC+pV|S;Kf`Ue9#*S*004K?c4569_z-<_?DnvgnA}+p7%3t#;ui> zR`f!tIIw%_9$?F-;Y8myX*Hw!e=b;JKAWu-o}NarT4H36B4Fs%s?IVdH&&;(dN*MrGs04MJ(CjbP_poKCh?zaZe?1@ zK~p@EbPClZ;c%|A)uw&So#h!?DrowAX}LW7EiC3{|Fyc2Yd=}DqASewCyvFjiXjLz z&mPsGQIOdQEih&|3q?hJPIgy=bmO9JE5pEWHiQmkj_N2si+~}}#~FMI7l+k7{fv#t zq&mgi8108Yhr7@2N29|CwTYv6`6*sRimj%06eVQnY{xXzQ()2Ce>xgUqJtAXx4BWc WFp`bi6KapG-3T^Q#W#N5eEP{1qEsV diff --git a/pygad/utils/__pycache__/mutation.cpython-310.pyc b/pygad/utils/__pycache__/mutation.cpython-310.pyc index 20719fc735f915a36ed7f9729ebc6a9c79e768a4..98d68747cea98df122ed33db92fbf6877a4ed058 100644 GIT binary patch delta 22 ccmdnl#kjMJkvpH4mx}=iST3e* Date: Thu, 3 Jul 2025 21:34:18 -0400 Subject: [PATCH 12/25] Optimize code --- example2.py | 39 ++ pygad/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 227 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 82172 -> 79681 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 282 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 15042 -> 15104 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 16386 -> 14559 bytes pygad/helper/misc.py | 7 +- pygad/helper/unique.py | 242 +------------ pygad/pygad.py | 335 ++++-------------- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 354 bytes .../__pycache__/crossover.cpython-310.pyc | Bin 6079 -> 6101 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 17721 -> 17743 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 0 -> 7299 bytes .../parent_selection.cpython-310.pyc | Bin 0 -> 14577 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 259 bytes .../__pycache__/plot.cpython-310.pyc | Bin 0 -> 13852 bytes 16 files changed, 138 insertions(+), 485 deletions(-) create mode 100644 example2.py create mode 100644 pygad/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/helper/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/nsga2.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/parent_selection.cpython-310.pyc create mode 100644 pygad/visualize/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/visualize/__pycache__/plot.cpython-310.pyc diff --git a/example2.py b/example2.py new file mode 100644 index 0000000..7c2ed72 --- /dev/null +++ b/example2.py @@ -0,0 +1,39 @@ +import pygad +import numpy + +function_inputs = [4,-2,3.5,5,-11,-4.7] +desired_output = 44 + +def fitness_func(ga_instance, solution, solution_idx): + output = numpy.sum(solution*function_inputs) + fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001) + return fitness + +num_genes = len(function_inputs) + +ga_instance = pygad.GA(num_generations=100, + num_parents_mating=10, + sol_per_pop=20, + num_genes=num_genes, + mutation_num_genes=6, + fitness_func=fitness_func, + init_range_low=1, + init_range_high=100, + # suppress_warnings=True, + random_mutation_min_val=1, + random_mutation_max_val=100, + mutation_by_replacement=True, + gene_type=[float, 1], + save_solutions=True, + allow_duplicate_genes=False, + # gene_space=numpy.unique(numpy.random.uniform(1, 100, size=100)), + gene_space=[range(0, 100), {"low": 0, "high": 100, 'step': 1}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], + gene_constraint=[lambda x: x[0]>=95,lambda x: x[1]>=95,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], + ) + +print(ga_instance.initial_population) + +# ga_instance.run() + +# print(ga_instance.gene_space_unpacked) +# print(ga_instance.population) diff --git a/pygad/__pycache__/__init__.cpython-310.pyc b/pygad/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a806e2b81e67ec11b18b41756b81ec76d7a3f16d GIT binary patch literal 227 zcmd1j<>g`k0=Ca-8BRd@F^GcG?{L(7F4DurrhF=k1tCtD$dN$i;rK)P{ayU2_}AprC7xP6)5N` zq-U07lqM+@6y;~7CYKb)fHlUXr{<-WWF{x(q~{j_733CFmSp7T=|U9iCMK5vjfAO< jkI&4@EQycTE2zB1VUwGmQks)$2Xa&~3y|Po;9vp(wJ1Em literal 0 HcmV?d00001 diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index 056b0f92c11a60af8a17c549a1b6b8bdec5360e5..30531f3315210e50eefcc49ccfa8eac695eafbd2 100644 GIT binary patch delta 7126 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 delta 9430 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 diff --git a/pygad/helper/__pycache__/__init__.cpython-310.pyc b/pygad/helper/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbca10c72f3f1a7f967cab21311c43ceb8412f0c GIT binary patch literal 282 zcmYjKL2AP=5R_y)gx2|kuRhqcJ(g1Xf?je67zg24!d9^*RgweFAM%iVAy4r&r+gu& ztP%oQ*x6Zjc1A50F9_QG7AjSW{frY)W^)7nPKk~QVG%R90HWkZT-f3=(w>JU6^h)WL8!MEg^ngs{4<0US z_F2M5k|m9B?XXF-QGdL4w!Ar&4VT=By6PHbK+x;awMu4paaPq1EFao?YDuY(!cm&* WahiYiiVYqA#t>#_05OX3oLm7Ma!TO< literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc index 6b6e06bf35b16d2f17de2cf7ee25c50fb5b66c9d..e07f6cd83500112a3cb79c51df3c2e08087f648a 100644 GIT binary patch delta 1432 zcmah|&u<$=7@gT4-nCsjv7I<^g&M1^gbfLiMGMp|X(CD?P(ejhB*3b>co%zJvtErS zX$xZo5>!qR&_P@(gu?-p8aFxQ+R9sYwbr!k;#{%m^499RMW1!sPJ{Pq{xzuAnvUmi zw_$afT?T^p{cF6{_2x$6Ijh0luD7dgIUV0&wc+ntCz%W4YgT8~sk`>oKo4m!bjN?u z%%>ngnObLLCRdf$k7ngplMiO+p%|oH&*gPyc}=I@>0V(KERO9fw%lfmJuSbe=H&hS zVfw86Gyn4Xb%hY2+H{q$lpt*)sI6?#n$p+%G0zZWKn2~7-eFx~KXMhGnxF-;Nx9yR zapOH@jj$)7YGQQ|-zI&-%Ri(`w3F=vpgOgy0)-BIJv8bYAU42mS9VSqebc)wj4)UO zj!CuCLKiWi+3J?sj|+ue6migeFH{kqBEY_lJjS4v8@48l!T2_TBR>guX8^O{rxyrd zEJUq9)@@@;1ygfN0gI2LgZ9ft2bx}w=P;rjzpSvEVZ1ivpAZ!660qBdrnnQX)wsbY z!d6!(cJ?xWh>$Oof< zxV}H)Utz=~MqK}QM(ERoK9iS~&nbIs)D^G-&)WwCy-DoU*XkzSphAUy4#DhTBCvj( zrv@2eZbO&C#A}1vu_)l)#hISY^}$r6Bkc?h&50~J2y zK*LWr(0c9%4j^}Y_B^bz1%R>|=sj*RjtgC%EnG==6{lGhWzPTv8T>2Od%o3h z>Yf80Hk+3p&Mec4{Aq?Kj-l{4z}xbL;-$owz|3q`Nvp%1;*{|cJBjNQ0K6BGYXGmE zEi3FakicM$-vMiO3f@3+2qh}=F*b*M8Q~QfFCCdYkMsz_s|c?noRdqX!!!ScUB=?rZQe=cp9Xa7eE`m*+4E@hRyh-U8~T#%nt4vz0xMaSH(%+v3Nzg6zU z-mIaK${t5`2>~-f(-)D?B2Tg`@;#GBqKCUi#;8N5Kt6bGiQbcoCwy9$w@xe^MW@l6 h@UBL;b^%!!Gd#?ax#|L)kZ)AyW8vJE<)_t?e*^ttWK{qF delta 1314 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. """ - range_min, range_max = self.get_random_mutation_range(gene_idx) + if gene_value is None: + # Use the initial population range. + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) + else: + # Use the mutation range. + range_min, range_max = self.get_random_mutation_range(gene_idx) if self.gene_space_nested: # Returning the current gene space from the 'gene_space' attribute. diff --git a/pygad/helper/unique.py b/pygad/helper/unique.py index 8b3e522..91b1f79 100644 --- a/pygad/helper/unique.py +++ b/pygad/helper/unique.py @@ -120,25 +120,13 @@ def solve_duplicate_genes_by_space(self, else: return new_solution, not_unique_indices, len(not_unique_indices) - # Do another try if there exist duplicate genes. - # If there are no possible values for the gene 3 with index 3 to solve the duplicate, try to change the value of the other gene with index 2. - if len(not_unique_indices) > 0: - not_unique_indices = set(numpy.where(new_solution == new_solution[list(not_unique_indices)[0]])[0]) - set([list(not_unique_indices)[0]]) - new_solution, not_unique_indices, num_unsolved_duplicates = self.unique_genes_by_space(new_solution=new_solution, - gene_type=gene_type, - not_unique_indices=not_unique_indices, - num_trials=10, - build_initial_pop=build_initial_pop) - else: - # DEEP-DUPLICATE-REMOVAL-NEEDED - # Search by this phrase to find where deep duplicates removal should be applied. - - # If there exist duplicate genes, then changing either of the 2 duplicating genes (with indices 2 and 3) will not solve the problem. - # This problem can be solved by randomly changing one of the non-duplicating genes that may make a room for a unique value in one the 2 duplicating genes. - # For example, if gene_space=[[3, 0, 1], [4, 1, 2], [0, 2], [3, 2, 0]] and the solution is [3 2 0 0], then the values of the last 2 genes duplicate. - # There are no possible changes in the last 2 genes to solve the problem. But it could be solved by changing the second gene from 2 to 4. - # As a result, any of the last 2 genes can take the value 2 and solve the duplicates. - return new_solution, not_unique_indices, len(not_unique_indices) + # DEEP-DUPLICATE-REMOVAL-NEEDED + # Search by this phrase to find where deep duplicates removal should be applied. + # If there exist duplicate genes, then changing either of the 2 duplicating genes (with indices 2 and 3) will not solve the problem. + # This problem can be solved by randomly changing one of the non-duplicating genes that may make a room for a unique value in one the 2 duplicating genes. + # For example, if gene_space=[[3, 0, 1], [4, 1, 2], [0, 2], [3, 2, 0]] and the solution is [3 2 0 0], then the values of the last 2 genes duplicate. + # There are no possible changes in the last 2 genes to solve the problem. But it could be solved by changing the second gene from 2 to 4. + # As a result, any of the last 2 genes can take the value 2 and solve the duplicates. return new_solution, not_unique_indices, num_unsolved_duplicates @@ -194,26 +182,6 @@ def unique_int_gene_from_range(self, sample_size=None, step=step) - """ - # For non-integer steps, the numpy.arange() function returns zeros if the dtype parameter is set to an integer data type. So, this returns zeros if step is non-integer and dtype is set to an int data type: numpy.arange(min_val, max_val, step, dtype=gene_type[0]) - # To solve this issue, the data type casting will not be handled inside numpy.arange(). The range is generated by numpy.arange() and then the data type is converted using the numpy.asarray() function. - all_gene_values = numpy.asarray(numpy.arange(min_val, - max_val, - step), - dtype=dtype[0]) - - # If mutation is by replacement, do not add the current gene value into the list. - # This is to avoid replacing the value by itself again. We are doing nothing in this case. - if mutation_by_replacement: - pass - else: - all_gene_values = all_gene_values + solution[gene_index] - - # After adding solution[gene_index] to the list, we have to change the data type again. - all_gene_values = numpy.asarray(all_gene_values, - dtype[0]) - """ - selected_value = self.select_unique_value(gene_values=random_values, solution=solution, gene_index=gene_index) @@ -352,196 +320,22 @@ def unique_gene_by_space(self, build_initial_pop=False, num_trials=10): - """ - Returns a unique value for a specific gene based on its value space to resolve duplicates. - - Args: - solution (list): A solution containing genes with duplicate values. - gene_idx (int): The index of the gene that has a duplicate value. - gene_type (type): The data type of the gene (e.g., int, float). - num_trials (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers. - - Returns: - Any: A unique value for the gene, if one exists; otherwise, the original gene value. """ - - if self.gene_space_nested: - if type(self.gene_space[gene_idx]) in [numpy.ndarray, list, tuple]: - # Return the current gene space from the 'gene_space' attribute. - curr_gene_space = list(self.gene_space[gene_idx]).copy() - else: - # Return the entire gene space from the 'gene_space' attribute. - # curr_gene_space = list(self.gene_space[gene_idx]).copy() - curr_gene_space = self.gene_space[gene_idx] - - # If the gene space has only a single value, use it as the new gene value. - if type(curr_gene_space) in pygad.GA.supported_int_float_types: - value_from_space = curr_gene_space - # If the gene space is None, apply mutation by adding a random value between the range defined by the 2 parameters 'random_mutation_min_val' and 'random_mutation_max_val'. - elif curr_gene_space is None: - dtype = self.get_gene_dtype(gene_index=gene_idx) - - if dtype[0] in pygad.GA.supported_int_types: - if build_initial_pop == True: - # If we are building the initial population, then use the range of the initial population. - min_val = self.init_range_low - max_val = self.init_range_high - else: - # If we are NOT building the initial population, then use the range of the random mutation. - min_val = self.random_mutation_min_val - max_val = self.random_mutation_max_val - - value_from_space = self.unique_int_gene_from_range(solution=solution, - gene_index=gene_idx, - min_val=min_val, - max_val=max_val, - mutation_by_replacement=True, - gene_type=dtype) - else: - if build_initial_pop == True: - low = self.init_range_low - high = self.init_range_high - else: - low = self.random_mutation_min_val - high = self.random_mutation_max_val - - """ - value_from_space = numpy.random.uniform(low=low, - high=high, - size=1)[0] - """ - - value_from_space = self.unique_float_gene_from_range(solution=solution, - gene_index=gene_idx, - min_val=low, - max_val=high, - mutation_by_replacement=True, - gene_type=dtype, - sample_size=num_trials) - - - elif type(curr_gene_space) is dict: - dtype = self.get_gene_dtype(gene_index=gene_idx) - - # Use index 0 to return the type from the list (e.g. [int, None] or [float, 2]). - if dtype[0] in pygad.GA.supported_int_types: - if 'step' in curr_gene_space.keys(): - step = curr_gene_space['step'] - else: - step = None - - value_from_space = self.unique_int_gene_from_range(solution=solution, - gene_index=gene_idx, - min_val=curr_gene_space['low'], - max_val=curr_gene_space['high'], - step=step, - mutation_by_replacement=True, - gene_type=dtype) - else: - if 'step' in curr_gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=curr_gene_space['low'], - stop=curr_gene_space['high'], - step=curr_gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=curr_gene_space['low'], - high=curr_gene_space['high'], - size=1)[0] - else: - # Selecting a value randomly based on the current gene's space in the 'gene_space' attribute. - # If the gene space has only 1 value, then select it. The old and new values of the gene are identical. - if len(curr_gene_space) == 1: - value_from_space = curr_gene_space[0] - if not self.suppress_warnings: warnings.warn(f"You set 'allow_duplicate_genes=False' but the space of the gene with index {gene_idx} has only a single value. Thus, duplicates are possible.") - # If the gene space has more than 1 value, then select a new one that is different from the current value. - else: - values_to_select_from = list(set(curr_gene_space) - set(solution)) - - if len(values_to_select_from) == 0: - # DEEP-DUPLICATE-REMOVAL-NEEDED - # Search by this phrase to find where deep duplicates removal should be applied. - - # Reaching this block means there is no value in the gene space of this gene to solve the duplicates. - # To solve the duplicate between the 2 genes, the solution is to change the value of a third gene that makes a room to solve the duplicate. - - if not self.suppress_warnings: warnings.warn("You set 'allow_duplicate_genes=False' but the gene space does not have enough values to prevent duplicates.") - - solution2 = self.solve_duplicates_deeply(solution) - if solution2 is None: - # Cannot solve duplicates. At the moment, we are changing the value of a third gene to solve the duplicates between 2 genes. - # Maybe a 4th, 5th, 6th, or even more genes need to be changed to solve the duplicates. - pass - else: - solution = solution2 - value_from_space = solution[gene_idx] - - else: - value_from_space = random.choice(values_to_select_from) - else: - # Selecting a value randomly from the global gene space in the 'gene_space' attribute. - if type(self.gene_space) is dict: - dtype = self.get_gene_dtype(gene_index=gene_idx) - - if dtype[0] in pygad.GA.supported_int_types: - if 'step' in self.gene_space.keys(): - step = self.gene_space['step'] - else: - step = None - - value_from_space = self.unique_int_gene_from_range(solution=solution, - gene_index=gene_idx, - min_val=self.gene_space['low'], - max_val=self.gene_space['high'], - step=step, - mutation_by_replacement=True, - gene_type=dtype) - else: - # When the gene_space is assigned a dict object, then it specifies the lower and upper limits of all genes in the space. - if 'step' in self.gene_space.keys(): - value_from_space = numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=1)[0] - else: - value_from_space = numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=1)[0] - else: - # If the space type is not of type dict, then a value is randomly selected from the gene_space attribute. - # Remove all the genes in the current solution from the gene_space. - # This only leaves the unique values that could be selected for the gene. - - # Before using the gene_space, use gene_space_unpacked instead of gene_space to make sure the numbers has the right data type and its values are rounded. - values_to_select_from = list(set(self.gene_space_unpacked[gene_idx]) - set(solution)) - - if len(values_to_select_from) == 0: - if not self.suppress_warnings: warnings.warn("You set 'allow_duplicate_genes=False' but the gene space does not have enough values to prevent duplicates.") - value_from_space = solution[gene_idx] - else: - value_from_space = random.choice(values_to_select_from) + """ + Returns a unique value for a specific gene based on its value space to resolve duplicates. - if value_from_space is None: - if build_initial_pop == True: - low = self.init_range_low - high = self.init_range_high - else: - low = self.random_mutation_min_val - high = self.random_mutation_max_val + Args: + solution (list): A solution containing genes with duplicate values. + gene_idx (int): The index of the gene that has a duplicate value. + gene_type (type): The data type of the gene (e.g., int, float). + num_trials (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers. - value_from_space = numpy.random.uniform(low=low, - high=high, - size=1)[0] + Returns: + Any: A unique value for the gene, if one exists; otherwise, the original gene value. + """ - # Similar to the round_genes() method in the pygad module, - # Create a round_gene() method to round a single gene. - dtype = self.get_gene_dtype(gene_index=gene_idx) - if not dtype[1] is None: - value_from_space = numpy.round(dtype[0](value_from_space), - dtype[1]) - else: - value_from_space = dtype[0](value_from_space) - return value_from_space + return value_from_space def find_two_duplicates(self, solution, diff --git a/pygad/pygad.py b/pygad/pygad.py index df44926..7accdfa 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -460,15 +460,16 @@ def __init__(self, for initial_solution_idx, initial_solution in enumerate(self.initial_population): if self.gene_space is None: self.initial_population[initial_solution_idx], _, _ = self.solve_duplicate_genes_randomly(solution=initial_solution, - min_val=self.init_range_low, - max_val=self.init_range_high, - mutation_by_replacement=self.mutation_by_replacement, - gene_type=self.gene_type, - sample_size=100) + min_val=self.init_range_low, + max_val=self.init_range_high, + mutation_by_replacement=True, + gene_type=self.gene_type, + sample_size=100) else: self.initial_population[initial_solution_idx], _, _ = self.solve_duplicate_genes_by_space(solution=initial_solution, - gene_type=self.gene_type, - num_trials=10) + gene_type=self.gene_type, + sample_size=100, + mutation_by_replacement=True) # Change the data type and round all genes within the initial population. self.initial_population = self.change_population_dtype_and_round(initial_population) @@ -1374,272 +1375,86 @@ def initialize_population(self, # The population will have sol_per_pop chromosome where each chromosome has num_genes genes. self.pop_size = (self.sol_per_pop, self.num_genes) - if self.gene_space is None: - # Create the initial population randomly. - - # Create an empty population. - self.population = numpy.zeros(shape=self.pop_size) + # There are 4 steps to build the initial population: + # 1) Generate the population. + # 2) Change the data type and round the values. + # 3) Check for the constraints. + # 4) Solve duplicates if not allowed. - # Loop through the genes, randomly generate the values of a single gene across the entire population, and add the values of each gene to the population. - for gene_idx in range(self.num_genes): - range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) + # Create an empty population. + self.population = numpy.zeros(shape=self.pop_size) - # A vector of all values of this single gene across all solutions in the population. - gene_values = numpy.random.uniform(low=range_min, - high=range_max, - size=self.pop_size[0]) - # Adding the current gene values to the population. - self.population[:, gene_idx] = gene_values + # 1) Create the initial population either randomly or using the gene space. + if self.gene_space is None: + # Create the initial population randomly. - # Change the data type and round all genes within the initial population. - self.population = self.change_population_dtype_and_round(self.population) + # Set gene_value=None to consider generating values for the initial population instead of generating values for mutation. + # Loop through the genes, randomly generate the values of a single gene at a time, and insert the values of each gene to the population. + for sol_idx in range(self.sol_per_pop): + for gene_idx in range(self.num_genes): + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) + self.population[sol_idx, gene_idx] = self.generate_gene_value_randomly(range_min=range_min, + range_max=range_max, + gene_idx=gene_idx, + mutation_by_replacement=True, + gene_value=None, + sample_size=1, + step=1) - # Enforce the gene constraints as much as possible. - if gene_constraint is None: - pass - else: - # Note that gene_constraint is not validated yet. - # We have to set it as a property of the pygad.GA instance to retrieve without passing it as an additional parameter. - self.gene_constraint = gene_constraint - for sol_idx, solution in enumerate(self.population): - for gene_idx in range(self.num_genes): - # Check that a constraint is available for the gene and that the current value does not satisfy that constraint - if self.gene_constraint[gene_idx]: - if not self.gene_constraint[gene_idx](solution): - range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) - # While initializing the population, we follow a mutation by replacement approach. So, the original gene value is not needed. - random_values_filtered = self.get_valid_gene_constraint_values(range_min=range_min, - range_max=range_max, - gene_value=None, - gene_idx=gene_idx, - mutation_by_replacement=True, - solution=solution, - sample_size=100) - if random_values_filtered is None: - if not self.suppress_warnings: - warnings.warn(f"No value satisfied the constraint for the gene at index {gene_idx} while creating the initial population.") - else: - self.population[sol_idx, gene_idx] = random.choice(random_values_filtered) + else: + # Generate the initial population using the gene_space. + for sol_idx in range(self.sol_per_pop): + for gene_idx in range(self.num_genes): + self.population[sol_idx, gene_idx] = self.generate_gene_value_from_space(gene_idx=gene_idx, + mutation_by_replacement=True, + gene_value=None, + sample_size=1) + + # 2) Change the data type and round all genes within the initial population. + self.population = self.change_population_dtype_and_round(self.population) + + # 3) Enforce the gene constraints as much as possible. + if gene_constraint is None: + pass + else: + # Note that gene_constraint is not validated yet. + # We have to set it as a property of the pygad.GA instance to retrieve without passing it as an additional parameter. + self.gene_constraint = gene_constraint + for sol_idx, solution in enumerate(self.population): + for gene_idx in range(self.num_genes): + # Check that a constraint is available for the gene and that the current value does not satisfy that constraint + if self.gene_constraint[gene_idx]: + if not self.gene_constraint[gene_idx](solution): + range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) + # While initializing the population, we follow a mutation by replacement approach. So, the original gene value is not needed. + values_filtered = self.get_valid_gene_constraint_values(range_min=range_min, + range_max=range_max, + gene_value=None, + gene_idx=gene_idx, + mutation_by_replacement=True, + solution=solution, + sample_size=100) + if values_filtered is None: + if not self.suppress_warnings: + warnings.warn(f"No value satisfied the constraint for the gene at index {gene_idx} while creating the initial population.") + else: + self.population[sol_idx, gene_idx] = random.choice(values_filtered) - if allow_duplicate_genes == False: - for solution_idx in range(self.population.shape[0]): - # self.logger.info("Before", self.population[solution_idx]) + # 4) Solve duplicate genes. + if allow_duplicate_genes == False: + for solution_idx in range(self.population.shape[0]): + if self.gene_space is None: self.population[solution_idx], _, _ = self.solve_duplicate_genes_randomly(solution=self.population[solution_idx], min_val=self.init_range_low, max_val=self.init_range_high, mutation_by_replacement=True, gene_type=gene_type, sample_size=100) - - elif self.gene_space_nested: - if self.gene_type_single == True: - # Reaching this block means: - # 1) gene_space is nested (gene_space_nested is True). - # 2) gene_type is not nested (gene_type_single is True). - self.population = numpy.zeros(shape=self.pop_size, - dtype=self.gene_type[0]) - for sol_idx in range(self.sol_per_pop): - for gene_idx in range(self.num_genes): - - range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) - - if self.gene_space[gene_idx] is None: - - # The above problem is solved by keeping the None value in the gene_space parameter. This forces PyGAD to generate this value for each solution. - self.population[sol_idx, gene_idx] = numpy.asarray(numpy.random.uniform(low=range_min, - high=range_max, - size=1), - dtype=self.gene_type[0])[0] - elif type(self.gene_space[gene_idx]) in [numpy.ndarray, list, tuple, range]: - # Check if the gene space has None values. If any, then replace it with randomly generated values according to the 3 attributes init_range_low, init_range_high, and gene_type. - if type(self.gene_space[gene_idx]) is range: - temp_gene_space = self.gene_space[gene_idx] - else: - # Convert to list because tuple and range do not have copy(). - # We copy the gene_space to a temp variable to keep its original value. - # In the next for loop, the gene_space is changed. - # Later, the gene_space is restored to its original value using the temp variable. - temp_gene_space = list(self.gene_space[gene_idx]).copy() - - for idx, val in enumerate(self.gene_space[gene_idx]): - if val is None: - self.gene_space[gene_idx][idx] = numpy.asarray(numpy.random.uniform(low=range_min, - high=range_max, - size=1), - dtype=self.gene_type[0])[0] - # Find the difference between the current gene space and the current values in the solution. - unique_gene_values = list(set(self.gene_space[gene_idx]).difference( - set(self.population[sol_idx, :gene_idx]))) - if len(unique_gene_values) > 0: - self.population[sol_idx, gene_idx] = random.choice(unique_gene_values) - else: - # If there is no unique values, then we have to select a duplicate value. - self.population[sol_idx, gene_idx] = random.choice( - self.gene_space[gene_idx]) - - self.population[sol_idx, gene_idx] = self.gene_type[0]( - self.population[sol_idx, gene_idx]) - # Restore the gene_space from the temp_gene_space variable. - self.gene_space[gene_idx] = list( - temp_gene_space).copy() - elif type(self.gene_space[gene_idx]) is dict: - if 'step' in self.gene_space[gene_idx].keys(): - self.population[sol_idx, gene_idx] = numpy.asarray(numpy.random.choice(numpy.arange(start=self.gene_space[gene_idx]['low'], - stop=self.gene_space[gene_idx]['high'], - step=self.gene_space[gene_idx]['step']), - size=1), - dtype=self.gene_type[0])[0] - else: - self.population[sol_idx, gene_idx] = numpy.asarray(numpy.random.uniform(low=self.gene_space[gene_idx]['low'], - high=self.gene_space[gene_idx]['high'], - size=1), - dtype=self.gene_type[0])[0] - elif type(self.gene_space[gene_idx]) in GA.supported_int_float_types: - self.population[sol_idx, gene_idx] = self.gene_space[gene_idx] - else: - # There is no more options. - pass - else: - # Reaching this block means: - # 1) gene_space is nested (gene_space_nested is True). - # 2) gene_type is nested (gene_type_single is False). - self.population = numpy.zeros(shape=self.pop_size, - dtype=object) - for sol_idx in range(self.sol_per_pop): - for gene_idx in range(self.num_genes): - - range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) - - if type(self.gene_space[gene_idx]) in [numpy.ndarray, list, tuple, range]: - # Convert to list because tuple and range do not have copy(). - # We copy the gene_space to a temp variable to keep its original value. - # In the next for loop, the gene_space is changed. - # Later, the gene_space is restored to its original value using the temp variable. - temp_gene_space = list(self.gene_space[gene_idx]).copy() - - # Check if the gene space has None values. If any, then replace it with randomly generated values according to the 3 attributes init_range_low, init_range_high, and gene_type. - for idx, val in enumerate(self.gene_space[gene_idx]): - if val is None: - self.gene_space[gene_idx][idx] = numpy.asarray(numpy.random.uniform(low=range_min, - high=range_max, - size=1), - dtype=self.gene_type[gene_idx][0])[0] - - self.population[sol_idx, gene_idx] = random.choice(self.gene_space[gene_idx]) - self.population[sol_idx, gene_idx] = self.gene_type[gene_idx][0](self.population[sol_idx, gene_idx]) - # Restore the gene_space from the temp_gene_space variable. - self.gene_space[gene_idx] = temp_gene_space.copy() - elif type(self.gene_space[gene_idx]) is dict: - if 'step' in self.gene_space[gene_idx].keys(): - self.population[sol_idx, gene_idx] = numpy.asarray(numpy.random.choice(numpy.arange(start=self.gene_space[gene_idx]['low'], - stop=self.gene_space[gene_idx]['high'], - step=self.gene_space[gene_idx]['step']), - size=1), - dtype=self.gene_type[gene_idx][0])[0] - else: - self.population[sol_idx, gene_idx] = numpy.asarray(numpy.random.uniform(low=self.gene_space[gene_idx]['low'], - high=self.gene_space[gene_idx]['high'], - size=1), - dtype=self.gene_type[gene_idx][0])[0] - elif self.gene_space[gene_idx] is None: - temp_gene_value = numpy.asarray(numpy.random.uniform(low=range_min, - high=range_max, - size=1), - dtype=self.gene_type[gene_idx][0])[0] - - self.population[sol_idx, gene_idx] = temp_gene_value.copy() - elif type(self.gene_space[gene_idx]) in GA.supported_int_float_types: - self.population[sol_idx, gene_idx] = self.gene_space[gene_idx] - else: - # There is no more options. - pass - else: - # Handle the non-nested gene_space. It can be assigned a numeric value, list, numpy.ndarray, or a dict. - if self.gene_type_single == True: - # Reaching this block means: - # 1) gene_space is not nested (gene_space_nested is False). - # 2) gene_type is not nested (gene_type_single is True). - - # Replace all the None values with random values using the init_range_low, init_range_high, and gene_type attributes. - for gene_idx, curr_gene_space in enumerate(self.gene_space): - - range_min, range_max = self.get_initial_population_range(gene_index=gene_idx) - - if curr_gene_space is None: - self.gene_space[gene_idx] = numpy.asarray(numpy.random.uniform(low=range_min, - high=range_max, - size=1), - dtype=self.gene_type[0])[0] - - # Creating the initial population by randomly selecting the genes' values from the values inside the 'gene_space' parameter. - if type(self.gene_space) is dict: - if 'step' in self.gene_space.keys(): - self.population = numpy.asarray(numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=self.pop_size), - dtype=self.gene_type[0]) - else: - self.population = numpy.asarray(numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=self.pop_size), - dtype=self.gene_type[0]) # A NumPy array holding the initial population. - else: - self.population = numpy.asarray(numpy.random.choice(self.gene_space, - size=self.pop_size), - dtype=self.gene_type[0]) # A NumPy array holding the initial population. - else: - # Reaching this block means: - # 1) gene_space is not nested (gene_space_nested is False). - # 2) gene_type is nested (gene_type_single is False). - - # Creating the initial population by randomly selecting the genes' values from the values inside the 'gene_space' parameter. - if type(self.gene_space) is dict: - # Create an empty population of dtype=object to support storing mixed data types within the same array. - self.population = numpy.zeros(shape=self.pop_size, - dtype=object) - # Loop through the genes, randomly generate the values of a single gene across the entire population, and add the values of each gene to the population. - for gene_idx in range(self.num_genes): - # Generate the values of the current gene across all solutions. - # A vector of all values of this single gene across all solutions in the population. - if 'step' in self.gene_space.keys(): - gene_values = numpy.asarray(numpy.random.choice(numpy.arange(start=self.gene_space['low'], - stop=self.gene_space['high'], - step=self.gene_space['step']), - size=self.pop_size[0]), - dtype=self.gene_type[gene_idx][0]) - else: - gene_values = numpy.asarray(numpy.random.uniform(low=self.gene_space['low'], - high=self.gene_space['high'], - size=self.pop_size[0]), - dtype=self.gene_type[gene_idx][0]) - # Adding the current gene values to the population. - self.population[:, gene_idx] = gene_values - else: - # Reaching this block means that the gene_space is not None or dict. - # It can be either range, numpy.ndarray, or list. - - # Create an empty population of dtype=object to support storing mixed data types within the same array. - self.population = numpy.zeros(shape=self.pop_size, dtype=object) - # Loop through the genes, randomly generate the values of a single gene across the entire population, and add the values of each gene to the population. - for gene_idx in range(self.num_genes): - # A vector of all values of this single gene across all solutions in the population. - gene_values = numpy.asarray(numpy.random.choice(self.gene_space, - size=self.pop_size[0]), - dtype=self.gene_type[gene_idx][0]) - # Adding the current gene values to the population. - self.population[:, gene_idx] = gene_values - - if self.gene_space is None: - pass - else: - if allow_duplicate_genes == False: - for sol_idx in range(self.population.shape[0]): - self.population[sol_idx], _, _ = self.solve_duplicate_genes_by_space(solution=self.population[sol_idx], + self.population[sol_idx], _, _ = self.solve_duplicate_genes_by_space(solution=self.population[solution_idx], gene_type=self.gene_type, - num_trials=10, - build_initial_pop=True) + sample_size=100, + mutation_by_replacement=True) # Keeping the initial population in the initial_population attribute. self.initial_population = self.population.copy() diff --git a/pygad/utils/__pycache__/__init__.cpython-310.pyc b/pygad/utils/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d55b00c9a1268d2a87884daecf7c3448c7ced177 GIT binary patch literal 354 zcmYk2y-vj-6vv@ow|ehQe1xtY+FW%p#u%T##f^{`Jnnxs%rl%JKFTv8lU zPze-|Nl(p7Ey+wy%t_BL0xHNYs4U6I&(nn{)=f+<$;{7#sVyzZ%qfo9T*K%r1^_w# BC@}y4 delta 89 zcmcbrzh9p(pO=@50SJoDr)5;yY~(Xyv~kxDElw>e)=$jHO-)HpOwo5qEzT~$;b_;EXl~v(=Vt5DJm_=%qiC2Jc-d+3;-fP9{K

=g-xmUe+xmubIUP zsF~!mbWL%THZ1$<==CcEyJv-_IEO|m$d9?zuVh)jPI7EGM6SelAk?@1cl?c z_kn}p5SZeyTyU6O6$XgH&ZPwVxv=2zCs91Xk$hMyr}(ht$?Jwm)6Q_oG^&!9diV|9 z12P~BrfoGmy34!Wf~`d)KUUKjkY=wV!PqH2l)5@E=B0T*-oKWkTDhpx)3!%^*iFu` zN75%RHm5VJ5&g2e>r@lF?9&+2bAe?krdc? zLSXmfQL@V3#INEclJZm4R8Q3Ac9-K+2^d2_}^jE zBPfSBQs9pOR`K*UI1KK9yI>QHfPEka#sNOou>C4A=^;t>V=5eLe_u>$JD>CMf1NL@ z91V8ZwH0RXQ-j^wqG1|Voo=$H$_QDvKPVsFmz`ju=-Zx7-;T8Zy*sL5@IMLhR(I_IQ6g(6!GAA!yy!BGVTNDJZ{u9o3wGg6+d-(9?&G&uZytkWm^1e>`YQ4SP z+r%e*Dp(2ru5X=;r1dMdNo_rARm^-bo7a=3ecP$c>f>hBbV|9i7_-3y=&yv(w|1PYB;MJtjy`vg5MSe+r==H~<_1M%ew(Fd1c2u@K(~ zU6;HDvZq-@Rpc=dwxpa{D$@j;RtI{LxJvY|`^0tNEw|#SWm&OdK;kMKPW|<5tlNTnVpjCs)}A?W-4lbb`Ga{4Qtw<*e<@ zl=63Jk~boI2)V}J9DVE4+*b#M@bYAu_M>1xK-*30i1z{taEINChDnaSh>np3_AUB= zEOIOM%#(5r+Bf$1a(Fxh_5rIv7QLvyA@1nx{>tB-Zy3XY&L-@oaga3GN8<=t=0A)to}4@7Agnq^4gRsI ze>cRf3jFuDPoZwIn~AM)8*C-9F?0^;i@-GCj=h3t5m*A2fd=4VtMV@PJXs;Pm_IfC E6FaNFrT_o{ diff --git a/pygad/utils/__pycache__/nsga2.cpython-310.pyc b/pygad/utils/__pycache__/nsga2.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f95b902c7b3663fe0d142179bd5b50b851402a2 GIT binary patch literal 7299 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 vPNsMo)umnTfQ^}WfAJv- zpY$%uo6aOJx(0AikTQm%qGCZXG$IdU>?0JTkBHb4cFFC44+J))VW)%7eO&FpejKms zGD*If#-9D~u3P-q-SJrOI~{C^J&2gv*`FcnC#x&EE~^dnv3buw$pY|E5DD_S!C3xf ziMfhIo|!1rOp1c$yw*VFo}Qm1VMn9!*mc<8#oi=SM7@FN3LS8qW|Wh|728oPL&b_R zO@}B72jpU9J%B$ast4p1)Fc71P@*PvdpE$S?)f_jDh!)}cPjwpB5D<*hyY%x72bH^ zoEBNwdG$zaHYb%wPf@Wz9ke<9kXPBRB1~t5jRLkJp{33^FIxc BmEiyY literal 0 HcmV?d00001 diff --git a/pygad/utils/__pycache__/parent_selection.cpython-310.pyc b/pygad/utils/__pycache__/parent_selection.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..491ba741c1657f376ed2ec7c603513cac8318feb GIT binary patch literal 14577 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 literal 0 HcmV?d00001 diff --git a/pygad/visualize/__pycache__/__init__.cpython-310.pyc b/pygad/visualize/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c367b5cc1c47805adcca3da71281298d99ffd67 GIT binary patch literal 259 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 Date: Sat, 5 Jul 2025 10:26:59 -0400 Subject: [PATCH 13/25] More tests --- .DS_Store | Bin 8196 -> 10244 bytes example2.py | 4 +- pygad/.DS_Store | Bin 0 -> 8196 bytes pygad/__pycache__/__init__.cpython-310.pyc | Bin 227 -> 0 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 79681 -> 0 bytes pygad/helper/.DS_Store | Bin 0 -> 6148 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 282 -> 0 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 15104 -> 0 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 14559 -> 0 bytes pygad/helper/misc.py | 10 +- pygad/helper/unique.py | 91 +++++--- pygad/pygad.py | 10 +- pygad/utils/.DS_Store | Bin 0 -> 6148 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 354 -> 0 bytes .../__pycache__/crossover.cpython-310.pyc | Bin 6101 -> 0 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 17743 -> 0 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 7299 -> 0 bytes .../parent_selection.cpython-310.pyc | Bin 14577 -> 0 bytes pygad/utils/crossover.py | 16 +- pygad/utils/mutation.py | 16 +- pygad/visualize/.DS_Store | Bin 0 -> 6148 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 259 -> 0 bytes .../__pycache__/plot.cpython-310.pyc | Bin 13852 -> 0 bytes test_gene_type.py | 214 ++++++++++++++++++ tests/test_gene_type.py | 214 ++++++++++++++++++ 25 files changed, 531 insertions(+), 44 deletions(-) create mode 100644 pygad/.DS_Store delete mode 100644 pygad/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/__pycache__/pygad.cpython-310.pyc create mode 100644 pygad/helper/.DS_Store delete mode 100644 pygad/helper/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/misc.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/unique.cpython-310.pyc create mode 100644 pygad/utils/.DS_Store delete mode 100644 pygad/utils/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/crossover.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/mutation.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/nsga2.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/parent_selection.cpython-310.pyc create mode 100644 pygad/visualize/.DS_Store delete mode 100644 pygad/visualize/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/visualize/__pycache__/plot.cpython-310.pyc create mode 100644 test_gene_type.py create mode 100644 tests/test_gene_type.py diff --git a/.DS_Store b/.DS_Store index e558d3d00f9ff8bdc903a1b088a6bd7b4b7c1fb6..64240825e515832f8bfe22b202781f0bfd9a3891 100644 GIT binary patch delta 240 zcmZp1XbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~50$SA%sU^hRb_+%b|Zb5E_RE7$M zM21|30)`wQ+i0?bs0b5N>gEoCx2)2F3?)FZVjvmMkPc+!0dYK#SIJN?xj;ai*B5Bf z$Nyl!z_7VUY%1f%hIV!#24@~ delta 106 zcmZn(XmOBWU|?W$DortDU;r^WfEYvza8E20o2aMAD7rCVH$S83WFCR;&1VJfv2K1V uHi=P?8K?vZB)EZuD@gUm!tczJ`Befr7$IgbOpfQ7y4hH?f)S{ppAi6078YIr diff --git a/example2.py b/example2.py index 7c2ed72..0bbcd75 100644 --- a/example2.py +++ b/example2.py @@ -28,12 +28,12 @@ def fitness_func(ga_instance, solution, solution_idx): allow_duplicate_genes=False, # gene_space=numpy.unique(numpy.random.uniform(1, 100, size=100)), gene_space=[range(0, 100), {"low": 0, "high": 100, 'step': 1}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], - gene_constraint=[lambda x: x[0]>=95,lambda x: x[1]>=95,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], + gene_constraint=[lambda x: x[0]>=70,lambda x: x[1]>=70,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], ) print(ga_instance.initial_population) -# ga_instance.run() +ga_instance.run() # print(ga_instance.gene_space_unpacked) # print(ga_instance.population) diff --git a/pygad/.DS_Store b/pygad/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8a926cf11cdeec628aff003864eb85651f231ff9 GIT binary patch literal 8196 zcmeHM&x_MQ6n@hmn}#A(P~2m{>!Mvz*kv!Jt_KfZtSF*VlQ!&zjY(mQ$R2xi>>#}AQ z+jA4B;7=6Mg!*{hqtO~$Iz$0cKok%KL;+FYZ%_cwY;BD#&wVp0wJ0D8{Fe&o{lP=g z*s-`Y$Xf?0P62>5bhDw2Jb-;XiyezggY1fHn(RSYS7lQSW!y3DX*#fDacR)FlQQn4 z?46a(P!!)Cejd|Fbqq=^3Wx&h3h3Ou$vJyONg;o~cp9gJAWj1?^q1-x%F$0Yw1}!S zfJahdeJio*bVB|nrg~js)|k0=MfIb^+o4kmDaHvyNaNz&E8*|B!7Llu^?V6NagrMR0H0%1~H~QU+G>wDHwjaEV!dbs{>v5V){3r>>><|q@ zP~N_Zl3_aQrqg7Yv2V`|xNgJkx9-g6`-km4@8Hp5&zm1U(9MSji-p^`efR#+^Zr@< zAxS@B`Y>hpIJ#lpuj1wTSCv*KJikXxA7BaFGFmxAchIV8y- zjSpcW`bc3>934p2&joUW$Mq0t!@^2p$@71;IY>hkxS9f0L*Lc;|C8JLM-Jy5kz$Kui;dSG%8 Nz%ocB3j9$8egOm*Q~Lk_ literal 0 HcmV?d00001 diff --git a/pygad/__pycache__/__init__.cpython-310.pyc b/pygad/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index a806e2b81e67ec11b18b41756b81ec76d7a3f16d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmd1j<>g`k0=Ca-8BRd@F^GcG?{L(7F4DurrhF=k1tCtD$dN$i;rK)P{ayU2_}AprC7xP6)5N` zq-U07lqM+@6y;~7CYKb)fHlUXr{<-WWF{x(q~{j_733CFmSp7T=|U9iCMK5vjfAO< jkI&4@EQycTE2zB1VUwGmQks)$2Xa&~3y|Po;9vp(wJ1Em diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc deleted file mode 100644 index 30531f3315210e50eefcc49ccfa8eac695eafbd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79681 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 RZ|T5%2pA#RBm=*|z&j>5H7WoA literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/__init__.cpython-310.pyc b/pygad/helper/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index dbca10c72f3f1a7f967cab21311c43ceb8412f0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmYjKL2AP=5R_y)gx2|kuRhqcJ(g1Xf?je67zg24!d9^*RgweFAM%iVAy4r&r+gu& ztP%oQ*x6Zjc1A50F9_QG7AjSW{frY)W^)7nPKk~QVG%R90HWkZT-f3=(w>JU6^h)WL8!MEg^ngs{4<0US z_F2M5k|m9B?XXF-QGdL4w!Ar&4VT=By6PHbK+x;awMu4paaPq1EFao?YDuY(!cm&* WahiYiiVYqA#t>#_05OX3oLm7Ma!TO< diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc deleted file mode 100644 index e07f6cd83500112a3cb79c51df3c2e08087f648a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15104 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 A4FCWD diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc deleted file mode 100644 index 97787498c12bf3454252376ecbfe5f919e842039..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14559 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 0: - new_solution, not_unique_indices, num_unsolved_duplicates = self.unique_genes_by_space(new_solution=new_solution, + new_solution, not_unique_indices, num_unsolved_duplicates = self.unique_genes_by_space(solution=new_solution, gene_type=gene_type, not_unique_indices=not_unique_indices, - num_trials=10, + sample_size=sample_size, + mutation_by_replacement=mutation_by_replacement, build_initial_pop=build_initial_pop) else: return new_solution, not_unique_indices, len(not_unique_indices) @@ -260,8 +261,11 @@ def select_unique_value(self, gene_values, solution, gene_index): values_to_select_from = list(set(list(gene_values)) - set(solution)) if len(values_to_select_from) == 0: + print("@@@@@@@@") + print(solution) + print(gene_values) # If there are no values, then keep the current gene value. - if not self.suppress_warnings: warnings.warn(f"'allow_duplicate_genes=False' but cannot find a unique value for the gene at index {gene_index}.") + if not self.suppress_warnings: warnings.warn(f"'allow_duplicate_genes=False' but cannot find a unique value for the gene at index {gene_index} with value {solution[gene_index]}.") selected_value = solution[gene_index] else: selected_value = random.choice(values_to_select_from) @@ -269,10 +273,11 @@ def select_unique_value(self, gene_values, solution, gene_index): return selected_value def unique_genes_by_space(self, - new_solution, + solution, gene_type, - not_unique_indices, - num_trials=10, + not_unique_indices, + mutation_by_replacement, + sample_size=100, build_initial_pop=False): """ @@ -280,10 +285,10 @@ def unique_genes_by_space(self, For each duplicate gene, a call is made to the `unique_gene_by_space()` function. Args: - new_solution (list): A solution containing genes with duplicate values. + solution (list): A solution containing genes with duplicate values. gene_type (type): The data type of the all the genes (e.g., int, float). not_unique_indices (list): The indices of genes with duplicate values. - num_trials (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers. + sample_size (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers. Returns: tuple: @@ -294,31 +299,33 @@ def unique_genes_by_space(self, num_unsolved_duplicates = 0 for duplicate_index in not_unique_indices: - temp_val = self.unique_gene_by_space(solution=new_solution, + temp_val = self.unique_gene_by_space(solution=solution, gene_idx=duplicate_index, gene_type=gene_type, - build_initial_pop=build_initial_pop, - num_trials=num_trials) + mutation_by_replacement=mutation_by_replacement, + sample_size=sample_size, + build_initial_pop=build_initial_pop) - if temp_val in new_solution: - # self.logger.info("temp_val, duplicate_index", temp_val, duplicate_index, new_solution) + if temp_val in solution: + # self.logger.info("temp_val, duplicate_index", temp_val, duplicate_index, solution) num_unsolved_duplicates = num_unsolved_duplicates + 1 - if not self.suppress_warnings: warnings.warn(f"Failed to find a unique value for gene with index {duplicate_index} whose value is {new_solution[duplicate_index]}. Consider adding more values in the gene space or use a wider range for initial population or random mutation.") + if not self.suppress_warnings: warnings.warn(f"Failed to find a unique value for gene with index {duplicate_index} whose value is {solution[duplicate_index]}. Consider adding more values in the gene space or use a wider range for initial population or random mutation.") else: - new_solution[duplicate_index] = temp_val + solution[duplicate_index] = temp_val # Update the list of duplicate indices after each iteration. - _, unique_gene_indices = numpy.unique(new_solution, return_index=True) - not_unique_indices = set(range(len(new_solution))) - set(unique_gene_indices) + _, unique_gene_indices = numpy.unique(solution, return_index=True) + not_unique_indices = set(range(len(solution))) - set(unique_gene_indices) - return new_solution, not_unique_indices, num_unsolved_duplicates + return solution, not_unique_indices, num_unsolved_duplicates def unique_gene_by_space(self, solution, gene_idx, - gene_type, - build_initial_pop=False, - num_trials=10): + gene_type, + mutation_by_replacement, + sample_size=100, + build_initial_pop=False): """ Returns a unique value for a specific gene based on its value space to resolve duplicates. @@ -327,15 +334,47 @@ def unique_gene_by_space(self, solution (list): A solution containing genes with duplicate values. gene_idx (int): The index of the gene that has a duplicate value. gene_type (type): The data type of the gene (e.g., int, float). - num_trials (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers. + sample_size (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers. Returns: Any: A unique value for the gene, if one exists; otherwise, the original gene value. """ + # When gene_value is None, this forces the gene value generators to select a value for use by the initial population. + # Otherwise, it considers selecting a value for mutation. + if build_initial_pop: + gene_value = None + else: + gene_value = solution[gene_idx] + if self.gene_constraint and self.gene_constraint[gene_idx]: + # A unique value is created out of the values that satisfy the constraint. + values = self.get_valid_gene_constraint_values(range_min=None, + range_max=None, + gene_value=gene_value, + gene_idx=gene_idx, + mutation_by_replacement=mutation_by_replacement, + solution=solution, + sample_size=sample_size) + # If there is no value satisfying the constraint, then return the current gene value. + if values is None: + return solution[gene_idx] + else: + pass + else: + # There is no constraint for the current gene. Return the same range. + values = self.generate_gene_value(range_min=None, + range_max=None, + gene_value=gene_value, + gene_idx=gene_idx, + mutation_by_replacement=mutation_by_replacement, + sample_size=sample_size) - return value_from_space + selected_value = self.select_unique_value(gene_values=values, + solution=solution, + gene_index=gene_idx) + + return selected_value def find_two_duplicates(self, solution, diff --git a/pygad/pygad.py b/pygad/pygad.py index 7accdfa..6960f67 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -469,7 +469,8 @@ def __init__(self, self.initial_population[initial_solution_idx], _, _ = self.solve_duplicate_genes_by_space(solution=initial_solution, gene_type=self.gene_type, sample_size=100, - mutation_by_replacement=True) + mutation_by_replacement=True, + build_initial_pop=True) # Change the data type and round all genes within the initial population. self.initial_population = self.change_population_dtype_and_round(initial_population) @@ -1382,7 +1383,7 @@ def initialize_population(self, # 4) Solve duplicates if not allowed. # Create an empty population. - self.population = numpy.zeros(shape=self.pop_size) + self.population = numpy.zeros(shape=self.pop_size, dtype=object) # 1) Create the initial population either randomly or using the gene space. if self.gene_space is None: @@ -1436,7 +1437,7 @@ def initialize_population(self, sample_size=100) if values_filtered is None: if not self.suppress_warnings: - warnings.warn(f"No value satisfied the constraint for the gene at index {gene_idx} while creating the initial population.") + warnings.warn(f"No value satisfied the constraint for the gene at index {gene_idx} with value {solution[gene_idx]} while creating the initial population.") else: self.population[sol_idx, gene_idx] = random.choice(values_filtered) @@ -1454,7 +1455,8 @@ def initialize_population(self, self.population[sol_idx], _, _ = self.solve_duplicate_genes_by_space(solution=self.population[solution_idx], gene_type=self.gene_type, sample_size=100, - mutation_by_replacement=True) + mutation_by_replacement=True, + build_initial_pop=True) # Keeping the initial population in the initial_population attribute. self.initial_population = self.population.copy() diff --git a/pygad/utils/.DS_Store b/pygad/utils/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a364f0c0bd6445a262c5f5901cffdae298ec52af GIT binary patch literal 6148 zcmeHKu};G<5Pc3s3JOCd7Dj&o609syRR#toCMIY@gd&9o5sJi?f8+!B6nN*eDw+gF zgsQvf{Orp;J9)O^7=W9t?@xg#fRrwn9IzNMxi3DjBu4g$F7#311-HCT@HEJ_z<*Rg z<}M3!R$+zj^Y?aHH^s7UmdudlaYaA>48|I8hyu@O(DG{Vijpmv_df5v$II}DHP^|4 zUXi&PW4W6iD`j4b2eQsa^FMm|M|_e6pQt2H)?~|CO)q{MH7R?&uxVxrm;$E24l2Ms zTP)2KtuzHp0aIY3fP5c3T`-T>DTYr6i*N)WP8bfxy8J8($4AT~c8a`1b4n#D)x{%* zQ##{5=H(GPMWw^V!-tC_yLduzVRZKILpofpXr(D&3T!K|r_HJC{}(@>|F=ojG6hV5 zol?L}=GXHXUn%abgO`)N*3<9kVv^S>ZYZpTt(dv86(7@sG47>8%p-P+jL_^yAjn{a JDe$KXd;wTAS1$kn literal 0 HcmV?d00001 diff --git a/pygad/utils/__pycache__/__init__.cpython-310.pyc b/pygad/utils/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index d55b00c9a1268d2a87884daecf7c3448c7ced177..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 354 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 Ch~BdR diff --git a/pygad/utils/__pycache__/nsga2.cpython-310.pyc b/pygad/utils/__pycache__/nsga2.cpython-310.pyc deleted file mode 100644 index 0f95b902c7b3663fe0d142179bd5b50b851402a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7299 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 vPNsMo)umnTfQ^}WfAJv- zpY$%uo6aOJx(0AikTQm%qGCZXG$IdU>?0JTkBHb4cFFC44+J))VW)%7eO&FpejKms zGD*If#-9D~u3P-q-SJrOI~{C^J&2gv*`FcnC#x&EE~^dnv3buw$pY|E5DD_S!C3xf ziMfhIo|!1rOp1c$yw*VFo}Qm1VMn9!*mc<8#oi=SM7@FN3LS8qW|Wh|728oPL&b_R zO@}B72jpU9J%B$ast4p1)Fc71P@*PvdpE$S?)f_jDh!)}cPjwpB5D<*hyY%x72bH^ zoEBNwdG$zaHYb%wPf@Wz9ke<9kXPBRB1~t5jRLkJp{33^FIxc BmEiyY diff --git a/pygad/utils/__pycache__/parent_selection.cpython-310.pyc b/pygad/utils/__pycache__/parent_selection.cpython-310.pyc deleted file mode 100644 index 491ba741c1657f376ed2ec7c603513cac8318feb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14577 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 diff --git a/pygad/utils/crossover.py b/pygad/utils/crossover.py index 8789135..aa954f0 100644 --- a/pygad/utils/crossover.py +++ b/pygad/utils/crossover.py @@ -74,7 +74,9 @@ def single_point_crossover(self, parents, offspring_size): else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring @@ -146,7 +148,9 @@ def two_points_crossover(self, parents, offspring_size): else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring def uniform_crossover(self, parents, offspring_size): @@ -213,7 +217,9 @@ def uniform_crossover(self, parents, offspring_size): else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring @@ -277,5 +283,7 @@ def scattered_crossover(self, parents, offspring_size): else: offspring[k], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[k], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring diff --git a/pygad/utils/mutation.py b/pygad/utils/mutation.py index dbb4a6a..d023365 100644 --- a/pygad/utils/mutation.py +++ b/pygad/utils/mutation.py @@ -79,7 +79,9 @@ def mutation_by_space(self, offspring): if self.allow_duplicate_genes == False: offspring[offspring_idx], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[offspring_idx], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring def mutation_probs_by_space(self, offspring): @@ -117,7 +119,9 @@ def mutation_probs_by_space(self, offspring): if self.allow_duplicate_genes == False: offspring[offspring_idx], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[offspring_idx], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring def mutation_process_gene_value(self, @@ -544,7 +548,9 @@ def adaptive_mutation_by_space(self, offspring): if self.allow_duplicate_genes == False: offspring[offspring_idx], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[offspring_idx], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring def adaptive_mutation_randomly(self, offspring): @@ -678,7 +684,9 @@ def adaptive_mutation_probs_by_space(self, offspring): if self.allow_duplicate_genes == False: offspring[offspring_idx], _, _ = self.solve_duplicate_genes_by_space(solution=offspring[offspring_idx], gene_type=self.gene_type, - num_trials=10) + sample_size=100, + mutation_by_replacement=self.mutation_by_replacement, + build_initial_pop=False) return offspring def adaptive_mutation_probs_randomly(self, offspring): diff --git a/pygad/visualize/.DS_Store b/pygad/visualize/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d6d23e9a0eb73b7451abf68d89614969ed97732f GIT binary patch literal 6148 zcmeHKF-`+P474Err&K)!M}G6zxW8l$m|3?dfTBRyB$>f?Ll==6r_L@kOERb3P=I&*|hm0QBev= z0V(jU0KX3nPV5SY#Q1a|@c7R48Igu@+$Dgm=3U{Ch!vP86_`}7D~2Z>`BrsZ;gFbg z^XQD{RA;Xnibr?kTa=r3iHcG{3XByv&gqi-|0VpF`~R4vnG}!$|4IR$t#8&Vyi)D0 wlb7RO+u--`FGFpVBa99Km~bmT-K%RHqpm9)5`&I>(1AJ#P#2jL_zML-0WRSuwg3PC literal 0 HcmV?d00001 diff --git a/pygad/visualize/__pycache__/__init__.cpython-310.pyc b/pygad/visualize/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 4c367b5cc1c47805adcca3da71281298d99ffd67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 259 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 Date: Sat, 5 Jul 2025 16:11:24 -0400 Subject: [PATCH 14/25] Fix selecting a unique value from gene space --- .DS_Store | Bin 10244 -> 10244 bytes example2.py | 52 +- pygad/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 205 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 0 -> 79860 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 260 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 0 -> 16011 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 0 -> 14713 bytes pygad/helper/misc.py | 27 +- pygad/helper/unique.py | 15 +- pygad/pygad.py | 274 +- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 332 bytes .../__pycache__/crossover.cpython-310.pyc | Bin 0 -> 6116 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 0 -> 17807 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 0 -> 7277 bytes .../parent_selection.cpython-310.pyc | Bin 0 -> 14555 bytes pygad/utils/mutation.py | 1 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 237 bytes .../__pycache__/plot.cpython-310.pyc | Bin 0 -> 13830 bytes test_adaptive_mutation.py | 2393 +++++++++++++++++ test_allow_duplicate_genes.py | 634 +++++ test_crossover_mutation.py | 287 ++ test_gene_space_allow_duplicate_genes.py | 1139 ++++++++ test_lifecycle_callbacks_calls.py | 247 ++ test_number_fitness_function_calls.py | 597 ++++ test_save_solutions.py | 1289 +++++++++ test_stop_criteria.py | 234 ++ tests/test_save_solutions.py | 2 +- 27 files changed, 7022 insertions(+), 169 deletions(-) create mode 100644 pygad/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/__pycache__/pygad.cpython-310.pyc create mode 100644 pygad/helper/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/helper/__pycache__/misc.cpython-310.pyc create mode 100644 pygad/helper/__pycache__/unique.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/crossover.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/mutation.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/nsga2.cpython-310.pyc create mode 100644 pygad/utils/__pycache__/parent_selection.cpython-310.pyc create mode 100644 pygad/visualize/__pycache__/__init__.cpython-310.pyc create mode 100644 pygad/visualize/__pycache__/plot.cpython-310.pyc create mode 100644 test_adaptive_mutation.py create mode 100644 test_allow_duplicate_genes.py create mode 100644 test_crossover_mutation.py create mode 100644 test_gene_space_allow_duplicate_genes.py create mode 100644 test_lifecycle_callbacks_calls.py create mode 100644 test_number_fitness_function_calls.py create mode 100644 test_save_solutions.py create mode 100644 test_stop_criteria.py diff --git a/.DS_Store b/.DS_Store index 64240825e515832f8bfe22b202781f0bfd9a3891..24f42ca2d5ca16fd106016873d8cb448533b0e22 100644 GIT binary patch delta 1184 zcmZn(XbG6$&nUYwU^hRb>|`E+4Qz~-3=E7hn-2-Nv*=1PlrW?+6a&e4hD3%GAT9v1 zGa1T&qVWv545dI`B3LA!A#ZY-sH&+fkcZHb!;r&}&rl9jn*vl@0OVydB!hK;^rr(= z=7CgCULl~WFNLHtnV|@%uNVmP!FCm4vF(JI>SRt)TQ-KvK=*!}JVDf8a*e1k@5ldO zzyJaoljn%a`+}TKq(d0=fR3#6%*jtq%E?axnjio)I}&6*%+$#%#7yd$Qjvv#$`p~@ z4)S9f&{xR}l|Y;WWW|I1401ve&?(6b*+73G#EP+5%HYeuz|;)06ljAgk`48Fz`)D} z8kPzSrg)%bnLtT!K!OcU1Ip$BtpP<8Fj@#&1q?qFtHhA3Dh4_j64{`r$N@)qWT!3>oQ#SA43sSLRc`V3(V`3yM>r9gHnP*k76g&`3rmk4Bq z0{N*7MPSiHh77QZ6oz!5TnbnnNPRX?Z9YQ*knavun+L=t44Dkc430oE(}6NYKwb%h zC`fYv(ApB9G5J7c`ap*WO`anrFQ^C9TnTiZAJlnZQC6_%WDaqY$v;E|C*PFd-n>h~ UmwjWyA;!(@3QVlS*_%9!068mdtpET3 diff --git a/example2.py b/example2.py index 0bbcd75..7383b7c 100644 --- a/example2.py +++ b/example2.py @@ -1,35 +1,24 @@ import pygad import numpy +import random -function_inputs = [4,-2,3.5,5,-11,-4.7] -desired_output = 44 +def fitness_func(ga, solution, idx): + return random.random() -def fitness_func(ga_instance, solution, solution_idx): - output = numpy.sum(solution*function_inputs) - fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001) - return fitness - -num_genes = len(function_inputs) - -ga_instance = pygad.GA(num_generations=100, - num_parents_mating=10, - sol_per_pop=20, - num_genes=num_genes, - mutation_num_genes=6, - fitness_func=fitness_func, - init_range_low=1, - init_range_high=100, - # suppress_warnings=True, +ga_instance = pygad.GA(num_generations=1, + num_parents_mating=5, + sol_per_pop=10, + num_genes=10, + random_seed=123, + # mutation_type=None, + # crossover_type=None, random_mutation_min_val=1, random_mutation_max_val=100, - mutation_by_replacement=True, - gene_type=[float, 1], - save_solutions=True, + fitness_func=fitness_func, + gene_space=[30, None, 40, 50, None, 60, 70, None, None, None], + gene_type=int, allow_duplicate_genes=False, - # gene_space=numpy.unique(numpy.random.uniform(1, 100, size=100)), - gene_space=[range(0, 100), {"low": 0, "high": 100, 'step': 1}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], - gene_constraint=[lambda x: x[0]>=70,lambda x: x[1]>=70,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], - ) + save_solutions=True) print(ga_instance.initial_population) @@ -37,3 +26,16 @@ def fitness_func(ga_instance, solution, solution_idx): # print(ga_instance.gene_space_unpacked) # print(ga_instance.population) + +""" +gene_space=[[0, 0], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], +""" diff --git a/pygad/__pycache__/__init__.cpython-310.pyc b/pygad/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a939de9e2e698a7f89fc506e58abc4558a6692ed GIT binary patch literal 205 zcmd1j<>g`kg6+F^GcG?{L(7F4DurrhF=k1tCtD$dN$i;rK)P{ayU2_}9y=!X^qCG-lIYq X;;_lhPbtkwwF6mS%mO4h7&w>!VCOT1 literal 0 HcmV?d00001 diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f65c82877625b9ca5ff5549f774de1f75f5d3d6 GIT binary patch literal 79860 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 YpY{L9C&2-+={NkdPB&9rtDIc_fA%lhZ~y=R literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/__init__.cpython-310.pyc b/pygad/helper/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b92603d2b45f76f85219da6837cef6d3d8aed1e0 GIT binary patch literal 260 zcmYjJK~BRk5VW17sv!A8uerp)6@*#|skaIVxa4A0^(wZA?O;1dffw0_n=;J|0Z(@hGvZuR!EXa+7m-tN|R=&S<6soZ6@;bJyJL>bxz7soTp&K|g@^sorNu^R*D4;DdHnh#%sM6eK!s`CGt% z@09_GulxzZdvpWef`zCLo4#{iM`fO;sGQ~h{rud|h0s<;M1& literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1a1090339cce542c6c646c8ec72c6b5da7524d7 GIT binary patch literal 16011 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 Cip3oO literal 0 HcmV?d00001 diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85a309de1f512292eff5678e0244ad6d7f8fae20 GIT binary patch literal 14713 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 D5SIny literal 0 HcmV?d00001 diff --git a/pygad/helper/misc.py b/pygad/helper/misc.py index db462af..f9a26e8 100644 --- a/pygad/helper/misc.py +++ b/pygad/helper/misc.py @@ -216,6 +216,7 @@ def get_initial_population_range(self, gene_index): def generate_gene_value_from_space(self, gene_idx, mutation_by_replacement, + solution=None, gene_value=None, sample_size=1): """ @@ -223,6 +224,7 @@ def generate_gene_value_from_space(self, It accepts: -gene_idx: The index of the gene in the solution. -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + -solution (iterable, optional): The solution where we need to generate a gene. Needed if you are selecting a single value (sample_size=1) to select a value that respects the allow_duplicate_genes parameter instead of selecting a value randomly. If None, then the gene value is selected randomly. -gene_value (int, optional): The original gene value before applying mutation. Needed if you are calling this method to apply mutation. If None, then a sample is created from the gene space without being summed to the gene value. -sample_size (int, optional): The number of random values to generate. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For float data types, a None value returns only a single value. @@ -314,10 +316,13 @@ def generate_gene_value_from_space(self, high=self.gene_space['high'], size=sample_size) else: - # Change the data type and round the generated values. - # Pass a copy of the gene_space to avoid changing its value. + curr_gene_space = list(self.gene_space).copy() + for idx in range(len(curr_gene_space)): + if curr_gene_space[idx] is None: + curr_gene_space[idx] = numpy.random.uniform(low=range_min, + high=range_max) curr_gene_space = self.change_gene_dtype_and_round(gene_index=gene_idx, - gene_value=self.gene_space) + gene_value=curr_gene_space) if gene_value is None: # Just generate the value(s) without being added to the gene value specially when initializing the population. @@ -334,8 +339,19 @@ def generate_gene_value_from_space(self, # After removing the current gene value from the space, there are no more values. # Then keep the current gene value. value_from_space = gene_value + if sample_size > 1: + value_from_space = numpy.array([gene_value]) elif sample_size == 1: - value_from_space = random.choice(value_from_space) + if self.allow_duplicate_genes == True: + # Select a value randomly from the current gene space. + value_from_space = random.choice(value_from_space) + else: + # We must check if the selected value will respect the allow_duplicate_genes parameter. + # Instead of selecting a value randomly, we have to select a value that will be unique if allow_duplicate_genes=False. + # Only select a value from the current gene space that is, hopefully, unique. + value_from_space = self.select_unique_value(gene_values=value_from_space, + solution=solution, + gene_index=gene_idx) # The gene space might be [None, 1, 7]. # It might happen that the value None is selected. @@ -425,6 +441,7 @@ def generate_gene_value(self, gene_value, gene_idx, mutation_by_replacement, + solution=None, range_min=None, range_max=None, sample_size=1, @@ -435,6 +452,7 @@ def generate_gene_value(self, -gene_value: The original gene value before applying mutation. -gene_idx: The index of the gene in the solution. -mutation_by_replacement: A flag indicating whether mutation by replacement is enabled or not. The reason is to make this helper method usable while generating the initial population. In this case, mutation_by_replacement does not matter and should be considered False. + -solution (iterable, optional): The solution where we need to generate a gene. Needed if you are selecting a single value (sample_size=1) to select a value that respects the allow_duplicate_genes parameter instead of selecting a value randomly. If None, then the gene value is selected randomly. -range_min (int, optional): The minimum value in the range from which a value is selected. It must be passed for generating the gene value randomly because we cannot decide whether it is the range for the initial population (init_range_low and init_range_high) or mutation (random_mutation_min_val and random_mutation_max_val). -range_max (int, optional): The maximum value in the range from which a value is selected. It must be passed for generating the gene value randomly because we cannot decide whether it is the range for the initial population (init_range_low and init_range_high) or mutation (random_mutation_min_val and random_mutation_max_val). -sample_size: The number of random values to generate/select and return. It tries to generate a number of values up to a maximum of sample_size. But it is not always guaranteed because the total number of values might not be enough or the random generator creates duplicate random values. For int data types, it could be None to keep all the values. For float data types, a None value returns only a single value. @@ -456,6 +474,7 @@ def generate_gene_value(self, output = self.generate_gene_value_from_space(gene_value=gene_value, gene_idx=gene_idx, mutation_by_replacement=mutation_by_replacement, + solution=solution, sample_size=sample_size) return output diff --git a/pygad/helper/unique.py b/pygad/helper/unique.py index 6cbd44c..327a8bb 100644 --- a/pygad/helper/unique.py +++ b/pygad/helper/unique.py @@ -120,7 +120,7 @@ def solve_duplicate_genes_by_space(self, build_initial_pop=build_initial_pop) else: return new_solution, not_unique_indices, len(not_unique_indices) - + # DEEP-DUPLICATE-REMOVAL-NEEDED # Search by this phrase to find where deep duplicates removal should be applied. # If there exist duplicate genes, then changing either of the 2 duplicating genes (with indices 2 and 3) will not solve the problem. @@ -179,6 +179,7 @@ def unique_int_gene_from_range(self, range_max=max_val, gene_value=solution[gene_index], gene_idx=gene_index, + solution=solution, mutation_by_replacement=mutation_by_replacement, sample_size=None, step=step) @@ -237,6 +238,7 @@ def unique_float_gene_from_range(self, range_max=max_val, gene_value=solution[gene_index], gene_idx=gene_index, + solution=solution, mutation_by_replacement=mutation_by_replacement, sample_size=sample_size) @@ -253,17 +255,15 @@ def select_unique_value(self, gene_values, solution, gene_index): Args: gene_values (NumPy Array): An array of values from which a unique value should be selected. solution (list): A solution containing genes, potentially with duplicate values. + gene_index (int): The index of the gene for which to find a unique value. Returns: selected_gene: The new (hopefully unique) value of the gene. If no unique value can be found, the original gene value is returned. """ values_to_select_from = list(set(list(gene_values)) - set(solution)) - + if len(values_to_select_from) == 0: - print("@@@@@@@@") - print(solution) - print(gene_values) # If there are no values, then keep the current gene value. if not self.suppress_warnings: warnings.warn(f"'allow_duplicate_genes=False' but cannot find a unique value for the gene at index {gene_index} with value {solution[gene_index]}.") selected_value = solution[gene_index] @@ -367,6 +367,7 @@ def unique_gene_by_space(self, range_max=None, gene_value=gene_value, gene_idx=gene_idx, + solution=solution, mutation_by_replacement=mutation_by_replacement, sample_size=sample_size) @@ -438,6 +439,10 @@ def unpack_gene_space(self, if self.gene_type_single == True: # Change the data type. + for idx in range(len(gene_space_unpacked)): + if gene_space_unpacked[idx] is None: + gene_space_unpacked[idx] = numpy.random.uniform(low=range_min, + high=range_max) gene_space_unpacked = numpy.array(gene_space_unpacked, dtype=self.gene_type[0]) if not self.gene_type[1] is None: diff --git a/pygad/pygad.py b/pygad/pygad.py index 6960f67..727e9e4 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -73,7 +73,7 @@ def __init__(self, num_parents_mating: Number of solutions to be selected as parents in the mating pool. fitness_func: Accepts a function/method and returns the fitness value of the solution. In PyGAD 2.20.0, a third parameter is passed referring to the 'pygad.GA' instance. If method, then it must accept 4 parameters where the fourth one refers to the method's object. - fitness_batch_size: Added in PyGAD 2.19.0. Supports calculating the fitness in batches. If the value is 1 or None, then the fitness function is called for each invidiaul solution. If given another value X where X is neither 1 nor None (e.g. X=3), then the fitness function is called once for each X (3) solutions. + fitness_batch_size: Added in PyGAD 2.19.0. Supports calculating the fitness in batches. If the value is 1 or None, then the fitness function is called for each individual solution. If given another value X where X is neither 1 nor None (e.g. X=3), then the fitness function is called once for each X (3) solutions. initial_population: A user-defined initial population. It is useful when the user wants to start the generations with a custom initial population. It defaults to None which means no initial population is specified by the user. In this case, PyGAD creates an initial population using the 'sol_per_pop' and 'num_genes' parameters. An exception is raised if the 'initial_population' is None while any of the 2 parameters ('sol_per_pop' or 'num_genes') is also None. sol_per_pop: Number of solutions in the population. @@ -81,20 +81,20 @@ def __init__(self, init_range_low: The lower value of the random range from which the gene values in the initial population are selected. It defaults to -4. Available in PyGAD 1.0.20 and higher. init_range_high: The upper value of the random range from which the gene values in the initial population are selected. It defaults to -4. Available in PyGAD 1.0.20. - # It is OK to set the value of any of the 2 parameters ('init_range_low' and 'init_range_high') to be equal, higher or lower than the other parameter (i.e. init_range_low is not needed to be lower than init_range_high). + # It is OK to set the value of the 2 parameters ('init_range_low' and 'init_range_high') to be equal, higher or lower than the other parameter (i.e. init_range_low is not needed to be lower than init_range_high). gene_type: The type of the gene. It is assigned to any of these types (int, numpy.int8, numpy.int16, numpy.int32, numpy.int64, numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64, float, numpy.float16, numpy.float32, numpy.float64) and forces all the genes to be of that type. parent_selection_type: Type of parent selection. - keep_parents: If 0, this means no parent in the current population will be used in the next population. If -1, this means all parents in the current population will be used in the next population. If set to a value > 0, then the specified value refers to the number of parents in the current population to be used in the next population. Some parent selection operators such as rank selection, favor population diversity and therefore keeping the parents in the next generation can be beneficial. However, some other parent selection operators, such as roulette wheel selection (RWS), have higher selection pressure and keeping more than one parent in the next generation can seriously harm population diversity. This parameter have an effect only when the keep_elitism parameter is 0. Thanks to Prof. Fernando Jiménez Barrionuevo (http://webs.um.es/fernan) for editing this sentence. + keep_parents: If 0, this means no parent in the current population will be used in the next population. If -1, this means all parents in the current population will be used in the next population. If set to a value > 0, then the specified value refers to the number of parents in the current population to be used in the next population. Some parent selection operators such as rank selection, favor population diversity and therefore keeping the parents in the next generation can be beneficial. However, some other parent selection operators, such as roulette wheel selection (RWS), have higher selection pressure and keeping more than one parent in the next generation can seriously harm population diversity. This parameter have an effect only when the keep_elitism parameter is 0. Thanks to Prof. Fernando Jiménez (http://webs.um.es/fernan) for editing this sentence. K_tournament: When the value of 'parent_selection_type' is 'tournament', the 'K_tournament' parameter specifies the number of solutions from which a parent is selected randomly. - keep_elitism: Added in PyGAD 2.18.0. It can take the value 0 or a positive integer that satisfies (0 <= keep_elitism <= sol_per_pop). It defaults to 1 which means only the best solution in the current generation is kept in the next generation. If assigned 0, this means it has no effect. If assigned a positive integer K, then the best K solutions are kept in the next generation. It cannot be assigned a value greater than the value assigned to the sol_per_pop parameter. If this parameter has a value different than 0, then the keep_parents parameter will have no effect. + keep_elitism: Added in PyGAD 2.18.0. It can take the value 0 or a positive integer that satisfies (0 <= keep_elitism <= sol_per_pop). It defaults to 1 which means only the best solution in the current generation is kept in the next generation. If assigned 0, this means it has no effect. If assigned a positive integer K, then the best K solutions are kept in the next generation. It cannot be assigned a value greater than the value assigned to the sol_per_pop parameter. If this parameter has a value different from 0, then the keep_parents parameter will have no effect. - crossover_type: Type of the crossover opreator. If crossover_type=None, then the crossover step is bypassed which means no crossover is applied and thus no offspring will be created in the next generations. The next generation will use the solutions in the current population. + crossover_type: Type of the crossover operator. If crossover_type=None, then the crossover step is bypassed which means no crossover is applied and thus no offspring will be created in the next generations. The next generation will use the solutions in the current population. crossover_probability: The probability of selecting a solution for the crossover operation. If the solution probability is <= crossover_probability, the solution is selected. The value must be between 0 and 1 inclusive. - mutation_type: Type of the mutation opreator. If mutation_type=None, then the mutation step is bypassed which means no mutation is applied and thus no changes are applied to the offspring created using the crossover operation. The offspring will be used unchanged in the next generation. + mutation_type: Type of the mutation operator. If mutation_type=None, then the mutation step is bypassed which means no mutation is applied and thus no changes are applied to the offspring created using the crossover operation. The offspring will be used unchanged in the next generation. mutation_probability: The probability of selecting a gene for the mutation operation. If the gene probability is <= mutation_probability, the gene is selected. It accepts either a single value for fixed mutation or a list/tuple/numpy.ndarray of 2 values for adaptive mutation. The values must be between 0 and 1 inclusive. If specified, then no need for the 2 parameters mutation_percent_genes and mutation_num_genes. mutation_by_replacement: An optional bool parameter. It works only when the selected type of mutation is random (mutation_type="random"). In this case, setting mutation_by_replacement=True means replace the gene by the randomly generated value. If False, then it has no effect and random mutation works by adding the random value to the gene. @@ -108,13 +108,13 @@ def __init__(self, gene_constraint: It accepts a list of constraints for the genes. Each constraint is a Python function. Added in PyGAD 3.5.0. - on_start: Accepts a function/method to be called only once before the genetic algorithm starts its evolution. If function, then it must accept a single parameter representing the instance of the genetic algorithm. If method, then it must accept 2 parameters where the second one refers to the method's object. Added in PyGAD 2.6.0. - on_fitness: Accepts a function/method to be called after calculating the fitness values of all solutions in the population. If function, then it must accept 2 parameters: 1) a list of all solutions' fitness values 2) the instance of the genetic algorithm. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. - on_parents: Accepts a function/method to be called after selecting the parents that mates. If function, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one represents the selected parents. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. - on_crossover: Accepts a function/method to be called each time the crossover operation is applied. If function, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one represents the offspring generated using crossover. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. - on_mutation: Accepts a function/method to be called each time the mutation operation is applied. If function, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one represents the offspring after applying the mutation. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. - on_generation: Accepts a function/method to be called after each generation. If function, then it must accept a single parameter representing the instance of the genetic algorithm. If the function returned "stop", then the run() method stops without completing the other generations. If method, then it must accept 2 parameters where the second one refers to the method's object. Added in PyGAD 2.6.0. - on_stop: Accepts a function/method to be called only once exactly before the genetic algorithm stops or when it completes all the generations. If function, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one is a list of fitness values of the last population's solutions. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. + on_start: Accepts a function/method to be called only once before the genetic algorithm starts its evolution. If functioned, then it must accept a single parameter representing the instance of the genetic algorithm. If method, then it must accept 2 parameters where the second one refers to the method's object. Added in PyGAD 2.6.0. + on_fitness: Accepts a function/method to be called after calculating the fitness values of all solutions in the population. If functioned, then it must accept 2 parameters: 1) a list of all solutions' fitness values 2) the instance of the genetic algorithm. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. + on_parents: Accepts a function/method to be called after selecting the parents that mates. If functioned, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one represents the selected parents. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. + on_crossover: Accepts a function/method to be called each time the crossover operation is applied. If functioned, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one represents the offspring generated using crossover. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. + on_mutation: Accepts a function/method to be called each time the mutation operation is applied. If functioned, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one represents the offspring after applying the mutation. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. + on_generation: Accepts a function/method to be called after each generation. If functioned, then it must accept a single parameter representing the instance of the genetic algorithm. If the function returned "stop", then the run() method stops without completing the other generations. If method, then it must accept 2 parameters where the second one refers to the method's object. Added in PyGAD 2.6.0. + on_stop: Accepts a function/method to be called only once exactly before the genetic algorithm stops or when it completes all the generations. If functioned, then it must accept 2 parameters: the first one represents the instance of the genetic algorithm and the second one is a list of fitness values of the last population's solutions. If method, then it must accept 3 parameters where the third one refers to the method's object. Added in PyGAD 2.6.0. save_best_solutions: Added in PyGAD 2.9.0 and its type is bool. If True, then the best solution in each generation is saved into the 'best_solutions' attribute. Use this parameter with caution as it may cause memory overflow when either the number of generations or the number of genes is large. save_solutions: Added in PyGAD 2.15.0 and its type is bool. If True, then all solutions in each generation are saved into the 'solutions' attribute. Use this parameter with caution as it may cause memory overflow when either the number of generations, number of genes, or number of solutions in population is large. @@ -175,7 +175,7 @@ def __init__(self, numpy.random.seed(self.random_seed) random.seed(self.random_seed) - # If suppress_warnings is bool and its valud is False, then print warning messages. + # If suppress_warnings is bool and its value is False, then print warning messages. if type(suppress_warnings) is bool: self.suppress_warnings = suppress_warnings else: @@ -288,13 +288,6 @@ def __init__(self, self.valid_parameters = False raise TypeError(f"Type mismatch between the 2 parameters 'init_range_low' {type(init_range_low)} and 'init_range_high' {type(init_range_high)}.") elif type(init_range_low) in [list, tuple, numpy.ndarray]: - # The self.num_genes attribute is not created yet. - # if len(init_range_low) == self.num_genes: - # pass - # else: - # self.valid_parameters = False - # raise ValueError(f"The length of the 'init_range_low' parameter is {len(init_range_low)} which is different from the number of genes {self.num_genes}.") - # Get the number of genes before validating the num_genes parameter. if num_genes is None: if initial_population is None: @@ -410,7 +403,7 @@ def __init__(self, if initial_population is None: if (sol_per_pop is None) or (num_genes is None): self.valid_parameters = False - raise TypeError("Error creating the initial population:\n\nWhen the parameter 'initial_population' is None, then the 2 parameters 'sol_per_pop' and 'num_genes' cannot be None too.\nThere are 2 options to prepare the initial population:\n1) Assinging the initial population to the 'initial_population' parameter. In this case, the values of the 2 parameters sol_per_pop and num_genes will be deduced.\n2) Assign integer values to the 'sol_per_pop' and 'num_genes' parameters so that PyGAD can create the initial population automatically.") + raise TypeError("Error creating the initial population:\n\nWhen the parameter 'initial_population' is None, then the 2 parameters 'sol_per_pop' and 'num_genes' cannot be None too.\nThere are 2 options to prepare the initial population:\n1) Assigning the initial population to the 'initial_population' parameter. In this case, the values of the 2 parameters sol_per_pop and num_genes will be deduced.\n2) Assign integer values to the 'sol_per_pop' and 'num_genes' parameters so that PyGAD can create the initial population automatically.") elif (type(sol_per_pop) is int) and (type(num_genes) is int): # Validating the number of solutions in the population (sol_per_pop) if sol_per_pop <= 0: @@ -455,6 +448,9 @@ def __init__(self, self.valid_parameters = False raise TypeError(f"The values in the initial population can be integers or floats but the value ({initial_population[row_idx][col_idx]}) of type {type(initial_population[row_idx][col_idx])} found.") + # Change the data type and round all genes within the initial population. + self.initial_population = self.change_population_dtype_and_round(initial_population) + # Check if duplicates are allowed. If not, then solve any existing duplicates in the passed initial population. if self.allow_duplicate_genes == False: for initial_solution_idx, initial_solution in enumerate(self.initial_population): @@ -472,9 +468,6 @@ def __init__(self, mutation_by_replacement=True, build_initial_pop=True) - # Change the data type and round all genes within the initial population. - self.initial_population = self.change_population_dtype_and_round(initial_population) - # A NumPy array holding the initial population. self.population = self.initial_population.copy() # Number of genes in the solution. @@ -589,7 +582,7 @@ def __init__(self, if crossover_type is None: self.crossover = None elif inspect.ismethod(crossover_type): - # Check if the crossover_type is a method that accepts 4 paramaters. + # Check if the crossover_type is a method that accepts 4 parameters. if crossover_type.__code__.co_argcount == 4: # The crossover method assigned to the crossover_type parameter is validated. self.crossover = crossover_type @@ -597,7 +590,7 @@ def __init__(self, self.valid_parameters = False raise ValueError(f"When 'crossover_type' is assigned to a method, then this crossover method must accept 4 parameters:\n1) Expected to be the 'self' object.\n2) The selected parents.\n3) The size of the offspring to be produced.\n4) The instance from the pygad.GA class.\n\nThe passed crossover method named '{crossover_type.__code__.co_name}' accepts {crossover_type.__code__.co_argcount} parameter(s).") elif callable(crossover_type): - # Check if the crossover_type is a function that accepts 2 paramaters. + # Check if the crossover_type is a function that accepts 2 parameters. if crossover_type.__code__.co_argcount == 3: # The crossover function assigned to the crossover_type parameter is validated. self.crossover = crossover_type @@ -609,13 +602,13 @@ def __init__(self, raise TypeError(f"The expected type of the 'crossover_type' parameter is either callable or str but {type(crossover_type)} found.") else: # type crossover_type is str crossover_type = crossover_type.lower() - if (crossover_type == "single_point"): + if crossover_type == "single_point": self.crossover = self.single_point_crossover - elif (crossover_type == "two_points"): + elif crossover_type == "two_points": self.crossover = self.two_points_crossover - elif (crossover_type == "uniform"): + elif crossover_type == "uniform": self.crossover = self.uniform_crossover - elif (crossover_type == "scattered"): + elif crossover_type == "scattered": self.crossover = self.scattered_crossover else: self.valid_parameters = False @@ -627,7 +620,7 @@ def __init__(self, if crossover_probability is None: self.crossover_probability = None elif type(crossover_probability) in GA.supported_int_float_types: - if crossover_probability >= 0 and crossover_probability <= 1: + if 0 <= crossover_probability <= 1: self.crossover_probability = crossover_probability else: self.valid_parameters = False @@ -642,7 +635,7 @@ def __init__(self, if mutation_type is None: self.mutation = None elif inspect.ismethod(mutation_type): - # Check if the mutation_type is a method that accepts 3 paramater. + # Check if the mutation_type is a method that accepts 3 parameters. if (mutation_type.__code__.co_argcount == 3): # The mutation method assigned to the mutation_type parameter is validated. self.mutation = mutation_type @@ -650,7 +643,7 @@ def __init__(self, self.valid_parameters = False raise ValueError(f"When 'mutation_type' is assigned to a method, then it must accept 3 parameters:\n1) Expected to be the 'self' object.\n2) The offspring to be mutated.\n3) The instance from the pygad.GA class.\n\nThe passed mutation method named '{mutation_type.__code__.co_name}' accepts {mutation_type.__code__.co_argcount} parameter(s).") elif callable(mutation_type): - # Check if the mutation_type is a function that accepts 2 paramater. + # Check if the mutation_type is a function that accepts 2 parameters. if (mutation_type.__code__.co_argcount == 2): # The mutation function assigned to the mutation_type parameter is validated. self.mutation = mutation_type @@ -662,15 +655,15 @@ def __init__(self, raise TypeError(f"The expected type of the 'mutation_type' parameter is either callable or str but {type(mutation_type)} found.") else: # type mutation_type is str mutation_type = mutation_type.lower() - if (mutation_type == "random"): + if mutation_type == "random": self.mutation = self.random_mutation - elif (mutation_type == "swap"): + elif mutation_type == "swap": self.mutation = self.swap_mutation - elif (mutation_type == "scramble"): + elif mutation_type == "scramble": self.mutation = self.scramble_mutation - elif (mutation_type == "inversion"): + elif mutation_type == "inversion": self.mutation = self.inversion_mutation - elif (mutation_type == "adaptive"): + elif mutation_type == "adaptive": self.mutation = self.adaptive_mutation else: self.valid_parameters = False @@ -682,10 +675,10 @@ def __init__(self, if not (self.mutation_type is None): if mutation_probability is None: self.mutation_probability = None - elif (mutation_type != "adaptive"): + elif mutation_type != "adaptive": # Mutation probability is fixed not adaptive. if type(mutation_probability) in GA.supported_int_float_types: - if mutation_probability >= 0 and mutation_probability <= 1: + if 0 <= mutation_probability <= 1: self.mutation_probability = mutation_probability else: self.valid_parameters = False @@ -699,7 +692,7 @@ def __init__(self, if len(mutation_probability) == 2: for el in mutation_probability: if type(el) in GA.supported_int_float_types: - if el >= 0 and el <= 1: + if 0 <= el <= 1: pass else: self.valid_parameters = False @@ -709,7 +702,7 @@ def __init__(self, raise TypeError(f"Unexpected type for a value assigned to the 'mutation_probability' parameter. A numeric value is expected but ({el}) of type {type(el)} found.") if mutation_probability[0] < mutation_probability[1]: if not self.suppress_warnings: - warnings.warn(f"The first element in the 'mutation_probability' parameter is {mutation_probability[0]} which is smaller than the second element {mutation_probability[1]}. This means the mutation rate for the high-quality solutions is higher than the mutation rate of the low-quality ones. This causes high disruption in the high qualitiy solutions while making little changes in the low quality solutions. Please make the first element higher than the second element.") + warnings.warn(f"The first element in the 'mutation_probability' parameter is {mutation_probability[0]} which is smaller than the second element {mutation_probability[1]}. This means the mutation rate for the high-quality solutions is higher than the mutation rate of the low-quality ones. This causes high disruption in the high quality solutions while making little changes in the low quality solutions. Please make the first element higher than the second element.") self.mutation_probability = mutation_probability else: self.valid_parameters = False @@ -724,7 +717,7 @@ def __init__(self, if not (self.mutation_type is None): if mutation_num_genes is None: # The mutation_num_genes parameter does not exist. Checking whether adaptive mutation is used. - if (mutation_type != "adaptive"): + if mutation_type != "adaptive": # The percent of genes to mutate is fixed not adaptive. if mutation_percent_genes == 'default'.lower(): mutation_percent_genes = 10 @@ -740,7 +733,7 @@ def __init__(self, mutation_num_genes = 1 elif type(mutation_percent_genes) in GA.supported_int_float_types: - if (mutation_percent_genes <= 0 or mutation_percent_genes > 100): + if mutation_percent_genes <= 0 or mutation_percent_genes > 100: self.valid_parameters = False raise ValueError(f"The percentage of selected genes for mutation (mutation_percent_genes) must be > 0 and <= 100 but ({mutation_percent_genes}) found.\n") else: @@ -768,7 +761,7 @@ def __init__(self, mutation_percent_genes, dtype=numpy.uint32) for idx, el in enumerate(mutation_percent_genes): if type(el) in GA.supported_int_float_types: - if (el <= 0 or el > 100): + if el <= 0 or el > 100: self.valid_parameters = False raise ValueError(f"The values assigned to the 'mutation_percent_genes' must be > 0 and <= 100 but ({mutation_percent_genes}) found.\n") else: @@ -785,8 +778,8 @@ def __init__(self, mutation_num_genes[idx] = 1 if mutation_percent_genes[0] < mutation_percent_genes[1]: if not self.suppress_warnings: - warnings.warn(f"The first element in the 'mutation_percent_genes' parameter is ({mutation_percent_genes[0]}) which is smaller than the second element ({mutation_percent_genes[1]}).\nThis means the mutation rate for the high-quality solutions is higher than the mutation rate of the low-quality ones. This causes high disruption in the high qualitiy solutions while making little changes in the low quality solutions.\nPlease make the first element higher than the second element.") - # At this point outside the loop, all values of the parameter 'mutation_percent_genes' are validated. Eveyrthing is OK. + warnings.warn(f"The first element in the 'mutation_percent_genes' parameter is ({mutation_percent_genes[0]}) which is smaller than the second element ({mutation_percent_genes[1]}).\nThis means the mutation rate for the high-quality solutions is higher than the mutation rate of the low-quality ones. This causes high disruption in the high quality solutions while making little changes in the low quality solutions.\nPlease make the first element higher than the second element.") + # At this point outside the loop, all values of the parameter 'mutation_percent_genes' are validated. Everything is OK. else: self.valid_parameters = False raise ValueError(f"When mutation_type='adaptive', then the 'mutation_percent_genes' parameter must have only 2 elements but ({len(mutation_percent_genes)}) element(s) found.") @@ -795,13 +788,13 @@ def __init__(self, self.valid_parameters = False raise TypeError(f"Unexpected type of the 'mutation_percent_genes' parameter. When mutation_type='adaptive', then the 'mutation_percent_genes' parameter should exist and assigned a list/tuple/numpy.ndarray with 2 values but ({mutation_percent_genes}) found.") # The mutation_num_genes parameter exists. Checking whether adaptive mutation is used. - elif (mutation_type != "adaptive"): + elif mutation_type != "adaptive": # Number of genes to mutate is fixed not adaptive. if type(mutation_num_genes) in GA.supported_int_types: - if (mutation_num_genes <= 0): + if mutation_num_genes <= 0: self.valid_parameters = False raise ValueError(f"The number of selected genes for mutation (mutation_num_genes) cannot be <= 0 but ({mutation_num_genes}) found. If you do not want to use mutation, please set mutation_type=None\n") - elif (mutation_num_genes > self.num_genes): + elif mutation_num_genes > self.num_genes: self.valid_parameters = False raise ValueError(f"The number of selected genes for mutation (mutation_num_genes), which is ({mutation_num_genes}), cannot be greater than the number of genes ({self.num_genes}).\n") else: @@ -813,10 +806,10 @@ def __init__(self, if len(mutation_num_genes) == 2: for el in mutation_num_genes: if type(el) in GA.supported_int_types: - if (el <= 0): + if el <= 0: self.valid_parameters = False raise ValueError(f"The values assigned to the 'mutation_num_genes' cannot be <= 0 but ({el}) found. If you do not want to use mutation, please set mutation_type=None\n") - elif (el > self.num_genes): + elif el > self.num_genes: self.valid_parameters = False raise ValueError(f"The values assigned to the 'mutation_num_genes' cannot be greater than the number of genes ({self.num_genes}) but ({el}) found.\n") else: @@ -825,8 +818,8 @@ def __init__(self, # At this point of the loop, the current value assigned to the parameter 'mutation_num_genes' is validated. if mutation_num_genes[0] < mutation_num_genes[1]: if not self.suppress_warnings: - warnings.warn(f"The first element in the 'mutation_num_genes' parameter is {mutation_num_genes[0]} which is smaller than the second element {mutation_num_genes[1]}. This means the mutation rate for the high-quality solutions is higher than the mutation rate of the low-quality ones. This causes high disruption in the high qualitiy solutions while making little changes in the low quality solutions. Please make the first element higher than the second element.") - # At this point outside the loop, all values of the parameter 'mutation_num_genes' are validated. Eveyrthing is OK. + warnings.warn(f"The first element in the 'mutation_num_genes' parameter is {mutation_num_genes[0]} which is smaller than the second element {mutation_num_genes[1]}. This means the mutation rate for the high-quality solutions is higher than the mutation rate of the low-quality ones. This causes high disruption in the high quality solutions while making little changes in the low quality solutions. Please make the first element higher than the second element.") + # At this point outside the loop, all values of the parameter 'mutation_num_genes' are validated. Everything is OK. else: self.valid_parameters = False raise ValueError(f"When mutation_type='adaptive', then the 'mutation_num_genes' parameter must have only 2 elements but ({len(mutation_num_genes)}) element(s) found.") @@ -849,18 +842,18 @@ def __init__(self, # select_parents: Refers to a method that selects the parents based on the parent selection type specified in the parent_selection_type attribute. # Validating the selected type of parent selection: parent_selection_type if inspect.ismethod(parent_selection_type): - # Check if the parent_selection_type is a method that accepts 4 paramaters. - if (parent_selection_type.__code__.co_argcount == 4): - # population: Added in PyGAD 2.16.0. It should used only to support custom parent selection functions. Otherwise, it should be left to None to retirve the population by self.population. + # Check if the parent_selection_type is a method that accepts 4 parameters. + if parent_selection_type.__code__.co_argcount == 4: + # population: Added in PyGAD 2.16.0. It should use only to support custom parent selection functions. Otherwise, it should be left to None to retrieve the population by self.population. # The parent selection method assigned to the parent_selection_type parameter is validated. self.select_parents = parent_selection_type else: self.valid_parameters = False raise ValueError(f"When 'parent_selection_type' is assigned to a method, then it must accept 4 parameters:\n1) Expected to be the 'self' object.\n2) The fitness values of the current population.\n3) The number of parents needed.\n4) The instance from the pygad.GA class.\n\nThe passed parent selection method named '{parent_selection_type.__code__.co_name}' accepts {parent_selection_type.__code__.co_argcount} parameter(s).") elif callable(parent_selection_type): - # Check if the parent_selection_type is a function that accepts 3 paramaters. - if (parent_selection_type.__code__.co_argcount == 3): - # population: Added in PyGAD 2.16.0. It should used only to support custom parent selection functions. Otherwise, it should be left to None to retirve the population by self.population. + # Check if the parent_selection_type is a function that accepts 3 parameters. + if parent_selection_type.__code__.co_argcount == 3: + # population: Added in PyGAD 2.16.0. It should use only to support custom parent selection functions. Otherwise, it should be left to None to retrieve the population by self.population. # The parent selection function assigned to the parent_selection_type parameter is validated. self.select_parents = parent_selection_type else: @@ -872,33 +865,33 @@ def __init__(self, raise TypeError(f"The expected type of the 'parent_selection_type' parameter is either callable or str but {type(parent_selection_type)} found.") else: parent_selection_type = parent_selection_type.lower() - if (parent_selection_type == "sss"): + if parent_selection_type == "sss": self.select_parents = self.steady_state_selection - elif (parent_selection_type == "rws"): + elif parent_selection_type == "rws": self.select_parents = self.roulette_wheel_selection - elif (parent_selection_type == "sus"): + elif parent_selection_type == "sus": self.select_parents = self.stochastic_universal_selection - elif (parent_selection_type == "random"): + elif parent_selection_type == "random": self.select_parents = self.random_selection - elif (parent_selection_type == "tournament"): + elif parent_selection_type == "tournament": self.select_parents = self.tournament_selection - elif (parent_selection_type == "tournament_nsga2"): # Supported in PyGAD >= 3.2 + elif parent_selection_type == "tournament_nsga2": # Supported in PyGAD >= 3.2 self.select_parents = self.tournament_selection_nsga2 - elif (parent_selection_type == "nsga2"): # Supported in PyGAD >= 3.2 + elif parent_selection_type == "nsga2": # Supported in PyGAD >= 3.2 self.select_parents = self.nsga2_selection - elif (parent_selection_type == "rank"): + elif parent_selection_type == "rank": self.select_parents = self.rank_selection else: self.valid_parameters = False raise TypeError(f"Undefined parent selection type: {parent_selection_type}. \nThe assigned value to the 'parent_selection_type' parameter does not refer to one of the supported parent selection techniques which are: \n-sss (steady state selection)\n-rws (roulette wheel selection)\n-sus (stochastic universal selection)\n-rank (rank selection)\n-random (random selection)\n-tournament (tournament selection)\n-tournament_nsga2: (Tournament selection for NSGA-II)\n-nsga2: (NSGA-II parent selection).\n") # For tournament selection, validate the K value. - if (parent_selection_type == "tournament"): - if (K_tournament > self.sol_per_pop): + if parent_selection_type == "tournament": + if K_tournament > self.sol_per_pop: K_tournament = self.sol_per_pop if not self.suppress_warnings: warnings.warn(f"K of the tournament selection ({K_tournament}) should not be greater than the number of solutions within the population ({self.sol_per_pop}).\nK will be clipped to be equal to the number of solutions in the population (sol_per_pop).\n") - elif (K_tournament <= 0): + elif K_tournament <= 0: self.valid_parameters = False raise ValueError(f"K of the tournament selection cannot be <=0 but ({K_tournament}) found.\n") @@ -908,7 +901,7 @@ def __init__(self, if not (type(keep_parents) in GA.supported_int_types): self.valid_parameters = False raise TypeError(f"Incorrect type of the value assigned to the keep_parents parameter. The value ({keep_parents}) of type {type(keep_parents)} found but an integer is expected.") - elif (keep_parents > self.sol_per_pop or keep_parents > self.num_parents_mating or keep_parents < -1): + elif keep_parents > self.sol_per_pop or keep_parents > self.num_parents_mating or keep_parents < -1: self.valid_parameters = False raise ValueError(f"Incorrect value to the keep_parents parameter: {keep_parents}. \nThe assigned value to the keep_parent parameter must satisfy the following conditions: \n1) Less than or equal to sol_per_pop\n2) Less than or equal to num_parents_mating\n3) Greater than or equal to -1.") @@ -922,7 +915,7 @@ def __init__(self, if not (type(keep_elitism) in GA.supported_int_types): self.valid_parameters = False raise TypeError(f"Incorrect type of the value assigned to the keep_elitism parameter. The value ({keep_elitism}) of type {type(keep_elitism)} found but an integer is expected.") - elif (keep_elitism > self.sol_per_pop or keep_elitism < 0): + elif keep_elitism > self.sol_per_pop or keep_elitism < 0: self.valid_parameters = False raise ValueError(f"Incorrect value to the keep_elitism parameter: {keep_elitism}. \nThe assigned value to the keep_elitism parameter must satisfy the following conditions: \n1) Less than or equal to sol_per_pop\n2) Greater than or equal to 0.") @@ -931,13 +924,13 @@ def __init__(self, # Validate keep_parents. if self.keep_elitism == 0: # Keep all parents in the next population. - if (self.keep_parents == -1): + if self.keep_parents == -1: self.num_offspring = self.sol_per_pop - self.num_parents_mating # Keep no parents in the next population. - elif (self.keep_parents == 0): + elif self.keep_parents == 0: self.num_offspring = self.sol_per_pop # Keep the specified number of parents in the next population. - elif (self.keep_parents > 0): + elif self.keep_parents > 0: self.num_offspring = self.sol_per_pop - self.keep_parents else: self.num_offspring = self.sol_per_pop - self.keep_elitism @@ -946,15 +939,15 @@ def __init__(self, # In PyGAD 2.19.0, a method can be passed to the fitness function. If function is passed, then it accepts 2 parameters. If method, then it accepts 3 parameters. # In PyGAD 2.20.0, a new parameter is passed referring to the instance of the `pygad.GA` class. So, the function accepts 3 parameters and the method accepts 4 parameters. if inspect.ismethod(fitness_func): - # If the fitness is calculated through a method, not a function, then there is a fourth 'self` paramaters. - if (fitness_func.__code__.co_argcount == 4): + # If the fitness is calculated through a method, not a function, then there is a fourth 'self` parameters. + if fitness_func.__code__.co_argcount == 4: self.fitness_func = fitness_func else: self.valid_parameters = False raise ValueError(f"In PyGAD 2.20.0, if a method is used to calculate the fitness value, then it must accept 4 parameters\n1) Expected to be the 'self' object.\n2) The instance of the 'pygad.GA' class.\n3) A solution to calculate its fitness value.\n4) The solution's index within the population.\n\nThe passed fitness method named '{fitness_func.__code__.co_name}' accepts {fitness_func.__code__.co_argcount} parameter(s).") elif callable(fitness_func): - # Check if the fitness function accepts 2 paramaters. - if (fitness_func.__code__.co_argcount == 3): + # Check if the fitness function accepts 2 parameters. + if fitness_func.__code__.co_argcount == 3: self.fitness_func = fitness_func else: self.valid_parameters = False @@ -978,16 +971,16 @@ def __init__(self, # Check if the on_start exists. if not (on_start is None): if inspect.ismethod(on_start): - # Check if the on_start method accepts 2 paramaters. - if (on_start.__code__.co_argcount == 2): + # Check if the on_start method accepts 2 parameters. + if on_start.__code__.co_argcount == 2: self.on_start = on_start else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_start parameter must accept only 2 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.\nThe passed method named '{on_start.__code__.co_name}' accepts {on_start.__code__.co_argcount} parameter(s).") # Check if the on_start is a function. elif callable(on_start): - # Check if the on_start function accepts only a single paramater. - if (on_start.__code__.co_argcount == 1): + # Check if the on_start function accepts only a single parameter. + if on_start.__code__.co_argcount == 1: self.on_start = on_start else: self.valid_parameters = False @@ -1003,16 +996,16 @@ def __init__(self, if not (on_fitness is None): # Check if the on_fitness is a method. if inspect.ismethod(on_fitness): - # Check if the on_fitness method accepts 3 paramaters. - if (on_fitness.__code__.co_argcount == 3): + # Check if the on_fitness method accepts 3 parameters. + if on_fitness.__code__.co_argcount == 3: self.on_fitness = on_fitness else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_fitness parameter must accept 3 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.3) The fitness values of all solutions.\nThe passed method named '{on_fitness.__code__.co_name}' accepts {on_fitness.__code__.co_argcount} parameter(s).") # Check if the on_fitness is a function. elif callable(on_fitness): - # Check if the on_fitness function accepts 2 paramaters. - if (on_fitness.__code__.co_argcount == 2): + # Check if the on_fitness function accepts 2 parameters. + if on_fitness.__code__.co_argcount == 2: self.on_fitness = on_fitness else: self.valid_parameters = False @@ -1027,16 +1020,16 @@ def __init__(self, if not (on_parents is None): # Check if the on_parents is a method. if inspect.ismethod(on_parents): - # Check if the on_parents method accepts 3 paramaters. - if (on_parents.__code__.co_argcount == 3): + # Check if the on_parents method accepts 3 parameters. + if on_parents.__code__.co_argcount == 3: self.on_parents = on_parents else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_parents parameter must accept 3 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.\n3) The fitness values of all solutions.\nThe passed method named '{on_parents.__code__.co_name}' accepts {on_parents.__code__.co_argcount} parameter(s).") # Check if the on_parents is a function. elif callable(on_parents): - # Check if the on_parents function accepts 2 paramaters. - if (on_parents.__code__.co_argcount == 2): + # Check if the on_parents function accepts 2 parameters. + if on_parents.__code__.co_argcount == 2: self.on_parents = on_parents else: self.valid_parameters = False @@ -1051,16 +1044,16 @@ def __init__(self, if not (on_crossover is None): # Check if the on_crossover is a method. if inspect.ismethod(on_crossover): - # Check if the on_crossover method accepts 3 paramaters. - if (on_crossover.__code__.co_argcount == 3): + # Check if the on_crossover method accepts 3 parameters. + if on_crossover.__code__.co_argcount == 3: self.on_crossover = on_crossover else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_crossover parameter must accept 3 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.\n2) The offspring generated using crossover.\nThe passed method named '{on_crossover.__code__.co_name}' accepts {on_crossover.__code__.co_argcount} parameter(s).") # Check if the on_crossover is a function. elif callable(on_crossover): - # Check if the on_crossover function accepts 2 paramaters. - if (on_crossover.__code__.co_argcount == 2): + # Check if the on_crossover function accepts 2 parameters. + if on_crossover.__code__.co_argcount == 2: self.on_crossover = on_crossover else: self.valid_parameters = False @@ -1075,16 +1068,16 @@ def __init__(self, if not (on_mutation is None): # Check if the on_mutation is a method. if inspect.ismethod(on_mutation): - # Check if the on_mutation method accepts 3 paramaters. - if (on_mutation.__code__.co_argcount == 3): + # Check if the on_mutation method accepts 3 parameters. + if on_mutation.__code__.co_argcount == 3: self.on_mutation = on_mutation else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_mutation parameter must accept 3 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.\n2) The offspring after applying the mutation operation.\nThe passed method named '{on_mutation.__code__.co_name}' accepts {on_mutation.__code__.co_argcount} parameter(s).") # Check if the on_mutation is a function. elif callable(on_mutation): - # Check if the on_mutation function accepts 2 paramaters. - if (on_mutation.__code__.co_argcount == 2): + # Check if the on_mutation function accepts 2 parameters. + if on_mutation.__code__.co_argcount == 2: self.on_mutation = on_mutation else: self.valid_parameters = False @@ -1099,16 +1092,16 @@ def __init__(self, if not (on_generation is None): # Check if the on_generation is a method. if inspect.ismethod(on_generation): - # Check if the on_generation method accepts 2 paramaters. - if (on_generation.__code__.co_argcount == 2): + # Check if the on_generation method accepts 2 parameters. + if on_generation.__code__.co_argcount == 2: self.on_generation = on_generation else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_generation parameter must accept 2 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.\nThe passed method named '{on_generation.__code__.co_name}' accepts {on_generation.__code__.co_argcount} parameter(s).") # Check if the on_generation is a function. elif callable(on_generation): - # Check if the on_generation function accepts only a single paramater. - if (on_generation.__code__.co_argcount == 1): + # Check if the on_generation function accepts only a single parameter. + if on_generation.__code__.co_argcount == 1: self.on_generation = on_generation else: self.valid_parameters = False @@ -1123,16 +1116,16 @@ def __init__(self, if not (on_stop is None): # Check if the on_stop is a method. if inspect.ismethod(on_stop): - # Check if the on_stop method accepts 3 paramaters. - if (on_stop.__code__.co_argcount == 3): + # Check if the on_stop method accepts 3 parameters. + if on_stop.__code__.co_argcount == 3: self.on_stop = on_stop else: self.valid_parameters = False raise ValueError(f"The method assigned to the on_stop parameter must accept 3 parameters:\n1) Expected to be the 'self' object.\n2) The instance of the genetic algorithm.\n2) A list of the fitness values of the solutions in the last population.\nThe passed method named '{on_stop.__code__.co_name}' accepts {on_stop.__code__.co_argcount} parameter(s).") # Check if the on_stop is a function. elif callable(on_stop): - # Check if the on_stop function accepts 2 paramaters. - if (on_stop.__code__.co_argcount == 2): + # Check if the on_stop function accepts 2 parameters. + if on_stop.__code__.co_argcount == 2: self.on_stop = on_stop else: self.valid_parameters = False @@ -1212,7 +1205,7 @@ def validate_multi_stop_criteria(self, stop_word, number): raise ValueError(f"For format of a single criterion in the 'stop_criteria' parameter is 'word_number' but '{stop_criteria}' found.") elif type(stop_criteria) in [list, tuple, numpy.ndarray]: - # Remove duplicate criterira by converting the list to a set then back to a list. + # Remove duplicate criteria by converting the list to a set then back to a list. stop_criteria = list(set(stop_criteria)) for idx, val in enumerate(stop_criteria): if type(val) is str: @@ -1262,7 +1255,7 @@ def validate_multi_stop_criteria(self, stop_word, number): if parallel_processing[0] in ["process", "thread"]: if (type(parallel_processing[1]) in GA.supported_int_types and parallel_processing[1] > 0) or (parallel_processing[1] == 0) or (parallel_processing[1] is None): if parallel_processing[1] == 0: - # If the number of processes/threads is 0, this means no parallel processing is used. It is equivelant to setting parallel_processing=None. + # If the number of processes/threads is 0, this means no parallel processing is used. It is equivalent to setting parallel_processing=None. self.parallel_processing = None else: # Whether the second value is None or a positive integer. @@ -1289,7 +1282,7 @@ def validate_multi_stop_criteria(self, stop_word, number): # The number of completed generations. self.generations_completed = 0 - # At this point, all necessary parameters validation is done successfully and we are sure that the parameters are valid. + # At this point, all necessary parameters validation is done successfully, and we are sure that the parameters are valid. # Set to True when all the parameters passed in the GA class constructor are valid. self.valid_parameters = True @@ -1383,7 +1376,7 @@ def initialize_population(self, # 4) Solve duplicates if not allowed. # Create an empty population. - self.population = numpy.zeros(shape=self.pop_size, dtype=object) + self.population = numpy.empty(shape=self.pop_size, dtype=object) # 1) Create the initial population either randomly or using the gene space. if self.gene_space is None: @@ -1409,18 +1402,20 @@ def initialize_population(self, self.population[sol_idx, gene_idx] = self.generate_gene_value_from_space(gene_idx=gene_idx, mutation_by_replacement=True, gene_value=None, + solution=self.population[sol_idx], sample_size=1) # 2) Change the data type and round all genes within the initial population. self.population = self.change_population_dtype_and_round(self.population) + # Note that gene_constraint is not validated yet. + # We have to set it as a property of the pygad.GA instance to retrieve without passing it as an additional parameter. + self.gene_constraint = gene_constraint + # 3) Enforce the gene constraints as much as possible. - if gene_constraint is None: + if self.gene_constraint is None: pass else: - # Note that gene_constraint is not validated yet. - # We have to set it as a property of the pygad.GA instance to retrieve without passing it as an additional parameter. - self.gene_constraint = gene_constraint for sol_idx, solution in enumerate(self.population): for gene_idx in range(self.num_genes): # Check that a constraint is available for the gene and that the current value does not satisfy that constraint @@ -1452,11 +1447,14 @@ def initialize_population(self, gene_type=gene_type, sample_size=100) else: - self.population[sol_idx], _, _ = self.solve_duplicate_genes_by_space(solution=self.population[solution_idx], - gene_type=self.gene_type, - sample_size=100, - mutation_by_replacement=True, - build_initial_pop=True) + self.population[solution_idx], _, _ = self.solve_duplicate_genes_by_space(solution=self.population[solution_idx].copy(), + gene_type=self.gene_type, + sample_size=100, + mutation_by_replacement=True, + build_initial_pop=True) + + # Change the data type and round all genes within the initial population. + self.population = self.change_population_dtype_and_round(self.population) # Keeping the initial population in the initial_population attribute. self.initial_population = self.population.copy() @@ -1475,11 +1473,15 @@ def cal_pop_fitness(self): # It is used to return the parent index using the 'in' membership operator of Python lists. This is much faster than using 'numpy.where()'. if self.last_generation_parents is not None: last_generation_parents_as_list = self.last_generation_parents.tolist() + else: + last_generation_parents_as_list = [] # 'last_generation_elitism_as_list' is the list version of 'self.last_generation_elitism' # It is used to return the elitism index using the 'in' membership operator of Python lists. This is much faster than using 'numpy.where()'. if self.last_generation_elitism is not None: last_generation_elitism_as_list = self.last_generation_elitism.tolist() + else: + last_generation_elitism_as_list = [] pop_fitness = ["undefined"] * len(self.population) if self.parallel_processing is None: @@ -1790,7 +1792,7 @@ def run(self): if self.save_best_solutions: self.best_solutions.append(list(best_solution)) - # Note: Any code that has loop-dependant statements (e.g. continue, break, etc) must be kept inside the loop of the 'run()' method. It can be moved to another method to clean the run() method. + # Note: Any code that has loop-dependant statements (e.g. continue, break, etc.) must be kept inside the loop of the 'run()' method. It can be moved to another method to clean the run() method. # If the on_generation attribute is not None, then cal the callback function after the generation. if not (self.on_generation is None): r = self.on_generation(self) @@ -1830,6 +1832,10 @@ def run(self): reach_fitness_value = criterion[obj_idx + 1] elif len(criterion[1:]) == 1: reach_fitness_value = criterion[1] + else: + # Unexpected to be reached, but it is safer to handle it. + self.valid_parameters = False + raise ValueError(f"The number of values does not equal the number of objectives.") if max(self.last_generation_fitness[:, obj_idx]) >= reach_fitness_value: pass @@ -1838,7 +1844,7 @@ def run(self): break elif criterion[0] == "saturate": criterion[1] = int(criterion[1]) - if (self.generations_completed >= criterion[1]): + if self.generations_completed >= criterion[1]: # Single-objective problem. if type(self.last_generation_fitness[0]) in GA.supported_int_float_types: if (self.best_solutions_fitness[self.generations_completed - criterion[1]] - self.best_solutions_fitness[self.generations_completed - 1]) == 0: @@ -1929,7 +1935,7 @@ def run_loop_head(self, best_solution_fitness): def run_select_parents(self, call_on_parents=True): """ - This method must be only callled from inside the run() method. It is not meant for use by the user. + This method must be only called from inside the run() method. It is not meant for use by the user. Generally, any method with a name starting with 'run_' is meant to be only called by PyGAD from inside the 'run()' method. The objective of the 'run_select_parents()' method is to select the parents and call the callable on_parents() if defined. @@ -1994,7 +2000,7 @@ def run_select_parents(self, call_on_parents=True): if on_parents_selected_parents.shape == self.last_generation_parents.shape: self.last_generation_parents = on_parents_selected_parents else: - raise ValueError(f"Size mismatch between the parents retrned by on_parents() {on_parents_selected_parents.shape} and the expected parents shape {self.last_generation_parents.shape}.") + raise ValueError(f"Size mismatch between the parents returned by on_parents() {on_parents_selected_parents.shape} and the expected parents shape {self.last_generation_parents.shape}.") else: raise ValueError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray but the first output type is {type(on_parents_selected_parents)}.") @@ -2005,6 +2011,7 @@ def run_select_parents(self, call_on_parents=True): if type(on_parents_selected_parents_indices) in [tuple, list, numpy.ndarray, range]: on_parents_selected_parents_indices = numpy.array(on_parents_selected_parents_indices) if on_parents_selected_parents_indices.shape == self.last_generation_parents_indices.shape: + # Add this new instance attribute. self.last_generation_parents_indices = on_parents_selected_parents_indices else: raise ValueError(f"Size mismatch between the parents indices returned by on_parents() {on_parents_selected_parents_indices.shape} and the expected crossover output {self.last_generation_parents_indices.shape}.") @@ -2016,7 +2023,7 @@ def run_select_parents(self, call_on_parents=True): def run_crossover(self): """ - This method must be only callled from inside the run() method. It is not meant for use by the user. + This method must be only called from inside the run() method. It is not meant for use by the user. Generally, any method with a name starting with 'run_' is meant to be only called by PyGAD from inside the 'run()' method. The objective of the 'run_crossover()' method is to apply crossover and call the callable on_crossover() if defined. @@ -2083,7 +2090,7 @@ def run_crossover(self): def run_mutation(self): """ - This method must be only callled from inside the run() method. It is not meant for use by the user. + This method must be only called from inside the run() method. It is not meant for use by the user. Generally, any method with a name starting with 'run_' is meant to be only called by PyGAD from inside the 'run()' method. The objective of the 'run_mutation()' method is to apply mutation and call the callable on_mutation() if defined. @@ -2133,7 +2140,7 @@ def run_mutation(self): def run_update_population(self): """ - This method must be only callled from inside the run() method. It is not meant for use by the user. + This method must be only called from inside the run() method. It is not meant for use by the user. Generally, any method with a name starting with 'run_' is meant to be only called by PyGAD from inside the 'run()' method. The objective of the 'run_update_population()' method is to update the 'population' attribute after completing the processes of crossover and mutation. @@ -2148,13 +2155,13 @@ def run_update_population(self): # Update the population attribute according to the offspring generated. if self.keep_elitism == 0: # If the keep_elitism parameter is 0, then the keep_parents parameter will be used to decide if the parents are kept in the next generation. - if (self.keep_parents == 0): + if self.keep_parents == 0: self.population = self.last_generation_offspring_mutation - elif (self.keep_parents == -1): + elif self.keep_parents == -1: # Creating the new population based on the parents and offspring. self.population[0:self.last_generation_parents.shape[0],:] = self.last_generation_parents self.population[self.last_generation_parents.shape[0]:, :] = self.last_generation_offspring_mutation - elif (self.keep_parents > 0): + elif self.keep_parents > 0: parents_to_keep, _ = self.steady_state_selection(self.last_generation_fitness, num_parents=self.keep_parents) self.population[0:parents_to_keep.shape[0],:] = parents_to_keep @@ -2250,7 +2257,7 @@ def line_separator(line_length=line_length, line_character=line_character): def create_row(columns, line_length=line_length, fill_character=fill_character, split_percentages=None): filled_columns = [] - if split_percentages == None: + if split_percentages is None: split_percentages = [int(100/len(columns))] * 3 columns_lengths = [int((split_percentages[idx] * line_length) / 100) for idx in range(len(split_percentages))] @@ -2259,7 +2266,6 @@ def create_row(columns, line_length=line_length, fill_character=fill_character, extra_characters = columns_lengths[column_idx] - \ current_column_length filled_column = column + fill_character * extra_characters - filled_column = column + fill_character * extra_characters filled_columns.append(filled_column) return "".join(filled_columns) diff --git a/pygad/utils/__pycache__/__init__.cpython-310.pyc b/pygad/utils/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d759c90d10eaed53530be5207e3626eadf84f7ae GIT binary patch literal 332 zcmYk2F;B!G6vu(Kch$Q~{2E=#sf&X#8jWto#N5I{NFgneLIc#Kei~Q5QdcJ@cPAf> zgOBj%_udQtM{v8{F@pHMRoy$BpDFnt1d?mm;Q~Y=i4E#8!#FVkCX6hc0cR{*I0w#I z4qUK8uAWh~{({@6!t==>yt0u8WmFTjvrs!~ybHm-Di2B7k5T+J3L9E+_EwzM=k@-v zTCvSAwL;co)F$}!GM^>MlN2EHhI6n9;LG_3^6S({@^eoQ0UA_vy^`QT?^Jk+ZlDj! xs;HYA(>kwX*FR0MbCxcH=rV|MuC0!o*TXcIm%WptQCApTL;AWc+TyfI@(W3FR22XK literal 0 HcmV?d00001 diff --git a/pygad/utils/__pycache__/crossover.cpython-310.pyc b/pygad/utils/__pycache__/crossover.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ca79420f7efcb810833950f6909b1f08e3f3424 GIT binary patch literal 6116 zcmeHLOK%)m6|SnTS68>4j$^kmnaR{ZCKHp`CgCN80EuRlVNeu}W(F1(s8sGMyUVVw z>fE{=$EI3}=(5w!L(B@WRj2R0 zb?&+6KI;3|C(!k6HYw62g>A4%{-__VHinx~_# zd4^}=SNEEpgXMWyLRWj(7^~dOYX|b%(ocRn)?lgW=_8pv5WO_fhb$Wp<+08!-s1A3 zcB+%!j(mu1SdtFp19@!lnbbHy9bS2Q@4&Dnx`&GrKeqK+{E}M=BuXKl2jYH)^Z*C zX*1WTdslCPj5(H zyO+?uP-#Phba6P_54B@@4V=6g_xIRb%P#(I4u@gjCw3A9NCE<`aO=X(PnPs#REGx=yH3vAN2Y)%WH_uA&MJ#e{iZ^q0XGJr>q#-OolTV3f_>k5%!{ zs$cW=eV>n5bj;O>hu7?zku9orFOIm2FSl^1Fy&1KsI599OnEG4n#cEs{u5DaWQ{Q1 z&D4Id)z4IN_|9XQwW0WgK;YIBkNW7DnZzMrBaSj-5Jb*BH$=g`FA9r;5y#5Mk+ZSq zFn<`jJ%4~5&ziI;4h@#I5_d2ReX%5uHFg?S*#e0!iFt_ZG{LYZU?^}yXBZEcyIH3) zfv$AiG7YN;z|#D|knaf_nI3O!;|OJXet>L*7o_}1$ z$-N2pvcX9Vx6;QRbTJa=1$*h zlF2)nM$3?7Mu64oVNR{w&=n`}?w6ITq9QvJ(qY6%SvTJfY!j?GmGv3COWudDX1nsN zVydRB%lc;>)l%l=SLAl%f~?4GnOfV5DO>VvV@^5WIImcWg_>IYEArewEklJaJ)M6k z4^(~Mp3u?s!H=S&Vcl zHAwFWy_vj|ngzXm4C>I!o}f3wGb)nN%NpqIcF~7s_6s5;&|5QYjxEre@#r*KG|(Fe zuXtn9I^b=mZXG@Jg5EUJ1HECMX;aXfUZXc6cr`6r3VPG1UiS{fS+xiqEANxHPiAqv zRZ!s^QQ^;I_BvMQ3|3J>c^2h)5FD(cw1B6JXAw`G4t3CBYuvhf8|`1Hv~xOKtkK~_ zFQ>z38XXcPT+PWVnUL11{VuO4?ubeW5B3zM2&kjrerQvscMj$qV4^baCOFh!2=(<(;*98Jv; z%p)LL&k-De8083}=QDDI3q_7#0z|_q6R771>aiTbB$zF7gu=SW5loRIlr2q8vx2V3 zeBK18B3no;%2GrQ^DFpg=5>nSsRT&>c} z=j9E#UJjJ^PXfwO6d<4;)N%3_0&L$!z)z9YWuyZ+SlRK2Q^=fSCqi+GbqgG`kw@g| zgYOmDL%0V}@q{+nA&p)<4Df!FA-4$lBq07Dg3M!RDIoMXWIpbm44D;7*07Oa^z#SE zY(~V_P7;q8V$u_FFf&B}j!1PXBAzY|*d#LjS^6%3B(Qbrft?H#mplcB8DAKe80qMSh9wI*hT15g?sEes>`iu5f}RoX}(lAb*9?034HvO;NwRSc{KH6@PVjHo|QYFS?Y6= zTW$Z<33N;u{m!yUSF{oCupB3|94EiUM!D@ccSmlRzhN59^;;yqP2zhbZjhJ+jG{zQ zBclr*ilq}ul}*W1%!YZ(e7Ai|yjfN=!*RU0=Qw#3EjWPsOf#05*K?P?i|;^6$gC<+ I_CKfm8&v7}9smFU literal 0 HcmV?d00001 diff --git a/pygad/utils/__pycache__/mutation.cpython-310.pyc b/pygad/utils/__pycache__/mutation.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f20aa55358a2d2329bd29bb5919132aac613e094 GIT binary patch literal 17807 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 literal 0 HcmV?d00001 diff --git a/pygad/utils/__pycache__/parent_selection.cpython-310.pyc b/pygad/utils/__pycache__/parent_selection.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..487dcd97223a51570e8593debde5f8d75e678df2 GIT binary patch literal 14555 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 literal 0 HcmV?d00001 diff --git a/pygad/utils/mutation.py b/pygad/utils/mutation.py index d023365..df9a78c 100644 --- a/pygad/utils/mutation.py +++ b/pygad/utils/mutation.py @@ -167,6 +167,7 @@ def mutation_process_gene_value(self, range_max=range_max, gene_value=solution[gene_idx], gene_idx=gene_idx, + solution=solution, mutation_by_replacement=self.mutation_by_replacement, sample_size=1) # Even that its name is singular, it might have a multiple values. diff --git a/pygad/visualize/__pycache__/__init__.cpython-310.pyc b/pygad/visualize/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..473b3f831e39a308d92cef5e00424e43e5c2b1cb GIT binary patch literal 237 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 m)~eND+nysX=wjf-qj>by=Yx^Hh@~Sqbkrz literal 0 HcmV?d00001 diff --git a/test_adaptive_mutation.py b/test_adaptive_mutation.py new file mode 100644 index 0000000..afac8d8 --- /dev/null +++ b/test_adaptive_mutation.py @@ -0,0 +1,2393 @@ +import pygad +import random +import numpy + +num_generations = 1 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +#### Define the fitness functions in the top-level of the module so that they are picklable and usable in the process-based parallel processing works. +#### If the functions are defined inside a class/method/function, they are not picklable and this error is raised: AttributeError: Can't pickle local object +#### Process-based parallel processing must have the used functions picklable. +def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + +def fitness_func_batch_single(ga, soluions, idxs): + return numpy.random.uniform(size=len(soluions)) + +def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + +def fitness_func_batch_multi(ga, soluions, idxs): + f = [] + for sol in soluions: + f.append([random.random(), random.random()]) + return f + +def output_adaptive_mutation(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + initial_population=None, + mutation_probability=None, + mutation_num_genes=None, + fitness_batch_size=None, + mutation_type="adaptive", + parent_selection_type='sss', + parallel_processing=None, + multi_objective=False): + + if fitness_batch_size is None or (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size == 1): + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + elif (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size > 1): + if multi_objective == True: + fitness_func = fitness_func_batch_multi + else: + fitness_func = fitness_func_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + initial_population=initial_population, + init_range_low=init_range_low, + init_range_high=init_range_high, + parent_selection_type=parent_selection_type, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=True, + mutation_by_replacement=mutation_by_replacement, + save_solutions=True, + ## Use a static 'mutation_probability'. + ## An ambigius error in GitHub actions happen when using mutation_num_genes and mutation_probability. I do not know the reason. + # mutation_num_genes=mutation_num_genes, + mutation_probability=[0.2, 0.1], + mutation_type=mutation_type, + suppress_warnings=True, + fitness_batch_size=fitness_batch_size, + parallel_processing=parallel_processing, + random_seed=1) + + ga_instance.run() + + return None, ga_instance + +def test_adaptive_mutation(): + result, ga_instance = output_adaptive_mutation() + + # assert result == True + +def test_adaptive_mutation_gene_space(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10)) + + # assert result == True + +def test_adaptive_mutation_int_gene_type(): + result, ga_instance = output_adaptive_mutation(gene_type=int) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + # assert result == True + +def test_adaptive_mutation_initial_population(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1) + +def test_adaptive_mutation_fitness_batch_size_2(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2) + +def test_adaptive_mutation_fitness_batch_size_3(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3) + +def test_adaptive_mutation_fitness_batch_size_4(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4) + +def test_adaptive_mutation_fitness_batch_size_5(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5) + +def test_adaptive_mutation_fitness_batch_size_6(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6) + +def test_adaptive_mutation_fitness_batch_size_7(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7) + +def test_adaptive_mutation_fitness_batch_size_8(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8) + +def test_adaptive_mutation_fitness_batch_size_9(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9) + +def test_adaptive_mutation_fitness_batch_size_10(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10) + +#### Single-Objective Mutation Probability +def test_adaptive_mutation_mutation_probability(): + result, ga_instance = output_adaptive_mutation(mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_probability(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10), + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_probability(): + result, ga_instance = output_adaptive_mutation(gene_type=int, + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_probability(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_probability(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_probability=[0.2, 0.1]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_probability(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_probability(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_probability(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_probability(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10, + mutation_probability=[0.2, 0.1]) + + +#### Single-Objective Mutation Number of Genes +def test_adaptive_mutation_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10), + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(gene_type=int, + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_num_genes=[6, 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_num_genes(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10, + mutation_num_genes=[6, 4]) + +#### Multi-Objective Mutation Probability +def test_adaptive_mutation_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=range(10), + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=int, + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_probability=[0.2, 0.1]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_probability_multi_objective(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_multi_objective(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=1, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=2, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=3, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=4, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=5, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=6, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=7, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=8, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=9, + mutation_probability=[0.2, 0.1]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_probability_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=10, + mutation_probability=[0.2, 0.1]) + +#### Multi-Objective Mutation Number of Genes +def test_adaptive_mutation_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=range(10), + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=int, + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_num_genes=[6, 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_num_genes_multi_objective(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_multi_objective(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=1, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=2, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=3, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=4, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=5, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=6, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=7, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=8, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=9, + mutation_num_genes=[6, 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_multi_objective(): + result, ga_instance = output_adaptive_mutation(multi_objective=True,fitness_batch_size=10, + mutation_num_genes=[6, 4]) + +######## Parallel Processing +#### #### Threads + +#### Single-Objective Mutation Probability +def test_adaptive_mutation_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10), + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_type=int, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_probability_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_probability_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + +#### Single-Objective Mutation Number of Genes +def test_adaptive_mutation_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10), + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_type=int, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_num_genes_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +#### Multi-Objective Mutation Probability +def test_adaptive_mutation_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=range(10), + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=int, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_probability_multi_objective_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_multi_objective_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=1, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=2, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=3, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=4, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=5, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=6, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=7, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=8, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=9, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_probability_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=10, + mutation_probability=[0.2, 0.1], + parallel_processing=['thread', 4]) + +#### Multi-Objective Mutation Number of Genes +def test_adaptive_mutation_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=range(10), + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=int, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_num_genes_multi_objective_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_multi_objective_parallel_processing_threads(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=1, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=2, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=3, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=4, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=5, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=6, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=7, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=8, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=9, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_multi_objective_parallel_processing_threads(): + result, ga_instance = output_adaptive_mutation(multi_objective=True,fitness_batch_size=10, + mutation_num_genes=[6, 4], + parallel_processing=['thread', 4]) + + +#### #### Processes + +#### Single-Objective Mutation Probability +def test_adaptive_mutation_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10), + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_type=int, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_probability_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_probability_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + +#### Single-Objective Mutation Number of Genes +def test_adaptive_mutation_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space=range(10), + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_type=int, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_num_genes_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=1, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=2, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=3, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=4, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=5, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=6, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=7, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=8, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=9, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(fitness_batch_size=10, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +#### Multi-Objective Mutation Probability +def test_adaptive_mutation_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=range(10), + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=int, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_probability_multi_objective_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_multi_objective_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=1, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=2, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=3, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=4, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=5, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=6, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=7, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=8, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=9, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_probability_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=10, + mutation_probability=[0.2, 0.1], + parallel_processing=['process', 4]) + +#### Multi-Objective Mutation Number of Genes +def test_adaptive_mutation_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=range(10), + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_int_gene_type_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=int, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space={"low": 0, "high": 10}, + gene_type=[float, 2], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + # assert result == True + +def test_adaptive_mutation_nested_gene_type_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_nested_gene_space_nested_gene_type_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_mutation_num_genes_multi_objective_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_multi_objective_parallel_processing_processes(): + global initial_population + result, ga_instance = output_adaptive_mutation(multi_objective=True, + initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + # assert result == True + +def test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=1, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=2, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=3, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=4, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=5, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=6, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=7, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=8, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True, + fitness_batch_size=9, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + +def test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_multi_objective_parallel_processing_processes(): + result, ga_instance = output_adaptive_mutation(multi_objective=True,fitness_batch_size=10, + mutation_num_genes=[6, 4], + parallel_processing=['process', 4]) + + +if __name__ == "__main__": + #### Single-objective mutation_probability + print() + test_adaptive_mutation_mutation_probability() + print() + + test_adaptive_mutation_int_gene_type_mutation_probability() + print() + + test_adaptive_mutation_gene_space_mutation_probability() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_probability() + print() + + test_adaptive_mutation_nested_gene_space_mutation_probability() + print() + + test_adaptive_mutation_nested_gene_type_mutation_probability() + print() + + test_adaptive_mutation_initial_population_mutation_probability() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_probability() + print() + + #### Single-objective mutation_num_genes + print() + test_adaptive_mutation_mutation_num_genes() + print() + + test_adaptive_mutation_int_gene_type_mutation_num_genes() + print() + + test_adaptive_mutation_gene_space_mutation_num_genes() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_num_genes() + print() + + test_adaptive_mutation_nested_gene_space_mutation_num_genes() + print() + + test_adaptive_mutation_nested_gene_type_mutation_num_genes() + print() + + test_adaptive_mutation_initial_population_mutation_num_genes() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes() + print() + + #### Multi-objective mutation_probability + print() + test_adaptive_mutation_mutation_probability() + print() + + test_adaptive_mutation_int_gene_type_mutation_probability() + print() + + test_adaptive_mutation_gene_space_mutation_probability() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_probability() + print() + + test_adaptive_mutation_nested_gene_space_mutation_probability() + print() + + test_adaptive_mutation_nested_gene_type_mutation_probability() + print() + + test_adaptive_mutation_initial_population_mutation_probability() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_probability() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_probability() + print() + + + + ######## Parallel Processing + #### #### Threads + #### Single-objective mutation_probability + print() + test_adaptive_mutation_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_int_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_gene_space_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_nested_gene_space_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_nested_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_initial_population_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_probability_parallel_processing_threads() + print() + + #### Single-objective mutation_num_genes + print() + test_adaptive_mutation_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_int_gene_type_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_gene_space_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_nested_gene_space_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_nested_gene_type_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_initial_population_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_parallel_processing_threads() + print() + + #### Multi-objective mutation_probability + print() + test_adaptive_mutation_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_int_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_gene_space_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_nested_gene_space_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_nested_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_initial_population_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_probability_parallel_processing_threads() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_probability_parallel_processing_threads() + print() + + #### #### Processes + #### Single-objective mutation_probability + print() + test_adaptive_mutation_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_int_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_gene_space_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_nested_gene_space_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_nested_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_initial_population_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_probability_parallel_processing_processes() + print() + + #### Single-objective mutation_num_genes + print() + test_adaptive_mutation_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_int_gene_type_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_gene_space_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_nested_gene_space_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_nested_gene_type_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_initial_population_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_num_genes_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_num_genes_parallel_processing_processes() + print() + + #### Multi-objective mutation_probability + print() + test_adaptive_mutation_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_int_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_gene_space_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_gene_space_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_nested_gene_space_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_nested_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_initial_population_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_initial_population_nested_gene_type_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_1_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_2_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_3_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_4_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_5_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_6_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_7_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_8_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_9_mutation_probability_parallel_processing_processes() + print() + + test_adaptive_mutation_fitness_batch_size_10_mutation_probability_parallel_processing_processes() + print() + + diff --git a/test_allow_duplicate_genes.py b/test_allow_duplicate_genes.py new file mode 100644 index 0000000..63037f3 --- /dev/null +++ b/test_allow_duplicate_genes.py @@ -0,0 +1,634 @@ +import pygad +import random +import numpy + +num_generations = 1 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +def number_duplicate_genes(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + random_seed=123, + initial_population=None, + parent_selection_type='sss', + multi_objective=False): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + initial_population=initial_population, + parent_selection_type=parent_selection_type, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=False, + mutation_by_replacement=mutation_by_replacement, + random_seed=random_seed, + save_solutions=True, + suppress_warnings=True) + + ga_instance.run() + + num_duplicates = 0 + for solution in ga_instance.solutions: + num = len(solution) - len(set(solution)) + if num != 0: + print(solution) + num_duplicates += num + + print(f"Number of duplicates is {num_duplicates}.") + return num_duplicates + +#### Single-Objective +def test_number_duplicates_default(): + num_duplicates = number_duplicate_genes() + + assert num_duplicates == 0 + +def test_number_duplicates_default_initial_population(): + num_duplicates = number_duplicate_genes(initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type_initial_population(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + initial_population=initial_population, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_initial_population(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement_initial_population(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space(): + num_duplicates = number_duplicate_genes(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space_initial_population(): + num_duplicates = number_duplicate_genes(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=range(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space_initial_population(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=range(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=numpy.arange(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space_initial_population(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=numpy.arange(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space(): + num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_initial_population(): + num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + + +# def test_number_duplicates_nested_gene_space_nested_gene_type(): + """ + This example causes duplicate genes that can only be solved by changing the values of a chain of genes. + Let's explain it using this solution: [0, 2, 3, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 6 at indices 5 and 6. + According to the gene space, none of these genes can has a different value that solves the duplicates. + -If the value of the gene at index 5 is changed from 6 to 5, then it causes another duplicate with the gene at index 4. + -If the value of the gene at index 6 is changed from 6 to 7, then it causes another duplicate with the gene at index 7. + The solution is to change a chain of genes that make a room to solve the duplicates between the 2 genes. + 1) Change the second gene from 2 to 1. + 2) Change the third gene from 3 to 2. + 3) Change the fourth gene from 4 to 3. + 4) Change the fifth gene from 5 to 4. + 5) Change the sixth gene from 6 to 5. This solves the duplicates. + But this is NOT SUPPORTED yet. + We support changing only a single gene that makes a room to solve the duplicates. + + Let's explain it using this solution: [1, 2, 2, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 2 at indices 1 and 2. + This is how the duplicates are solved: + 1) Change the first gene from 1 to 0. + 2) Change the second gene from 2 to 1. This solves the duplicates. + The result is [0, 1, 2, 4, 5, 6, 6, 7, 8, 9] + """ + # num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + # [1, 2], + # [2, 3], + # [3, 4], + # [4, 5], + # [5, 6], + # [6, 7], + # [7, 8], + # [8, 9], + # [9, 10]], + # gene_type=[int, int, int, int, int, int, int, int, int, int], + # num_genes=10) + + # assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_nested_gene_type_initial_population(): + num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=[int, int, int, int, int, int, int, int, int, int], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +#### Multi-Objective +def test_number_duplicates_default_multi_objective(): + num_duplicates = number_duplicate_genes() + + assert num_duplicates == 0 + +def test_number_duplicates_default_initial_population_multi_objective(): + num_duplicates = number_duplicate_genes(initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type_multi_objective(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_float_gene_type_initial_population_multi_objective(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_type=float, + num_genes=num_genes, + init_range_low=0, + init_range_high=1, + initial_population=initial_population, + random_mutation_min_val=0, + random_mutation_max_val=1) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_initial_population_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=False, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_int_gene_type_replacement_initial_population_multi_objective(): + num_genes = 10 + init_range_low = 0 + init_range_high = init_range_low + num_genes + random_mutation_min_val = 0 + random_mutation_max_val = random_mutation_min_val + num_genes + num_duplicates = number_duplicate_genes(gene_type=int, + mutation_by_replacement=True, + num_genes=num_genes, + init_range_low=init_range_low, + init_range_high=init_range_high, + initial_population=initial_population, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space_multi_objective(): + num_duplicates = number_duplicate_genes(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_single_gene_space_initial_population_multi_objective(): + num_duplicates = number_duplicate_genes(gene_space=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space_multi_objective(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=range(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_range_gene_space_initial_population_multi_objective(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=range(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space_multi_objective(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=numpy.arange(num_genes), + num_genes=num_genes) + + assert num_duplicates == 0 + +def test_number_duplicates_single_numpy_range_gene_space_initial_population_multi_objective(): + num_genes = 10 + num_duplicates = number_duplicate_genes(gene_space=numpy.arange(num_genes), + num_genes=num_genes, + initial_population=initial_population) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_multi_objective(): + num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10) + + assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_initial_population_multi_objective(): + num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=int, + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + + +# def test_number_duplicates_nested_gene_space_nested_gene_type_multi_objective(): + """ + This example causes duplicate genes that can only be solved by changing the values of a chain of genes. + Let's explain it using this solution: [0, 2, 3, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 6 at indices 5 and 6. + According to the gene space, none of these genes can has a different value that solves the duplicates. + -If the value of the gene at index 5 is changed from 6 to 5, then it causes another duplicate with the gene at index 4. + -If the value of the gene at index 6 is changed from 6 to 7, then it causes another duplicate with the gene at index 7. + The solution is to change a chain of genes that make a room to solve the duplicates between the 2 genes. + 1) Change the second gene from 2 to 1. + 2) Change the third gene from 3 to 2. + 3) Change the fourth gene from 4 to 3. + 4) Change the fifth gene from 5 to 4. + 5) Change the sixth gene from 6 to 5. This solves the duplicates. + But this is NOT SUPPORTED yet. + We support changing only a single gene that makes a room to solve the duplicates. + + Let's explain it using this solution: [1, 2, 2, 4, 5, 6, 6, 7, 8, 9] + It has 2 genes with the value 2 at indices 1 and 2. + This is how the duplicates are solved: + 1) Change the first gene from 1 to 0. + 2) Change the second gene from 2 to 1. This solves the duplicates. + The result is [0, 1, 2, 4, 5, 6, 6, 7, 8, 9] + """ + # num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + # [1, 2], + # [2, 3], + # [3, 4], + # [4, 5], + # [5, 6], + # [6, 7], + # [7, 8], + # [8, 9], + # [9, 10]], + # gene_type=[int, int, int, int, int, int, int, int, int, int], + # num_genes=10) + + # assert num_duplicates == 0 + +def test_number_duplicates_nested_gene_space_nested_gene_type_initial_population_multi_objective(): + num_duplicates = number_duplicate_genes(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + gene_type=[int, int, int, int, int, int, int, int, int, int], + num_genes=10, + initial_population=initial_population) + + assert num_duplicates == 0 + +if __name__ == "__main__": + #### Single-objective + print() + test_number_duplicates_default() + print() + test_number_duplicates_default_initial_population() + print() + + test_number_duplicates_float_gene_type() + print() + test_number_duplicates_float_gene_type_initial_population() + print() + + test_number_duplicates_int_gene_type() + print() + test_number_duplicates_int_gene_type_initial_population() + print() + + test_number_duplicates_int_gene_type_replacement() + print() + test_number_duplicates_int_gene_type_replacement_initial_population() + print() + + test_number_duplicates_single_gene_space() + print() + test_number_duplicates_single_gene_space_initial_population() + print() + + test_number_duplicates_single_range_gene_space() + print() + test_number_duplicates_single_range_gene_space_initial_population() + print() + + test_number_duplicates_single_numpy_range_gene_space() + print() + test_number_duplicates_single_numpy_range_gene_space_initial_population() + print() + + test_number_duplicates_nested_gene_space() + print() + test_number_duplicates_nested_gene_space_initial_population() + print() + + # This example causes duplicates that can only be solved by changing a chain of genes. + # test_number_duplicates_nested_gene_space_nested_gene_type() + # print() + test_number_duplicates_nested_gene_space_nested_gene_type_initial_population() + print() + + #### Multi-objective + print() + test_number_duplicates_default_initial_population_multi_objective() + print() + + test_number_duplicates_float_gene_type_multi_objective() + print() + test_number_duplicates_float_gene_type_initial_population_multi_objective() + print() + + test_number_duplicates_int_gene_type_multi_objective() + print() + test_number_duplicates_int_gene_type_initial_population_multi_objective() + print() + + test_number_duplicates_int_gene_type_replacement_multi_objective() + print() + test_number_duplicates_int_gene_type_replacement_initial_population_multi_objective() + print() + + test_number_duplicates_single_gene_space_multi_objective() + print() + test_number_duplicates_single_gene_space_initial_population_multi_objective() + print() + + test_number_duplicates_single_range_gene_space_multi_objective() + print() + test_number_duplicates_single_range_gene_space_initial_population_multi_objective() + print() + + test_number_duplicates_single_numpy_range_gene_space_multi_objective() + print() + test_number_duplicates_single_numpy_range_gene_space_initial_population_multi_objective() + print() + + test_number_duplicates_nested_gene_space_multi_objective() + print() + test_number_duplicates_nested_gene_space_initial_population_multi_objective() + print() + + # This example causes duplicates that can only be solved by changing a chain of genes. + # test_number_duplicates_nested_gene_space_nested_gene_type_multi_objective() + # print() + test_number_duplicates_nested_gene_space_nested_gene_type_initial_population_multi_objective() + print() + + diff --git a/test_crossover_mutation.py b/test_crossover_mutation.py new file mode 100644 index 0000000..acc3894 --- /dev/null +++ b/test_crossover_mutation.py @@ -0,0 +1,287 @@ +import pygad +import random +import numpy + +num_generations = 1 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +def output_crossover_mutation(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + initial_population=None, + crossover_probability=None, + mutation_probability=None, + crossover_type=None, + mutation_type=None, + parent_selection_type='sss', + multi_objective=False): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + parent_selection_type=parent_selection_type, + initial_population=initial_population, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=True, + mutation_by_replacement=mutation_by_replacement, + save_solutions=True, + crossover_probability=crossover_probability, + mutation_probability=mutation_probability, + crossover_type=crossover_type, + mutation_type=mutation_type, + suppress_warnings=True, + random_seed=1) + + ga_instance.run() + + comparison_result = [] + for solution_idx, solution in enumerate(ga_instance.population): + if list(solution) in ga_instance.initial_population.tolist(): + comparison_result.append(True) + else: + comparison_result.append(False) + + comparison_result = numpy.array(comparison_result) + result = numpy.all(comparison_result == True) + + print(f"Comparison result is {result}") + return result, ga_instance + +def test_no_crossover_no_mutation(): + result, ga_instance = output_crossover_mutation() + + assert result == True + +def test_no_crossover_no_mutation_gene_space(): + result, ga_instance = output_crossover_mutation(gene_space=range(10)) + + assert result == True + +def test_no_crossover_no_mutation_int_gene_type(): + result, ga_instance = output_crossover_mutation(gene_type=int) + + assert result == True + + +def test_no_crossover_no_mutation_gene_space_gene_type(): + result, ga_instance = output_crossover_mutation(gene_space={"low": 0, "high": 10}, + gene_type=[float, 2]) + + assert result == True + + +def test_no_crossover_no_mutation_nested_gene_space(): + result, ga_instance = output_crossover_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]]) + assert result == True + +def test_no_crossover_no_mutation_nested_gene_type(): + result, ga_instance = output_crossover_mutation(gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert result == True + +def test_no_crossover_no_mutation_nested_gene_space_nested_gene_type(): + result, ga_instance = output_crossover_mutation(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert result == True + +def test_no_crossover_no_mutation_initial_population(): + global initial_population + result, ga_instance = output_crossover_mutation(initial_population=initial_population) + + assert result == True + +def test_no_crossover_no_mutation_initial_population_nested_gene_type(): + global initial_population + result, ga_instance = output_crossover_mutation(initial_population=initial_population, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert result == True + +def test_crossover_no_mutation_zero_crossover_probability(): + global initial_population + result, ga_instance = output_crossover_mutation(crossover_type="single_point", + crossover_probability=0.0) + + assert result == True + +def test_zero_crossover_probability_zero_mutation_probability(): + global initial_population + result, ga_instance = output_crossover_mutation(crossover_type="single_point", + crossover_probability=0.0, + mutation_type="random", + mutation_probability=0.0) + + assert result == True + +def test_random_mutation_manual_call(): + result, ga_instance = output_crossover_mutation(mutation_type="random", + random_mutation_min_val=888, + random_mutation_max_val=999) + ga_instance.mutation_num_genes = 9 + + temp_offspring = numpy.array(initial_population[0:1]) + offspring = ga_instance.random_mutation(offspring=temp_offspring.copy()) + + comp = offspring - temp_offspring + comp_sorted = sorted(comp.copy()) + comp_sorted = numpy.abs(numpy.unique(comp_sorted)) + + # The other 1 added to include the last value in the range. + assert len(comp_sorted) in range(1, 1 + 1 + ga_instance.mutation_num_genes) + assert comp_sorted[0] == 0 + +def test_random_mutation_manual_call2(): + result, ga_instance = output_crossover_mutation(mutation_type="random", + random_mutation_min_val=888, + random_mutation_max_val=999) + ga_instance.mutation_num_genes = 10 + + temp_offspring = numpy.array(initial_population[0:1]) + offspring = ga_instance.random_mutation(offspring=temp_offspring.copy()) + + comp = offspring - temp_offspring + comp_sorted = sorted(comp.copy()) + comp_sorted = numpy.abs(numpy.unique(comp_sorted)) + + # The other 1 added to include the last value in the range. + assert len(comp_sorted) in range(1, 1 + 1 + ga_instance.mutation_num_genes) + # assert comp_sorted[0] == 0 + +def test_random_mutation_manual_call3(): + # Use random_mutation_min_val & random_mutation_max_val as numbers. + random_mutation_min_val = 888 + random_mutation_max_val = 999 + result, ga_instance = output_crossover_mutation(mutation_type="random", + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + mutation_by_replacement=True) + ga_instance.mutation_num_genes = 10 + + temp_offspring = numpy.array(initial_population[0:1]) + offspring = ga_instance.random_mutation(offspring=temp_offspring.copy()) + + comp = offspring + comp_sorted = sorted(comp.copy()) + comp_sorted = numpy.abs(numpy.unique(comp)) + + value_space = list(range(random_mutation_min_val, random_mutation_max_val)) + for value in comp_sorted: + assert value in value_space + +def test_random_mutation_manual_call4(): + # Use random_mutation_min_val & random_mutation_max_val as lists. + random_mutation_min_val = [888]*10 + random_mutation_max_val = [999]*10 + result, ga_instance = output_crossover_mutation(mutation_type="random", + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + mutation_by_replacement=True) + ga_instance.mutation_num_genes = 10 + + temp_offspring = numpy.array(initial_population[0:1]) + offspring = ga_instance.random_mutation(offspring=temp_offspring.copy()) + + comp = offspring + comp_sorted = sorted(comp.copy()) + comp_sorted = numpy.abs(numpy.unique(comp)) + + value_space = list(range(random_mutation_min_val[0], random_mutation_max_val[0])) + for value in comp_sorted: + assert value in value_space + +if __name__ == "__main__": + #### Single-objective + print() + test_no_crossover_no_mutation() + print() + + test_no_crossover_no_mutation_int_gene_type() + print() + + test_no_crossover_no_mutation_gene_space() + print() + + test_no_crossover_no_mutation_gene_space_gene_type() + print() + + test_no_crossover_no_mutation_nested_gene_space() + print() + + test_no_crossover_no_mutation_nested_gene_type() + print() + + test_no_crossover_no_mutation_initial_population() + print() + + test_no_crossover_no_mutation_initial_population_nested_gene_type() + print() + + test_crossover_no_mutation_zero_crossover_probability() + print() + + test_zero_crossover_probability_zero_mutation_probability() + print() + + test_random_mutation_manual_call() + print() + + test_random_mutation_manual_call2() + print() + + test_random_mutation_manual_call3() + print() + + test_random_mutation_manual_call4() + print() diff --git a/test_gene_space_allow_duplicate_genes.py b/test_gene_space_allow_duplicate_genes.py new file mode 100644 index 0000000..c35f388 --- /dev/null +++ b/test_gene_space_allow_duplicate_genes.py @@ -0,0 +1,1139 @@ +""" +This script is identical to the test_gene_space.py script except for: + Setting allow_duplicate_genes=False instead of True. +""" + +import pygad +import random +import numpy + +num_generations = 100 + +initial_population = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + +# Test single gene space with nested gene type. + +def number_respect_gene_space(gene_space=None, + gene_type=float, + num_genes=10, + mutation_by_replacement=False, + random_mutation_min_val=-1, + random_mutation_max_val=1, + init_range_low=-4, + init_range_high=4, + initial_population=None, + parent_selection_type='sss', + multi_objective=False): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga, solution, idx): + return [random.random(), random.random()] + + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + parent_selection_type=parent_selection_type, + num_genes=num_genes, + gene_space=gene_space, + gene_type=gene_type, + initial_population=initial_population, + init_range_low=init_range_low, + init_range_high=init_range_high, + random_mutation_min_val=random_mutation_min_val, + random_mutation_max_val=random_mutation_max_val, + allow_duplicate_genes=False, + mutation_by_replacement=mutation_by_replacement, + save_solutions=True, + suppress_warnings=True, + random_seed=2) + + ga_instance.run() + ga_instance.solutions = numpy.array(ga_instance.solutions, + dtype=object) + + # gene_space_unpacked = ga_instance.unpack_gene_space(num_values_from_inf_range=100) + num_outside = 0 + if ga_instance.gene_space_nested == True: + for gene_idx in range(ga_instance.num_genes): + all_gene_values = ga_instance.solutions[:, gene_idx] + if type(ga_instance.gene_space[gene_idx]) in [list, tuple, range, numpy.ndarray]: + current_gene_space = list(ga_instance.gene_space[gene_idx]) + for val in all_gene_values: + if val in current_gene_space: + # print(val, current_gene_space) + pass + else: + # print(gene_idx, val, current_gene_space) + num_outside += 1 + elif type(ga_instance.gene_space[gene_idx]) is dict: + if not "step" in ga_instance.gene_space[gene_idx].keys(): + for val in all_gene_values: + if val >= ga_instance.gene_space[gene_idx]["low"] and val < ga_instance.gene_space[gene_idx]["high"]: + pass + else: + num_outside += 1 + else: + gene_space_values = numpy.arange(ga_instance.gene_space[gene_idx]["low"], + ga_instance.gene_space[gene_idx]["high"], + ga_instance.gene_space[gene_idx]["step"]) + for val in all_gene_values: + if val in gene_space_values: + pass + else: + num_outside += 1 + elif type(ga_instance.gene_space[gene_idx]) in ga_instance.supported_int_float_types: + for val in all_gene_values: + if val == ga_instance.gene_space[gene_idx]: + pass + else: + num_outside += 1 + else: + for gene_idx in range(ga_instance.num_genes): + all_gene_values = ga_instance.solutions[:, gene_idx] + # print("all_gene_values", gene_idx, all_gene_values) + if type(ga_instance.gene_space) in [list, tuple, range, numpy.ndarray]: + current_gene_space = list(ga_instance.gene_space) + for val in all_gene_values: + if val in current_gene_space: + pass + else: + num_outside += 1 + elif type(ga_instance.gene_space) is dict: + if not "step" in ga_instance.gene_space.keys(): + for val in all_gene_values: + if val >= ga_instance.gene_space["low"] and val < ga_instance.gene_space["high"]: + pass + else: + num_outside += 1 + else: + gene_space_values = numpy.arange(ga_instance.gene_space["low"], + ga_instance.gene_space["high"], + ga_instance.gene_space["step"]) + for val in all_gene_values: + if val in gene_space_values: + pass + else: + num_outside += 1 + + print(f"Number of outside range is {num_outside}.") + return num_outside, ga_instance + +#### Single-Objective +def test_gene_space_range(): + num_outside, _ = number_respect_gene_space(gene_space=range(10)) + + assert num_outside == 0 + +def test_gene_space_numpy_arange(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10)) + + assert num_outside == 0 + +def test_gene_space_list(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10))) + + assert num_outside == 0 + +def test_gene_space_numpy(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10)))) + + assert num_outside == 0 + +def test_gene_space_dict_without_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}) + + assert num_outside == 0 + +def test_gene_space_dict_with_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}) + + assert num_outside == 0 + +def test_gene_space_list_single_value(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5]) + + assert num_outside == 0 + +def test_gene_space_range_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_list_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_numpy_nested_gene_type(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_dict_without_step_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + assert num_outside == 0 + +def test_gene_space_dict_with_step_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_gene_space_list_single_value_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_nested_gene_space_range(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[range(0, 10), + range(10, 20), + range(20, 30), + range(30, 40), + range(40, 50), + range(50, 60), + range(60, 70), + range(70, 80), + range(80, 90), + range(90, 100)]) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}]) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_float_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + gene_type=[float, 3]) + + assert num_outside == 0 + +def test_nested_gene_space_dict_with_step(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10, "step": 1}, + {"low": 10, "high": 20, "step": 1.5}, + {"low": 20, "high": 30, "step": 2}, + {"low": 30, "high": 40, "step": 2.5}, + {"low": 40, "high": 50, "step": 3}, + {"low": 50, "high": 60, "step": 3.5}, + {"low": 60, "high": 70, "step": 4}, + {"low": 70, "high": 80, "step": 4.5}, + {"low": 80, "high": 90, "step": 5}, + {"low": 90, "high": 100, "step": 5.5}]) + + assert num_outside == 0 + + +def test_nested_gene_space_numpy_arange(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[numpy.arange(0, 10), + numpy.arange(10, 20), + numpy.arange(20, 30), + numpy.arange(30, 40), + numpy.arange(40, 50), + numpy.arange(50, 60), + numpy.arange(60, 70), + numpy.arange(70, 80), + numpy.arange(80, 90), + numpy.arange(90, 100)]) + + assert num_outside == 0 + +def test_nested_gene_space_list(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [-10, 10, 20, 30, 40, 50, 60, 70, 80, 90], + [-11, 11, 22, 33, 44, 55, 66, 77, 88, 99], + [-100, 100, 200, 300, 400, 500, 600, 700, 800, 900], + [-4.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], + [-5.1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9], + [-10.5, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], + [-15, 15, 25, 35, 45, 55, 65, 75, 85, 95], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]]) + + assert num_outside == 0 + +def test_nested_gene_space_list2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]]) + + assert num_outside == 0 + +def test_nested_gene_space_mix(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=int) + + assert num_outside == 0 + +def test_nested_gene_space_mix_nested_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 00, "high": 10, "step": 1}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]]) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_single_gene_type(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[float, 4]) + + assert num_outside == 0 + +#### Multi-Objective +def test_gene_space_range_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_without_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_with_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_single_value_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_range_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_nested_gene_type_multi_objective(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_dict_without_step_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + assert num_outside == 0 + +def test_gene_space_dict_with_step_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_list_single_value_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_range_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[range(0, 10), + range(10, 20), + range(20, 30), + range(30, 40), + range(40, 50), + range(50, 60), + range(60, 70), + range(70, 80), + range(80, 90), + range(90, 100)], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_float_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + gene_type=[float, 3], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_dict_with_step_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10, "step": 1}, + {"low": 10, "high": 20, "step": 1.5}, + {"low": 20, "high": 30, "step": 2}, + {"low": 30, "high": 40, "step": 2.5}, + {"low": 40, "high": 50, "step": 3}, + {"low": 50, "high": 60, "step": 3.5}, + {"low": 60, "high": 70, "step": 4}, + {"low": 70, "high": 80, "step": 4.5}, + {"low": 80, "high": 90, "step": 5}, + {"low": 90, "high": 100, "step": 5.5}], + multi_objective=True) + + assert num_outside == 0 + + +def test_nested_gene_space_numpy_arange_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[numpy.arange(0, 10), + numpy.arange(10, 20), + numpy.arange(20, 30), + numpy.arange(30, 40), + numpy.arange(40, 50), + numpy.arange(50, 60), + numpy.arange(60, 70), + numpy.arange(70, 80), + numpy.arange(80, 90), + numpy.arange(90, 100)], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_list_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [-10, 10, 20, 30, 40, 50, 60, 70, 80, 90], + [-11, 11, 22, 33, 44, 55, 66, 77, 88, 99], + [-100, 100, 200, 300, 400, 500, 600, 700, 800, 900], + [-4.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], + [-5.1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9], + [-10.5, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], + [-15, 15, 25, 35, 45, 55, 65, 75, 85, 95], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_list2_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=int, + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_nested_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 00, "high": 10, "step": 1}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[float, 4], + multi_objective=True) + + assert num_outside == 0 + +#### Multi-Objective NSGA-II Parent Selection +def test_gene_space_range_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_arange_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_without_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_with_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_single_value_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_range_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=range(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True) + + assert num_outside == 0 + +def test_gene_space_numpy_arange_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.arange(10), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=list(range(10)), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_numpy_nested_gene_type_multi_objective_nsga2(): + num_outside, _ = number_respect_gene_space(gene_space=numpy.array(list(range(10))), + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_dict_without_step_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + assert num_outside == 0 + +def test_gene_space_dict_with_step_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space={"low": 0, "high": 10, "step": 2}, + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_gene_space_list_single_value_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[5], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_range_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[range(0, 10), + range(10, 20), + range(20, 30), + range(30, 40), + range(40, 50), + range(50, 60), + range(60, 70), + range(70, 80), + range(80, 90), + range(90, 100)], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_dict_without_step_float_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10}, + {"low": 10, "high": 20}, + {"low": 20, "high": 30}, + {"low": 30, "high": 40}, + {"low": 40, "high": 50}, + {"low": 50, "high": 60}, + {"low": 60, "high": 70}, + {"low": 70, "high": 80}, + {"low": 80, "high": 90}, + {"low": 90, "high": 100}], + gene_type=[float, 3], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_dict_with_step_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[{"low": 0, "high": 10, "step": 1}, + {"low": 10, "high": 20, "step": 1.5}, + {"low": 20, "high": 30, "step": 2}, + {"low": 30, "high": 40, "step": 2.5}, + {"low": 40, "high": 50, "step": 3}, + {"low": 50, "high": 60, "step": 3.5}, + {"low": 60, "high": 70, "step": 4}, + {"low": 70, "high": 80, "step": 4.5}, + {"low": 80, "high": 90, "step": 5}, + {"low": 90, "high": 100, "step": 5.5}], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + + +def test_nested_gene_space_numpy_arange_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[numpy.arange(0, 10), + numpy.arange(10, 20), + numpy.arange(20, 30), + numpy.arange(30, 40), + numpy.arange(40, 50), + numpy.arange(50, 60), + numpy.arange(60, 70), + numpy.arange(70, 80), + numpy.arange(80, 90), + numpy.arange(90, 100)], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_list_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [-10, 10, 20, 30, 40, 50, 60, 70, 80, 90], + [-11, 11, 22, 33, 44, 55, 66, 77, 88, 99], + [-100, 100, 200, 300, 400, 500, 600, 700, 800, 900], + [-4.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], + [-5.1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9], + [-10.5, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], + [-15, 15, 25, 35, 45, 55, 65, 75, 85, 95], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_list2_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [9, 10]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=int, + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_nested_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4], + numpy.arange(5, 10), + range(10, 15), + {"low": 15, "high": 20}, + {"low": 20, "high": 30, "step": 2}, + None, + numpy.arange(30, 35), + numpy.arange(35, 40), + numpy.arange(40, 45), + [45, 46, 47, 48, 49]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 00, "high": 10, "step": 1}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[int, float, numpy.float64, [float, 3], [float, 4], numpy.int16, [numpy.float32, 1], int, float, [float, 3]], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + +def test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective_nsga2(): + num_outside, ga_instance = number_respect_gene_space(gene_space=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + numpy.arange(0, 10), + range(0, 10), + {"low": 0, "high": 10}, + {"low": 0, "high": 10}, + range(0, 10), + numpy.arange(0, 10), + numpy.arange(0, 10), + {"low": 0, "high": 10}, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], + gene_type=[float, 4], + multi_objective=True, + parent_selection_type='nsga2') + + assert num_outside == 0 + + +if __name__ == "__main__": + #### Single-objective + print() + test_gene_space_range() + print() + test_gene_space_range_nested_gene_type() + print() + + test_gene_space_numpy_arange() + print() + test_gene_space_numpy_arange_nested_gene_type() + print() + + test_gene_space_list() + print() + test_gene_space_list_nested_gene_type() + print() + + test_gene_space_list_single_value() + print() + test_gene_space_list_single_value_nested_gene_type() + print() + + test_gene_space_numpy() + print() + test_gene_space_numpy_nested_gene_type() + print() + + test_gene_space_dict_without_step() + print() + test_gene_space_dict_without_step_nested_gene_type() + print() + + test_gene_space_dict_with_step() + print() + test_gene_space_dict_with_step_nested_gene_type() + print() + + test_nested_gene_space_range() + print() + + test_nested_gene_space_dict_without_step() + print() + + test_nested_gene_space_dict_without_step_float_gene_type() + print() + + test_nested_gene_space_dict_with_step() + print() + + test_nested_gene_space_numpy_arange() + print() + + test_nested_gene_space_list() + print() + + test_nested_gene_space_list2() + print() + + test_nested_gene_space_mix() + print() + + test_nested_gene_space_mix_nested_gene_type() + print() + + test_nested_gene_space_mix_initial_population() + print() + + test_nested_gene_space_mix_initial_population_single_gene_type() + print() + + + + #### Multi-objective + print() + test_gene_space_range_multi_objective() + print() + test_gene_space_range_nested_gene_type_multi_objective() + print() + + test_gene_space_numpy_arange_multi_objective() + print() + test_gene_space_numpy_arange_nested_gene_type_multi_objective() + print() + + test_gene_space_list_multi_objective() + print() + test_gene_space_list_nested_gene_type_multi_objective() + print() + + test_gene_space_list_single_value_multi_objective() + print() + test_gene_space_list_single_value_nested_gene_type_multi_objective() + print() + + test_gene_space_numpy_multi_objective() + print() + test_gene_space_numpy_nested_gene_type_multi_objective() + print() + + test_gene_space_dict_without_step_multi_objective() + print() + test_gene_space_dict_without_step_nested_gene_type_multi_objective() + print() + + test_gene_space_dict_with_step_multi_objective() + print() + test_gene_space_dict_with_step_nested_gene_type_multi_objective() + print() + + test_nested_gene_space_range_multi_objective() + print() + + test_nested_gene_space_dict_without_step_multi_objective() + print() + + test_nested_gene_space_dict_without_step_float_gene_type_multi_objective() + print() + + test_nested_gene_space_dict_with_step_multi_objective() + print() + + test_nested_gene_space_numpy_arange_multi_objective() + print() + + test_nested_gene_space_list_multi_objective() + print() + + test_nested_gene_space_list2_multi_objective() + print() + + test_nested_gene_space_mix_multi_objective() + print() + + test_nested_gene_space_mix_nested_gene_type_multi_objective() + print() + + test_nested_gene_space_mix_initial_population_multi_objective() + print() + + test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective() + print() + + + #### Multi-objective NSGA-II Parent Selection + print() + test_gene_space_range_multi_objective_nsga2() + print() + test_gene_space_range_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_numpy_arange_multi_objective_nsga2() + print() + test_gene_space_numpy_arange_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_list_multi_objective_nsga2() + print() + test_gene_space_list_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_list_single_value_multi_objective_nsga2() + print() + test_gene_space_list_single_value_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_numpy_multi_objective_nsga2() + print() + test_gene_space_numpy_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_dict_without_step_multi_objective_nsga2() + print() + test_gene_space_dict_without_step_nested_gene_type_multi_objective_nsga2() + print() + + test_gene_space_dict_with_step_multi_objective_nsga2() + + print() + test_gene_space_dict_with_step_nested_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_range_multi_objective_nsga2() + print() + + test_nested_gene_space_dict_without_step_multi_objective_nsga2() + print() + + test_nested_gene_space_dict_without_step_float_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_dict_with_step_multi_objective_nsga2() + print() + + test_nested_gene_space_numpy_arange_multi_objective_nsga2() + print() + + test_nested_gene_space_list_multi_objective_nsga2() + print() + + test_nested_gene_space_list2_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_nested_gene_type_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_initial_population_multi_objective_nsga2() + print() + + test_nested_gene_space_mix_initial_population_single_gene_type_multi_objective_nsga2() + print() + + diff --git a/test_lifecycle_callbacks_calls.py b/test_lifecycle_callbacks_calls.py new file mode 100644 index 0000000..b3159e8 --- /dev/null +++ b/test_lifecycle_callbacks_calls.py @@ -0,0 +1,247 @@ +import pygad +import random + +num_generations = 100 + +def number_lifecycle_callback_functions_calls(stop_criteria=None, + on_generation_stop=None, + crossover_type="single_point", + mutation_type="random"): + actual_num_callbacks_calls = 0 + + def fitness_func(ga_instanse, solution, solution_idx): + return random.random() + + def on_start(ga_instance): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_fitness(ga_instance, population_fitness): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_parents(ga_instance, selected_parents): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_crossover(ga_instance, offspring_crossover): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_mutation(ga_instance, offspring_mutation): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_generation(ga_instance): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + if on_generation_stop: + if ga_instance.generations_completed == on_generation_stop: + return "stop" + + def on_stop(ga_instance, last_population_fitness): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=fitness_func, + sol_per_pop=10, + num_genes=5, + crossover_type=crossover_type, + mutation_type=mutation_type, + on_start=on_start, + on_fitness=on_fitness, + on_parents=on_parents, + on_crossover=on_crossover, + on_mutation=on_mutation, + on_generation=on_generation, + on_stop=on_stop, + stop_criteria=stop_criteria, + suppress_warnings=True) + + ga_instance.run() + + # The total number is: + # 1 [for on_start()] + + # num_generations [for on_fitness()] + + # num_generations [for on_parents()] + + # num_generations [for on_crossover()] + + # num_generations [for on_mutation()] + + # num_generations [for on_generation()] + + # 1 [for on_stop()] + # = 1 + num_generations * 5 + 1 + + # Use 'generations_completed' instead of 'num_generations' because the evolution may stops in the on_generation() callback. + expected_num_callbacks_calls = 1 + ga_instance.generations_completed * 5 + 1 + + print(f"Expected {expected_num_callbacks_calls}.") + print(f"Actual {actual_num_callbacks_calls}.") + return actual_num_callbacks_calls, expected_num_callbacks_calls + +def number_lifecycle_callback_methods_calls(stop_criteria=None, + on_generation_stop=None, + crossover_type="single_point", + mutation_type="random"): + actual_num_callbacks_calls = 0 + + class Callbacks: + def fitness_func(self, ga_instanse, solution, solution_idx): + return 1 + + def on_start(self, ga_instance): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_fitness(self, ga_instance, population_fitness): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_parents(self, ga_instance, selected_parents): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_crossover(self, ga_instance, offspring_crossover): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_mutation(self, ga_instance, offspring_mutation): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + def on_generation(self, ga_instance): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + if on_generation_stop: + if ga_instance.generations_completed == on_generation_stop: + return "stop" + + def on_stop(self, ga_instance, last_population_fitness): + nonlocal actual_num_callbacks_calls + actual_num_callbacks_calls = actual_num_callbacks_calls + 1 + + Callbacks_obj = Callbacks() + ga_instance = pygad.GA(num_generations=num_generations, + num_parents_mating=5, + fitness_func=Callbacks_obj.fitness_func, + sol_per_pop=10, + num_genes=5, + crossover_type=crossover_type, + mutation_type=mutation_type, + on_start=Callbacks_obj.on_start, + on_fitness=Callbacks_obj.on_fitness, + on_parents=Callbacks_obj.on_parents, + on_crossover=Callbacks_obj.on_crossover, + on_mutation=Callbacks_obj.on_mutation, + on_generation=Callbacks_obj.on_generation, + on_stop=Callbacks_obj.on_stop, + stop_criteria=stop_criteria, + suppress_warnings=True) + + ga_instance.run() + + # The total number is: + # 1 [for on_start()] + + # num_generations [for on_fitness()] + + # num_generations [for on_parents()] + + # num_generations [for on_crossover()] + + # num_generations [for on_mutation()] + + # num_generations [for on_generation()] + + # 1 [for on_stop()] + # = 1 + num_generations * 5 + 1 + + # Use 'generations_completed' instead of 'num_generations' because the evolution may stops in the on_generation() callback. + expected_num_callbacks_calls = 1 + ga_instance.generations_completed * 5 + 1 + + print(f"Expected {expected_num_callbacks_calls}.") + print(f"Actual {actual_num_callbacks_calls}.") + return actual_num_callbacks_calls, expected_num_callbacks_calls + +def test_number_lifecycle_callback_functions_calls(): + actual, expected = number_lifecycle_callback_functions_calls() + + assert actual == expected + +def test_number_lifecycle_callback_functions_calls_stop_criteria(): + actual, expected = number_lifecycle_callback_functions_calls(on_generation_stop=30) + + assert actual == expected + +def test_number_lifecycle_callback_methods_calls(): + actual, expected = number_lifecycle_callback_methods_calls() + + assert actual == expected + +def test_number_lifecycle_callback_methods_calls_stop_criteria(): + actual, expected = number_lifecycle_callback_methods_calls(on_generation_stop=30) + + assert actual == expected + +def test_number_lifecycle_callback_functions_calls_no_crossover(): + actual, expected = number_lifecycle_callback_functions_calls(crossover_type=None) + + assert actual == expected + +def test_number_lifecycle_callback_functions_calls_no_mutation(): + actual, expected = number_lifecycle_callback_functions_calls(mutation_type=None) + + assert actual == expected + +def test_number_lifecycle_callback_functions_calls_no_crossover_no_mutation(): + actual, expected = number_lifecycle_callback_functions_calls(crossover_type=None, + mutation_type=None) + + assert actual == expected + +def test_number_lifecycle_callback_methods_calls_no_crossover(): + actual, expected = number_lifecycle_callback_methods_calls(crossover_type=None) + + assert actual == expected + +def test_number_lifecycle_callback_methods_calls_no_mutation(): + actual, expected = number_lifecycle_callback_methods_calls(mutation_type=None) + + assert actual == expected + +def test_number_lifecycle_callback_methods_calls_no_crossover_no_mutation(): + actual, expected = number_lifecycle_callback_methods_calls(crossover_type=None, + mutation_type=None) + + assert actual == expected + +if __name__ == "__main__": + print() + test_number_lifecycle_callback_functions_calls() + print() + + test_number_lifecycle_callback_functions_calls_stop_criteria() + print() + + test_number_lifecycle_callback_methods_calls() + print() + + test_number_lifecycle_callback_methods_calls_stop_criteria() + print() + + test_number_lifecycle_callback_functions_calls_no_crossover() + print() + + test_number_lifecycle_callback_functions_calls_no_crossover() + print() + + test_number_lifecycle_callback_functions_calls_no_mutation() + print() + + test_number_lifecycle_callback_functions_calls_no_crossover_no_mutation() + print() + + test_number_lifecycle_callback_methods_calls_no_crossover() + print() + + test_number_lifecycle_callback_methods_calls_no_mutation() + print() + + test_number_lifecycle_callback_methods_calls_no_crossover_no_mutation() + print() diff --git a/test_number_fitness_function_calls.py b/test_number_fitness_function_calls.py new file mode 100644 index 0000000..916a88f --- /dev/null +++ b/test_number_fitness_function_calls.py @@ -0,0 +1,597 @@ +import pygad +import random +import numpy + +actual_num_fitness_calls_default_keep = 0 +actual_num_fitness_calls_no_keep = 0 +actual_num_fitness_calls_keep_elitism = 0 +actual_num_fitness_calls_keep_parents = 0 + +num_generations = 100 +sol_per_pop = 10 +num_parents_mating = 5 + +# TODO: Calculate the number when fitness_batch_size is used. + +def number_calls_fitness_function(keep_elitism=1, + keep_parents=-1, + mutation_type="random", + mutation_percent_genes="default", + parent_selection_type='sss', + multi_objective=False, + fitness_batch_size=None): + + actual_num_fitness_calls = 0 + def fitness_func_no_batch_single(ga, solution, idx): + nonlocal actual_num_fitness_calls + actual_num_fitness_calls = actual_num_fitness_calls + 1 + return random.random() + + def fitness_func_no_batch_multi(ga_instance, solution, solution_idx): + nonlocal actual_num_fitness_calls + actual_num_fitness_calls = actual_num_fitness_calls + 1 + return [random.random(), random.random()] + + def fitness_func_batch_single(ga_instance, solution, solution_idx): + nonlocal actual_num_fitness_calls + actual_num_fitness_calls = actual_num_fitness_calls + 1 + f = [] + for sol in solution: + f.append(random.random()) + return f + + def fitness_func_batch_multi(ga_instance, solution, solution_idx): + nonlocal actual_num_fitness_calls + actual_num_fitness_calls = actual_num_fitness_calls + 1 + f = [] + for sol in solution: + f.append([random.random(), random.random()]) + return f + + if fitness_batch_size is None or (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size == 1): + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + elif (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size > 1): + if multi_objective == True: + fitness_func = fitness_func_batch_multi + else: + fitness_func = fitness_func_batch_single + + ga_optimizer = pygad.GA(num_generations=num_generations, + sol_per_pop=sol_per_pop, + num_genes=6, + num_parents_mating=num_parents_mating, + fitness_func=fitness_func, + mutation_type=mutation_type, + parent_selection_type=parent_selection_type, + mutation_percent_genes=mutation_percent_genes, + keep_elitism=keep_elitism, + keep_parents=keep_parents, + suppress_warnings=True, + fitness_batch_size=fitness_batch_size) + + ga_optimizer.run() + + if fitness_batch_size is None: + if keep_elitism == 0: + if keep_parents == 0: + # 10 (for initial population) + 100*10 (for other generations) = 1010 + expected_num_fitness_calls = sol_per_pop + num_generations * sol_per_pop + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * sol_per_pop + elif keep_parents == -1: + # 10 (for initial population) + 100*num_parents_mating (for other generations) + expected_num_fitness_calls = sol_per_pop + num_generations * (sol_per_pop - num_parents_mating) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * (sol_per_pop - num_parents_mating) + else: + # 10 (for initial population) + 100*keep_parents (for other generations) + expected_num_fitness_calls = sol_per_pop + num_generations * (sol_per_pop - keep_parents) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * (sol_per_pop - keep_parents) + else: + # 10 (for initial population) + 100*keep_elitism (for other generations) + expected_num_fitness_calls = sol_per_pop + num_generations * (sol_per_pop - keep_elitism) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * (sol_per_pop - keep_elitism) + else: + if keep_elitism == 0: + if keep_parents == 0: + # 10 (for initial population) + 100*10 (for other generations) = 1010 + expected_num_fitness_calls = int(numpy.ceil(sol_per_pop/fitness_batch_size)) + num_generations * int(numpy.ceil(sol_per_pop/fitness_batch_size)) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * int(numpy.ceil(sol_per_pop/fitness_batch_size)) + elif keep_parents == -1: + # 10 (for initial population) + 100*num_parents_mating (for other generations) + expected_num_fitness_calls = int(numpy.ceil(sol_per_pop/fitness_batch_size)) + num_generations * int(numpy.ceil((sol_per_pop - num_parents_mating)/fitness_batch_size)) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * int(numpy.ceil((sol_per_pop - num_parents_mating)/fitness_batch_size)) + else: + # 10 (for initial population) + 100*keep_parents (for other generations) + expected_num_fitness_calls = int(numpy.ceil(sol_per_pop/fitness_batch_size)) + num_generations * int(numpy.ceil((sol_per_pop - keep_parents)/fitness_batch_size)) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * int(numpy.ceil((sol_per_pop - keep_parents)/fitness_batch_size)) + else: + # 10 (for initial population) + 100*keep_elitism (for other generations) + expected_num_fitness_calls = int(numpy.ceil(sol_per_pop/fitness_batch_size)) + num_generations * int(numpy.ceil((sol_per_pop - keep_elitism)/fitness_batch_size)) + if mutation_type == "adaptive": + expected_num_fitness_calls += num_generations * int(numpy.ceil((sol_per_pop - keep_elitism)/fitness_batch_size)) + + print(f"Expected number of fitness function calls is {expected_num_fitness_calls}.") + print(f"Actual number of fitness function calls is {actual_num_fitness_calls}.") + return actual_num_fitness_calls, expected_num_fitness_calls + +def test_number_calls_fitness_function_default_keep(): + actual, expected = number_calls_fitness_function() + assert actual == expected + +def test_number_calls_fitness_function_no_keep(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0) + assert actual == expected + +def test_number_calls_fitness_function_keep_elitism(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=0) + assert actual == expected + +def test_number_calls_fitness_function_keep_parents(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_adaptive_mutation(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5]) + assert actual == expected + +def test_number_calls_fitness_function_default_adaptive_mutation(): + actual, expected = number_calls_fitness_function(mutation_type="adaptive", + mutation_percent_genes=[10, 5]) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_adaptive_mutation(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5]) + assert actual == expected + +#### Multi Objective +def test_number_calls_fitness_function_no_keep_multi_objective(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_keep_elitism_multi_objective(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=0, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_keep_parents_multi_objective(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=4, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_multi_objective(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_default_adaptive_mutation_multi_objective(): + actual, expected = number_calls_fitness_function(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert actual == expected + +#### Multi Objective NSGA-II Parent Selection +def test_number_calls_fitness_function_no_keep_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_keep_elitism_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=0, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_keep_parents_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=4, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_default_adaptive_mutation_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective_nsga2(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert actual == expected + + +######## Batch Fitness Calculation +#### Single Objective +def test_number_calls_fitness_function_no_keep_batch_1(): + actual, expected = number_calls_fitness_function(fitness_batch_size=1) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_batch_4(): + actual, expected = number_calls_fitness_function(fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_batch_9(): + actual, expected = number_calls_fitness_function(fitness_batch_size=9) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_batch_10(): + actual, expected = number_calls_fitness_function(fitness_batch_size=10) + assert actual == expected + +def test_number_calls_fitness_function_keep_elitism_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=0, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_keep_parents_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=4, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_adaptive_mutation_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_default_adaptive_mutation_batch_4(): + actual, expected = number_calls_fitness_function(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_adaptive_mutation_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + fitness_batch_size=4) + assert actual == expected + +#### Multi Objective +def test_number_calls_fitness_function_no_keep_multi_objective_batch_1(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=1) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_multi_objective_batch_9(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=9) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_multi_objective_batch_10(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=10) + assert actual == expected + +def test_number_calls_fitness_function_keep_elitism_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=0, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_keep_parents_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_default_adaptive_mutation_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +#### Multi Objective NSGA-II Parent Selection +def test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_1(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=1) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_9(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=9) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_10(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + multi_objective=True, + fitness_batch_size=10) + assert actual == expected + +def test_number_calls_fitness_function_keep_elitism_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=0, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_keep_parents_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_default_adaptive_mutation_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + +def test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective_nsga2_batch_4(): + actual, expected = number_calls_fitness_function(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert actual == expected + + +if __name__ == "__main__": + #### Single-objective + print() + test_number_calls_fitness_function_default_keep() + print() + test_number_calls_fitness_function_no_keep() + print() + test_number_calls_fitness_function_keep_elitism() + print() + test_number_calls_fitness_function_keep_parents() + print() + test_number_calls_fitness_function_both_keep() + print() + test_number_calls_fitness_function_no_keep_adaptive_mutation() + print() + test_number_calls_fitness_function_default_adaptive_mutation() + print() + test_number_calls_fitness_function_both_keep_adaptive_mutation() + print() + + #### Multi-Objective + print() + test_number_calls_fitness_function_no_keep_multi_objective() + print() + test_number_calls_fitness_function_keep_elitism_multi_objective() + print() + test_number_calls_fitness_function_keep_parents_multi_objective() + print() + test_number_calls_fitness_function_both_keep_multi_objective() + print() + test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective() + print() + test_number_calls_fitness_function_default_adaptive_mutation_multi_objective() + print() + test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective() + print() + + #### Multi-Objective NSGA-II Parent Selection + print() + test_number_calls_fitness_function_no_keep_multi_objective_nsga2() + print() + test_number_calls_fitness_function_keep_elitism_multi_objective_nsga2() + print() + test_number_calls_fitness_function_keep_parents_multi_objective_nsga2() + print() + test_number_calls_fitness_function_both_keep_multi_objective_nsga2() + print() + test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective_nsga2() + print() + test_number_calls_fitness_function_default_adaptive_mutation_multi_objective_nsga2() + print() + test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective_nsga2() + print() + + + ######## Batch Fitness + #### Single-objective + print() + test_number_calls_fitness_function_no_keep_batch_1() + print() + test_number_calls_fitness_function_no_keep_batch_4() + print() + test_number_calls_fitness_function_no_keep_batch_9() + print() + test_number_calls_fitness_function_no_keep_batch_10() + print() + test_number_calls_fitness_function_keep_elitism_batch_4() + print() + test_number_calls_fitness_function_keep_parents_batch_4() + print() + test_number_calls_fitness_function_both_keep_batch_4() + print() + test_number_calls_fitness_function_no_keep_adaptive_mutation_batch_4() + print() + test_number_calls_fitness_function_default_adaptive_mutation_batch_4() + print() + test_number_calls_fitness_function_both_keep_adaptive_mutation_batch_4() + print() + + #### Multi-Objective + print() + test_number_calls_fitness_function_no_keep_multi_objective_batch_1() + print() + test_number_calls_fitness_function_no_keep_multi_objective_batch_4() + print() + test_number_calls_fitness_function_no_keep_multi_objective_batch_9() + print() + test_number_calls_fitness_function_no_keep_multi_objective_batch_10() + print() + test_number_calls_fitness_function_keep_elitism_multi_objective_batch_4() + print() + test_number_calls_fitness_function_keep_parents_multi_objective_batch_4() + print() + test_number_calls_fitness_function_both_keep_multi_objective_batch_4() + print() + test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective_batch_4() + print() + test_number_calls_fitness_function_default_adaptive_mutation_multi_objective_batch_4() + print() + test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective_batch_4() + print() + + #### Multi-Objective NSGA-II Parent Selection + print() + test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_1() + print() + test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_4() + print() + test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_9() + print() + test_number_calls_fitness_function_no_keep_multi_objective_nsga2_batch_10() + print() + test_number_calls_fitness_function_keep_elitism_multi_objective_nsga2_batch_4() + print() + test_number_calls_fitness_function_keep_parents_multi_objective_nsga2_batch_4() + print() + test_number_calls_fitness_function_both_keep_multi_objective_nsga2_batch_4() + print() + test_number_calls_fitness_function_no_keep_adaptive_mutation_multi_objective_nsga2_batch_4() + print() + test_number_calls_fitness_function_default_adaptive_mutation_multi_objective_nsga2_batch_4() + print() + test_number_calls_fitness_function_both_keep_adaptive_mutation_multi_objective_nsga2_batch_4() + print() + diff --git a/test_save_solutions.py b/test_save_solutions.py new file mode 100644 index 0000000..537f4ea --- /dev/null +++ b/test_save_solutions.py @@ -0,0 +1,1289 @@ +import pygad +import random + +num_generations = 100 +sol_per_pop = 10 +num_parents_mating = 5 + +# TODO Verify that each entry in 'solutions_fitness' and 'best_solutions_fitness' has values equal to the number of objectives. + +def number_saved_solutions(keep_elitism=1, + keep_parents=-1, + mutation_type="random", + mutation_percent_genes="default", + parent_selection_type='sss', + multi_objective=False, + fitness_batch_size=None, + save_solutions=False, + save_best_solutions=False): + + def fitness_func_no_batch_single(ga, solution, idx): + return random.random() + + def fitness_func_no_batch_multi(ga_instance, solution, solution_idx): + return [random.random(), random.random()] + + def fitness_func_batch_single(ga_instance, solution, solution_idx): + f = [] + for sol in solution: + f.append(random.random()) + return f + + def fitness_func_batch_multi(ga_instance, solution, solution_idx): + f = [] + for sol in solution: + f.append([random.random(), random.random()]) + return f + + if fitness_batch_size is None or (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size == 1): + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + elif (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size > 1): + if multi_objective == True: + fitness_func = fitness_func_batch_multi + else: + fitness_func = fitness_func_batch_single + + ga_optimizer = pygad.GA(num_generations=num_generations, + sol_per_pop=sol_per_pop, + num_genes=6, + num_parents_mating=num_parents_mating, + fitness_func=fitness_func, + mutation_type=mutation_type, + parent_selection_type=parent_selection_type, + mutation_percent_genes=mutation_percent_genes, + keep_elitism=keep_elitism, + keep_parents=keep_parents, + suppress_warnings=True, + fitness_batch_size=fitness_batch_size, + save_best_solutions=save_best_solutions, + save_solutions=save_solutions) + + ga_optimizer.run() + + if save_solutions == True: + expected_num_solutions = sol_per_pop + num_generations * sol_per_pop + else: + expected_num_solutions = 0 + + if save_best_solutions == True: + expected_num_best_solutions = 1 + num_generations + else: + expected_num_best_solutions = 0 + + print(f"Expected number of solutions is {expected_num_solutions}.") + print(f"Actual number of solutions is {len(ga_optimizer.solutions)}.") + print(f"Expected number of best solutions is {expected_num_best_solutions}.") + print(f"Actual number of best solutions is {len(ga_optimizer.best_solutions)}.") + return expected_num_solutions, len(ga_optimizer.solutions), len(ga_optimizer.solutions_fitness), expected_num_best_solutions, len(ga_optimizer.best_solutions) + +#### Single Objective +def test_save_solutions_default_keep(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions() + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions() + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_save_solutions(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(save_solutions=True, + save_best_solutions=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_save_solutions(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + save_solutions=True, + save_best_solutions=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_save_solutions(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_save_solutions(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + save_solutions=True, + save_best_solutions=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5]) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_save_solutions(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_default_adaptive_mutation(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(mutation_type="adaptive", + mutation_percent_genes=[10, 5]) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5]) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_save_solutions(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + + +#### Multi Objective +def test_save_solutions_default_keep_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_save_solutions_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(save_solutions=True, + save_best_solutions=True, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_save_solutions_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + save_solutions=True, + save_best_solutions=True, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_save_solutions_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_save_solutions_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + save_solutions=True, + save_best_solutions=True, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_default_adaptive_mutation_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + + +#### Multi Objective NSGA-II Parent Selection +def test_save_solutions_default_keep_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_save_solutions_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_save_solutions_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_save_solutions_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_save_solutions_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_default_adaptive_mutation_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective_nsga2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2') + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +######## Batch Fitness +#### Single Objective +def test_save_solutions_no_keep_batch_1(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=1) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=2) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_3(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=3) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_5(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=5) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_6(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=6) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_7(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=7) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_8(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=8) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_9(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=9) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_batch_10(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(fitness_batch_size=10) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + + +def test_save_solutions_no_keep_save_solutions_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(save_solutions=True, + save_best_solutions=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_save_solutions_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + save_solutions=True, + save_best_solutions=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_save_solutions_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_save_solutions_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + save_solutions=True, + save_best_solutions=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_save_solutions_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_default_adaptive_mutation_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_save_solutions_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + + +#### Multi Objective +def test_save_solutions_no_keep_multi_objective_batch_1(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=1) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=2) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_3(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=3) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_5(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=5) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_6(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=6) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_7(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=7) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_8(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=8) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_9(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=9) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_batch_10(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + fitness_batch_size=10) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_save_solutions_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(save_solutions=True, + save_best_solutions=True, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_save_solutions_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_save_solutions_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_save_solutions_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_default_adaptive_mutation_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + + +#### Multi Objective NSGA-II Parent Selection +def test_save_solutions_no_keep_multi_objective_nsga2_batch_1(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=1) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_2(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=2) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_3(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=3) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_5(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=5) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_6(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=6) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_7(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=7) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_8(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=8) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_9(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=9) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_multi_objective_nsga2_batch_10(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=10) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_save_solutions_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_elitism_save_solutions_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=0, + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_keep_parents_save_solutions_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=4, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_save_solutions_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=0, + keep_parents=0, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_default_adaptive_mutation_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + +def test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective_nsga2_batch_4(): + expected_solutions, actual_solutions, actual_solutions_fitness, expected_best_solutions, actual_best_solutions = number_saved_solutions(keep_elitism=3, + keep_parents=4, + mutation_type="adaptive", + mutation_percent_genes=[10, 5], + save_solutions=True, + save_best_solutions=True, + multi_objective=True, + parent_selection_type='nsga2', + fitness_batch_size=4) + assert expected_solutions == actual_solutions + assert expected_solutions == actual_solutions_fitness + assert expected_best_solutions == actual_best_solutions + + + + +if __name__ == "__main__": + #### Single Objective + print() + test_save_solutions_default_keep() + print() + test_save_solutions_no_keep() + print() + test_save_solutions_no_keep_save_solutions() + print() + test_save_solutions_keep_elitism() + print() + test_save_solutions_keep_elitism_save_solutions() + print() + test_save_solutions_keep_parents() + print() + test_save_solutions_keep_parents_save_solutions() + print() + test_save_solutions_both_keep() + print() + test_save_solutions_both_keep_save_solutions() + print() + test_save_solutions_no_keep_adaptive_mutation() + print() + test_save_solutions_no_keep_adaptive_mutation_save_solutions() + print() + test_save_solutions_default_adaptive_mutation() + print() + test_save_solutions_both_keep_adaptive_mutation() + print() + test_save_solutions_both_keep_adaptive_mutation_save_solutions() + print() + + #### Multi-Objective + print() + test_save_solutions_default_keep_multi_objective() + print() + test_save_solutions_no_keep_multi_objective() + print() + test_save_solutions_no_keep_save_solutions_multi_objective() + print() + test_save_solutions_keep_elitism_multi_objective() + print() + test_save_solutions_keep_elitism_save_solutions_multi_objective() + print() + test_save_solutions_keep_parents_multi_objective() + print() + test_save_solutions_keep_parents_save_solutions_multi_objective() + print() + test_save_solutions_both_keep_multi_objective() + print() + test_save_solutions_both_keep_save_solutions_multi_objective() + print() + test_save_solutions_no_keep_adaptive_mutation_multi_objective() + print() + test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective() + print() + test_save_solutions_default_adaptive_mutation_multi_objective() + print() + test_save_solutions_both_keep_adaptive_mutation_multi_objective() + print() + test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective() + print() + + + #### Multi-Objective NSGA-II Parent Selection + print() + test_save_solutions_default_keep_multi_objective_nsga2() + print() + test_save_solutions_no_keep_multi_objective_nsga2() + print() + test_save_solutions_no_keep_save_solutions_multi_objective_nsga2() + print() + test_save_solutions_keep_elitism_multi_objective_nsga2() + print() + test_save_solutions_keep_elitism_save_solutions_multi_objective_nsga2() + print() + test_save_solutions_keep_parents_multi_objective_nsga2() + print() + test_save_solutions_keep_parents_save_solutions_multi_objective_nsga2() + print() + test_save_solutions_both_keep_multi_objective_nsga2() + print() + test_save_solutions_both_keep_save_solutions_multi_objective_nsga2() + print() + test_save_solutions_no_keep_adaptive_mutation_multi_objective_nsga2() + print() + test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective_nsga2() + print() + test_save_solutions_default_adaptive_mutation_multi_objective_nsga2() + print() + test_save_solutions_both_keep_adaptive_mutation_multi_objective_nsga2() + print() + test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective_nsga2() + print() + + ######## Batch Fitness Calculation + #### Single Objective + print() + test_save_solutions_no_keep_batch_1() + print() + test_save_solutions_no_keep_batch_2() + print() + test_save_solutions_no_keep_batch_3() + print() + test_save_solutions_no_keep_batch_4() + print() + test_save_solutions_no_keep_batch_5() + print() + test_save_solutions_no_keep_batch_6() + print() + test_save_solutions_no_keep_batch_7() + print() + test_save_solutions_no_keep_batch_8() + print() + test_save_solutions_no_keep_batch_9() + print() + test_save_solutions_no_keep_batch_10() + print() + test_save_solutions_no_keep_save_solutions_batch_4() + print() + test_save_solutions_keep_elitism_batch_4() + print() + test_save_solutions_keep_elitism_save_solutions_batch_4() + print() + test_save_solutions_keep_parents_batch_4() + print() + test_save_solutions_keep_parents_save_solutions_batch_4() + print() + test_save_solutions_both_keep_batch_4() + print() + test_save_solutions_both_keep_save_solutions_batch_4() + print() + test_save_solutions_no_keep_adaptive_mutation_batch_4() + print() + test_save_solutions_no_keep_adaptive_mutation_save_solutions_batch_4() + print() + test_save_solutions_default_adaptive_mutation_batch_4() + print() + test_save_solutions_both_keep_adaptive_mutation_batch_4() + print() + test_save_solutions_both_keep_adaptive_mutation_save_solutions_batch_4() + print() + + #### Multi-Objective + print() + test_save_solutions_no_keep_multi_objective_batch_1() + print() + test_save_solutions_no_keep_multi_objective_batch_2() + print() + test_save_solutions_no_keep_multi_objective_batch_3() + print() + test_save_solutions_no_keep_multi_objective_batch_4() + print() + test_save_solutions_no_keep_multi_objective_batch_5() + print() + test_save_solutions_no_keep_multi_objective_batch_6() + print() + test_save_solutions_no_keep_multi_objective_batch_7() + print() + test_save_solutions_no_keep_multi_objective_batch_8() + print() + test_save_solutions_no_keep_multi_objective_batch_9() + print() + test_save_solutions_no_keep_multi_objective_batch_10() + print() + test_save_solutions_no_keep_save_solutions_multi_objective_batch_4() + print() + test_save_solutions_keep_elitism_multi_objective_batch_4() + print() + test_save_solutions_keep_elitism_save_solutions_multi_objective_batch_4() + print() + test_save_solutions_keep_parents_multi_objective_batch_4() + print() + test_save_solutions_keep_parents_save_solutions_multi_objective_batch_4() + print() + test_save_solutions_both_keep_multi_objective_batch_4() + print() + test_save_solutions_both_keep_save_solutions_multi_objective_batch_4() + print() + test_save_solutions_no_keep_adaptive_mutation_multi_objective_batch_4() + print() + test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective_batch_4() + print() + test_save_solutions_default_adaptive_mutation_multi_objective_batch_4() + print() + test_save_solutions_both_keep_adaptive_mutation_multi_objective_batch_4() + print() + test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective_batch_4() + print() + + + #### Multi-Objective NSGA-II Parent Selection + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_1() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_2() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_3() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_4() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_5() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_6() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_7() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_8() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_9() + print() + test_save_solutions_no_keep_multi_objective_nsga2_batch_10() + print() + test_save_solutions_no_keep_save_solutions_multi_objective_nsga2_batch_4() + print() + test_save_solutions_keep_elitism_multi_objective_nsga2_batch_4() + print() + test_save_solutions_keep_elitism_save_solutions_multi_objective_nsga2_batch_4() + print() + test_save_solutions_keep_parents_multi_objective_nsga2_batch_4() + print() + test_save_solutions_keep_parents_save_solutions_multi_objective_nsga2_batch_4() + print() + test_save_solutions_both_keep_multi_objective_nsga2_batch_4() + print() + test_save_solutions_both_keep_save_solutions_multi_objective_nsga2_batch_4() + print() + test_save_solutions_no_keep_adaptive_mutation_multi_objective_nsga2_batch_4() + print() + test_save_solutions_no_keep_adaptive_mutation_save_solutions_multi_objective_nsga2_batch_4() + print() + test_save_solutions_default_adaptive_mutation_multi_objective_nsga2_batch_4() + print() + test_save_solutions_both_keep_adaptive_mutation_multi_objective_nsga2_batch_4() + print() + test_save_solutions_both_keep_adaptive_mutation_save_solutions_multi_objective_nsga2_batch_4() + print() diff --git a/test_stop_criteria.py b/test_stop_criteria.py new file mode 100644 index 0000000..3ee1737 --- /dev/null +++ b/test_stop_criteria.py @@ -0,0 +1,234 @@ +import pygad +import numpy + +actual_num_fitness_calls_default_keep = 0 +actual_num_fitness_calls_no_keep = 0 +actual_num_fitness_calls_keep_elitism = 0 +actual_num_fitness_calls_keep_parents = 0 + +num_generations = 50 +sol_per_pop = 10 +num_parents_mating = 5 + +function_inputs1 = [4,-2,3.5,5,-11,-4.7] # Function 1 inputs. +function_inputs2 = [-2,0.7,-9,1.4,3,5] # Function 2 inputs. +desired_output1 = 50 # Function 1 output. +desired_output2 = 30 # Function 2 output. + +#### Define the fitness functions in the top-level of the module so that they are picklable and usable in the process-based parallel processing works. +#### If the functions are defined inside a class/method/function, they are not picklable and this error is raised: AttributeError: Can't pickle local object +#### Process-based parallel processing must have the used functions picklable. +def fitness_func_batch_multi(ga_instance, solution, solution_idx): + f = [] + for sol in solution: + output1 = numpy.sum(sol*function_inputs1) + output2 = numpy.sum(sol*function_inputs2) + fitness1 = 1.0 / (numpy.abs(output1 - desired_output1) + 0.000001) + fitness2 = 1.0 / (numpy.abs(output2 - desired_output2) + 0.000001) + f.append([fitness1, fitness2]) + return f + +def fitness_func_no_batch_multi(ga_instance, solution, solution_idx): + output1 = numpy.sum(solution*function_inputs1) + output2 = numpy.sum(solution*function_inputs2) + fitness1 = 1.0 / (numpy.abs(output1 - desired_output1) + 0.000001) + fitness2 = 1.0 / (numpy.abs(output2 - desired_output2) + 0.000001) + return [fitness1, fitness2] + +def fitness_func_batch_single(ga_instance, solution, solution_idx): + f = [] + for sol in solution: + output = numpy.sum(solution*function_inputs1) + fitness = 1.0 / (numpy.abs(output - desired_output1) + 0.000001) + f.append(fitness) + return f + +def fitness_func_no_batch_single(ga_instance, solution, solution_idx): + output = numpy.sum(solution*function_inputs1) + fitness = 1.0 / (numpy.abs(output - desired_output1) + 0.000001) + return fitness + + +def multi_objective_problem(keep_elitism=1, + keep_parents=-1, + fitness_batch_size=None, + stop_criteria=None, + parent_selection_type='sss', + mutation_type="random", + mutation_percent_genes="default", + multi_objective=False, + parallel_processing=None): + + if fitness_batch_size is None or (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size == 1): + if multi_objective == True: + fitness_func = fitness_func_no_batch_multi + else: + fitness_func = fitness_func_no_batch_single + elif (type(fitness_batch_size) in pygad.GA.supported_int_types and fitness_batch_size > 1): + if multi_objective == True: + fitness_func = fitness_func_batch_multi + else: + fitness_func = fitness_func_batch_single + + ga_optimizer = pygad.GA(num_generations=num_generations, + sol_per_pop=sol_per_pop, + num_genes=6, + num_parents_mating=num_parents_mating, + fitness_func=fitness_func, + fitness_batch_size=fitness_batch_size, + mutation_type=mutation_type, + mutation_percent_genes=mutation_percent_genes, + keep_elitism=keep_elitism, + keep_parents=keep_parents, + stop_criteria=stop_criteria, + parent_selection_type=parent_selection_type, + parallel_processing=parallel_processing, + suppress_warnings=True) + + ga_optimizer.run() + + return None + +def test_number_calls_fitness_function_no_parallel_processing(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=None) + +def test_number_calls_fitness_function_parallel_processing_thread_1(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=['thread', 1]) + +def test_number_calls_fitness_function_parallel_processing_thread_2(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=['thread', 2]) + +def test_number_calls_fitness_function_parallel_processing_thread_5(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=['thread', 5]) + +def test_number_calls_fitness_function_parallel_processing_thread_5_patch_4(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=4, + parallel_processing=['thread', 5]) + +def test_number_calls_fitness_function_parallel_processing_thread_5_patch_4_multi_objective(): + multi_objective_problem(multi_objective=True, + fitness_batch_size=4, + parallel_processing=['thread', 5]) + +def test_number_calls_fitness_function_parallel_processing_process_1(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=['process', 1]) + +def test_number_calls_fitness_function_parallel_processing_process_2(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=['process', 2]) + +def test_number_calls_fitness_function_parallel_processing_process_5(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=None, + parallel_processing=['process', 5]) + +def test_number_calls_fitness_function_parallel_processing_process_5_patch_4(): + multi_objective_problem(multi_objective=False, + fitness_batch_size=4, + parallel_processing=['process', 5]) + +def test_number_calls_fitness_function_parallel_processing_process_5_patch_4_multi_objective(): + multi_objective_problem(multi_objective=True, + fitness_batch_size=4, + parallel_processing=['process', 5]) + +# Stop Criteria +def test_number_calls_fitness_function_multi_objective_stop_criteria_str_single_value(): + multi_objective_problem(multi_objective=True, + stop_criteria='reach_10') + +def test_number_calls_fitness_function_multi_objective_stop_criteria_str(): + multi_objective_problem(multi_objective=True, + stop_criteria='reach_10_20') + +def test_number_calls_fitness_function_multi_objective_stop_criteria_str_decimal(): + multi_objective_problem(multi_objective=True, + stop_criteria='reach_-1.0_0.5') + +def test_number_calls_fitness_function_multi_objective_stop_criteria_list(): + multi_objective_problem(multi_objective=True, + stop_criteria=['reach_10_20', 'reach_5_2']) + +def test_number_calls_fitness_function_multi_objective_stop_criteria_list_decimal(): + multi_objective_problem(multi_objective=True, + stop_criteria=['reach_-1.0_0.5', 'reach_5_-2.8']) + +def test_number_calls_fitness_function_single_objective_stop_criteria_str(): + multi_objective_problem(multi_objective=True, + stop_criteria='reach_10') + +def test_number_calls_fitness_function_single_objective_stop_criteria_str_decimal(): + multi_objective_problem(multi_objective=True, + stop_criteria='reach_-1.7') + +def test_number_calls_fitness_function_single_objective_stop_criteria_list(): + multi_objective_problem(multi_objective=True, + stop_criteria=['reach_10', 'reach_5']) + +def test_number_calls_fitness_function_single_objective_stop_criteria_list_decimal(): + multi_objective_problem(multi_objective=True, + stop_criteria=['reach_-1.5', 'reach_-2.8']) + +if __name__ == "__main__": + print() + test_number_calls_fitness_function_no_parallel_processing() + print() + + #### Thread-based Parallel Processing + test_number_calls_fitness_function_parallel_processing_thread_1() + print() + test_number_calls_fitness_function_parallel_processing_thread_2() + print() + test_number_calls_fitness_function_parallel_processing_thread_5() + print() + test_number_calls_fitness_function_parallel_processing_thread_5_patch_4() + print() + test_number_calls_fitness_function_parallel_processing_thread_5_patch_4_multi_objective() + print() + + #### Thread-based Parallel Processing + test_number_calls_fitness_function_parallel_processing_process_1() + print() + test_number_calls_fitness_function_parallel_processing_process_2() + print() + test_number_calls_fitness_function_parallel_processing_process_5() + print() + test_number_calls_fitness_function_parallel_processing_process_5_patch_4() + print() + test_number_calls_fitness_function_parallel_processing_process_5_patch_4_multi_objective() + print() + + #### Multi-Objective Stop Criteria + test_number_calls_fitness_function_multi_objective_stop_criteria_str_single_value() + print() + test_number_calls_fitness_function_multi_objective_stop_criteria_str() + print() + test_number_calls_fitness_function_multi_objective_stop_criteria_str_decimal() + print() + test_number_calls_fitness_function_multi_objective_stop_criteria_list() + print() + test_number_calls_fitness_function_multi_objective_stop_criteria_list_decimal() + print() + + #### Single-Objective Stop Criteria + test_number_calls_fitness_function_single_objective_stop_criteria_str() + print() + test_number_calls_fitness_function_single_objective_stop_criteria_str_decimal() + print() + test_number_calls_fitness_function_single_objective_stop_criteria_list() + print() + test_number_calls_fitness_function_single_objective_stop_criteria_list_decimal() + print() + diff --git a/tests/test_save_solutions.py b/tests/test_save_solutions.py index 45216e1..537f4ea 100644 --- a/tests/test_save_solutions.py +++ b/tests/test_save_solutions.py @@ -5,7 +5,7 @@ sol_per_pop = 10 num_parents_mating = 5 -# TODO Verify that the each entry in 'solutions_fitness' and 'best_solutions_fitness' have values equal to the number of objectives. +# TODO Verify that each entry in 'solutions_fitness' and 'best_solutions_fitness' has values equal to the number of objectives. def number_saved_solutions(keep_elitism=1, keep_parents=-1, From ce702a9025b2ec195b4e3fabe4eb64d0ab3cfe1e Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Sat, 5 Jul 2025 16:57:02 -0400 Subject: [PATCH 15/25] Fix selecting a unique value from gene space --- example2.py | 12 ++++++------ pygad/__pycache__/pygad.cpython-310.pyc | Bin 79860 -> 79860 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 16011 -> 16051 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 14713 -> 14713 bytes pygad/helper/misc.py | 2 ++ pygad/pygad.py | 2 +- test_gene_space_allow_duplicate_genes.py | 3 ++- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/example2.py b/example2.py index 7383b7c..88d815c 100644 --- a/example2.py +++ b/example2.py @@ -9,14 +9,14 @@ def fitness_func(ga, solution, idx): num_parents_mating=5, sol_per_pop=10, num_genes=10, - random_seed=123, + random_seed=2, # mutation_type=None, # crossover_type=None, - random_mutation_min_val=1, - random_mutation_max_val=100, + # random_mutation_min_val=1, + # random_mutation_max_val=100, fitness_func=fitness_func, - gene_space=[30, None, 40, 50, None, 60, 70, None, None, None], - gene_type=int, + gene_space=[5], + gene_type=float, allow_duplicate_genes=False, save_solutions=True) @@ -25,7 +25,7 @@ def fitness_func(ga, solution, idx): ga_instance.run() # print(ga_instance.gene_space_unpacked) -# print(ga_instance.population) +print(ga_instance.population) """ gene_space=[[0, 0], diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index 9f65c82877625b9ca5ff5549f774de1f75f5d3d6..383d6f076c02e0685a34aa3d3e692a7c81286854 100644 GIT binary patch delta 1248 zcmZvaYiJZ_6o&Vm*^BO&M1nCPB)UHZ2!<&fqz$x1D20l(vpQblHHyZ>yR{+2yJ?K8S#j4(lW6*#-86-efqCZ4Iq$iA zmqg5-h}qqtv^2L(KA+SbbYEz31s&lk5vnm;TY5G8Vcu%}Qq_K!;hY_t}~&E3&Qitps0{1jWK z2Y-`wQYEDCTbB%;G3vW_WnN;*_Y%5Yl(F4nl*F>LuCh6#`8jiCZ09H(Hg5MW!db)H zw-M*fy1rRXT&HFQdvS}ZR0MF7eo!$TH>~R_mc!W7XSaL~%yZ=a$whB7ZZddx#9xdX zR-GFfaKWnAFvl+IwP#=*E?OUsM%<1D<7#())_hj3XZRS+j3(NUh4K0^wl6ap87++C zj3Fy%v<_t9`ABq7{dinNr?uV#fwbgy2c-U2MRZa}0SXWy*93f~b#jp>j2L}00k!D1 zFcEo5UR?TgRqWzW1tYB#eb8@+stn+c{A93om}2(IfS`x-guvcw6XG&&Q&ATa~C@GutBuX8L;AR!epG zQ?2}|R{M0h4(&aQa~N@IF2-dfXzvsR^0=WsLcHThFQcDfgTy}+odORn(UC#9KK$g2 zawtw`e0T$sj{4;eMd*$nJ1QMgA6C42>Kt60J>*0pGI#Aro2@2Y8cv%6rC-4)`8xYW zGF$bQ>OJs^_w?gb%pLPw%5+7nke@cSX8UGoo76iMMY83j5=_sG$w=~M>0<^ChBlR; zqPU6um5if|GR?f*aND7BZs>x7&(m_w!GkTX_van3WwONPQLc}+7*u?#WV zBR{&EUBhk{CvdS;pSLu68m?qNm0sN?9&+#zqnPoSUQI&?2dJtHA88Hjy2dz1`^%7{ zB`@~_c4+i@IkK&1Wq1p;QpXGgo$VZGqly6h1^3v#FA;QXShKo%%g}94r|$xon3*oe l((}YzR;MxAs40Ma9HGttDm9g-u4TWS=9J?Dq;uQae*rETTQ&dy delta 1251 zcmZvadq`Ay6vyX#@65Gkv=Px1(QPbj=W0%xHC7Az3JJ=DCD_7+jUp?dn)atnt$*|& zcA+BJxC@8ajg1JFg~HmadvQy8S!QZax3#k9Wvi<>LgV%rH~XC#GY~J_&pr2??>Xo9 z`<-7nWDkez&Okzf(RYp;HR6J4ws-7OT_e`5J#1M|%p;VBYU30ve6Uy+NEZ_w_zj@=lM+ z&#-lN^fy{3Rzmvr#>wD0<6!^Jq!yO^B%#|yA=_<6ekj#`O`67zp=5JKC>RgRxE1*V z=MC@BI$SjO56#nXgIW|Ez+Y6Z!jE?PS;cJJw63dI4m4Rg4lHq`-{OLQGujz^9HN79 z)7tNZ3-oCs?A9R{KC;V#oo=i~v-Mdbp4-u2TMp*iERqW(YF=LSNALAv>%SJUKRGSUYJK^SWg#+2h(f*gA>Kyte8|l{k z>2f^?)3Fb61cOFtLnZ~haB3rkw|jATPDJW;wY1v3v?{R4vp`f-m5Yj%^F3l@O}oc! z^`#D9td%d;>WD7);TmRf8Y4_CxoE>69hd=s1~=3vh&LP=V%Q)7#%t=G0S{hLd>(4F zzF6+rJiI_H)%)b})X_a3wv=d6T&;NZ*bpXg>X>oV$=p>FZMMou($H)Qlwk$&^3{e# zG+Xr+=&#`w!*pmS7AE&gnXZVhvwF<2gg+oxGsA1z3gw z`aynl7rVwiD^B2Iu|BUTaTczm_DZiFCmwU~2_u)$MT4^tz;P-s#OJ;Sc3o#&U|eK0 zF)lHh8PU7`h^=_qR)kcmuMjVR+tf7&W!gOs+@)ead|40Jekf7qSi5pX#m2FNaMO=| qOiOahh3Xk%A*&M@cd6NrOw`jqew4WEypJIJ9rSS#K7pIt2HyfV?OW&o diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc index b1a1090339cce542c6c646c8ec72c6b5da7524d7..84001db4713ac660e627b647b17e21e1115f5973 100644 GIT binary patch delta 114 zcmeCK-CWC?&&$ij00a#aGc$f|&&$ij00g0}nHi}Yc@xE%{xEGW5dY1zxnF7~2jiy6n#!A5SeUC+C;u~* m+Wb$ske#t~bAfgu7vql2mrNtr86R%ewiRV$RGI8(R|EiD0~;#< diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc index 85a309de1f512292eff5678e0244ad6d7f8fae20..2ce6182865844b3581722349379ad89a1ddd51fc 100644 GIT binary patch delta 19 Zcmexa^s|U7pO=@50SGcDZsf|h1OP+Y1 Date: Sat, 5 Jul 2025 17:07:02 -0400 Subject: [PATCH 16/25] Fix selecting a unique value from gene space --- pygad/__pycache__/pygad.cpython-310.pyc | Bin 79860 -> 79900 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 16051 -> 16104 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 14713 -> 14743 bytes pygad/helper/misc.py | 3 +++ pygad/helper/unique.py | 2 +- pygad/pygad.py | 3 ++- test_gene_space_allow_duplicate_genes.py | 2 +- 7 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc index 383d6f076c02e0685a34aa3d3e692a7c81286854..2b3bea3e37f373faecdf8496f49983404c3f6fe1 100644 GIT binary patch delta 1878 zcmZvc3v5$W7{~Ab-rn2wwUx208|=lS(6R>`j}GVJ1DS#Y43eOkAWpl%kTG`KVZd7$ z5))9DfH&qyfVde5A;Cl`SW!km zh}V=fL2acOvNY2w_-JI(6tC2X*U8+Y}b0@?RQnM&ixb8t)BUW3}#SD~z-ErpRJXyeU}IR%OOc{q*@_ z3=Ae*m@HX#aB;cht6iuDgKu_}0HNS#zf3k^Pw@AvWn$(oDzKGM6vG6vzUFvIpAjxc zNZ%eV#NOb=a1n4oPw8BO7QM04fzN|mI$OoOeKFyFvc8WEBN=1KJ~HyjAM{PPRwf>! zkPRGegF2S%Hoc(Bk#@2lUr zEUT2@!5Owu!Z@4`os{q@*fVw*5Dw+bm?954NBPea&JgHgs22#QL+i}2Kt93;xuIqY ziVZw%M*_U^QSN#@RBA_;0ekfmw=$B;NKr+|BOE8RvU$nKO5H>D3BmzF8=-|mZ|F@z zLnxSxqrleCatAWwaE)!s!W0~4*Rrr2VOEumi8vA3mW|P{MYhGmrQ1X`%&rW<5ScdQ z9J36C-x8+8*9mvn2Sc$0w;6^Z38z9S!!QlF%)G;~JH4AnxFvNZ1(p)-5$+TIWKTG` zf<)o^jmh88*WRAsjqB) zwCrX#(nX5^?oK6HH*3woaol6;bKyy+;_f*05zYKX;I9fn-SeaJdQ~|7cG@eiD;&Ep;!W_}N^zY%g$vO()QY7ATc`7T+L~4rKNt#=P zy95I*=K-5gh*xljt>u4DTFKS#vm*f|i`MC(A+5jN$d^!4p)TFQhK|7=#|`dt8`a-v z{t6+7aFunBK`p*ym18k6e=oUyCeQ^}={l>22}cNY>7rNYSF&tuc@Z4!##p=$Uq~HC zN3NY+8IN-1BF%TOQV;T-m&m@%Q7+G`m|MN*|J!8etuywi2U&?^y3->sR@D|wam0fR be9NwRFx4W{BA1X~VNVs|Y1pZQdSv_qw)X%< delta 1802 zcmZ`(3s6*L6u#fx-3#opfWXQ_5CkEvh=Ax)YBu@G_oU8P5rkU{BP1;RyU@s7G;?Yy zqO@E6Q`!v7G&arDly12`up}Rdg89zKrtF1?NeN{tO8?n4%gJ`_>*aZvWFGQar($R4%Q>45=ug-sB&_KZ@2f+tSmk z=T*(7DnBLGf%4XG+IEl4Vge)y&E!?eoYg<%B zxE7=ZJ7rCA8nqB-urs7FsNSrZ{d%9^N4U3ohBZYx&6=`7a$2+y(hDo}ZBsKKRj>9$ zHAQP?%3rQ%CQT`orwDI#tp=y{NqHSupm3$!<_y#CKM=M0qF@g9vn`?$sXRtJu_C)c zWDjpt3JrnS6sbP6KD7FX7{vuVaZ*!!Gbu(0rcwMO8cqLY-?ks3rhuk6!6`41)M1XF zy~htMGU(YI?Z9dEG{kcRF=5i#jYApRgH&zL2A=p5p{c?@2d&L zR{h4cMfhBQaB#V4pK!19?i!q^;3)5x@g`32Mg#0P&i5KHK{+Pkb$PEk;(f73+ym2o zF+S&8YQh{-(n(>nQv}Be=sL463H+k*Lc~v-alzCke3`y>3nm#w5@Q6MrUSxNoY`aM*V=8QHLUNoyDIp3J(rH3g&mCv%GDrNCq9rsz3>KE5RdwZkux?LQ9l z6RgDHDJf>Ir;hoireXnbmcKL_+Y|Z)M>ViDM4d@6K=3=kW$s8rDth^|X;_M@{7f2> z|K``+ksq6mH04sT&<^^hr(-?P&rjZkcQBwY+?5y+PolVg&Hru&M&k?C-0cuQOf&W~cUT(}q2Eem&G1AsU#0vzM2xMBwAADIZ z9zq}ACSH@xK>PANW7wE0CWLfz2JZuE>y)^-%fYaP+M}cyNLS4MV zj&aJjWPit-?T9ypi3;D~yX{DdBvVcBe?yryK>y8-L~P^X`Iv7p&{}HAZ{(%amEs^Ebc6xGDe{Ij2g}q9!Z8;&Kk~O22J5a=gHY-GMhI^-DKm6;_!xo z$=!;Qd{taFaG)tOd70uhHr9fo%)FAx)k!;nnjiusugP5G3S!5B zh{(ysrkadlljoY6Fz(%a$@CzbWIRNT21pvL31o&QQ&HmNH8y%crMGST8J|towk={z cnmo@oNFWlV5Nuc?h?O$Q&Vf;LvZGxQ0LXo0yZ`_I delta 349 zcmaD+ySbJxpO=@50SFo33ODE zB#_XQo_s;^+T^uLYZ$jpj#8e^#m36S$i&F@kBfctf8}U)(NK^|dl10`BIH0s_~d+T zQ^xYmOSLa@GKNkrH(O)1p z2zIGILE`A_RwoCEyZ^yS?-lJkJomhx_eJiys;{bXj>QvoE}};@ry-YCHQlsPAlzWY z^?3Fo8#ggo4>(uouJpvAWb*+(;A|nl0YAqv6X~s%PR53EAUvQeF%SAhHUc0LNs!NF*rmf7A43<2qSjl_=KtoQ6-@ zWS1V9d9*_Z<|ZnIBlBEJ)#1?E7!8`U^lg{rWy~5)CWb^x(4PX70eQd)LnJz$UB5IG qvIr#^P@?x%MmhyOqtDio9<<)Q-3W^+FkTVrI|5>F`KDKvxXsyxmdY`WdUmn zQw{TMh7{%+AT7xNWUgWb=jifD8#f9wsFw4n`Ihu74~{n_tWFax%JY{;4*bBfu9ZUE~KO zZZYST=HB8+%}XiB&&(?c(PS#}2Fdw=2u2{G$ylTXVuK6>O2Rc3v4I4bp{gf8(Qo2+ z0gBvWFU`wL%P-0;@|av|;LPYgd8dIPqvz(=2FYv!DIkM%;6&==ICELmOd#Kz2`G^T zWN5M#d4lAHK!g{Fa0U^vlNXqaGkQ+mVJ^d#01`-?e9K&gEd#{Np3Gu#L% Date: Sun, 6 Jul 2025 12:55:38 -0400 Subject: [PATCH 17/25] Fix selecting a unique value from gene space --- .DS_Store | Bin 10244 -> 8196 bytes example.py | 49 - example2.py | 41 - pygad/.DS_Store | Bin 8196 -> 10244 bytes pygad/__pycache__/__init__.cpython-310.pyc | Bin 205 -> 0 bytes pygad/__pycache__/pygad.cpython-310.pyc | Bin 79900 -> 0 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 260 -> 0 bytes pygad/helper/__pycache__/misc.cpython-310.pyc | Bin 16104 -> 0 bytes .../helper/__pycache__/unique.cpython-310.pyc | Bin 14743 -> 0 bytes pygad/helper/misc.py | 3 - pygad/helper/unique.py | 14 +- pygad/pygad.py | 1 - .../__pycache__/__init__.cpython-310.pyc | Bin 332 -> 0 bytes .../__pycache__/crossover.cpython-310.pyc | Bin 6116 -> 0 bytes .../__pycache__/mutation.cpython-310.pyc | Bin 17807 -> 0 bytes pygad/utils/__pycache__/nsga2.cpython-310.pyc | Bin 7277 -> 0 bytes .../parent_selection.cpython-310.pyc | Bin 14555 -> 0 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 237 -> 0 bytes .../__pycache__/plot.cpython-310.pyc | Bin 13830 -> 0 bytes test_adaptive_mutation.py | 2393 ----------------- test_allow_duplicate_genes.py | 634 ----- test_crossover_mutation.py | 287 -- test_gene_constraint.py | 223 -- test_gene_space.py | 1605 ----------- test_gene_space_allow_duplicate_genes.py | 1140 -------- test_gene_type.py | 214 -- test_lifecycle_callbacks_calls.py | 247 -- test_number_fitness_function_calls.py | 597 ---- test_save_solutions.py | 1289 --------- test_stop_criteria.py | 234 -- .../test_gene_space_allow_duplicate_genes.py | 7 +- 31 files changed, 13 insertions(+), 8965 deletions(-) delete mode 100644 example.py delete mode 100644 example2.py delete mode 100644 pygad/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/__pycache__/pygad.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/misc.cpython-310.pyc delete mode 100644 pygad/helper/__pycache__/unique.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/crossover.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/mutation.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/nsga2.cpython-310.pyc delete mode 100644 pygad/utils/__pycache__/parent_selection.cpython-310.pyc delete mode 100644 pygad/visualize/__pycache__/__init__.cpython-310.pyc delete mode 100644 pygad/visualize/__pycache__/plot.cpython-310.pyc delete mode 100644 test_adaptive_mutation.py delete mode 100644 test_allow_duplicate_genes.py delete mode 100644 test_crossover_mutation.py delete mode 100644 test_gene_constraint.py delete mode 100644 test_gene_space.py delete mode 100644 test_gene_space_allow_duplicate_genes.py delete mode 100644 test_gene_type.py delete mode 100644 test_lifecycle_callbacks_calls.py delete mode 100644 test_number_fitness_function_calls.py delete mode 100644 test_save_solutions.py delete mode 100644 test_stop_criteria.py diff --git a/.DS_Store b/.DS_Store index 24f42ca2d5ca16fd106016873d8cb448533b0e22..fcaff950d423a13d205b6cdde9babb188a7afd17 100644 GIT binary patch delta 128 zcmZn(XmOBWU|?W$DortDU;r^WfEYvza8E20o2aMAD6lbLH$S7mWFCR=&1VHf85xZx zr%SqS))0EgH2J0k_vBp?YZ#3-r%P^N5@ZJI0|E(dAmIu!Vq@WV=E?jjfgFqwgBd2r P^Gu!0D;Wvav6cw{v&kEZ literal 10244 zcmeHM&2Jk;6n~TE!*1HfNu7iSMKX$zdMIgHft08RTqh9<6cWWDBs67Nuh;I9^^VzH z=YtR_5+}Iw2k-}QhktjEP88 zI`w-*B_hh9GA&%gqetQIT+fxhxMvzvK%PjFx8h2x=E~YRZrz~~&EhTPmO>^KqGL90Iv@vRHo&aoMDoq^FtlJEzjSs1N>9MG5y)9b6U#iE}>l{hZ;uN;VykNiAxVi}oz&;dvQhByCPq?EbN1O6bJq5Q zc6}K{z+}tLVGZKQK5nlph)%dQyEV_p6~9c7lrOKjya$DcxGPZPqp&d*3J& z7o``Pj%WKyz^U6{+4;cn>Ta2`d(t0`TN-cO%jp_LfAW9`KQ5#E4Rh*EQc}l{0Vt9-%r@ey7`*jT!}-i5mC~UhKAFDmFan+4-6BX4H|#{`eYZ z?J8{{r#`2z=^Of%zM~)L7y6C<6oaBDro~+`FCL1w#Ut^)*c46Sik8@m6!djN%^9C4 zVjnMwQ14ZJ&y`*iy)1D<&Hpy}$ofx^X%^OrSyTxx+;m2$wc^J_X69)fS@w{Y@MRfN zt!xgdRU;`;fE}AYr4Bi&)xw(xYk{(PKw;XV%I&0l4xBdlJMg;4ileeS$$tyHHe^CF zIKxrfXgjorUIYGi@MfwW+-zQ?F|~$GgZoUQi?vVEK6KTUPcC#@N+auUg40vnJ$&W$ zC8c>Pq1jUL2;jL?zWV>nTaJ+UBN3s=gwC!v20Y}^GAcfNbyNg;c*xO9X&dXQEdZt_ zboSf>I3g(>x1Xbf!)h&8|6+hY}%B!;gfG;n+ykh36fseO;pj6AL> z6n25@Ss%~q^ZO#A)b~XqO0Om?c$h~ms=)@wya9V*Jcn3s17AH@H#Gtp0gZr0;Bp~wB^qvt z@Bf!C{{R1SfucvN5x8Usi2kMOQU!zVUJ+!+&v)%5)Q?fQFyBc;l?Wc{Iv!MA$K!>s v<8Pxf?;KMuXqRJp5>fP^{O5lL;CHYxtk3`5= 90 - -ga_instance = pygad.GA(num_generations=100, - num_parents_mating=10, - sol_per_pop=20, - num_genes=num_genes, - mutation_num_genes=6, - fitness_func=fitness_func, - init_range_low=1, - init_range_high=100, - # suppress_warnings=True, - random_mutation_min_val=1, - random_mutation_max_val=100, - mutation_by_replacement=True, - # gene_type=[float, 1], - gene_type=[float, [float, 5], [float, 4], [float, 3], [float, 2], [float, 1]], - # save_solutions=True, - # allow_duplicate_genes=False, - # gene_space=numpy.unique(numpy.random.uniform(1, 100, size=100)), - gene_space=[range(100), {"low": 1, "high": 100}, 2.5891221, [1,2,3,4], None, numpy.unique(numpy.random.uniform(1, 100, size=4))], - # gene_space=numpy.linspace(1, 100, 200), - gene_constraint=[const,lambda x: x[1]>=90,lambda x: x[2]<98,lambda x: x[3]<98,lambda x: x[4]<98,lambda x: x[5]<98], - ) - -# print(ga_instance.initial_population) - -ga_instance.run() - -# print(ga_instance.gene_space_unpacked) -print(ga_instance.population) - -print(calls) \ No newline at end of file diff --git a/example2.py b/example2.py deleted file mode 100644 index 88d815c..0000000 --- a/example2.py +++ /dev/null @@ -1,41 +0,0 @@ -import pygad -import numpy -import random - -def fitness_func(ga, solution, idx): - return random.random() - -ga_instance = pygad.GA(num_generations=1, - num_parents_mating=5, - sol_per_pop=10, - num_genes=10, - random_seed=2, - # mutation_type=None, - # crossover_type=None, - # random_mutation_min_val=1, - # random_mutation_max_val=100, - fitness_func=fitness_func, - gene_space=[5], - gene_type=float, - allow_duplicate_genes=False, - save_solutions=True) - -print(ga_instance.initial_population) - -ga_instance.run() - -# print(ga_instance.gene_space_unpacked) -print(ga_instance.population) - -""" -gene_space=[[0, 0], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - [5, 6], - [6, 7], - [7, 8], - [8, 9], - [9, 10]], -""" diff --git a/pygad/.DS_Store b/pygad/.DS_Store index 8a926cf11cdeec628aff003864eb85651f231ff9..3e6d027cc6e2afc8222e9c82ed6348bdefdf5e7b 100644 GIT binary patch delta 441 zcmZp1XbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~50$SA!rU^hRb^kg1^cy?xnWQIJ3 zyvcioni!2HI|w@qLq*Dhi}G^v^U{GD87F55=uXZS;D)Pm6-{SIgy=+3H%UO7lZ62! zhhnBETnt6^E`fazJ16fE^x|h{$Yw}oC<5v&2AKf2%UK#t2E`0!A$3+JRNDl>d}LLd z)rBM&8I3kO2&(`okS`~%5K-P(x|L0k8R$PCP~ZjjD zC+7&sPo5{tJGowX9izcyJ(1qY>qJ#HZxj<{1aqAyb4fgevh*dDH=pLQVHRWt8VUpw d+(5z=Wd6p&@640=RRTE}A+|DXj^~-f3;g`kg6+F^GcG?{L(7F4DurrhF=k1tCtD$dN$i;rK)P{ayU2_}9y=!X^qCG-lIYq X;;_lhPbtkwwF6mS%mO4h7&w>!VCOT1 diff --git a/pygad/__pycache__/pygad.cpython-310.pyc b/pygad/__pycache__/pygad.cpython-310.pyc deleted file mode 100644 index 2b3bea3e37f373faecdf8496f49983404c3f6fe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79900 zcmeFa349#KbtXO+27>`GxWQB8h&ljJBzVb^Op&%JQKDrjv@B7UJeD#Zq6Zg&0oXGT z#c{({Ov$3+D5mTrP8=%+?PMKo?)4^4cAdjlj?E_C&F#r1&avK&lHDA3Hk&Yx`TxFG zM|aNwfRr3(fB#ULMt619t5>gHy?XD}tLlaB?sNqI{>%Gjrhni|vB;nDA^4APUx#0L z1s5mcMCT$o^*5S}%5y9ilfUs?T>d6 z`_5cvG;%EBv^mMAB2Kc{G21oUjcaDM2iM-&K3w}}2a4(0!Py~Phi5kwJ7+h}ZaUYI z+w8PEsiz{jEl%{o$XLfqz%p`tELv+@s?5xlag&@b&Mg*Q+;u!YQ(h{}%~XpM%oY51 z10G(7UwLaZ5-CR5vRtefFD7Q&@Jr&?j$aDD4*b&ib>i2BUw1Jx)tT!l_Ts(|zkd7% zkk*zPEcO%!H$-wnPE`LFbK=EbC&6|SYovDImv%aF?Q*&$S4OU;1mE&M{XBVt)8q6$ z70nGheNI1Gy}=o92Jv^JGvo~8?-|f!0bDeWNa%7zwoEwpHZO2H*XvbK`&W?0PrX%C*ac**U;r$Mw@ZMVN z*pcHeef9F?%P%EBv0A)bF297Q%P)a+k(c63>nhKbrsj(I#f6ztrIvIHC1+v&?pnK3 zoGdKORi=;!|Ni>t&CP31OpK{CxgN%~6Tdt0EC2aOBywS(`iI`A6J3h9dn=KZXeD+r zx;*NQuEZ;m*~G9`nZhw@6DlEVC1-9o|k=VkPa3@vhWY z>GVbcsq0+Ky|t9CbUX1%#v45s{oaU^cp-i+`mU%q>P7NB-UzO}-iTmwU#Yv&H`~85 z;6-M838xag&v@6Li&h4K@q~9hN)45|oi_X(2J|GL>YlT?+EuEb%R^21 z4M^Rr^L~%bn^LKPyql4CqrBgQcNOn5b-&OiwBH4I*DUWw`fk&Xsc7Ze*&Q1qj{`^d8qgH) zP@+fW5jsfO3(?b2rx&!?bovV#7oo$c7|QLoY1b{WUEf8lR<6Go0cL%Y;}NGHDK}h< z2-f#G1Hq@#-4^pfmHxev7h=RX0B1RQP>hztK|R^T0mvRC>_j zwN<4Ds9@lb3Nbesf^#VZ=Mt#kL%mcQZ3We)U*qE$pn@4=+ff*D8ehcYF^%gL$RXKcUUJ4_0iyfhcx_@{fcKSDL zD@SC6j-qCaVw6qj(cRIs>{n6tuWi|}=4DIUZH{I=vQyPy^W3ERM(S2PkpfSQc^$a! z^wOFu;_e+DX56V5Qg4z`wF`3a186g3qR54~JBu9XC6w4L{W}ZDX=fMv4Qb;{E1y&; z(%VuZ1aBb(@4SU4WnDf4nl6i{P}w(y%6`1A?8lqn@`E9G9~X-3s%&+3dui!Kp$5wV z{~%8!++We08+X5~IXCWp-crBF=)ekgJwak>|++tbjx zA49E3m0nG_A3zzuR}=2rSSHk~3HSAcW!rzVw+C%_l{b#-Eh;bWZdGt_x8ntjBxnx? zE_*lOKC5y{?#mZaz*gtA(m$*NJpssOR7Ym_K!T3px6^sua~w;1HUG4^Q(jWFz5Hd( zT?zLeY>SSFG|YHO^wvJe%r^HSKnD)y38gzuMUlS2Vt$90MBR64U9DZeAF=O`3fJ6a z-UFNcz-$v>90N@-_u*;(sW_i)nY+~+z&*yd`1^V-v63i%0BJXSn<*b=Z}K)d_dJ(? zCi(le6>k8}XaP#ytFXtNrVXB!m@~d H2l?P7HH7Vs%?{D8&r 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 AB>(^b diff --git a/pygad/helper/__pycache__/__init__.cpython-310.pyc b/pygad/helper/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index b92603d2b45f76f85219da6837cef6d3d8aed1e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmYjJK~BRk5VW17sv!A8uerp)6@*#|skaIVxa4A0^(wZA?O;1dffw0_n=;J|0Z(@hGvZuR!EXa+7m-tN|R=&S<6soZ6@;bJyJL>bxz7soTp&K|g@^sorNu^R*D4;DdHnh#%sM6eK!s`CGt% z@09_GulxzZdvpWef`zCLo4#{iM`fO;sGQ~h{rud|h0s<;M1& diff --git a/pygad/helper/__pycache__/misc.cpython-310.pyc b/pygad/helper/__pycache__/misc.cpython-310.pyc deleted file mode 100644 index 123c531aadb87af95284cd52653b805ece4622da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16104 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 diff --git a/pygad/helper/__pycache__/unique.cpython-310.pyc b/pygad/helper/__pycache__/unique.cpython-310.pyc deleted file mode 100644 index 9a4f0d878facb06a20e518256ff42f50bd215673..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14743 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