Skip to content

Commit 33f443e

Browse files
authored
Merge pull request #324 from ahmedfgad/master
Update the code
2 parents 1070ae7 + 44a9070 commit 33f443e

File tree

2 files changed

+123
-145
lines changed

2 files changed

+123
-145
lines changed

pygad/pygad.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def __init__(self,
4949
random_mutation_min_val=-1.0,
5050
random_mutation_max_val=1.0,
5151
gene_space=None,
52+
gene_constraint=None,
5253
allow_duplicate_genes=True,
5354
on_start=None,
5455
on_fitness=None,
@@ -104,6 +105,8 @@ def __init__(self,
104105
105106
gene_space: It accepts a list of all possible values of the gene. This list is used in the mutation step. Should be used only if the gene space is a set of discrete values. No need for the 2 parameters (random_mutation_min_val and random_mutation_max_val) if the parameter gene_space exists. Added in PyGAD 2.5.0. In PyGAD 2.11.0, the gene_space can be assigned a dict.
106107
108+
gene_constraint: It accepts a list of constraints for the genes. Each constraint is a Python function. Added in PyGAD 3.5.0.
109+
107110
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.
108111
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.
109112
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.
@@ -558,33 +561,58 @@ def __init__(self,
558561
self.random_mutation_min_val = random_mutation_min_val
559562
self.random_mutation_max_val = random_mutation_max_val
560563

564+
# Validate that gene_constraint is a list or tuple and every element inside it is either None or callable.
565+
if gene_constraint:
566+
if type(gene_constraint) in [list, tuple]:
567+
for constraint_idx, item in enumerate(gene_constraint):
568+
# Check whether the element is None or a callable.
569+
if item and callable(item):
570+
if item.__code__.co_argcount == 1:
571+
# Every callable is valid if it receives a single argument.
572+
# This argument represents the solution.
573+
pass
574+
else:
575+
self.valid_parameters = False
576+
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).")
577+
else:
578+
self.valid_parameters = False
579+
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.")
580+
else:
581+
self.valid_parameters = False
582+
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.")
583+
else:
584+
# It is None.
585+
pass
586+
587+
self.gene_constraint = gene_constraint
588+
561589
# Validating the number of parents to be selected for mating (num_parents_mating)
562590
if num_parents_mating <= 0:
563591
self.valid_parameters = False
564592
raise ValueError(f"The number of parents mating (num_parents_mating) parameter must be > 0 but ({num_parents_mating}) found. \nThe following parameters must be > 0: \n1) Population size (i.e. number of solutions per population) (sol_per_pop).\n2) Number of selected parents in the mating pool (num_parents_mating).\n")
565593

566594
# Validating the number of parents to be selected for mating: num_parents_mating
567-
if (num_parents_mating > self.sol_per_pop):
595+
if num_parents_mating > self.sol_per_pop:
568596
self.valid_parameters = False
569597
raise ValueError(f"The number of parents to select for mating ({num_parents_mating}) cannot be greater than the number of solutions in the population ({self.sol_per_pop}) (i.e., num_parents_mating must always be <= sol_per_pop).\n")
570598

571599
self.num_parents_mating = num_parents_mating
572600

573601
# crossover: Refers to the method that applies the crossover operator based on the selected type of crossover in the crossover_type property.
574602
# Validating the crossover type: crossover_type
575-
if (crossover_type is None):
603+
if crossover_type is None:
576604
self.crossover = None
577605
elif inspect.ismethod(crossover_type):
578606
# Check if the crossover_type is a method that accepts 4 paramaters.
579-
if (crossover_type.__code__.co_argcount == 4):
607+
if crossover_type.__code__.co_argcount == 4:
580608
# The crossover method assigned to the crossover_type parameter is validated.
581609
self.crossover = crossover_type
582610
else:
583611
self.valid_parameters = False
584612
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).")
585613
elif callable(crossover_type):
586614
# Check if the crossover_type is a function that accepts 2 paramaters.
587-
if (crossover_type.__code__.co_argcount == 3):
615+
if crossover_type.__code__.co_argcount == 3:
588616
# The crossover function assigned to the crossover_type parameter is validated.
589617
self.crossover = crossover_type
590618
else:

0 commit comments

Comments
 (0)