From 25ef75284dc3bc45a2a2ce38631656e24480911c Mon Sep 17 00:00:00 2001 From: SevgiAkten Date: Tue, 13 Aug 2024 10:46:41 +0300 Subject: [PATCH] (#50) add tests for the optimization methods --- src/example/example_mcccga.py | 32 +++-- src/tests/test_optimizer_alpha_cga.py | 195 ++++++++++++++++++++++++++ src/tests/test_optimizer_ccga.py | 51 +++++++ src/tests/test_optimizer_cga.py | 195 ++++++++++++++++++++++++++ src/tests/test_optimizer_mccga.py | 52 +++++++ src/tests/test_optimizer_sync_cga.py | 193 +++++++++++++++++++++++++ 6 files changed, 703 insertions(+), 15 deletions(-) create mode 100644 src/tests/test_optimizer_alpha_cga.py create mode 100644 src/tests/test_optimizer_ccga.py create mode 100644 src/tests/test_optimizer_cga.py create mode 100644 src/tests/test_optimizer_mccga.py create mode 100644 src/tests/test_optimizer_sync_cga.py diff --git a/src/example/example_mcccga.py b/src/example/example_mcccga.py index 130e654..e81fc31 100644 --- a/src/example/example_mcccga.py +++ b/src/example/example_mcccga.py @@ -3,13 +3,15 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import optimizer +import numpy as np from individual import GeneType -class ExampleProblem: +class RealProblem: """ Example problem class to be minimized. - This class implements a simple binary optimization problem, where the goal is to maximize the number of 1s. + This class implements a simple sum of squares function with a global minimum value of 0, + achieved when all elements of the chromosome are equal to 0. """ def __init__(self): @@ -19,19 +21,19 @@ def f(self, x): """ Compute the objective function value. - This method implements a simple sum of binary values. + This method implements the sum of squares function. Parameters ---------- x : list or numpy.ndarray - The input chromosome represented as a list or array of binary values (0s and 1s). + The input chromosome represented as a list or array of real values. Returns ------- - int + float The computed value of the function given the input x. """ - return sum(x) + return sum(np.power(xi, 2) for xi in x) def run_mcccga_example(): """ @@ -39,8 +41,8 @@ def run_mcccga_example(): using the optimizer module. The mcccga is configured with a 5x5 grid, 100 generations, and a chromosome size of 10. - The problem being solved is an instance of the ExampleProblem class, - with binary genes, constrained by specified mins and maxs. + The problem being solved is an instance of the RealProblem class, + with real genes, constrained by specified mins and maxs. Returns ------- @@ -48,18 +50,18 @@ def run_mcccga_example(): A tuple containing the best solution chromosome and its corresponding value. """ # Create an instance of the problem - problem_instance = ExampleProblem() + problem_instance = RealProblem() result = optimizer.mcccga( n_cols=5, n_rows=5, - n_gen=100, - ch_size=10, - gen_type=GeneType.BINARY, - problem=problem_instance, # Pass the ExampleProblem instance + n_gen=500, + ch_size=5, + gen_type=GeneType.REAL, + problem=problem_instance, # Pass the RealProblem instance selection=optimizer.TournamentSelection, - mins=[0] * 10, # Minimum values for each gene (binary) - maxs=[1] * 10 # Maximum values for each gene (binary) + mins=[-3.768] * 5, + maxs=[3.768] * 5 ) # Print the results diff --git a/src/tests/test_optimizer_alpha_cga.py b/src/tests/test_optimizer_alpha_cga.py new file mode 100644 index 0000000..04e8083 --- /dev/null +++ b/src/tests/test_optimizer_alpha_cga.py @@ -0,0 +1,195 @@ +import pytest +from optimizer import alpha_cga, GeneType, TournamentSelection, ByteOnePointCrossover, ByteMutationRandom, OnePointCrossover, BitFlipMutation, PMXCrossover, SwapMutation +import numpy as np +from typing import List + +class RealProblem: + """ + Example problem class to be minimized. + + This class implements a simple sum of squares function with a global minimum value of 0, + achieved when all elements of the chromosome are equal to 0. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method implements the sum of squares function. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of real values. + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(np.power(xi, 2) for xi in x) + +def test_optimizer_alpha_cga_real(): + """Test alpha_cga on a real-valued sum of squares problem.""" + result = alpha_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=5, + gen_type=GeneType.REAL, + p_crossover=0.9, + p_mutation=0.2, + problem=RealProblem(), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + mins=[-32.768] * 5, + maxs=[32.768] * 5 + + ) + assert result[1] == 0.0, "The alpha_cga did not find the global minimum." + assert result[0] == [0.0] * 5, "The chromosome does not match the global minimum." + +class BinaryProblem: + """ + Example problem class to be maximized for binary chromosomes. + + This class implements the OneMax problem where the goal is to maximize the number of 1s in a binary string. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method counts the number of 1s in the binary chromosome. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of binary values (0s and 1s). + + Returns + ------- + int + The computed value, which is the count of 1s in the chromosome. + """ + return -sum(x) + +def test_optimizer_alpha_cga_binary(): + """Test alpha_cga on a binary OneMax problem.""" + result = alpha_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=10, + gen_type=GeneType.BINARY, + p_crossover=0.9, + p_mutation=0.2, + problem=BinaryProblem(), + selection=TournamentSelection, + recombination=OnePointCrossover, + mutation=BitFlipMutation, + mins=[0] * 10, + maxs=[1] * 10 + ) + assert result[1] == -10, "The alpha_cga did not maximize the number of 1s." + assert result[0] == [1] * 10, "The chromosome does not match the optimal binary sequence." + + + +class PermutationProblem: + """ + Example problem class to be minimized using a permutation-based approach. + + This class implements a simple objective function that measures the sum of absolute differences + between the chromosome and a target permutation. + """ + + def __init__(self, target: List[int]): + """ + Initialize the PermutationProblem with a target permutation. + + Parameters + ---------- + target : list of int + The target permutation that the algorithm aims to find. + """ + self.target = target + + def f(self, x: List[int]) -> float: + """ + Compute the objective function value. + + This method implements the sum of absolute differences function. + + Parameters + ---------- + x : list + The input chromosome represented as a list of integers (permutation). + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(abs(xi - ti) for xi, ti in zip(x, self.target)) + + + def test_optimizer_alpha_cga_permutation(self): + """ + Test alpha_cga on a permutation-based problem where the target is the identity permutation. + """ + target_permutation = [i for i in range(10)] + problem = PermutationProblem(target=target_permutation) + + result = alpha_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=10, + gen_type=GeneType.PERMUTATION, + p_crossover=0.9, + p_mutation=0.2, + problem=problem.f(target_permutation), + selection=TournamentSelection(), + recombination=PMXCrossover(), + mutation=SwapMutation(), + mins=[0] * 10, + maxs=[9] * 10 + ) + + # Assert that the alpha_cga finds the global minimum + print(result[0]) + print(result[1]) + assert result[1] == 0.0, "The alpha_cga did not find the global minimum." + assert result[0] == target_permutation, "The chromosome does not match the target permutation." + + +def test_optimizer_alpha_cga_no_variation(): + """Test alpha_cga with no crossover or mutation.""" + result = alpha_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=5, + gen_type=GeneType.REAL, + p_crossover=0.0, + p_mutation=0.0, + problem=RealProblem(), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + mins=[-32.768] * 5, + maxs=[32.768] * 5 + ) + assert result[1] != 0.0, "With no crossover or mutation, the solution should not reach the global minimum." + + +if __name__ == "__main__": + pytest.main() diff --git a/src/tests/test_optimizer_ccga.py b/src/tests/test_optimizer_ccga.py new file mode 100644 index 0000000..9e7cf90 --- /dev/null +++ b/src/tests/test_optimizer_ccga.py @@ -0,0 +1,51 @@ +import pytest +from optimizer import ccga, GeneType, TournamentSelection, ByteOnePointCrossover, ByteMutationRandom, OnePointCrossover, BitFlipMutation, PMXCrossover, SwapMutation +import numpy as np +from typing import List + +class BinaryProblem: + """ + Example problem class to be maximized for binary chromosomes. + + This class implements the OneMax problem where the goal is to maximize the number of 1s in a binary string. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method counts the number of 1s in the binary chromosome. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of binary values (0s and 1s). + + Returns + ------- + int + The computed value, which is the count of 1s in the chromosome. + """ + return sum(x) + +def test_optimizer_ccga_binary(): + """Test ccga on a binary OneMax problem.""" + result = ccga( + n_cols=5, + n_rows=5, + n_gen=200, + ch_size=5, + gen_type=GeneType.BINARY, + problem=BinaryProblem(), + selection=TournamentSelection, + mins=[0] * 5, + maxs=[1] * 5 + ) + assert result[1] == 5, "The ccga did not maximize the number of 1s." + assert result[0] == [1] * 5, "The chromosome does not match the optimal binary sequence." + +if __name__ == "__main__": + pytest.main() diff --git a/src/tests/test_optimizer_cga.py b/src/tests/test_optimizer_cga.py new file mode 100644 index 0000000..5f241d7 --- /dev/null +++ b/src/tests/test_optimizer_cga.py @@ -0,0 +1,195 @@ +import pytest +from optimizer import cga, GeneType, TournamentSelection, ByteOnePointCrossover, ByteMutationRandom, OnePointCrossover, BitFlipMutation, PMXCrossover, SwapMutation +import numpy as np +from typing import List + +class RealProblem: + """ + Example problem class to be minimized. + + This class implements a simple sum of squares function with a global minimum value of 0, + achieved when all elements of the chromosome are equal to 0. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method implements the sum of squares function. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of real values. + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(np.power(xi, 2) for xi in x) + +def test_optimizer_cga_real(): + """Test CGA on a real-valued sum of squares problem.""" + result = cga( + n_cols=5, + n_rows=5, + n_gen=100, + ch_size=5, + gen_type=GeneType.REAL, + p_crossover=0.9, + p_mutation=0.2, + problem=RealProblem(), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + mins=[-32.768] * 5, + maxs=[32.768] * 5 + ) + assert result[1] == 0.0, "The CGA did not find the global minimum." + assert result[0] == [0.0] * 5, "The chromosome does not match the global minimum." + +class BinaryProblem: + """ + Example problem class to be maximized for binary chromosomes. + + This class implements the OneMax problem where the goal is to maximize the number of 1s in a binary string. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method counts the number of 1s in the binary chromosome. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of binary values (0s and 1s). + + Returns + ------- + int + The computed value, which is the count of 1s in the chromosome. + """ + return -sum(x) + +def test_optimizer_cga_binary(): + """Test CGA on a binary OneMax problem.""" + result = cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=10, + gen_type=GeneType.BINARY, + p_crossover=0.9, + p_mutation=0.2, + problem=BinaryProblem(), + selection=TournamentSelection, + recombination=OnePointCrossover, + mutation=BitFlipMutation, + mins=[0] * 10, + maxs=[1] * 10 + ) + assert result[1] == -10, "The CGA did not maximize the number of 1s." + assert result[0] == [1] * 10, "The chromosome does not match the optimal binary sequence." + + + +class PermutationProblem: + """ + Example problem class to be minimized using a permutation-based approach. + + This class implements a simple objective function that measures the sum of absolute differences + between the chromosome and a target permutation. + """ + + def __init__(self, target: List[int]): + """ + Initialize the PermutationProblem with a target permutation. + + Parameters + ---------- + target : list of int + The target permutation that the algorithm aims to find. + """ + self.target = target + + def f(self, x: List[int]) -> float: + """ + Compute the objective function value. + + This method implements the sum of absolute differences function. + + Parameters + ---------- + x : list + The input chromosome represented as a list of integers (permutation). + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(abs(xi - ti) for xi, ti in zip(x, self.target)) + + + def test_optimizer_cga_permutation(self): + """ + Test CGA on a permutation-based problem where the target is the identity permutation. + """ + target_permutation = [i for i in range(10)] + problem = PermutationProblem(target=target_permutation) + + result = cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=10, + gen_type=GeneType.PERMUTATION, + p_crossover=0.9, + p_mutation=0.2, + problem=problem.f(target_permutation), + selection=TournamentSelection(), + recombination=PMXCrossover(), + mutation=SwapMutation(), + mins=[0] * 10, + maxs=[9] * 10 + ) + + # Assert that the CGA finds the global minimum + print(result[0]) + print(result[1]) + assert result[1] == 0.0, "The CGA did not find the global minimum." + assert result[0] == target_permutation, "The chromosome does not match the target permutation." + + +def test_optimizer_cga_no_variation(): + """Test CGA with no crossover or mutation.""" + result = cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=5, + gen_type=GeneType.REAL, + p_crossover=0.0, + p_mutation=0.0, + problem=RealProblem(), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + mins=[-32.768] * 5, + maxs=[32.768] * 5 + ) + assert result[1] != 0.0, "With no crossover or mutation, the solution should not reach the global minimum." + + + +if __name__ == "__main__": + pytest.main() diff --git a/src/tests/test_optimizer_mccga.py b/src/tests/test_optimizer_mccga.py new file mode 100644 index 0000000..f9de4a2 --- /dev/null +++ b/src/tests/test_optimizer_mccga.py @@ -0,0 +1,52 @@ +import pytest +from optimizer import mcccga, GeneType, TournamentSelection, ByteOnePointCrossover, ByteMutationRandom, OnePointCrossover, BitFlipMutation, PMXCrossover, SwapMutation +import numpy as np +from typing import List + +class RealProblem: + """ + Example problem class to be minimized. + + This class implements a simple sum of squares function with a global minimum value of 0, + achieved when all elements of the chromosome are equal to 0. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method implements the sum of squares function. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of real values. + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(np.power(xi, 2) for xi in x) + +def test_optimizer_mcccga_binary(): + """Test mcccga on a binary OneMax problem.""" + result = mcccga( + n_cols=5, + n_rows=5, + n_gen=500, + ch_size=5, + gen_type=GeneType.REAL, + problem=RealProblem(), + selection=TournamentSelection, + mins=[-3.768] * 5, + maxs=[3.768] * 5 + ) + assert result[1] == 0.0, "The mcccga did not find the global minimum." + assert result[0] == [0.0] * 5, "The chromosome does not match the global minimum." + +if __name__ == "__main__": + pytest.main() diff --git a/src/tests/test_optimizer_sync_cga.py b/src/tests/test_optimizer_sync_cga.py new file mode 100644 index 0000000..854eade --- /dev/null +++ b/src/tests/test_optimizer_sync_cga.py @@ -0,0 +1,193 @@ +import pytest +from optimizer import sync_cga, GeneType, TournamentSelection, ByteOnePointCrossover, ByteMutationRandom, OnePointCrossover, BitFlipMutation, PMXCrossover, SwapMutation +import numpy as np +from typing import List + +class RealProblem: + """ + Example problem class to be minimized. + + This class implements a simple sum of squares function with a global minimum value of 0, + achieved when all elements of the chromosome are equal to 0. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method implements the sum of squares function. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of real values. + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(np.power(xi, 2) for xi in x) + +def test_optimizer_sync_cga_real(): + """Test sync_cga on a real-valued sum of squares problem.""" + result = sync_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=5, + gen_type=GeneType.REAL, + p_crossover=0.9, + p_mutation=0.2, + problem=RealProblem(), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + mins=[-32.768] * 5, + maxs=[32.768] * 5 + ) + assert result[1] == 0.0, "The sync_cga did not find the global minimum." + assert result[0] == [0.0] * 5, "The chromosome does not match the global minimum." + +class BinaryProblem: + """ + Example problem class to be maximized for binary chromosomes. + + This class implements the OneMax problem where the goal is to maximize the number of 1s in a binary string. + """ + + def __init__(self): + pass + + def f(self, x): + """ + Compute the objective function value. + + This method counts the number of 1s in the binary chromosome. + + Parameters + ---------- + x : list or numpy.ndarray + The input chromosome represented as a list or array of binary values (0s and 1s). + + Returns + ------- + int + The computed value, which is the count of 1s in the chromosome. + """ + return -sum(x) + +def test_optimizer_sync_cga_binary(): + """Test sync_cga on a binary OneMax problem.""" + result = sync_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=10, + gen_type=GeneType.BINARY, + p_crossover=0.9, + p_mutation=0.2, + problem=BinaryProblem(), + selection=TournamentSelection, + recombination=OnePointCrossover, + mutation=BitFlipMutation, + mins=[0] * 10, + maxs=[1] * 10 + ) + assert result[1] == -10, "The sync_cga did not maximize the number of 1s." + assert result[0] == [1] * 10, "The chromosome does not match the optimal binary sequence." + + + +class PermutationProblem: + """ + Example problem class to be minimized using a permutation-based approach. + + This class implements a simple objective function that measures the sum of absolute differences + between the chromosome and a target permutation. + """ + + def __init__(self, target: List[int]): + """ + Initialize the PermutationProblem with a target permutation. + + Parameters + ---------- + target : list of int + The target permutation that the algorithm aims to find. + """ + self.target = target + + def f(self, x: List[int]) -> float: + """ + Compute the objective function value. + + This method implements the sum of absolute differences function. + + Parameters + ---------- + x : list + The input chromosome represented as a list of integers (permutation). + + Returns + ------- + float + The computed value of the function given the input x. + """ + return sum(abs(xi - ti) for xi, ti in zip(x, self.target)) + + + def test_optimizer_sync_cga_permutation(self): + """ + Test sync_cga on a permutation-based problem where the target is the identity permutation. + """ + target_permutation = [i for i in range(10)] + problem = PermutationProblem(target=target_permutation) + + result = sync_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=10, + gen_type=GeneType.PERMUTATION, + p_crossover=0.9, + p_mutation=0.2, + problem=problem.f(target_permutation), + selection=TournamentSelection(), + recombination=PMXCrossover(), + mutation=SwapMutation(), + mins=[0] * 10, + maxs=[9] * 10 + ) + + # Assert that the sync_cga finds the global minimum + print(result[0]) + print(result[1]) + assert result[1] == 0.0, "The sync_cga did not find the global minimum." + assert result[0] == target_permutation, "The chromosome does not match the target permutation." + + +def test_optimizer_sync_cga_no_variation(): + """Test sync_cga with no crossover or mutation.""" + result = sync_cga( + n_cols=5, + n_rows=5, + n_gen=50, + ch_size=5, + gen_type=GeneType.REAL, + p_crossover=0.0, + p_mutation=0.0, + problem=RealProblem(), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + mins=[-32.768] * 5, + maxs=[32.768] * 5 + ) + assert result[1] != 0.0, "With no crossover or mutation, the solution should not reach the global minimum." + +if __name__ == "__main__": + pytest.main()