diff --git a/README.md b/README.md index bacb23b..a94d7e2 100644 --- a/README.md +++ b/README.md @@ -56,45 +56,49 @@ Here’s how we can define this problem in Python using the `ExampleProblem` cla ```python from mpmath import power as pw +from typing import List from pycellga.optimizer import cga -from pycellga.individual import GeneType -from pycellga.selection import TournamentSelection from pycellga.recombination import ByteOnePointCrossover from pycellga.mutation import ByteMutationRandom +from pycellga.selection import TournamentSelection +from pycellga.problems import AbstractProblem +from pycellga.common import GeneType -class ExampleProblem: - - def __init__(self): - pass - - def f(self, x): - - return sum(pw(xi, 2) for xi in x) +class ExampleProblem(AbstractProblem): + + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) + + def f(self, x: List[float]) -> float: + return round(sum(pw(xi, 2) for xi in x),3) ``` **Usage:** ```python 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=ExampleProblem(), # Replace with a real problem instance as needed - selection=TournamentSelection, - recombination=ByteOnePointCrossover, - mutation=ByteMutationRandom, - mins=[-32.768] * 5, # Minimum values for each gene - maxs=[32.768] * 5, # Maximum values for each gene - seed_par=100 # Ensures the random number generation is repeatable -) - -# Print the best solution details -print("Best solution chromosome:", result.chromosome) -print("Best fitness value:", result.fitness_value) + n_cols=5, + n_rows=5, + n_gen=100, + ch_size=5, + p_crossover=0.9, + p_mutation=0.2, + problem=ExampleProblem(n_var=5), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + seed_par=100 + ) + + # Print the results + print("Best solution chromosome:", result.chromosome) + print("Best fitness value:", result.fitness_value) # Expected Output: # Best solution chromosome: [0.0, 0.0, 0.0, 0.0, 0.0] diff --git a/docs/conf.py b/docs/conf.py index 47355e2..ae8874b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,7 +31,8 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] +html_static_path = ['_static', 'images'] + html_theme_options = { 'collapse_navigation': False, diff --git a/docs/images/alpha_cga.png b/docs/images/alpha_cga.png new file mode 100644 index 0000000..0bec5f5 Binary files /dev/null and b/docs/images/alpha_cga.png differ diff --git a/docs/images/bit_flip_mutation.png b/docs/images/bit_flip_mutation.png new file mode 100644 index 0000000..6c3792a Binary files /dev/null and b/docs/images/bit_flip_mutation.png differ diff --git a/docs/images/cga.png b/docs/images/cga.png new file mode 100644 index 0000000..5a3b224 Binary files /dev/null and b/docs/images/cga.png differ diff --git a/docs/images/compact_9_example.png b/docs/images/compact_9_example.png new file mode 100644 index 0000000..c7847af Binary files /dev/null and b/docs/images/compact_9_example.png differ diff --git a/docs/images/cycle_cga.png b/docs/images/cycle_cga.png new file mode 100644 index 0000000..751e47b Binary files /dev/null and b/docs/images/cycle_cga.png differ diff --git a/docs/images/f_byte.png b/docs/images/f_byte.png new file mode 100644 index 0000000..b8a82ab Binary files /dev/null and b/docs/images/f_byte.png differ diff --git a/docs/images/icga.png b/docs/images/icga.png new file mode 100644 index 0000000..7ccc204 Binary files /dev/null and b/docs/images/icga.png differ diff --git a/docs/images/ieee.png b/docs/images/ieee.png new file mode 100644 index 0000000..21a5362 Binary files /dev/null and b/docs/images/ieee.png differ diff --git a/docs/images/linear_5_example.png b/docs/images/linear_5_example.png new file mode 100644 index 0000000..49070fa Binary files /dev/null and b/docs/images/linear_5_example.png differ diff --git a/docs/images/one_point_c.png b/docs/images/one_point_c.png new file mode 100644 index 0000000..471cc57 Binary files /dev/null and b/docs/images/one_point_c.png differ diff --git a/docs/images/roulette_wheel_selection.png b/docs/images/roulette_wheel_selection.png new file mode 100644 index 0000000..19d5101 Binary files /dev/null and b/docs/images/roulette_wheel_selection.png differ diff --git a/docs/images/swap_mutation.png b/docs/images/swap_mutation.png new file mode 100644 index 0000000..fe6ad80 Binary files /dev/null and b/docs/images/swap_mutation.png differ diff --git a/docs/images/two_point_c.png b/docs/images/two_point_c.png new file mode 100644 index 0000000..1a93fcc Binary files /dev/null and b/docs/images/two_point_c.png differ diff --git a/docs/images/uniform_c.png b/docs/images/uniform_c.png new file mode 100644 index 0000000..cc5bce8 Binary files /dev/null and b/docs/images/uniform_c.png differ diff --git a/docs/index.rst b/docs/index.rst index 28249b8..3b1ac68 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,6 +17,14 @@ promoting diversity and maintaining a balance between exploration and exploitati `pycellga` has machine coded operators with byte implementations. Beside it has Alpha-male CGA, Machine Coded Compact CGA and Improved CGA with Machine Coded Operators for real-valued optimization problems. +.. image:: images/cycle_cga.png + :scale: 40% + :alt: Reproductive cycle of an individual in cGA + :align: center + + +A representation of the reproductive cycle of an individual in a cellular genetic algorithm. + .. toctree:: :maxdepth: 1 :caption: Table of Contents : diff --git a/docs/pycellga.example.rst b/docs/pycellga.example.rst deleted file mode 100644 index c35cf41..0000000 --- a/docs/pycellga.example.rst +++ /dev/null @@ -1,66 +0,0 @@ -Example Implementations in pycellga -=================================== - -The `pycellga.example` package includes example implementations of various cellular genetic algorithms (CGAs) and their variants. These examples demonstrate how to set up and run different CGA methods using the `pycellga` framework, providing users with practical insights into the application of these algorithms. - -Available Example Modules -------------------------- - -### Example: Alpha-CGA -**Module**: `pycellga.example.example_alpha_cga` - -Demonstrates the Alpha Cellular Genetic Algorithm (Alpha-CGA), an enhanced version of CGA that incorporates an "alpha male" mechanism for improved convergence and exploration. - -.. automodule:: pycellga.example.example_alpha_cga - :members: - :undoc-members: - :show-inheritance: - -### Example: Compact-CGA (CCGA) -**Module**: `pycellga.example.example_ccga` - -Provides an example of the Compact Cellular Genetic Algorithm (CCGA), which utilizes a compact population structure to minimize memory usage and improve computational efficiency. - -.. automodule:: pycellga.example.example_ccga - :members: - :undoc-members: - :show-inheritance: - -### Example: Standard CGA -**Module**: `pycellga.example.example_cga` - -Illustrates the implementation of a standard Cellular Genetic Algorithm (CGA), highlighting the core principles of CGA with basic settings. - -.. automodule:: pycellga.example.example_cga - :members: - :undoc-members: - :show-inheritance: - -### Example: Machine-Coded Compact CGA (MCC-CGA) -**Module**: `pycellga.example.example_mcccga` - -Demonstrates the Machine-Coded Compact Cellular Genetic Algorithm (MCC-CGA), which uses byte-level encoding for efficient real-valued optimization in a compact population setting. - -.. automodule:: pycellga.example.example_mcccga - :members: - :undoc-members: - :show-inheritance: - -### Example: Synchronous CGA (Sync-CGA) -**Module**: `pycellga.example.example_sync_cga` - -Illustrates the Synchronous Cellular Genetic Algorithm (Sync-CGA), where all individuals in the population are updated simultaneously, providing an alternative synchronization mechanism in CGA. - -.. automodule:: pycellga.example.example_sync_cga - :members: - :undoc-members: - :show-inheritance: - -## Full Module Overview - -For a comprehensive overview of the example implementations available in the `example` package, refer to the module documentation below: - -.. automodule:: pycellga.example - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/pycellga.mutation.rst b/docs/pycellga.mutation.rst index 85ad41f..a958403 100644 --- a/docs/pycellga.mutation.rst +++ b/docs/pycellga.mutation.rst @@ -1,12 +1,54 @@ Mutation Operators -============================== +================== -The `pycellga.mutation` module provides a comprehensive set of mutation operators designed to introduce variation into the population during the genetic algorithm optimization process. Each operator serves a unique purpose based on the representation of the chromosome (binary, real-valued, or permutation-based) and the specific requirements of the optimization problem. +Mutation operators introduce variation into the population by modifying one or more genes of an individual. In cellular genetic algorithms (CGAs), mutation is a key mechanism that ensures diversity and enables exploration of the solution space, preventing premature convergence. -This module includes the following mutation operators: +**Understanding Mutation** +-------------------------- + +Mutation in genetic algorithms mimics the natural process of genetic mutation, where random changes occur in an individual’s genome. These changes help the algorithm discover new solutions and improve its overall performance. + +**The Role of Mutation in CGA** + +- **Diversity Preservation**: Mutation ensures that the population does not converge prematurely to suboptimal solutions. +- **Exploration**: Introduces new solutions to explore unexplored regions of the solution space. +- **Fine-Tuning**: Makes small adjustments to solutions, aiding in reaching optimal solutions. + +**Mutation Examples** +-------------------------- + +Below is an example of a **Bit-Flip Mutation** applied to a binary chromosome: + +.. image:: images/bit_flip_mutation.png + :scale: 50% + :alt: Bit-flip Mutation Structure + :align: center + +Figure 1: Bit-flip mutation flips a single gene in a binary chromosome. + +Another example is the **Swap Mutation**, often used for permutation-based problems. It swaps two genes, introducing a small but significant change in the chromosome: + +.. image:: images/swap_mutation.png + :scale: 50% + :align: center + +Figure 2: Swap mutation changes the order of two genes in a chromosome. + +**Common Mutation Types** + +1. **Bit-Flip Mutation**: Flips a bit in binary-encoded chromosomes. +2. **Swap Mutation**: Exchanges two genes in permutation-based chromosomes. +3. **Byte Mutation**: Applies byte-level changes to real-valued genes. +4. **Two-Opt Mutation**: Reverses a segment of the chromosome, particularly useful in path optimization. + + +**API References** +------------------ + +The following sections provide detailed documentation for the mutation operators available in the `pycellga.mutation` package. **Bit Flip Mutation** ------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ Applies a bitwise flip to binary-encoded chromosomes. This operator is a classic choice for binary genetic algorithms, offering a simple yet effective mutation approach. @@ -15,8 +57,8 @@ Applies a bitwise flip to binary-encoded chromosomes. This operator is a classic :undoc-members: :show-inheritance: -**Byte-Level Mutation** ------------------------------- +**Byte Mutation** +^^^^^^^^^^^^^^^^^^^^^^^^ Performs mutations at the byte level for real-valued chromosomes. This operator leverages byte manipulation to create small, precise adjustments in the solution space, optimizing the algorithm's performance for continuous functions. @@ -26,7 +68,7 @@ Performs mutations at the byte level for real-valued chromosomes. This operator :show-inheritance: **Randomized Byte Mutation** ------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Introduces randomness at the byte level, enabling broader exploration in real-valued optimization tasks. This operator is particularly effective when a high degree of variation is desirable. @@ -36,7 +78,7 @@ Introduces randomness at the byte level, enabling broader exploration in real-va :show-inheritance: **Uniform Float Mutation** ------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Applies uniform random mutations across real-valued chromosomes. This operator is suitable for continuous optimization, where each gene is adjusted within a defined range to enhance solution diversity. @@ -46,7 +88,7 @@ Applies uniform random mutations across real-valued chromosomes. This operator i :show-inheritance: **Insertion-Based Mutation** ------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A mutation strategy tailored for permutation-based representations, such as in sequencing and scheduling problems. This operator repositions a randomly selected gene within the chromosome, altering the order while preserving elements. @@ -57,7 +99,7 @@ A mutation strategy tailored for permutation-based representations, such as in s **Shuffle Mutation** ------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ Randomly rearranges a subset of genes in the chromosome. This operator is effective in permutation-based problems, promoting diversity by shuffling segments without altering individual gene values. @@ -67,7 +109,7 @@ Randomly rearranges a subset of genes in the chromosome. This operator is effect :show-inheritance: **Swap Mutation** -------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ Swaps the positions of two genes, introducing subtle changes ideal for permutation-based optimizations. This operator is commonly applied in combinatorial problems where order is significant. @@ -77,12 +119,11 @@ Swaps the positions of two genes, introducing subtle changes ideal for permutati :show-inheritance: **Two-Opt Mutation** ----------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ A mutation operator frequently used in path optimization problems, such as the Traveling Salesman Problem. It reverses a segment of the chromosome, allowing for new path configurations without altering the gene order. .. automodule:: pycellga.mutation.two_opt_mutation :members: :undoc-members: - :show-inheritance: - + :show-inheritance: \ No newline at end of file diff --git a/docs/pycellga.neighborhoods.rst b/docs/pycellga.neighborhoods.rst index a6c23e7..f6acf86 100644 --- a/docs/pycellga.neighborhoods.rst +++ b/docs/pycellga.neighborhoods.rst @@ -1,14 +1,74 @@ Neighborhood Operators -================================== +======================= -The `pycellga.neighborhoods` module provides various neighborhood structures that define how individuals interact with their neighbors in the cellular genetic algorithm (CGA). These neighborhood operators are essential for managing the flow of information and maintaining diversity within the population. -Each neighborhood module defines a specific spatial structure. Depending on the problem and the desired interaction level, users can select different neighborhood sizes and arrangements. +This module provides various neighborhood structures for cellular genetic algorithms (CGAs). Neighborhood structures define how individuals interact with others in their immediate vicinity, playing a critical role in regulating the flow of information and maintaining diversity within the population. +In cellular genetic algorithms, the concept of neighborhoods is integral to the evolutionary process. As shown in the diagram below, each individual in the population interacts with its neighbors during key stages of the evolutionary cycle: Selection, Recombination, Mutation, and Replacement. This localized interaction helps balance exploration and exploitation, promoting better local optimization while preserving diversity. + +.. image:: images/cycle_cga.png + :scale: 40% + :alt: Reproductive cycle of an individual in cGA + :align: center + +Figure 1: A representation of the reproduction cycle with neighborhood structure in a cellular genetic algorithm. + +The neighborhood structure governs which individuals are selected for interactions during the evolutionary cycle. Depending on the problem requirements, users can choose different types of neighborhoods, such as linear or compact structures, to optimize the algorithm's performance. + + +**The Concept of Neighborhood** +------------------------------- + +Neighborhood refers to a spatial structure used to improve solutions in optimization problems. In most cases, the neighborhood of a solution represents solutions obtained through small modifications. + +Below are some popular neighborhood structures: + +- **Linear Neighborhood**: Individuals interact with neighbors arranged in a straight line. +- **Compact Neighborhood**: A structure formed by cells surrounding an individual, allowing denser interaction. + +**Why Use Different Structures?** + +Each neighborhood structure is suited for specific problem types: +- **Linear structures** are more appropriate for sequential or ordered problems. +- **Compact structures** are ideal for scenarios requiring dense information sharing and local exploration. + + +**Neighborhood Scheme Examples** +------------------------ + +**Linear Neighborhood** +A linear neighborhood structure refers to an arrangement where individuals interact with a specific number of neighbors in a straight line. For instance, consider a linear structure with 5 neighbors: + +.. image:: images/linear_5_example.png + :scale: 40% + :alt: Linear 5 Neighborhood Structure + :align: center + +Figure 2: Linear 5 Neighborhood Structure + +Each individual shares information only with its immediate neighbors. This structure is suitable for cases requiring limited information flow. + +**Compact Neighborhood** +Compact structures involve individuals sharing information intensively with all their surrounding neighbors. Below is an example of a compact structure with 9 neighbors: + +.. image:: images/compact_9_example.png + :scale: 40% + :alt: Compact 9 Neighborhood Structure + :align: center + +Figure 3: Compact 9 Neighborhood Structure + +This structure can achieve faster convergence but must be used cautiously to maintain diversity. + + +**API References** +------------------ + +The following sections provide detailed documentation for the neighborhood operators available in the `pycellga.neighborhoods` package. **Linear 5** --------------- +^^^^^^^^^^^^ -Defines a linear neighborhood where each individual interacts with 5 neighbors arranged in a line. This structure is suitable for problems where limited, sequential interaction is beneficial. +Description: A structure with 5 neighbors in a linear arrangement. .. automodule:: pycellga.neighborhoods.linear_5 :members: @@ -16,9 +76,9 @@ Defines a linear neighborhood where each individual interacts with 5 neighbors a :show-inheritance: **Linear 9** --------------- +^^^^^^^^^^^^ -A linear arrangement with 9 neighbors, encouraging a higher level of information flow along a line. This structure is ideal for applications that require extended sequential interactions. +Description: A linear arrangement with 9 neighbors for greater information flow. .. automodule:: pycellga.neighborhoods.linear_9 :members: @@ -26,9 +86,9 @@ A linear arrangement with 9 neighbors, encouraging a higher level of information :show-inheritance: **Compact 9** --------------- +^^^^^^^^^^^^^ -A compact neighborhood structure with 9 neighbors. This layout offers dense interaction among individuals, facilitating rapid convergence while maintaining diversity. +Description: A compact structure with 9 neighbors for dense interaction. .. automodule:: pycellga.neighborhoods.compact_9 :members: @@ -36,9 +96,9 @@ A compact neighborhood structure with 9 neighbors. This layout offers dense inte :show-inheritance: **Compact 13** --------------- +^^^^^^^^^^^^^^ -Defines a compact neighborhood structure where each individual interacts with its immediate and extended neighbors in a 13 grid. This structure allows moderate information sharing across neighboring cells. +Description: A compact structure with 13 neighbors. .. automodule:: pycellga.neighborhoods.compact_13 :members: @@ -46,9 +106,9 @@ Defines a compact neighborhood structure where each individual interacts with it :show-inheritance: **Compact 21** --------------- +^^^^^^^^^^^^^^ -Provides a compact arrangement where individuals have a 21 neighborhood structure. This layout encourages local exploration and is useful in tightly clustered populations. +Description: A compact structure with 21 neighbors for broader information sharing. .. automodule:: pycellga.neighborhoods.compact_21 :members: @@ -56,11 +116,11 @@ Provides a compact arrangement where individuals have a 21 neighborhood structur :show-inheritance: **Compact 25** --------------- +^^^^^^^^^^^^^^ -An extended compact neighborhood that includes more neighbors, with a 25 structure. This layout promotes broader information sharing, enhancing convergence in larger populations. +Description: An extended compact structure with 25 neighbors for enhanced information sharing. .. automodule:: pycellga.neighborhoods.compact_25 :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/pycellga.problems.rst b/docs/pycellga.problems.rst index ef5b79c..3288387 100644 --- a/docs/pycellga.problems.rst +++ b/docs/pycellga.problems.rst @@ -1,13 +1,70 @@ Problem Definitions =============================== -The `pycellga.problems` package provides a collection of problem definitions for optimization tasks. Each problem is structured as a class inheriting from a common abstract base, ensuring consistency and flexibility across various types of optimization tasks. This module includes both single-objective and multi-objective problems, designed to serve as benchmarks or examples for testing and applying genetic algorithms. +The `pycellga.problems` package provides a collection of problem definitions for optimization tasks. Each problem is structured as a class inheriting from a common abstract base, ensuring consistency and flexibility across various types of optimization tasks. This module includes single-objective problems, designed to serve as benchmarks or examples for testing and applying genetic algorithms. -**Abstract Problem Base** +**AbstractProblem Class** ------------------------------ -This module defines an abstract base class, `AbstractProblem`, which provides a standard interface for defining optimization problems. By inheriting from this base class, users can create custom problem definitions that are compatible with the rest of the `pycellga` framework. Key components of the base class include methods for evaluating objective values, setting constraints, and managing design variables. +The `AbstractProblem` class is an abstract base class designed to standardize the definition of optimization problems within the `pycellga` framework. It integrates seamlessly with the **pymoo** optimization library and provides a consistent structure for implementing custom problem definitions. + +Key Features +------------ + +- **Gene Type Specification**: + The `gen_type` attribute allows users to define the type of genes (`REAL`, `BINARY`, etc.), ensuring flexibility for various problem domains. + +- **Design Variable Management**: + Users can define the number of variables (`n_var`) and their bounds (`xl` and `xu`), accurately capturing the problem's decision space. + +- **Abstract Fitness Function**: + The `f` method must be implemented in subclasses to compute the fitness of a solution, serving as the core of any optimization problem. + +- **Compatibility with Optimizers**: + The `evaluate` method ensures smooth integration with `pymoo` optimizers, handling batch evaluations and storing results efficiently. + +Attributes +---------- + +- **`gen_type`**: Specifies the gene type for the problem (e.g., `REAL`, `BINARY`). +- **`n_var`**: Number of design variables in the problem. +- **`xl` and `xu`**: Lower and upper bounds for each design variable, provided as lists or arrays. + +Methods +------- + +- **`f(x: List[Any]) -> float`**: + Abstract method for computing the fitness of a solution. This must be implemented in derived classes. + +- **`evaluate(x, out, *args, **kwargs)`**: + A method compatible with `pymoo` optimizers that wraps the `f` method and stores computed fitness values in an output dictionary. + +Example +------- + +Below is an example of how to create a custom optimization problem by inheriting from `AbstractProblem`: + +.. code-block:: python + + from pycellga.problems import AbstractProblem + from pycellga.common import GeneType + from typing import List + import numpy as np + + class MyProblem(AbstractProblem): + def __init__(self): + super().__init__(gen_type=GeneType.REAL, n_var=10, xl=10, xu=10) + + def f(self, x: List[float]) -> float: + # Example fitness function: Sphere function + return np.sum(np.square(x)) + +This structure simplifies the process of defining optimization problems, enabling experimentation with diverse formulations while ensuring compatibility with modern optimization libraries. + + +**API References** +------------------ .. automodule:: pycellga.problems.abstract_problem :members: diff --git a/docs/pycellga.problems.single_objective.continuous.rst b/docs/pycellga.problems.single_objective.continuous.rst index 9b7889f..84eaaa9 100644 --- a/docs/pycellga.problems.single_objective.continuous.rst +++ b/docs/pycellga.problems.single_objective.continuous.rst @@ -132,7 +132,7 @@ This function is used to test the efficiency of algorithms on smooth, differenti :undoc-members: :show-inheritance: -**Rothellipsoid Function** +**Rotated Hyper-Ellipsoid Function** ---------------------------- This function is used to test the efficiency of algorithms on smooth, differentiable landscapes. @@ -142,7 +142,7 @@ This function is used to test the efficiency of algorithms on smooth, differenti :show-inheritance: -**Schaffer Function** +**Modified Schaffer Function #1** ---------------------- This function is used to test the efficiency of algorithms on smooth, differentiable landscapes. @@ -151,7 +151,7 @@ This function is used to test the efficiency of algorithms on smooth, differenti :undoc-members: :show-inheritance: -**Schaffer2 Function** +**Modified Schaffer Function #2** ----------------------- This function is used to test the efficiency of algorithms on smooth, differentiable landscapes. @@ -178,7 +178,7 @@ This function is used to test the efficiency of algorithms on smooth, differenti :undoc-members: :show-inheritance: -**Styblinskitang Function** +**Styblinski-Tang Function** ------------------------------ This function is used to test the efficiency of algorithms on smooth, differentiable landscapes. @@ -187,7 +187,7 @@ This function is used to test the efficiency of algorithms on smooth, differenti :undoc-members: :show-inheritance: -**Sumofdifferentpowers Function** +**Sum of Different Powers Function** ---------------------------------- This function is used to test the efficiency of algorithms on smooth, differentiable landscapes. @@ -196,7 +196,7 @@ This function is used to test the efficiency of algorithms on smooth, differenti :undoc-members: :show-inheritance: -**Threehumps Function** +**Three Hump Camel Function** ------------------------- This function is used to test the efficiency of algorithms on smooth, differentiable landscapes. diff --git a/docs/pycellga.problems.single_objective.discrete.binary.rst b/docs/pycellga.problems.single_objective.discrete.binary.rst index b95a553..4cd8523 100644 --- a/docs/pycellga.problems.single_objective.discrete.binary.rst +++ b/docs/pycellga.problems.single_objective.discrete.binary.rst @@ -14,7 +14,7 @@ A binary satisfaction problem, often used to evaluate an algorithm’s ability t :undoc-members: :show-inheritance: -**ECC Problem** +**Error Correcting Codes Design Problem (ECC)** ----------------- The ECC problem tests the efficiency of algorithms in solving problems related to error-correcting codes, which have discrete solution spaces and are commonly encountered in communication systems. @@ -24,10 +24,10 @@ The ECC problem tests the efficiency of algorithms in solving problems related t :undoc-members: :show-inheritance: -**Fletcher-Powell (FMS) Binary Problem** +**Frequency Modulation Sounds Problem (FMS)** ---------------------------------------------- -A binary version of the Fletcher-Powell function, used to evaluate robustness and efficiency in finding optimal solutions within a binary space. +A binary version of the Frequency Modulation Sounds function, used to evaluate robustness and efficiency in finding optimal solutions within a binary space. .. automodule:: pycellga.problems.single_objective.discrete.binary.fms :members: @@ -64,7 +64,7 @@ A denser version of the max-cut problem with a density of 0.9, requiring algorit :undoc-members: :show-inheritance: -**Multi-modal Deceptive Problem (MMDP)** +**Massively Multimodal Deceptive Problem (MMDP)** ---------------------------------------------- A challenging binary problem with deceptive local optima, commonly used to assess an algorithm's ability to escape local traps in a binary landscape. diff --git a/docs/pycellga.recombination.rst b/docs/pycellga.recombination.rst index 4f5de68..338c9da 100644 --- a/docs/pycellga.recombination.rst +++ b/docs/pycellga.recombination.rst @@ -1,116 +1,140 @@ Recombination Operators =================================== -The `pycellga.recombination` package includes various recombination (crossover) operators used to combine genetic information from parent chromosomes to create offspring in genetic algorithms. Each crossover operator serves a unique purpose and is suitable for different types of genetic representations, including binary, real-valued, and permutation-based chromosomes. +Recombination (crossover) is a fundamental genetic algorithm operator that combines the genetic information of two parents to generate new offspring. The choice of a recombination operator depends on the chromosome representation (binary, real-valued, or permutation-based) and the nature of the optimization problem. +**Understanding Recombination** +-------------------------------- + +Recombination operators promote diversity and exploration in the solution space. They allow offspring to inherit traits from both parents, which can lead to better solutions over generations. + +Below are some common recombination techniques used in genetic algorithms: + +- **Binary Chromosomes**: Techniques like One-Point Crossover and Uniform Crossover are well-suited for binary representations, where each gene is a bit (0 or 1). +- **Real-Valued Chromosomes**: Methods such as Byte One Point Crossover, Arithmetic Crossover and BLX-Alpha Crossover facilitate exploration in continuous domains. +- **Permutation-Based Chromosomes**: Operators like PMX (Partially Matched Crossover) ensure valid offspring while preserving order relationships in sequencing problems. + + +**Recombination Examples** +-------------------------- + +**One-Point Crossover** + +One-Point Crossover is one of the simplest and most widely used techniques for binary chromosomes. A random crossover point is selected, and segments from the parents are swapped to create offspring. + +.. image:: images/one_point_c.png + :scale: 50% + :align: center + :alt: One-Point Crossover + +Figure 1: An example of One-Point Crossover. + + +**Two-Point Crossover** + +Two-Point Crossover extends the idea of One-Point Crossover by selecting two random crossover points. The segment between the two points is swapped between the parents, producing offspring with potentially more diverse genetic combinations. + +.. image:: images/two_point_c.png + :scale: 50% + :align: center + :alt: Two-Point Crossover + +Figure 2: An example of Two-Point Crossover. + + +**Uniform Crossover** + +In Uniform Crossover, a mask composed of bits is determined over the length of the chromosome. These bits, which take the value 0 or 1, specify which parent the gene for the offspring will be chosen from. Bits with the value 1 are distributed uniformly with a probability of 0.5. + +For example, as illustrated below, bits in the mask with a 0 value indicate that the gene will be selected from Parent X, while bits with a 1 value indicate that the gene will be selected from Parent Y. The reverse process is applied to produce the second offspring. + +.. image:: images/uniform_c.png + :scale: 50% + :align: center + :alt: Uniform Crossover + +Figure 3: An example of Uniform Crossover. -**Arithmetic Crossover** -------------------------- -Performs arithmetic operations on parent genes to produce offspring. Often used in real-valued genetic algorithms to create intermediate values between parent genes. +**API References** +------------------ +The following sections provide detailed documentation for the recombination operators available in the `pycellga.recombination` package. + +**Arithmetic Crossover** +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.arithmetic_crossover :members: :undoc-members: :show-inheritance: **BLX-Alpha Crossover** ------------------------- - -Generates offspring by creating genes within a specified range around parent genes, controlled by the `alpha` parameter. Effective in real-valued optimization for maintaining diversity. - +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.blxalpha_crossover :members: :undoc-members: :show-inheritance: **Byte One-Point Crossover** ------------------------------- - -A one-point crossover operator specifically designed for byte-based chromosomes, suitable for machine-coded genetic algorithms. - +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.byte_one_point_crossover :members: :undoc-members: :show-inheritance: **Byte Uniform Crossover** ---------------------------- - -Applies uniform crossover at the byte level, allowing fine control over gene mixing in machine-coded chromosomes. - +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.byte_uniform_crossover :members: :undoc-members: :show-inheritance: **Flat Crossover** -------------------- - -Creates offspring by generating random values within a range defined by the parent genes. Suitable for real-valued chromosomes. - +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.flat_crossover :members: :undoc-members: :show-inheritance: **Linear Crossover** ---------------------- - -Generates offspring by linearly combining genes of the parents. This operator is useful in real-valued optimization for exploring intermediate values. - +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.linear_crossover :members: :undoc-members: :show-inheritance: **One-Point Crossover** ------------------------- - -A classic crossover technique where a single crossover point is chosen to swap segments between parents. Commonly used in binary-encoded genetic algorithms. - +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.one_point_crossover :members: :undoc-members: :show-inheritance: **Partially Matched Crossover (PMX)** ---------------------------------------- - -A crossover method designed for permutation-based chromosomes, such as sequencing problems. Maintains gene order by partially matching segments between parents. - +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.pmx_crossover :members: :undoc-members: :show-inheritance: **Two-Point Crossover** ------------------------- - -A crossover method with two crossover points, allowing for a higher level of gene exchange between parents. Widely used in binary-encoded algorithms. - +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.two_point_crossover :members: :undoc-members: :show-inheritance: **Unfair Average Crossover** ------------------------------- - -Calculates the average of parent genes with a slight bias, leading to offspring that retain more traits of one parent. Used in real-valued genetic algorithms. - +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.unfair_avarage_crossover :members: :undoc-members: :show-inheritance: **Uniform Crossover** ----------------------- - -Swaps genes randomly between parents, creating diverse offspring. Suitable for both binary and real-valued genetic representations. - +^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: pycellga.recombination.uniform_crossover :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/pycellga.rst b/docs/pycellga.rst index c30307e..1515104 100644 --- a/docs/pycellga.rst +++ b/docs/pycellga.rst @@ -1,7 +1,7 @@ -pycellga: API Reference -======================== +Components of pycellga +======================= -The `pycellga` package is a Python library for implementing and testing cellular genetic algorithms (CGAs). This guide provides an in-depth look at each module, offering descriptions and use cases to help users understand and utilize the library effectively. +`pycellga` is a comprehensive Python library designed for implementing and experimenting with **cellular genetic algorithms (CGAs)**. CGAs are evolutionary algorithms characterized by structured populations and localized interactions, making them suitable for a wide range of optimization problems. This guide provides a detailed breakdown of each module within `pycellga`, highlighting their purpose, key features, and practical use cases. .. toctree:: :maxdepth: 1 @@ -14,8 +14,8 @@ The `pycellga` package is a Python library for implementing and testing cellular pycellga.example -**Core Modules** ------------------- +**Core Modules API Refences** +------------------------------ Population Management ---------------------- @@ -28,7 +28,6 @@ Handles the initialization and management of the population in CGA. It includes :show-inheritance: - Individual Representation -------------------------- @@ -40,7 +39,6 @@ Represents an individual in the population, encapsulating attributes like the ch :show-inheritance: - Grid Structure -------------- @@ -52,7 +50,6 @@ Defines the grid structure for the cellular genetic algorithm. The grid layout r :show-inheritance: - Byte Operators -------------- @@ -66,11 +63,127 @@ Implements low-level byte-based operations that support machine-coded genetic al Optimizer --------------- +=========== + +The `pycellga.optimizer` module is the core component of the library, responsible for managing the execution of cellular genetic algorithms (CGAs). It integrates key evolutionary processes such as selection, mutation, recombination, and evaluation. The optimizer module also provides flexibility to implement and experiment with different CGA variants, each suited to specific optimization challenges. + + + +**Cellular Genetic Algorithm (cga)** +------------------- + +The standard Cellular Genetic Algorithm (cga) operates with a fixed neighborhood structure and asynchronous updates. This approach provides robust performance for a wide range of problems, leveraging local interactions to explore the search space efficiently. + +.. image:: images/cga.png + :scale: 50% + :align: center + :alt: Standard CGA Example + +Figure 1: Illustration of the standard CGA process. + + +**Synchronous Cellular Genetic Algorithm (sync_cga)** +------------------- + +Synchronous CGA (sync_cga) updates the entire population simultaneously in each generation. This ensures consistent progress across the population but may lead to premature convergence in some cases. + + +**Alpha Male Cellular Genetic Algorithm (alpha_cga)** +------------------- + +This strategy developed to enhance the performance of Cellular Genetic Algorithms is the Alpha-Male Cellular Genetic Algorithm (alpha_cga). Alpha-Male Genetic Algorithm divides individuals in a population into social groups. A social group consists of females that select the same alpha male. In each social group, one individual is labeled as the alpha male, while the rest are productive females. + +In their research, scientists integrated the alpha-male structure into the genetic algorithm and conducted simulation studies on some test functions. As a result, the new algorithm was observed to produce better outcomes compared to classical genetic algorithms. Similarly, this algorithm was developed by integrating a structure that allows a certain number of alpha male individuals in the population to pair with a defined number of female individuals into the Cellular Genetic Algorithm structure. + +However, due to its inherent structure, Cellular GA is already limited to pairing with neighbors. Although directly combining the cellular structure with the alpha-male algorithm is not possible, a new structure incorporating both approaches is illustrated in the following figure: + +.. image:: images/alpha_cga.png + :scale: 50% + :align: center + :alt: Alpha CGA Example + +Figure 2: Integration of alpha-male structure into Cellular Genetic Algorithm. + +**Improved CGA with Machine-Coded Operators** +------------------------------------------------ + +Enhanced performance in real-valued optimization problems is achieved through the use of `machine-coded byte operators`. This approach focuses on leveraging the representation of numerical data in memory to improve the efficiency and accuracy of genetic algorithms. + + +**Understanding Machine-Coded (Byte) Genetic Operators** + +In computer programs, numerical data is typically stored in memory as byte sequences. A byte consists of eight bits, each capable of holding a value of zero or one. For small numbers, a few bytes suffice, but larger numbers or those requiring higher precision need more bytes. This problem is mitigated by the use of data types in compilers and interpreters. + +The storage of a numerical value in memory involves a finite number of bytes, implying that representing real values with absolute precision is impossible. As the number of bytes increases, so does precision, allowing for a more accurate representation of real values. The byte sequence shown in Table 1 was generated using a formulation algorithm specified in the IEEE 754 Standard for Floating-Point Arithmetic. Compilers commonly implement this standard when converting real numbers to and from byte sequences. -The core optimization module that manages the genetic algorithm's execution. This includes processes like selection, mutation, recombination, and evaluation of individuals. The `optimizer` module serves as the main interface for running different CGA variants. +For instance, consider a variable `f` with a numerical value accurate to 15 decimal places: + +f = 12.508239423942167 + +When the variable f is converted to bytes, an 8-element byte array is obtained, each of which ranges from 0 to 255. This byte array is shown in Table 1. + +.. image:: images/f_byte.png + :scale: 50% + :align: center + :alt: f example + +Table 1: Byte representation of variable f. + +As shown in Figure 3, traditional crossover operators can be directly applied to the byte representation of real values. + +.. image:: images/icga.png + :scale: 50% + :align: center + :alt: f example + +Figure 3: Application of traditional traversal operators to byte arrays + + +**Machine-Coded Compact Cellular Genetic Algorithm (mcccga)** +------------------------------------------------------------- + +The Machine-Coded Compact Cellular Genetic Algorithm (mcccga) is a specialized approach to solving real-valued optimization problems by combining the principles of compact genetic algorithms and cellular structures. This method is particularly effective for memory-efficient optimization in large solution spaces. + +**Overview of Compact Genetic Algorithm** + +Compact Genetic Algorithms are evolutionary algorithms designed to efficiently handle binary-encoded representations of solutions. Instead of maintaining a population of solutions, cGA uses a **probability vector** to represent the population's characteristics. Each element of the vector corresponds to the probability of a particular bit being `1` at a specific position in the solution space. + +Key features of Compact GA: + +- **Compact Representation**: Uses a probability vector instead of a full population. +- **Efficient Updates**: Updates the vector through a compact rule based on fitness comparisons. +- **Memory Efficiency**: Requires significantly less memory than classical genetic algorithms. + +**Transition to Machine-Coded Compact GA** + +Building on the compact GA framework, **Machine-Coded Compact GA** extends the algorithm to real-valued optimization problems by leveraging the IEEE 754 standard for floating-point arithmetic. Real-valued variables are encoded into binary form and processed using the cGA principles. When needed, the binary representation is decoded back to real values using the same standard. + +**Integration with Cellular Structures** + +The Machine-Coded Compact Cellular GA adapts the compact GA principles to a cellular structure, where each individual interacts with its neighbors. This adaptation enables local exploration and facilitates parallelism in optimization. + +Key enhancements: + +- **Real-Valued Adaptation**: Compact GA is extended to handle real-valued problems through encoding and decoding of variables. +- **Cellular Structure**: Implements a grid-based interaction model, enhancing local exploration and reducing premature convergence. +- **Dynamic Probability Updates**: Starts with a narrowed probability vector based on the variable's range, improving convergence speed. + +In Figure 4, a sample coding of the number -12345.6789 with 1 bit for the sign part, 8 bits for the exponent part and 23 bits for the decimal part, totaling 32 bits, is realized according to the IEEE-754 standard. + +.. image:: images/ieee.png + :scale: 50% + :align: center + :alt: MMCGA Illustration + +Figure 4: Encoding the number according to the IEEE-754 standard. + + +**API References** +------------------- + +The following section provides the API reference for the `pycellga.optimizer` module. .. automodule:: pycellga.optimizer :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/pycellga.selection.rst b/docs/pycellga.selection.rst index 808c877..8f6d48f 100644 --- a/docs/pycellga.selection.rst +++ b/docs/pycellga.selection.rst @@ -1,11 +1,41 @@ Selection Operators ===================== -The `pycellga.selection` package provides various selection operators that determine how individuals are chosen from the population to act as parents in genetic algorithms. Selection operators play a key role in balancing exploration and exploitation during the optimization process, directly impacting convergence and diversity. +The `pycellga.selection` module provides selection operators that determine how individuals are chosen from the population to act as parents in genetic algorithms. These operators play a critical role in balancing exploration and exploitation during the optimization process, directly impacting convergence and diversity. +**Selection Mechanism** +------------------------ + +Selection in genetic algorithms determines which individuals contribute their genetic material to the next generation. The choice of selection mechanism can significantly influence the algorithm's performance, impacting how quickly the population converges and whether diversity is preserved. + +**Importance of Selection** + +- **Exploration**: Encourages searching new areas in the solution space. +- **Exploitation**: Focuses on refining solutions near already known good solutions. + + +**Selection Example** +------------------------ + +**Roulette Wheel Selection** + +Roulette Wheel Selection resembles the classic roulette wheel game. As shown in the diagram below, each chromosome occupies a portion of the wheel proportional to its fitness value. After a fixed point on the wheel is chosen, the roulette wheel is spun. The individual whose section aligns with the fixed point is selected. The higher the fitness value of an individual, the greater their chance of being chosen. + +.. image:: images/roulette_wheel_selection.png + :scale: 50% + :align: center + :alt: Roulette Wheel Selection Example + +Figure 1: An example of Roulette Wheel Selection. + + +**API References** +------------------ + +The following sections provide detailed documentation for the selection operators available in the `pycellga.selectlon` package. **Tournament Selection** --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ Implements a tournament selection method, where a subset of individuals competes, and the best-performing individual is selected as a parent. This method is useful in maintaining diversity and is effective in multimodal optimization landscapes. @@ -15,7 +45,7 @@ Implements a tournament selection method, where a subset of individuals competes :show-inheritance: **Roulette Wheel Selection** ------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Implements a roulette wheel selection mechanism where each individual’s chance of being selected is proportional to its fitness. This method is widely used for its simplicity and is effective in problems where fitness proportionate selection is beneficial. diff --git a/docs/usage_examples.rst b/docs/usage_examples.rst index 0ee3348..d3d08d7 100644 --- a/docs/usage_examples.rst +++ b/docs/usage_examples.rst @@ -17,42 +17,50 @@ Here’s how we can define this problem in Python using the `ExampleProblem` cla .. code-block:: python from mpmath import power as pw + from typing import List from pycellga.optimizer import cga - from pycellga.individual import GeneType - from pycellga.selection import TournamentSelection from pycellga.recombination import ByteOnePointCrossover from pycellga.mutation import ByteMutationRandom + from pycellga.selection import TournamentSelection + from pycellga.problems import AbstractProblem + from pycellga.common import GeneType + + class ExampleProblem(AbstractProblem): + + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) + + def f(self, x: List[float]) -> float: + return round(sum(pw(xi, 2) for xi in x),3) - class ExampleProblem: - - def __init__(self): - pass - - def f(self, x): - return sum(pw(xi, 2) for xi in x) **Usage** .. code-block:: python 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 = ExampleProblem(), # Replace with a real problem instance as needed - selection = TournamentSelection, - recombination = ByteOnePointCrossover, - mutation = ByteMutationRandom, - mins = [-32.768] * 5, # Minimum values for each gene - maxs = [32.768] * 5, # Maximum values for each gene - seed_par = 100 # Ensures the random number generation is repeatable + n_cols=5, + n_rows=5, + n_gen=100, + ch_size=5, + p_crossover=0.9, + p_mutation=0.2, + problem=ExampleProblem(n_var=5), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + seed_par=100 ) - print("Best solution:", result.chromosome) + + # Print the results + print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) # The result is @@ -71,21 +79,31 @@ The Alpha Cellular Genetic Algorithm optimizes a real-valued problem using Blxal .. code-block:: python - from mpmath import power as pw + from numpy import power as pw + from typing import List from pycellga.optimizer import alpha_cga - from pycellga.individual import GeneType - from pycellga.selection import TournamentSelection from pycellga.recombination import BlxalphaCrossover from pycellga.mutation import FloatUniformMutation + from pycellga.selection import TournamentSelection + from pycellga.problems import AbstractProblem + from pycellga.common import GeneType - class ExampleProblem: - - def __init__(self): - pass - - def f(self, x): - return sum(pw(xi, 2) for xi in x) + + class ExampleProblem(AbstractProblem): + + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) + + def f(self, x: List[float]) -> float: + + return round(sum(pw(xi, 2) for xi in x),3) result = alpha_cga( @@ -93,21 +111,19 @@ The Alpha Cellular Genetic Algorithm optimizes a real-valued problem using Blxal n_rows=5, n_gen=100, ch_size=10, - gen_type=GeneType.REAL, p_crossover=0.9, p_mutation=0.2, - problem=ExampleProblem(), + problem=ExampleProblem(n_var=10), selection=TournamentSelection, recombination=BlxalphaCrossover, mutation=FloatUniformMutation, - mins=[-15] * 10, - maxs=[15] * 10, seed_par=100 ) + + # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) ---- **Example 2: Compact Cellular Genetic Algorithm (ccga)** @@ -115,28 +131,36 @@ The Compact Cellular Genetic Algorithm optimizes a binary problem where the goal .. code-block:: python + from pycellga.optimizer import ccga - from pycellga.individual import GeneType from pycellga.selection import TournamentSelection + from pycellga.problems import AbstractProblem + from pycellga.common import GeneType - class ExampleProblem: - def __init__(self): - pass + class ExampleProblem(AbstractProblem): + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) + def f(self, x): return sum(x) result = ccga( n_cols=5, n_rows=5, - n_gen=100, + n_gen=200, ch_size=10, - gen_type=GeneType.BINARY, - problem=ExampleProblem(), - selection=TournamentSelection, - mins=[0] * 10, - maxs=[1] * 10 + problem=ExampleProblem(n_var=10), + selection=TournamentSelection ) + + # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) @@ -147,15 +171,24 @@ The Machine-Coded Compact Cellular Genetic Algorithm solves real-valued optimiza .. code-block:: python + import numpy as np + from pycellga.optimizer import mcccga - from pycellga.individual import GeneType from pycellga.selection import TournamentSelection - - - class RealProblem: - def __init__(self): - pass + from pycellga.problems import AbstractProblem + from pycellga.common import GeneType + class RealProblem(AbstractProblem): + + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-3.768, + xu=3.768 + ) + def f(self, x): return sum(np.power(xi, 2) for xi in x) @@ -164,12 +197,11 @@ The Machine-Coded Compact Cellular Genetic Algorithm solves real-valued optimiza n_rows=5, n_gen=500, ch_size=5, - gen_type=GeneType.REAL, - problem=RealProblem(), - selection=TournamentSelection, - mins=[-3.55] * 5, - maxs=[3.55] * 5 + problem=RealProblem(n_var=5), + selection=TournamentSelection ) + + # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) @@ -180,35 +212,47 @@ The Synchronous Cellular Genetic Algorithm optimizes a real-valued problem in a .. code-block:: python + from numpy import power as pw + from typing import List + from pycellga.optimizer import sync_cga - from pycellga.individual import GeneType - from pycellga.selection import TournamentSelection from pycellga.recombination import BlxalphaCrossover from pycellga.mutation import FloatUniformMutation + from pycellga.selection import TournamentSelection + from pycellga.problems import AbstractProblem + from pycellga.common import GeneType - class ExampleProblem: - def __init__(self): - pass - def f(self, x): - return sum(pw(xi, 2) for xi in x) + class ExampleProblem(AbstractProblem): + + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) + + def f(self, x: List[float]) -> float: + return round(sum(pw(xi, 2) for xi in x),3) + result = sync_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=ExampleProblem(), + problem=ExampleProblem(n_var=5), selection=TournamentSelection, recombination=BlxalphaCrossover, mutation=FloatUniformMutation, - mins=[-32.768] * 5, - maxs=[32.768] * 5, seed_par=100 ) + + # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) @@ -220,11 +264,16 @@ Customization Scenarios **Selecting Gene Types** -You can change the `gen_type` parameter based on the type of problem: +You can use the `gen_type` parameter of the problem based on the defined problem: + +.. code-block:: python + + from pycellga.common import GeneType + -- `pycellga.individual.GeneType.REAL` for real-valued problems. -- `pycellga.individual.GeneType.BINARY` for binary problems. -- `pycellga.individual.GeneType.PERMUTATION` for permutation-based problems. +- `GeneType.REAL` for real-valued problems. +- `GeneType.BINARY` for binary problems. +- `GeneType.PERMUTATION` for permutation-based problems. **Problem Selection** @@ -235,89 +284,106 @@ You can change the `gen_type` parameter based on the type of problem: For continuous optimization problems, the following functions are available: -- `pycellga.problems.Ackley` -- `pycellga.problems.Bentcigar` -- `pycellga.problems.Bohachevsky` -- `pycellga.problems.Chichinadze` -- `pycellga.problems.Dropwave` -- `pycellga.problems.Fms` -- `pycellga.problems.Griewank` -- `pycellga.problems.Holzman` -- `pycellga.problems.Levy` -- `pycellga.problems.Matyas` -- `pycellga.problems.Pow` -- `pycellga.problems.Powell` -- `pycellga.problems.Rastrigin` -- `pycellga.problems.Rosenbrock` -- `pycellga.problems.Rothellipsoid` -- `pycellga.problems.Schaffer` -- `pycellga.problems.Schaffer2` -- `pycellga.problems.Schwefel` -- `pycellga.problems.Sphere` -- `pycellga.problems.StyblinskiTang` -- `pycellga.problems.Sumofdifferentpowers` -- `pycellga.problems.Threehumps` -- `pycellga.problems.Zakharov` -- `pycellga.problems.Zettle` +.. code-block:: python + + from pycellga.problems.single_objective.continuous.ackley import Ackley + from pycellga.problems.single_objective.continuous.bentcigar import Bentcigar + from pycellga.problems.single_objective.continuous.bohachevsky import Bohachevsky + from pycellga.problems.single_objective.continuous.chichinadze import Chichinadze + from pycellga.problems.single_objective.continuous.dropwave import Dropwave + from pycellga.problems.single_objective.continuous.fms import Fms + from pycellga.problems.single_objective.continuous.griewank import Griewank + from pycellga.problems.single_objective.continuous.holzman import Holzman + from pycellga.problems.single_objective.continuous.levy import Levy + from pycellga.problems.single_objective.continuous.matyas import Matyas + from pycellga.problems.single_objective.continuous.pow import Pow + from pycellga.problems.single_objective.continuous.powell import Powell + from pycellga.problems.single_objective.continuous.rastrigin import Rastrigin + from pycellga.problems.single_objective.continuous.rosenbrock import Rosenbrock + from pycellga.problems.single_objective.continuous.rothellipsoid import Rothellipsoid + from pycellga.problems.single_objective.continuous.schaffer import Schaffer + from pycellga.problems.single_objective.continuous.schaffer2 import Schaffer2 + from pycellga.problems.single_objective.continuous.schwefel import Schwefel + from pycellga.problems.single_objective.continuous.sphere import Sphere + from pycellga.problems.single_objective.continuous.styblinskiTang import StyblinskiTang + from pycellga.problems.single_objective.continuous.sumofdifferentpowers import Sumofdifferentpowers + from pycellga.problems.single_objective.continuous.threehumps import Threehumps + from pycellga.problems.single_objective.continuous.zakharov import Zakharov + from pycellga.problems.single_objective.continuous.zettle import Zettle + **Binary Problems** For binary optimization problems, use the following built-in options: -- `pycellga.problems.CountSat` -- `pycellga.problems.Ecc` -- `pycellga.problems.Maxcut20_01` -- `pycellga.problems.Maxcut20_09` -- `pycellga.problems.Maxcut100` -- `pycellga.problems.Mmdp` -- `pycellga.problems.OneMax` -- `pycellga.problems.Peak` + +.. code-block:: python + + from pycellga.problems.single_objective.discrete.binary.count_sat import CountSat + from pycellga.problems.single_objective.discrete.binary.ecc import Ecc + from pycellga.problems.single_objective.discrete.binary.maxcut20_01 import Maxcut20_01 + from pycellga.problems.single_objective.discrete.binary.maxcut20_09 import Maxcut20_09 + from pycellga.problems.single_objective.discrete.binary.maxcut100 import Maxcut100 + from pycellga.problems.single_objective.discrete.binary.mmdp import Mmdp + from pycellga.problems.single_objective.discrete.binary.one_max import OneMax + from pycellga.problems.single_objective.discrete.binary.peak import Peak + **Permutation Problems** For permutation-based optimization problems, the following option is available: -- `pycellga.problems.Tsp` +.. code-block:: python + + from problems.single_objective.discrete.permutation.tsp import Tsp + These built-in problems provide a diverse set of test cases, allowing users to explore `pycellga`'s capabilities across a wide range of optimization challenges. Users can also define custom problems to suit their specific needs. **Selection Operators** -Choose from a variety of selection methods: +Choose from selection methods: + +.. code-block:: python + + from selection.tournament_selection import TournamentSelection + from selection.roulette_wheel_selection import RouletteWheelSelection -- `pycellga.selection.TournamentSelection` -- `pycellga.selection.RouletteWheelSelection` **Recombination Operators** The package provides multiple crossover operators: -- `pycellga.recombination.OnePointCrossover` -- `pycellga.recombination.PMXCrossover` -- `pycellga.recombination.TwoPointCrossover` -- `pycellga.recombination.UniformCrossover` -- `pycellga.recombination.ByteUniformCrossover` -- `pycellga.recombination.ByteOnePointCrossover` -- `pycellga.recombination.FlatCrossover` -- `pycellga.recombination.ArithmeticCrossover` -- `pycellga.recombination.BlxalphaCrossover` -- `pycellga.recombination.LinearCrossover` -- `pycellga.recombination.UnfairAvarageCrossover` +.. code-block:: python + + from pycellga.recombination.one_point_crossover import OnePointCrossover + from pycellga.recombination.pmx_crossover import PMXCrossover + from pycellga.recombination.two_point_crossover import TwoPointCrossover + from pycellga.recombination.uniform_crossover import UniformCrossover + from pycellga.recombination.byte_uniform_crossover import ByteUniformCrossover + from pycellga.recombination.byte_one_point_crossover import ByteOnePointCrossover + from pycellga.recombination.flat_crossover import FlatCrossover + from pycellga.recombination.arithmetic_crossover import ArithmeticCrossover + from pycellga.recombination.blxalpha_crossover import BlxalphaCrossover + from pycellga.recombination.linear_crossover import LinearCrossover + from pycellga.recombination.unfair_avarage_crossover import UnfairAvarageCrossover + **Mutation Operators** You can customize mutation with these options: -- `pycellga.mutation.BitFlipMutation` -- `pycellga.mutation.ByteMutation` -- `pycellga.mutation.ByteMutationRandom` -- `pycellga.mutation.InsertionMutation` -- `pycellga.mutation.ShuffleMutation` -- `pycellga.mutation.SwapMutation` -- `pycellga.mutation.TwoOptMutation` -- `pycellga.mutation.FloatUniformMutation` -- `pycellga.mutation.MutationOperator` +.. code-block:: python + + from pycellga.mutation.bit_flip_mutation import BitFlipMutation + from pycellga.mutation.byte_mutation import ByteMutation + from pycellga.mutation.byte_mutation_random import ByteMutationRandom + from pycellga.mutation.insertion_mutation import InsertionMutation + from pycellga.mutation.shuffle_mutation import ShuffleMutation + from pycellga.mutation.swap_mutation import SwapMutation + from pycellga.mutation.two_opt_mutation import TwoOptMutation + from pycellga.mutation.float_uniform_mutation import FloatUniformMutation **Example Scenarios** @@ -329,8 +395,7 @@ Optimize a binary problem using tournament selection, one-point crossover, and b .. code-block:: python from pycellga.optimizer import cga - from pycellga.individual import GeneType - from pycellga.problems import OneMax + from problems.single_objective.discrete.binary.one_max import OneMax from pycellga.selection import TournamentSelection from pycellga.recombination import OnePointCrossover from pycellga.mutation import BitFlipMutation @@ -340,15 +405,12 @@ Optimize a binary problem using tournament selection, one-point crossover, and b n_rows=5, n_gen=100, ch_size=10, - gen_type=GeneType.BINARY, p_crossover=0.8, p_mutation=0.1, - problem=OneMax(), # Built-in binary optimization problem + problem=OneMax(n_var=10), # Built-in binary optimization problem selection=TournamentSelection, recombination=OnePointCrossover, - mutation=BitFlipMutation, - mins=[0] * 10, - maxs=[1] * 10, + mutation=BitFlipMutation seed_par=100 ) print("Best solution:", result.chromosome) @@ -362,8 +424,7 @@ Solve a real-valued optimization problem using Byte One Point Crossover and Byte .. code-block:: python from pycellga.optimizer import cga - from pycellga.individual import GeneType - from pycellga.problems import Ackley + from problems.single_objective.continuous.ackley import Ackley from pycellga.selection import RouletteWheelSelection from pycellga.recombination import ByteOnePointCrossover from pycellga.mutation import ByteMutation @@ -373,15 +434,12 @@ Solve a real-valued optimization problem using Byte One Point Crossover and Byte n_rows=5, n_gen=100, ch_size=10, - gen_type=GeneType.REAL, p_crossover=0.9, p_mutation=0.2, - problem=Ackley(10), # Built-in real-valued optimization problem + problem=Ackley(n_var=10), # Built-in real-valued optimization problem selection=RouletteWheelSelection, recombination=ByteOnePointCrossover, mutation=ByteMutation, - mins=[-32.768] * 10, - maxs=[32.768] * 10, seed_par=100 ) print("Best solution:", result.chromosome) @@ -394,8 +452,7 @@ Optimize a TSP using permutation encoding, PMX crossover, and swap mutation. .. code-block:: python from pycellga.optimizer import cga - from pycellga.individual import GeneType - from pycellga.problems import Tsp + from problems.single_objective.discrete.permutation.tsp import Tsp from pycellga.selection import TournamentSelection from pycellga.recombination import PMXCrossover from pycellga.mutation import SwapMutation @@ -405,15 +462,12 @@ Optimize a TSP using permutation encoding, PMX crossover, and swap mutation. n_rows=5, n_gen=300, ch_size=14, # Number of cities - gen_type=GeneType.PERMUTATION, p_crossover=0.85, p_mutation=0.15, - problem=Tsp(), # Built-in TSP optimization problem + problem=Tsp(n_var=14), # Built-in TSP optimization problem selection=TournamentSelection, recombination=PMXCrossover, mutation=SwapMutation, - mins=[1] * 14, - maxs=[14] * 14, seed_par=100 ) print("Best solution:", result.chromosome) diff --git a/paper/paper.md b/paper/paper.md index 323fe49..e7a20d9 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -100,44 +100,49 @@ Here’s how we can define this problem in Python using the `ExampleProblem` cla ```python from mpmath import power as pw +from typing import List from pycellga.optimizer import cga -from pycellga.individual import GeneType -from pycellga.selection import TournamentSelection from pycellga.recombination import ByteOnePointCrossover from pycellga.mutation import ByteMutationRandom +from pycellga.selection import TournamentSelection +from pycellga.problems import AbstractProblem +from pycellga.common import GeneType + +class ExampleProblem(AbstractProblem): + + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) -class ExampleProblem: - - def __init__(self): - pass - - def f(self, x): - return sum(pw(xi, 2) for xi in x) + def f(self, x: List[float]) -> float: + return round(sum(pw(xi, 2) for xi in x),3) ``` **Usage:** ```python 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=ExampleProblem(), # Replace with a real problem instance as needed - selection=TournamentSelection, - recombination=ByteOnePointCrossover, - mutation=ByteMutationRandom, - mins=[-32.768] * 5, # Minimum values for each gene - maxs=[32.768] * 5, # Maximum values for each gene - seed_par=100 # Ensures the random number generation is repeatable -) - -# Print the best solution details -print("Best solution chromosome:", result.chromosome) -print("Best fitness value:", result.fitness_value) + n_cols=5, + n_rows=5, + n_gen=100, + ch_size=5, + p_crossover=0.9, + p_mutation=0.2, + problem=ExampleProblem(n_var=5), + selection=TournamentSelection, + recombination=ByteOnePointCrossover, + mutation=ByteMutationRandom, + seed_par=100 + ) + + # Print the results + print("Best solution chromosome:", result.chromosome) + print("Best fitness value:", result.fitness_value) # Expected Output: # Best solution chromosome: [0.0, 0.0, 0.0, 0.0, 0.0] diff --git a/pycellga/common.py b/pycellga/common.py new file mode 100644 index 0000000..e097cba --- /dev/null +++ b/pycellga/common.py @@ -0,0 +1,10 @@ +from enum import Enum + +class GeneType(Enum): + """ + GeneType is an enumeration class that represents the type of genome representation for an individual in an evolutionary algorithm. + The three types of genome representation are BINARY, PERMUTATION, and REAL. + """ + BINARY = 1 + PERMUTATION = 2 + REAL = 3 \ No newline at end of file diff --git a/pycellga/example/example_alpha_cga.py b/pycellga/example/example_alpha_cga.py index 42262ff..b2b569c 100644 --- a/pycellga/example/example_alpha_cga.py +++ b/pycellga/example/example_alpha_cga.py @@ -3,82 +3,52 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from numpy import power as pw +from typing import List from optimizer import alpha_cga -from individual import GeneType from recombination import BlxalphaCrossover from mutation import FloatUniformMutation from selection import TournamentSelection +from problems import AbstractProblem +from common import GeneType -class ExampleProblem: - """ - Example problem class to be minimized. +class ExampleProblem(AbstractProblem): - 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. + def __init__(self, n_var): - This method implements the sum of squares function. + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) - Parameters - ---------- - x : list or numpy.ndarray - The input chromosome represented as a list or array of real values. + def f(self, x: List[float]) -> float: + + return round(sum(pw(xi, 2) for xi in x),3) - Returns - ------- - float - The computed value of the function given the input x. - """ - return sum(pw(xi, 2) for xi in x) def run_alpha_cga_example(): - """ - Run the Alpha Cellular Genetic Algorithm (alpha_cga) using the optimizer module. - The alpha_cga 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 real-valued genes, constrained by specified mins and maxs. - Returns - ------- - Result - A Result instance containing the best solution's chromosome, its fitness value, - and the generation in which it was found. - """ - # Create an instance of the problem - problem_instance = ExampleProblem() - - # Run the optimizer and get the result result = alpha_cga( n_cols=5, n_rows=5, n_gen=100, ch_size=10, - gen_type=GeneType.REAL, p_crossover=0.9, p_mutation=0.2, - problem=problem_instance, # Pass the ExampleProblem instance + problem=ExampleProblem(n_var=10), selection=TournamentSelection, recombination=BlxalphaCrossover, mutation=FloatUniformMutation, - mins=[-32.768] * 10, # Minimum values for each gene - maxs=[32.768] * 10, # Maximum values for each gene seed_par=100 ) # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) - print("Generation found:", result.generation_found) if __name__ == "__main__": run_alpha_cga_example() diff --git a/pycellga/example/example_ccga.py b/pycellga/example/example_ccga.py index 7be82b6..51d2076 100644 --- a/pycellga/example/example_ccga.py +++ b/pycellga/example/example_ccga.py @@ -3,72 +3,38 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from optimizer import ccga -from individual import GeneType from selection import TournamentSelection -from individual import GeneType +from problems import AbstractProblem +from common import GeneType -class ExampleProblem: - """ - Example problem class to be minimized. +class ExampleProblem(AbstractProblem): - This class implements a simple binary optimization problem, where the goal is to maximize the number of 1s. - """ - - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x): - """ - Compute the objective function value. - - This method implements a simple sum of binary values. - - 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 of the function given the input x. - """ return sum(x) def run_ccga_example(): - """ - Run the Compact Cellular Genetic Algorithm (ccga) using the optimizer module. - - The ccga 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. - - Returns - ------- - Result - A Result instance containing the best solution's chromosome, its fitness value, - and the generation in which it was found. - """ - # Create an instance of the problem - problem_instance = ExampleProblem() - # Run the optimizer and get the result result = ccga( n_cols=5, n_rows=5, - n_gen=100, + n_gen=200, ch_size=10, - gen_type=GeneType.BINARY, - problem=problem_instance, # Pass the ExampleProblem instance - selection=TournamentSelection, - mins=[0] * 10, # Minimum values for each gene (binary) - maxs=[1] * 10 # Maximum values for each gene (binary) + problem=ExampleProblem(n_var=10), + selection=TournamentSelection ) # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) - print("Generation found:", result.generation_found) if __name__ == "__main__": run_ccga_example() diff --git a/pycellga/example/example_cga.py b/pycellga/example/example_cga.py index 0285fdb..dc68947 100644 --- a/pycellga/example/example_cga.py +++ b/pycellga/example/example_cga.py @@ -1,82 +1,51 @@ import sys import os sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from numpy import power as pw + +from mpmath import power as pw +from typing import List from optimizer import cga -from individual import GeneType from recombination import ByteOnePointCrossover from mutation import ByteMutationRandom from selection import TournamentSelection +from problems import AbstractProblem +from common import GeneType +class ExampleProblem(AbstractProblem): -class ExampleProblem: - """ - 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. + def __init__(self, n_var): - This method implements the sum of squares function. + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) - Parameters - ---------- - x : list or numpy.ndarray - The input chromosome represented as a list or array of real values. + def f(self, x: List[float]) -> float: + return round(sum(pw(xi, 2) for xi in x),3) - Returns - ------- - float - The computed value of the function given the input x. - """ - return sum(pw(xi, 2) for xi in x) def run_cga_example(): - """ - Run the Cellular Genetic Algorithm (cga) using the optimizer module. - - The cga is configured with a 5x5 grid, 100 generations, and a chromosome size of 5. - The problem being solved is an instance of the ExampleProblem class, - with real-valued genes, constrained by specified mins and maxs. - - Returns - ------- - Result - A Result instance containing the best solution's chromosome, its fitness value, - and the generation in which it was found. - """ - # Create an instance of the problem - problem_instance = ExampleProblem() 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=problem_instance, # Pass the ExampleProblem instance + problem=ExampleProblem(n_var=5), selection=TournamentSelection, recombination=ByteOnePointCrossover, mutation=ByteMutationRandom, - mins=[-32.768] * 5, # Minimum values for each gene - maxs=[32.768] * 5, # Maximum values for each gene seed_par=100 ) # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) - print("Generation found:", result.generation_found) if __name__ == "__main__": run_cga_example() diff --git a/pycellga/example/example_mcccga.py b/pycellga/example/example_mcccga.py index 0807e61..9e01eea 100644 --- a/pycellga/example/example_mcccga.py +++ b/pycellga/example/example_mcccga.py @@ -5,72 +5,40 @@ import numpy as np from optimizer import mcccga -from individual import GeneType from selection import TournamentSelection +from problems import AbstractProblem +from common import GeneType -class RealProblem: - """ - Example problem class to be minimized. +class RealProblem(AbstractProblem): - 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 __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-3.768, + xu=3.768 + ) 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 run_mcccga_example(): - """ - Run the Machine-Coded Compact Cellular Genetic Algorithm (mcccga) - 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 RealProblem class, - with real genes, constrained by specified mins and maxs. - - Returns - ------- - Result - A Result instance containing the best solution's chromosome, its fitness value, - and the generation in which it was found. - """ - # Create an instance of the problem - problem_instance = RealProblem() result = mcccga( n_cols=5, n_rows=5, n_gen=500, ch_size=5, - gen_type=GeneType.REAL, - problem=problem_instance, # Pass the RealProblem instance - selection=TournamentSelection, - mins=[-3.768] * 5, - maxs=[3.768] * 5 + problem=RealProblem(n_var=5), + selection=TournamentSelection ) # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) - print("Generation found:", result.generation_found) if __name__ == "__main__": run_mcccga_example() diff --git a/pycellga/example/example_sync_cga.py b/pycellga/example/example_sync_cga.py index 4a13a6f..08fb284 100644 --- a/pycellga/example/example_sync_cga.py +++ b/pycellga/example/example_sync_cga.py @@ -3,81 +3,51 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from numpy import power as pw +from typing import List from optimizer import sync_cga -from individual import GeneType from recombination import BlxalphaCrossover from mutation import FloatUniformMutation from selection import TournamentSelection +from problems import AbstractProblem +from common import GeneType -class ExampleProblem: - """ - Example problem class to be minimized. +class ExampleProblem(AbstractProblem): - 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. + def __init__(self, n_var): - This method implements the sum of squares function. + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) - Parameters - ---------- - x : list or numpy.ndarray - The input chromosome represented as a list or array of real values. + def f(self, x: List[float]) -> float: - Returns - ------- - float - The computed value of the function given the input x. - """ - return sum(pw(xi, 2) for xi in x) + return round(sum(pw(xi, 2) for xi in x),3) def run_sync_cga_example(): - """ - Run the Synchronous Cellular Genetic Algorithm (sync_cga) using the optimizer module. - The sync_cga is configured with a 5x5 grid, 100 generations, and a chromosome size of 5. - The problem being solved is an instance of the ExampleProblem class, - with real-valued genes, constrained by specified mins and maxs. - - Returns - ------- - Result - A Result instance containing the best solution's chromosome, its fitness value, - and the generation in which it was found. - """ - # Create an instance of the problem - problem_instance = ExampleProblem() result = sync_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=problem_instance, # Pass the ExampleProblem instance + problem=ExampleProblem(n_var=5), selection=TournamentSelection, recombination=BlxalphaCrossover, mutation=FloatUniformMutation, - mins=[-32.768] * 5, # Minimum values for each gene - maxs=[32.768] * 5, # Maximum values for each gene seed_par=100 ) # Print the results print("Best solution chromosome:", result.chromosome) print("Best fitness value:", result.fitness_value) - print("Generation found:", result.generation_found) if __name__ == "__main__": run_sync_cga_example() diff --git a/pycellga/individual.py b/pycellga/individual.py index 7b077e8..77e1f09 100644 --- a/pycellga/individual.py +++ b/pycellga/individual.py @@ -1,19 +1,13 @@ -from enum import Enum +import sys +import os +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + + from numpy import random import numpy as np import random as rd from problems.abstract_problem import AbstractProblem - - -class GeneType(Enum): - """ - GeneType is an enumeration class that represents the type of genome representation for an individual in an evolutionary algorithm. - The three types of genome representation are BINARY, PERMUTATION, and REAL. - """ - BINARY = 1 - PERMUTATION = 2 - REAL = 3 - +from common import GeneType class Individual: diff --git a/pycellga/optimizer.py b/pycellga/optimizer.py index 08bc4ca..72a0b6a 100644 --- a/pycellga/optimizer.py +++ b/pycellga/optimizer.py @@ -27,15 +27,12 @@ def cga( n_rows: int, n_gen: int, ch_size: int, - gen_type: str, p_crossover: float, p_mutation: float, problem: AbstractProblem, selection: SelectionOperator, recombination: RecombinationOperator, mutation: MutationOperator, - mins: List[float] = [], - maxs: List[float] = [], seed_par: int = None ) -> Result: """ @@ -89,8 +86,15 @@ def cga( method_name = OptimizationMethod.CGA # Generate Initial Population - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, mins=mins, maxs=maxs).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + gen_type = problem.gen_type, + problem = problem, + mins = problem.xl, + maxs = problem.xu).initial_population() pop_list_ordered = sorted(pop_list, key=lambda x: x.fitness_value) @@ -153,15 +157,12 @@ def sync_cga( n_rows: int, n_gen: int, ch_size: int, - gen_type: str, p_crossover: float, p_mutation: float, problem: Callable[[List[float]], float], selection: SelectionOperator, recombination: RecombinationOperator, mutation: MutationOperator, - mins: List[float] = [], - maxs: List[float] = [], seed_par: int = None ) -> Result: """ @@ -215,8 +216,15 @@ def sync_cga( method_name = OptimizationMethod.SYNCGA # Generate Initial Population - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, mins=mins, maxs=maxs).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + gen_type = problem.gen_type, + problem = problem, + mins = problem.xl, + maxs = problem.xu).initial_population() # Sort population by fitness value pop_list_ordered = sorted(pop_list, key=lambda x: x.fitness_value) @@ -296,15 +304,12 @@ def alpha_cga( n_rows: int, n_gen: int, ch_size: int, - gen_type: str, p_crossover: float, p_mutation: float, problem: AbstractProblem, selection: SelectionOperator, recombination: RecombinationOperator, mutation: MutationOperator, - mins: List[float] = [], - maxs: List[float] = [], seed_par: int = None ) -> Result: """ @@ -358,8 +363,15 @@ def alpha_cga( method_name = OptimizationMethod.ALPHA_CGA # Generate Initial Population - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, mins=mins, maxs=maxs).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + gen_type = problem.gen_type, + problem = problem, + mins = problem.xl, + maxs = problem.xu).initial_population() # Sort population by fitness value pop_list_ordered = sorted(pop_list, key=lambda x: x.fitness_value) @@ -458,11 +470,8 @@ def ccga( n_rows: int, n_gen: int, ch_size: int, - gen_type: str, problem: AbstractProblem, - selection: SelectionOperator, - mins: List[float] = [], - maxs: List[float] = [] + selection: SelectionOperator ) -> Result: """ Perform optimization using a Cooperative Coevolutionary Genetic Algorithm (CCGA). @@ -503,8 +512,15 @@ def ccga( method_name = OptimizationMethod.CCGA # Generate Initial Population - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, vector, mins=mins, maxs=maxs).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + gen_type = problem.gen_type, + problem = problem, + mins = problem.xl, + maxs = problem.xu).initial_population() # Sort population by fitness value pop_list_ordered = sorted(pop_list, key=lambda x: x.fitness_value) @@ -539,8 +555,15 @@ def ccga( update_vector(vector, winner, loser, pop_size) # Re-generate the population based on the updated vector - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, vector, mins=mins, maxs=maxs).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + gen_type = problem.gen_type, + problem = problem, + mins = problem.xl, + maxs = problem.xu).initial_population() # Update best ever solution if the current best solution is better if best.fitness_value > best_ever_solution.fitness_value: @@ -563,11 +586,8 @@ def mcccga( n_rows: int, n_gen: int, ch_size: int, - gen_type: str, problem: AbstractProblem, - selection: SelectionOperator, - mins: List[float], - maxs: List[float] + selection: SelectionOperator ) -> Result: """ Optimize the given problem using a multi-population machine-coded compact genetic algorithm (MCCGA). @@ -582,16 +602,10 @@ def mcccga( Number of generations to evolve. ch_size : int Size of the chromosome. - gen_type : str - Type of the genome representation (e.g., 'Binary', 'Permutation', 'Real'). problem : AbstractProblem Problem instance for fitness evaluation. selection : SelectionOperator Function or class used for selecting parents. - mins : List[float] - List of minimum values for the probability vector generation. - maxs : List[float] - List of maximum values for the probability vector generation. Returns ------- @@ -605,12 +619,22 @@ def mcccga( avg_objectives = [] method_name = OptimizationMethod.MCCCGA + mins = problem.xl + maxs = problem.xu + # Generate initial probability vector vector = generate_probability_vector(mins, maxs, pop_size) # Generate Initial Population - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, vector).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + problem.gen_type, + problem, + vector).initial_population() + # Sort population by fitness value pop_list_ordered = sorted(pop_list, key=lambda x: x.fitness_value) @@ -647,8 +671,14 @@ def mcccga( update_vector(vector, winner, loser, pop_size) # Re-generate the population based on the updated vector - pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, vector).initial_population() + pop_list = Population( + method_name, + ch_size, + n_rows, + n_cols, + problem.gen_type, + problem, + vector).initial_population() # Track the best fitness value and update the best solution if necessary best_objectives.append(best.fitness_value) diff --git a/pycellga/problems/abstract_problem.py b/pycellga/problems/abstract_problem.py index 13d3b57..d6db1c7 100644 --- a/pycellga/problems/abstract_problem.py +++ b/pycellga/problems/abstract_problem.py @@ -1,54 +1,64 @@ from abc import ABC, abstractmethod -from typing import List, Tuple, Union, Any +from typing import List, Any from pymoo.core.problem import Problem +from common import GeneType class AbstractProblem(Problem, ABC): """ - Abstract base class for optimization problems. + Abstract base class for defining optimization problems compatible with pymoo. + + This class provides a structure for defining optimization problems, where the + user specifies the gene type, the number of variables, and their bounds. It includes + an abstract method `f` for evaluating the fitness of a solution, which must be + implemented by subclasses. + + Attributes + ---------- + gen_type : GeneType + The type of genes used in the problem (e.g., REAL, BINARY). + n_var : int + The number of design variables. + xl : List[float] or numpy.ndarray + The lower bounds for the design variables. + xu : List[float] or numpy.ndarray + The upper bounds for the design variables. + + Methods + ------- + f(x: List[Any]) -> float + Abstract method to compute the fitness value for a given solution. + Must be implemented by subclasses. + evaluate(x, out, *args, **kwargs) + Computes the objective value(s) for pymoo's optimization framework. """ - def __init__(self, - design_variables: Union[int, List[str]], - bounds: List[Tuple[float, float]], - objectives: Union[str, int, List[str]], - constraints: Union[str, int, List[str]] = []): + def __init__(self, gen_type: GeneType, n_var, xl, xu): """ - Initialize the problem with variables, bounds, objectives, and constraints. - + Initialize the AbstractProblem with gene type, variable count, and bounds. + Parameters ---------- - design_variables : int or List[str] - If an integer, it specifies the number of design variables. - If a list of strings, it specifies the names of design variables. - bounds : List[Tuple[float, float]] - Bounds for each design variable as (min, max). - objectives : str, int, or List[str] - Objectives for optimization, e.g., "minimize" or "maximize". - constraints : str, int, or List[str], optional - Constraints for the problem (default is an empty list). + gen_type : Any + The type of genes used in the problem (e.g., REAL, BINARY). + n_var : int + The number of design variables. + xl : float + The lower bound for the design variables. + xu : float + The upper bound for the design variables. """ - # Ensure objectives and constraints are always lists - objectives = [str(objectives)] if isinstance(objectives, (str, int)) else list(objectives) - constraints = [str(constraints)] if isinstance(constraints, (str, int)) else list(constraints) + self.gen_type = gen_type - # Pymoo-specific attributes - n_var = design_variables if isinstance(design_variables, int) else len(design_variables) - xl = [bound[0] for bound in bounds] - xu = [bound[1] for bound in bounds] - - super().__init__(n_var=n_var, n_obj=len(objectives), n_constr=len(constraints), xl=xl, xu=xu) - - # Custom attributes - self.design_variables = [f"x{i+1}" for i in range(n_var)] if isinstance(design_variables, int) else design_variables - self.bounds = bounds - self.objectives = objectives - self.constraints = constraints + super().__init__(n_var=n_var, n_obj=1, n_constr=0, xl=xl, xu=xu) @abstractmethod def f(self, x: List[Any]) -> float: """ Abstract method for evaluating the fitness of a solution. - + + This method must be implemented by subclasses to define the objective function + of the optimization problem. + Parameters ---------- x : list @@ -57,14 +67,17 @@ def f(self, x: List[Any]) -> float: Returns ------- float - Fitness value. + The computed fitness value for the given solution. """ raise NotImplementedError("Subclasses should implement this method.") def evaluate(self, x, out, *args, **kwargs): """ Evaluate function for compatibility with pymoo's optimizer. - + + This method wraps the `f` method and allows pymoo to handle batch evaluations + by storing the computed fitness values in the output dictionary. + Parameters ---------- x : numpy.ndarray diff --git a/pycellga/problems/single_objective/continuous/ackley.py b/pycellga/problems/single_objective/continuous/ackley.py index 6f854e2..5230e39 100644 --- a/pycellga/problems/single_objective/continuous/ackley.py +++ b/pycellga/problems/single_objective/continuous/ackley.py @@ -1,68 +1,78 @@ -from numpy import pi, e, cos, sqrt, exp,power +from numpy import pi, e, cos, sqrt, exp, power from problems.abstract_problem import AbstractProblem +from typing import List +from common import GeneType + class Ackley(AbstractProblem): """ Ackley function implementation for optimization problems. The Ackley function is widely used for testing optimization algorithms. - It has a nearly flat outer region and a large hole at the center. + It is characterized by a nearly flat outer region and a large hole at the center. The function is usually evaluated on the hypercube x_i ∈ [-32.768, 32.768], for all i = 1, 2, ..., d. Attributes ---------- - design_variables : List[str] - List of variable names. - bounds : List[Tuple[float, float]] - Bounds for each variable. - objectives : List[str] - Objectives for optimization. - constraints : List[str] - Any constraints for the problem. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for each variable (fixed to -32.768). + xu : float + Upper bound for each variable (fixed to 32.768). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Ackley function value for given variables. - f(x) - Alias for evaluate to maintain compatibility with the rest of the codebase. + __init__(n_var: int) + Initialize the Ackley problem with the specified number of variables. + + f(x: List[float]) -> float + Compute the Ackley function value for a single solution. - Notes - ----- - -32.768 ≤ xi ≤ 32.768 - Global minimum at f(0, 0) = 0 + Parameters + ---------- + x : list or numpy.ndarray + Array of input variables. + + Returns + ------- + float + The computed fitness value for the given solution. """ - def __init__(self, dimension: int): - design_variables = [f"x{i+1}" for i in range(dimension)] - bounds = [(-32.768, 32.768) for _ in range(dimension)] - objectives = ["minimize"] - constraints = [] + def __init__(self, n_var): + """ + Initialize the Ackley problem. + + Parameters + ---------- + n_var : int + Number of variables (dimensions) in the problem. + """ + gen_type = GeneType.REAL + xl = -32.768 + xu = 32.768 - super().__init__(design_variables, bounds, objectives, constraints) - self.dimension = dimension + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x: List[float]) -> float: """ - Calculate the Ackley function value for a given list of variables. + Compute the Ackley function value for a single solution. Parameters ---------- - x : numpy.ndarray + x : list or numpy.ndarray Array of input variables. - out : dict - Dictionary to store the output fitness values. + + Returns + ------- + float + The computed fitness value for the given solution. """ - sum1 = sum(power(gene,2) for gene in x) + sum1 = sum(power(gene, 2) for gene in x) sum2 = sum(cos(2 * pi * gene) for gene in x) - - fitness = -20.0 * exp(-0.2 * sqrt(sum1 / self.dimension)) - exp(sum2 / self.dimension) + 20.0 + e - out["F"] = round(fitness, 3) - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + fitness = -20.0 * exp(-0.2 * sqrt(sum1 / self.n_var)) - exp(sum2 / self.n_var) + 20.0 + e + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/bentcigar.py b/pycellga/problems/single_objective/continuous/bentcigar.py index 5b91f6b..7e2d9f5 100644 --- a/pycellga/problems/single_objective/continuous/bentcigar.py +++ b/pycellga/problems/single_objective/continuous/bentcigar.py @@ -1,7 +1,8 @@ from problems.abstract_problem import AbstractProblem from mpmath import power as pw from typing import List -import numpy as np +from common import GeneType + class Bentcigar(AbstractProblem): """ @@ -12,21 +13,19 @@ class Bentcigar(AbstractProblem): Attributes ---------- - design_variables : List[str] - List of variable names. - bounds : List[Tuple[float, float]] - Bounds for each variable. - objectives : List[str] - Objectives for optimization. - constraints : List[str] - Any constraints for the problem. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for each variable (fixed to -100). + xu : float + Upper bound for each variable (fixed to 100). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Bentcigar function value for given variables. - f(x) - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Bentcigar function value for a single solution. Notes ----- @@ -34,37 +33,38 @@ class Bentcigar(AbstractProblem): Global minimum at f(0,...,0) = 0 """ - def __init__(self, dimension: int): - design_variables = [f"x{i+1}" for i in range(dimension)] - bounds = [(-100, 100) for _ in range(dimension)] - objectives = ["minimize"] - constraints = [] + def __init__(self, n_var: int): + """ + Initialize the Bentcigar problem. + + Parameters + ---------- + n_var : int + Number of variables (dimensions) in the problem. + """ + gen_type = GeneType.REAL + xl = -100.0 + xu = 100.0 - super().__init__(design_variables, bounds, objectives, constraints) - self.dimension = dimension + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x: List[float]) -> float: """ - Calculate the Bentcigar function value for a given list of variables. + Compute the Bentcigar function value for a single solution. Parameters ---------- - x : numpy.ndarray + x : list or numpy.ndarray Array of input variables. - out : dict - Dictionary to store the output fitness values. - """ - a = pw(x[0], 2) - b = pw(10, 6) - sum_val = sum(pw(xi, 2) for xi in x[1:]) - - fitness = a + (b * sum_val) - out["F"] = round(fitness, 3) - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. + Returns + ------- + float + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + a = pw(x[0], 2) + b = pw(10, 6) + sum_val = sum(pw(xi, 2) for xi in x[1:]) + + fitness = a + (b * sum_val) + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/bohachevsky.py b/pycellga/problems/single_objective/continuous/bohachevsky.py index 6092ec5..70e07f7 100644 --- a/pycellga/problems/single_objective/continuous/bohachevsky.py +++ b/pycellga/problems/single_objective/continuous/bohachevsky.py @@ -1,32 +1,31 @@ from numpy import cos, pi from mpmath import power as pw -from typing import List, Dict, Any +from typing import List from problems.abstract_problem import AbstractProblem +from common import GeneType class Bohachevsky(AbstractProblem): """ Bohachevsky function implementation for optimization problems. The Bohachevsky function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-15, 15], for all i = 1, 2, ..., n. + It is usually evaluated on the hypercube x_i ∈ [-15, 15], for all i = 1, 2, ..., n. Attributes ---------- - design_variables : List[str] - List of variable names. - bounds : List[Tuple[float, float]] - Bounds for each variable. - objectives : List[str] - Objectives for optimization. - constraints : List[str] - Any constraints for the problem. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for each variable (fixed to -15). + xu : float + Upper bound for each variable (fixed to 15). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Bohachevsky function value for given variables. - f(x) - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Bohachevsky function value for a single solution. Notes ----- @@ -34,25 +33,34 @@ class Bohachevsky(AbstractProblem): Global minimum at f(0,...,0) = 0 """ - def __init__(self, dimension: int): - design_variables = [f"x{i+1}" for i in range(dimension)] - bounds = [(-15, 15) for _ in range(dimension)] - objectives = ["minimize"] - constraints = [] + def __init__(self, n_var: int): + """ + Initialize the Bohachevsky problem. + + Parameters + ---------- + n_var : int + Number of variables (dimensions) in the problem. + """ + gen_type = GeneType.REAL + xl = -15.0 + xu = 15.0 - super().__init__(design_variables, bounds, objectives, constraints) - self.dimension = dimension + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x: List[float], out: Dict[str, Any], *args, **kwargs): + def f(self, x: List[float]) -> float: """ - Calculate the Bohachevsky function value for a given list of variables. + Compute the Bohachevsky function value for a single solution. Parameters ---------- - x : list - A list of float variables. - out : dict - Dictionary to store the output fitness values. + x : list or numpy.ndarray + Array of input variables. + + Returns + ------- + float + The computed fitness value for the given solution. """ fitness = sum([ pw(x[i], 2) + (2 * pw(x[i + 1], 2)) @@ -60,12 +68,4 @@ def evaluate(self, x: List[float], out: Dict[str, Any], *args, **kwargs): - (0.4 * cos(4 * pi * x[i + 1])) + 0.7 for i in range(len(x) - 1) ]) - out["F"] = round(fitness, 3) - - def f(self, x: List[float]) -> float: - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/chichinadze.py b/pycellga/problems/single_objective/continuous/chichinadze.py index 02c899f..02b6ed2 100644 --- a/pycellga/problems/single_objective/continuous/chichinadze.py +++ b/pycellga/problems/single_objective/continuous/chichinadze.py @@ -1,31 +1,31 @@ import numpy as np from typing import List from problems.abstract_problem import AbstractProblem +from common import GeneType + class Chichinadze(AbstractProblem): """ Chichinadze function implementation for optimization problems. The Chichinadze function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x, y ∈ [-30, 30]. + It is usually evaluated on the hypercube x, y ∈ [-30, 30]. Attributes ---------- - design_variables : List[str] - List of variable names. - bounds : List[Tuple[float, float]] - Bounds for each variable. - objectives : List[str] - Objectives for optimization. - constraints : List[str] - Any constraints for the problem. + n_var : int + Number of variables (fixed to 2 for x and y). + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for the variables (fixed to -30). + xu : float + Upper bound for the variables (fixed to 30). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Chichinadze function value for given variables. - f(x) - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Chichinadze function value for a single solution. Notes ----- @@ -34,23 +34,29 @@ class Chichinadze(AbstractProblem): """ def __init__(self): - design_variables = ["x", "y"] - bounds = [(-30, 30), (-30, 30)] - objectives = ["minimize"] - constraints = [] + """ + Initialize the Chichinadze problem. + """ + gen_type = GeneType.REAL + n_var = 2 # Fixed to 2 for x and y + xl = -30.0 + xu = 30.0 - super().__init__(design_variables, bounds, objectives, constraints) + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x: List[float]) -> float: """ - Calculate the Chichinadze function value for a given list of variables. + Compute the Chichinadze function value for a single solution. Parameters ---------- - x : numpy.ndarray + x : list or numpy.ndarray Array of input variables. - out : dict - Dictionary to store the output fitness values. + + Returns + ------- + float + The computed fitness value for the given solution. """ x_val, y_val = x[0], x[1] term1 = x_val**2 - 12 * x_val + 11 @@ -59,12 +65,4 @@ def evaluate(self, x, out, *args, **kwargs): term4 = (1.0 / np.sqrt(5)) * np.exp(-((y_val - 0.5)**2) / 2) fitness = term1 + term2 + term3 - term4 - out["F"] = round(fitness, 4) - - def f(self, x: List[float]) -> float: - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(fitness, 4) diff --git a/pycellga/problems/single_objective/continuous/dropwave.py b/pycellga/problems/single_objective/continuous/dropwave.py index 455f0b3..0251025 100644 --- a/pycellga/problems/single_objective/continuous/dropwave.py +++ b/pycellga/problems/single_objective/continuous/dropwave.py @@ -1,5 +1,8 @@ from problems.abstract_problem import AbstractProblem from numpy import power, cos, sqrt +from typing import List +from common import GeneType + class Dropwave(AbstractProblem): """ @@ -10,63 +13,56 @@ class Dropwave(AbstractProblem): Attributes ---------- - design_variables : list - The names of the variables, in this case ["x1", "x2"]. - bounds : list of tuples - The lower and upper bounds for each variable, [-5.12, 5.12] for both x1 and x2. - objectives : list - List defining the optimization objective, which is to "minimize" for this function. - num_variables : int - The number of variables (dimensions) for the function, which is 2 in this case. + n_var : int + Number of variables (dimensions) in the problem (fixed to 2). + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for the variables (fixed to -5.12). + xu : float + Upper bound for the variables (fixed to 5.12). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the value of the Dropwave function at a given point x. - f(x) - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Dropwave function value for a single solution. + + Notes + ----- + -5.12 ≤ xi ≤ 5.12 for i = 1, 2 + Global minimum at f(0, 0) = -1 """ def __init__(self): - # Dropwave problem-specific parameters - design_variables = ["x1", "x2"] - bounds = [(-5.12, 5.12), (-5.12, 5.12)] - objectives = ["minimize"] + """ + Initialize the Dropwave problem. + """ + gen_type = GeneType.REAL + n_var = 2 # Fixed to 2 for x1 and x2 + xl = -5.12 + xu = 5.12 - # Initialize the AbstractProblem with specific parameters - super().__init__(design_variables, bounds, objectives) - self.num_variables = 2 + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x: List[float]) -> float: """ - Calculate the Dropwave function value for a given list of variables. + Compute the Dropwave function value for a single solution. Parameters ---------- - x : list - A list of two floats representing the coordinates [x1, x2]. - out : dict - Dictionary to store the output fitness values. + x : list or numpy.ndarray + Array of input variables. - Notes - ----- - The Dropwave function is defined as: - f(x1, x2) = - (1 + cos(12 * sqrt(x1^2 + x2^2))) / (0.5 * (x1^2 + x2^2) + 2) - where x1 and x2 are the input variables. + Returns + ------- + float + The computed fitness value for the given solution. """ - if len(x) != self.num_variables: - raise ValueError(f"Input must have exactly {self.num_variables} variables.") + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") x1, x2 = x sqrts_sums = power(x1, 2) + power(x2, 2) denominator = 0.5 * sqrts_sums + 2 fitness = -(1 + cos(12 * sqrt(sqrts_sums))) / denominator - out["F"] = round(fitness, 3) - - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/fms.py b/pycellga/problems/single_objective/continuous/fms.py index facf48f..3d8d502 100644 --- a/pycellga/problems/single_objective/continuous/fms.py +++ b/pycellga/problems/single_objective/continuous/fms.py @@ -1,6 +1,8 @@ from problems.abstract_problem import AbstractProblem from numpy import pi, sin -import numpy as np +from typing import List +from common import GeneType + class Fms(AbstractProblem): """ @@ -10,42 +12,49 @@ class Fms(AbstractProblem): Attributes ---------- - design_variables : List[str] - List of variable names. - bounds : List[Tuple[float, float]] - Bounds for each variable. - objectives : List[str] - Objectives for optimization. - constraints : List[str] - Any constraints for the problem. + n_var : int + Number of variables (dimensions) in the problem (fixed to 6). + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for the variables (fixed to -6.4). + xu : float + Upper bound for the variables (fixed to 6.35). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Fms function value for given variables. - f(x) - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Fms function value for a single solution. """ def __init__(self): - design_variables = ["a1", "w1", "a2", "w2", "a3", "w3"] - bounds = [(-6.4, 6.35)] * 6 # Same bounds for each variable - objectives = ["minimize"] - constraints = [] - - super().__init__(design_variables, bounds, objectives, constraints) + """ + Initialize the Fms problem. + """ + gen_type = GeneType.REAL + n_var = 6 # Fixed to 6 for [a1, w1, a2, w2, a3, w3] + xl = -6.4 + xu = 6.35 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x: List[float]) -> float: """ - Calculate the Fms function value for a given list of variables. + Compute the Fms function value for a single solution. Parameters ---------- - x : numpy.ndarray + x : list or numpy.ndarray Array of input variables. - out : dict - Dictionary to store the output fitness values. + + Returns + ------- + float + The computed fitness value for the given solution. """ + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") + theta = (2.0 * pi) / 100.0 a1, w1, a2, w2, a3, w3 = x @@ -54,15 +63,10 @@ def yzero(t): partial_fitness = 0.0 for k in range(101): - distance = a1 * sin((w1 * theta * k) - (a2 * sin((w2 * theta * k) + (a3 * sin(w3 * theta * k))))) - yzero(k) + distance = ( + a1 * sin((w1 * theta * k) - (a2 * sin((w2 * theta * k) + (a3 * sin(w3 * theta * k))))) + - yzero(k) + ) partial_fitness += distance ** 2 - out["F"] = round(partial_fitness, 3) - - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(partial_fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/griewank.py b/pycellga/problems/single_objective/continuous/griewank.py index cb60cbc..f9d07bd 100644 --- a/pycellga/problems/single_objective/continuous/griewank.py +++ b/pycellga/problems/single_objective/continuous/griewank.py @@ -1,31 +1,31 @@ from problems.abstract_problem import AbstractProblem import math from typing import List +from common import GeneType + class Griewank(AbstractProblem): """ Griewank function implementation for optimization problems. The Griewank function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-600, 600], for all i = 1, 2, ..., n. + It is usually evaluated on the hypercube xi ∈ [-600, 600], for all i = 1, 2, ..., n. Attributes ---------- - design_variables : List[str] - Names of the design variables. - bounds : List[Tuple[float, float]] - Bounds for each design variable. - objectives : List[str] - Objectives for optimization, typically ["minimize"]. - constraints : List[str] - Any constraints for the optimization problem. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for the variables (fixed to -600). + xu : float + Upper bound for the variables (fixed to 600). Methods ------- - evaluate(x, out, *args, **kwargs) - Evaluates the Griewank function value for a given list of variables. - f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Griewank function value for a single solution. Notes ----- @@ -33,53 +33,36 @@ class Griewank(AbstractProblem): Global minimum at f(0,...,0) = 0 """ - def __init__(self, dimensions=10): + def __init__(self, n_var: int = 10): """ - Initialize the Griewank function with the specified number of dimensions. + Initialize the Griewank problem. Parameters ---------- - dimensions : int, optional - The number of dimensions (design variables) for the Griewank function, by default 10. + n_var : int, optional + Number of variables (dimensions) in the problem, by default 10. """ - design_variables = [f"x{i+1}" for i in range(dimensions)] - bounds = [(-600, 600)] * dimensions - objectives = ["minimize"] - constraints = [] - - super().__init__(design_variables, bounds, objectives, constraints) - self.dimensions = dimensions + gen_type = GeneType.REAL + xl = -600.0 + xu = 600.0 - def evaluate(self, x: List[float], out, *args, **kwargs): - """ - Calculate the Griewank function value for a given list of variables. - - Parameters - ---------- - x : list - A list of float variables. - out : dict - Dictionary to store the output fitness value. - """ - sum_sq = sum(xi ** 2 for xi in x) - prod_cos = math.prod(math.cos(xi / math.sqrt(i + 1)) for i, xi in enumerate(x)) - fitness = 1 + sum_sq / 4000 - prod_cos - out["F"] = round(fitness, 3) + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: List[float]) -> float: """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. + Compute the Griewank function value for a single solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Griewank function value. + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + sum_sq = sum(xi ** 2 for xi in x) + prod_cos = math.prod(math.cos(xi / math.sqrt(i + 1)) for i, xi in enumerate(x)) + fitness = 1 + sum_sq / 4000 - prod_cos + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/holzman.py b/pycellga/problems/single_objective/continuous/holzman.py index 010d0cb..1fb944d 100644 --- a/pycellga/problems/single_objective/continuous/holzman.py +++ b/pycellga/problems/single_objective/continuous/holzman.py @@ -1,31 +1,31 @@ from problems.abstract_problem import AbstractProblem from mpmath import power as pw from typing import List +from common import GeneType + class Holzman(AbstractProblem): """ Holzman function implementation for optimization problems. The Holzman function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-10, 10], for all i = 1, 2, ..., n. + It is usually evaluated on the hypercube xi ∈ [-10, 10], for all i = 1, 2, ..., n. Attributes ---------- - design_variables : List[str] - Names of the design variables. - bounds : List[Tuple[float, float]] - Bounds for each variable. - objectives : List[str] - Objectives for optimization. - constraints : List[str] - Any constraints for the problem. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bound for the variables (fixed to -10). + xu : float + Upper bound for the variables (fixed to 10). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Holzman function value for given variables. - f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Holzman function value for a single solution. Notes ----- @@ -33,33 +33,34 @@ class Holzman(AbstractProblem): Global minimum at f(0,...,0) = 0 """ - def __init__(self, design_variables: int = 2): - var_names = [f"x{i+1}" for i in range(design_variables)] - bounds = [(-10, 10) for _ in range(design_variables)] - objectives = ["minimize"] - constraints = [] - - super().__init__(var_names, bounds, objectives, constraints) - self.design_variables = design_variables - - def evaluate(self, x: List[float], out, *args, **kwargs): + def __init__(self, n_var: int = 2): """ - Calculate the Holzman function value for a given list of variables. + Initialize the Holzman problem. Parameters ---------- - x : list - A list of float variables. - out : dict - Dictionary to store the output fitness value. + n_var : int, optional + Number of variables (dimensions) in the problem, by default 2. """ - fitness = sum((i + 1) * pw(x[i], 4) for i in range(len(x))) - out["F"] = round(fitness, 3) + gen_type = GeneType.REAL + xl = -10.0 + xu = 10.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: List[float]) -> float: """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. + Compute the Holzman function value for a single solution. + + Parameters + ---------- + x : list or numpy.ndarray + Array of input variables. + + Returns + ------- + float + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + fitness = sum((i + 1) * pw(x[i], 4) for i in range(len(x))) + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/levy.py b/pycellga/problems/single_objective/continuous/levy.py index d6cadaf..ee89f08 100644 --- a/pycellga/problems/single_objective/continuous/levy.py +++ b/pycellga/problems/single_objective/continuous/levy.py @@ -2,84 +2,85 @@ import math from mpmath import power as pw from typing import List +from common import GeneType + class Levy(AbstractProblem): """ Levy function implementation for optimization problems. The Levy function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-10, 10], for all i = 1, 2, ..., n. + It evaluates inputs over the hypercube x_i ∈ [-10, 10]. Attributes ---------- - design_variables : List[str] - The names of the design variables. - bounds : List[Tuple[float, float]] - The bounds for each variable, typically [(-10, 10), (-10, 10), ...]. - objectives : List[str] - Objectives for optimization, usually "minimize" for single-objective functions. - constraints : List[str] - Any constraints for the optimization problem. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (REAL). + xl : float + Lower bounds for the variables, fixed to -10. + xu : float + Upper bounds for the variables, fixed to 10. Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Levy function value for a given list of variables and stores in `out`. - f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. - - Notes - ----- - -10 ≤ xi ≤ 10 for i = 1,…,n - Global minimum at f(1,1,...,1) = 0 + f(x: List[float]) -> float + Compute the Levy function value for a given solution. + evaluate(x: List[float], out: dict, *args, **kwargs) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, dimension: int = 2): - design_variables = [f"x{i+1}" for i in range(dimension)] - bounds = [(-10, 10) for _ in range(dimension)] - objectives = ["minimize"] - constraints = [] + def __init__(self, n_var: int = 2): + """ + Initialize the Levy problem. - super().__init__(design_variables, bounds, objectives, constraints) - self.dimension = dimension + Parameters + ---------- + n_var : int, optional + Number of variables (dimensions) for the problem, by default 2. + """ + gen_type = GeneType.REAL + xl = -10.0 + xu = 10.0 - def evaluate(self, x: List[float], out, *args, **kwargs): + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + + def f(self, x: List[float]) -> float: """ - Evaluate the Levy function at a given point. + Compute the Levy function value for a given solution. Parameters ---------- x : list A list of float variables. - out : dict - Dictionary to store the output fitness value. + + Returns + ------- + float + The Levy function value. """ - if len(x) != self.dimension: - raise ValueError(f"Input must have exactly {self.dimension} variables.") + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") fitness = 0.0 - for i in range(self.dimension - 1): + for i in range(self.n_var - 1): term1 = pw(math.sin(3 * x[i] * math.pi), 2) term2 = (pw((x[i] - 1), 2)) * (1 + pw(math.sin(3 * x[i + 1] * math.pi), 2)) term3 = (pw((x[i + 1] - 1), 2)) * (1 + pw(math.sin(2 * x[i + 1] * math.pi), 2)) fitness += term1 + term2 + term3 - out["F"] = round(fitness, 3) + return round(fitness, 3) - def f(self, x: List[float]) -> float: + def evaluate(self, x: List[float], out: dict, *args, **kwargs) -> None: """ - Alias for evaluate to maintain compatibility with the rest of the codebase. + Evaluate method for compatibility with pymoo's framework. Parameters ---------- - x : list - A list of float variables. - - Returns - ------- - float - The calculated Levy function value. + x : numpy.ndarray + Array of input variables. + out : dict + Dictionary to store the output fitness values. """ - result = {} - self.evaluate(x, result) - return result["F"] + out["F"] = self.f(x) diff --git a/pycellga/problems/single_objective/continuous/matyas.py b/pycellga/problems/single_objective/continuous/matyas.py index deadee3..27a7619 100644 --- a/pycellga/problems/single_objective/continuous/matyas.py +++ b/pycellga/problems/single_objective/continuous/matyas.py @@ -1,5 +1,6 @@ from problems.abstract_problem import AbstractProblem from mpmath import power as pw +from common import GeneType class Matyas(AbstractProblem): """ @@ -7,53 +8,58 @@ class Matyas(AbstractProblem): The Matyas function is commonly used to evaluate the performance of optimization algorithms. It is a simple, continuous, convex function that has a global minimum at the origin. - + Attributes ---------- - design_variables : list of str - The names of the design variables, typically ["x1", "x2"] for 2 variables. - bounds : list of tuple - The bounds for each variable, typically [(-10, 10), (-10, 10)]. - objectives : list of str - The objectives for optimization, set to ["minimize"]. + gen_type : GeneType + The type of genes used in the problem (fixed to REAL). + n_var : int + Number of variables (dimensions) in the problem (fixed to 2). + xl : float + Lower bound for the variables (fixed to -10). + xu : float + Upper bound for the variables (fixed to 10). Methods ------- - evaluate(x, out, *args, **kwargs) - Computes the Matyas function value for a given list of variables. f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. + Computes the Matyas function value for a given solution. """ def __init__(self): - design_variables = ["x1", "x2"] - bounds = [(-10, 10), (-10, 10)] - objectives = ["minimize"] + """ + Initialize the Matyas problem. - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + This problem is defined for exactly 2 variables with bounds [-10, 10]. - def evaluate(self, x, out, *args, **kwargs): + Parameters + ---------- + None """ - Calculate the Matyas function value for a given list of variables. + gen_type = GeneType.REAL + n_var = 2 + xl = -10.0 + xu = 10.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + + def f(self, x): + """ + Compute the Matyas function value for a given solution. Parameters ---------- x : list of float A list of float variables representing a point in the solution space. - out : dict - Dictionary to store the output fitness value with key "F". + + Returns + ------- + float + The computed fitness value for the given solution. """ if len(x) != 2: raise ValueError("Matyas function is defined for exactly 2 variables.") x1, x2 = x fitness = 0.26 * (pw(x1, 2) + pw(x2, 2)) - 0.48 * x1 * x2 - out["F"] = round(fitness, 2) - - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(fitness, 2) diff --git a/pycellga/problems/single_objective/continuous/pow.py b/pycellga/problems/single_objective/continuous/pow.py index f490751..4b75b71 100644 --- a/pycellga/problems/single_objective/continuous/pow.py +++ b/pycellga/problems/single_objective/continuous/pow.py @@ -1,6 +1,8 @@ from problems.abstract_problem import AbstractProblem from mpmath import power as pw -from typing import List, Tuple +from typing import List +from common import GeneType + class Pow(AbstractProblem): """ @@ -12,67 +14,56 @@ class Pow(AbstractProblem): Attributes ---------- - design_variables : List[str] - The names of the design variables. - bounds : List[Tuple[float, float]] - The bounds for each variable, typically [(-5.0, 15.0) for each dimension]. - objectives : List[str] - Objectives for optimization, e.g., "minimize". + gen_type : GeneType + The type of genes used in the problem (REAL). + n_var : int + The number of design variables. + xl : float + The lower bound for the variables (-5.0). + xu : float + The upper bound for the variables (15.0). Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Pow function value for compatibility with Pymoo's optimizer. - f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Pow function value for a given solution. """ - def __init__(self, design_variables=5): - # Define design variable names - design_variable_names = [f"x{i+1}" for i in range(design_variables)] - bounds = [(-5.0, 15.0) for _ in range(design_variables)] - objectives = ["minimize"] - - # Initialize the AbstractProblem - super().__init__(design_variable_names, bounds, objectives) - self.design_variables = design_variables - - def evaluate(self, x, out, *args, **kwargs): + def __init__(self, n_var: int = 5): """ - Calculate the Pow function value for a given list of variables. + Initialize the Pow problem. Parameters ---------- - x : list - A list of float variables representing the point in the solution space. - out : dict - Dictionary to store the output fitness values. + n_var : int, optional + The number of variables (dimensions) in the problem, by default 5. """ - if len(x) != self.design_variables: - raise ValueError(f"Input must have exactly {self.design_variables} variables.") + gen_type = GeneType.REAL + xl = -5.0 + xu = 15.0 - fitness = (pw(x[0] - 5, 2) + - pw(x[1] - 7, 2) + - pw(x[2] - 9, 2) + - pw(x[3] - 3, 2) + - pw(x[4] - 2, 2)) - - out["F"] = round(fitness, 2) + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: List[float]) -> float: """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. + Compute the Pow function value for a given solution. Parameters ---------- - x : list - A list of float variables. + x : list of float + A list of float variables representing a point in the solution space. Returns ------- float - The Pow function value. + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") + + # Define the target values + target = [5, 7, 9, 3, 2] + + # Compute the fitness as the sum of squared differences + fitness = sum(pw(xi - ti, 2) for xi, ti in zip(x, target)) + return round(fitness, 2) diff --git a/pycellga/problems/single_objective/continuous/powell.py b/pycellga/problems/single_objective/continuous/powell.py index f1d1292..79b3934 100644 --- a/pycellga/problems/single_objective/continuous/powell.py +++ b/pycellga/problems/single_objective/continuous/powell.py @@ -1,72 +1,72 @@ from problems.abstract_problem import AbstractProblem +from common import GeneType from mpmath import power as pw +from typing import List class Powell(AbstractProblem): """ Powell function implementation for optimization problems. The Powell function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-4, 5], for all i = 1, 2, ..., n. - + It is typically evaluated on the hypercube x_i ∈ [-4, 5], for all i = 1, 2, ..., n. + Attributes ---------- - design_variables : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-4, 5), (-4, 5), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (REAL for this implementation). + xl : float + Lower bound for the variables (fixed to -4). + xu : float + Upper bound for the variables (fixed to 5). Methods ------- - evaluate(x, out, *args, **kwargs) -> None - Calculates the Powell function value and stores in the output dictionary. - - f(x: list) -> float - Wrapper for evaluate to maintain compatibility with the rest of the codebase. + f(x: List[float]) -> float + Compute the Powell function value for a given solution. """ - def __init__(self, design_variables=4): - bounds = [(-4, 5) for _ in range(design_variables)] - super().__init__(design_variables=design_variables, bounds=bounds, objectives=["minimize"]) - - def evaluate(self, x, out, *args, **kwargs): + def __init__(self, n_var: int = 4): """ - Evaluate the Powell function at a given point. + Initialize the Powell problem. Parameters ---------- - x : list or numpy array - Input variables. - out : dict - Output dictionary to store the function value. + n_var : int, optional + Number of variables (dimensions) in the problem, by default 4. """ - fitness = 0.0 - d = len(x) // 4 - - for i in range(d): - a = pw(x[4 * i] + 10 * x[4 * i + 1], 2) - b = pw(x[4 * i + 2] - x[4 * i + 3], 2) - c = pw(x[4 * i + 1] - 2 * x[4 * i + 2], 4) - e = pw(x[4 * i] - x[4 * i + 3], 4) - fitness += a + 5 * b + c + 10 * e + gen_type = GeneType.REAL + xl = -4.0 + xu = 5.0 - out["F"] = round(fitness, 1) + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def f(self, x): + def f(self, x: List[float]) -> float: """ - Wrapper for the evaluate method to maintain compatibility. + Compute the Powell function value for a given solution. Parameters ---------- - x : list - A list of float variables. + x : list of float + A list of float variables representing a point in the solution space. Returns ------- float - The computed Powell function value. + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + if len(x) % 4 != 0: + raise ValueError("Powell function requires the number of variables to be a multiple of 4.") + + fitness = 0.0 + d = len(x) // 4 + + for i in range(d): + a = pw(x[4 * i] + 10 * x[4 * i + 1], 2) + b = pw(x[4 * i + 2] - x[4 * i + 3], 2) + c = pw(x[4 * i + 1] - 2 * x[4 * i + 2], 4) + e = pw(x[4 * i] - x[4 * i + 3], 4) + fitness += a + 5 * b + c + 10 * e + + return round(fitness, 1) diff --git a/pycellga/problems/single_objective/continuous/rastrigin.py b/pycellga/problems/single_objective/continuous/rastrigin.py index 20a07d4..fab067e 100644 --- a/pycellga/problems/single_objective/continuous/rastrigin.py +++ b/pycellga/problems/single_objective/continuous/rastrigin.py @@ -1,5 +1,7 @@ from numpy import cos, pi +from typing import List from problems.abstract_problem import AbstractProblem +from common import GeneType class Rastrigin(AbstractProblem): """ @@ -10,38 +12,37 @@ class Rastrigin(AbstractProblem): Attributes ---------- - design_variables : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-5.12, 5.12), (-5.12, 5.12), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + gen_type : GeneType + The type of genes used in the problem, set to REAL. + n_var : int + The number of variables (dimensions) in the problem. + xl : float + The lower bound for each variable, set to -5.12. + xu : float + The upper bound for each variable, set to 5.12. Methods ------- - f(x: list) -> float - Calculates the Rastrigin function value for a given list of variables. - - Notes - ----- - -5.12 ≤ xi ≤ 5.12 for i = 1,…,n - Global minimum at f(0,...,0) = 0 + f(x: List[float]) -> float + Computes the Rastrigin function value for a given solution. """ - def __init__(self, design_variables=2): + def __init__(self, n_var: int = 2): """ Initialize the Rastrigin problem with the specified number of variables. Parameters ---------- - design_variables : int, optional - The number of design variables, by default 2. + n_var : int, optional + The number of design variables (dimensions), by default 2. """ - self.design_variables = design_variables - self.bounds = [(-5.12, 5.12) for _ in range(design_variables)] - self.objectives = 1 + gen_type = GeneType.REAL + xl = -5.12 + xu = 5.12 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def f(self, x: list) -> float: + def f(self, x: List[float]) -> float: """ Calculate the Rastrigin function value for a given list of variables. @@ -53,11 +54,11 @@ def f(self, x: list) -> float: Returns ------- float - The Rastrigin function value. + The computed Rastrigin function value. """ - if len(x) != self.design_variables: - raise ValueError(f"Input must have exactly {self.design_variables} variables.") + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") A = 10.0 - fitness = (A * self.design_variables) + sum([(xi ** 2) - (A * cos(2 * pi * xi)) for xi in x]) + fitness = (A * self.n_var) + sum([(xi ** 2) - (A * cos(2 * pi * xi)) for xi in x]) return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/rosenbrock.py b/pycellga/problems/single_objective/continuous/rosenbrock.py index 0b11b1e..294126a 100644 --- a/pycellga/problems/single_objective/continuous/rosenbrock.py +++ b/pycellga/problems/single_objective/continuous/rosenbrock.py @@ -1,5 +1,8 @@ from problems.abstract_problem import AbstractProblem from mpmath import power as pw +from common import GeneType +from typing import List + class Rosenbrock(AbstractProblem): """ @@ -10,59 +13,52 @@ class Rosenbrock(AbstractProblem): Attributes ---------- - design_variables : int - Number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-5, 10), (-5, 10), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bounds for the variables (fixed to -5). + xu : float + Upper bounds for the variables (fixed to 10). Methods ------- - evaluate(x, out, *args, **kwargs) - Evaluates the Rosenbrock function value for a given list of variables. - f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. - - Notes - ----- - -5 ≤ xi ≤ 10 for i = 1,…,n - Global minimum at f(1,...,1) = 0 + f(x: List[float]) -> float + Compute the Rosenbrock function value for a single solution. """ - def __init__(self, design_variables=2): - self.design_variables = design_variables - bounds = [(-5, 10) for _ in range(design_variables)] - super().__init__(design_variables=[f"x{i+1}" for i in range(design_variables)], bounds=bounds, objectives=["minimize"]) - - def evaluate(self, x, out, *args, **kwargs): + def __init__(self, n_var: int = 2): """ - Calculate the Rosenbrock function value for a given list of variables. + Initialize the Rosenbrock problem. Parameters ---------- - x : list - A list of float variables. - out : dict - Dictionary to store the output fitness values. + n_var : int, optional + Number of variables (dimensions) in the problem, by default 2. """ - fitness = sum([(100 * pw((x[i + 1] - pw(x[i], 2)), 2)) + pw((1 - x[i]), 2) for i in range(len(x) - 1)]) - out["F"] = round(fitness, 3) + gen_type = GeneType.REAL + xl = -5.0 + xu = 10.0 - def f(self, x: list) -> float: + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + + def f(self, x: List[float]) -> float: """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. + Compute the Rosenbrock function value for a single solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Rosenbrock function value. + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") + + fitness = sum([(100 * pw((x[i + 1] - pw(x[i], 2)), 2)) + pw((1 - x[i]), 2) for i in range(self.n_var - 1)]) + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/rothellipsoid.py b/pycellga/problems/single_objective/continuous/rothellipsoid.py index a1a3250..7bd4549 100644 --- a/pycellga/problems/single_objective/continuous/rothellipsoid.py +++ b/pycellga/problems/single_objective/continuous/rothellipsoid.py @@ -1,68 +1,78 @@ from problems.abstract_problem import AbstractProblem from mpmath import power as pw +from common import GeneType class Rothellipsoid(AbstractProblem): """ Rotated Hyper-Ellipsoid function implementation for optimization problems. This function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-100, 100], for all i = 1, 2, ..., n. + It is usually evaluated on the hypercube x_i ∈ [-100, 100], for all i = 1, 2, ..., n. Attributes ---------- - design_variables : int + n_var : int Number of variables (dimensions) for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-100, 100), (-100, 100), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + gen_type : GeneType + The type of genes used in the problem, set to REAL. + xl : float + Lower bound for the variables, set to -100. + xu : float + Upper bound for the variables, set to 100. Methods ------- f(x: list) -> float - Alias for evaluate, calculates the Rotated Hyper-Ellipsoid function value for a given list of variables. + Compute the Rotated Hyper-Ellipsoid function value for a given list of variables. evaluate(x, out, *args, **kwargs) - Pymoo-compatible function for calculating the fitness values and storing them in the `out` dictionary. + Computes the fitness value for pymoo compatibility. """ - def __init__(self, design_variables=3): - # Initialize the parameters as required by AbstractProblem - super().__init__( - design_variables=[f"x{i+1}" for i in range(design_variables)], - bounds=[(-100, 100)] * design_variables, - objectives=["minimize"] - ) - - def evaluate(self, x, out, *args, **kwargs): + def __init__(self, n_var: int = 3): """ - Calculate the Rotated Hyper-Ellipsoid function value for pymoo compatibility. + Initialize the Rothellipsoid problem. Parameters ---------- - x : numpy.ndarray - Array of input variables. - out : dict - Dictionary to store the output fitness values. + n_var : int, optional + Number of variables (dimensions) for the problem, by default 3. """ - fitness = 0.0 - for i in range(len(x)): - fitness += (i + 1) * pw(x[i], 2) - out["F"] = round(fitness, 3) + gen_type = GeneType.REAL + xl = -100.0 + xu = 100.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x): """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - + Compute the Rotated Hyper-Ellipsoid function value for a given solution. + Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Rotated Hyper-Ellipsoid function value. + The computed fitness value for the given solution. + """ + fitness = sum((i + 1) * pw(x[i], 2) for i in range(len(x))) + return round(fitness, 3) + + def evaluate(self, x, out, *args, **kwargs): + """ + Evaluate the function for pymoo compatibility. + + Parameters + ---------- + x : numpy.ndarray + Array of input variables. + out : dict + Dictionary to store the computed fitness value. + + Notes + ----- + Stores the computed fitness value in `out["F"]`. """ - result = {} - self.evaluate(x, result) - return result["F"] + out["F"] = self.f(x) diff --git a/pycellga/problems/single_objective/continuous/schaffer.py b/pycellga/problems/single_objective/continuous/schaffer.py index 9c48ac3..9485c17 100644 --- a/pycellga/problems/single_objective/continuous/schaffer.py +++ b/pycellga/problems/single_objective/continuous/schaffer.py @@ -1,49 +1,61 @@ import numpy as np -from mpmath import power as pw from problems.abstract_problem import AbstractProblem +from common import GeneType class Schaffer(AbstractProblem): """ - Schaffer's Function for optimization problems. + Modified Schaffer function #1 for optimization problems. This class implements the Schaffer's function, a common benchmark problem for optimization algorithms. The function is defined over a multidimensional input and is used to test the performance of optimization methods. Attributes ---------- - design_variables : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically set to [(-100, 100), (-100, 100), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + gen_type : GeneType + The type of gene, set to REAL. + n_var : int + The number of design variables. + xl : float + The lower bound for the design variables, set to -100. + xu : float + The upper bound for the design variables, set to 100. Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Schaffer's function value for compatibility with pymoo. f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. + Computes the Schaffer's function value for a given list of variables. + evaluate(x, out, *args, **kwargs) + Wrapper for pymoo compatibility to calculate the fitness value. """ - def __init__(self, design_variables=2): - super().__init__( - design_variables=[f"x{i+1}" for i in range(design_variables)], - bounds=[(-100, 100) for _ in range(design_variables)], - objectives=["minimize"] - ) - self.design_variables_count = design_variables + def __init__(self, n_var=2): + """ + Initialize the Schaffer's problem. + + Parameters + ---------- + n_var : int, optional + The number of design variables, by default 2. + """ + gen_type = GeneType.REAL + xl = -100.0 + xu = 100.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x): """ - Evaluate the Schaffer's function for a given point using pymoo compatibility. + Compute the Schaffer's function value for a given solution. Parameters ---------- - x : numpy.ndarray + x : list or numpy.ndarray Array of input variables. - out : dict - Dictionary to store the output fitness value. + + Returns + ------- + float + The calculated fitness value. """ fitness = 0.0 for i in range(len(x) - 1): @@ -52,12 +64,4 @@ def evaluate(self, x, out, *args, **kwargs): term1 = np.sin(xi**2 + xi1**2)**2 term2 = 1 + 0.001 * (xi**2 + xi1**2) fitness += 0.5 + (term1 - 0.5)**2 / term2**2 - out["F"] = round(fitness, 3) - - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/schaffer2.py b/pycellga/problems/single_objective/continuous/schaffer2.py index 84e6f9c..9d4772f 100644 --- a/pycellga/problems/single_objective/continuous/schaffer2.py +++ b/pycellga/problems/single_objective/continuous/schaffer2.py @@ -1,58 +1,66 @@ import numpy as np from numpy import power as pw from problems.abstract_problem import AbstractProblem +from common import GeneType class Schaffer2(AbstractProblem): """ Modified Schaffer function #2 implementation for optimization problems. The Modified Schaffer function #2 is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-100, 100], for all i = 1, 2, ..., n. + The function is evaluated on the hypercube x_i ∈ [-100, 100], for all i = 1, 2, ..., n. Attributes ---------- - design_variables : int - The number of design variables. - bounds : list of tuple - The bounds for each variable, typically [(-100, 100), (-100, 100), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + n_var : int + The number of variables (dimensions) for the problem. + gen_type : GeneType + Type of genes used in the problem, fixed to REAL. + xl : float + Lower bounds for the variables, fixed to -100. + xu : float + Upper bounds for the variables, fixed to 100. Methods ------- - evaluate(x, out, *args, **kwargs) - Evaluates the Modified Schaffer function #2 value for a given list of variables. f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. + Compute the Modified Schaffer function #2 value for a single solution. + evaluate(x, out, *args, **kwargs) + Compute the fitness value(s) for pymoo's optimization framework. """ - def __init__(self, design_variables=2): - super().__init__(design_variables=[f"x{i+1}" for i in range(design_variables)], - bounds=[(-100, 100)] * design_variables, - objectives=["minimize"]) + def __init__(self, n_var: int = 2): + """ + Initialize the Modified Schaffer function #2. + + Parameters + ---------- + n_var : int, optional + Number of variables (dimensions) for the problem, by default 2. + """ + gen_type = GeneType.REAL + xl = -100.0 + xu = 100.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def evaluate(self, x, out, *args, **kwargs): + def f(self, x: list) -> float: """ - Evaluate the Modified Schaffer function #2 value for a given list of variables. + Compute the Modified Schaffer function #2 value for a single solution. Parameters ---------- - x : numpy.ndarray + x : list or numpy.ndarray Array of input variables. - out : dict - Dictionary to store the output fitness values. + + Returns + ------- + float + The computed fitness value for the given solution. """ fitness = 0.0 for i in range(len(x) - 1): term1 = np.sin(pw(x[i], 2) - pw(x[i + 1], 2)) ** 2 term2 = (1 + 0.001 * (pw(x[i], 2) + pw(x[i + 1], 2))) ** 2 fitness += 0.5 + ((term1 - 0.5) / term2) - out["F"] = round(fitness, 3) - - def f(self, x): - """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. - """ - result = {} - self.evaluate(x, result) - return result["F"] + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/schwefel.py b/pycellga/problems/single_objective/continuous/schwefel.py index 2512a8f..7eaa5c0 100644 --- a/pycellga/problems/single_objective/continuous/schwefel.py +++ b/pycellga/problems/single_objective/continuous/schwefel.py @@ -1,63 +1,66 @@ -from numpy import sin, sqrt +from numpy import sin, sqrt, abs from problems.abstract_problem import AbstractProblem +from common import GeneType class Schwefel(AbstractProblem): """ Schwefel function implementation for optimization problems. - This function is commonly used for testing optimization algorithms and is evaluated on the range - [-500, 500] for each variable. + The Schwefel function is commonly used for testing optimization algorithms. + It is evaluated on the range [-500, 500] for each variable and has a global minimum + at f(420.9687,...,420.9687) = 0. Attributes ---------- - design_variables : int - The number of variables in the problem. - bounds : list of tuple - The bounds for each variable, typically [(-500, 500), (-500, 500), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + n_var : int + The number of variables (dimensions) for the problem. + gen_type : GeneType + The type of genes used in the problem, fixed to REAL. + xl : float + The lower bounds for the variables, fixed to -500. + xu : float + The upper bounds for the variables, fixed to 500. Methods ------- - evaluate(x, out, *args, **kwargs) - Calculates the Schwefel function value for a given list of variables, compatible with pymoo. f(x: list) -> float - Alias for evaluate to maintain compatibility with the rest of the codebase. - """ + Compute the Schwefel function value for a single solution. - def __init__(self, design_variables=2): - bounds = [(-500, 500) for _ in range(design_variables)] - super().__init__(design_variables=design_variables, bounds=bounds, objectives=1) + Notes + ----- + -500 ≤ xi ≤ 500 for i = 1,…,n + Global minimum at f(420.9687,...,420.9687) = 0 + """ - def evaluate(self, x, out, *args, **kwargs): + def __init__(self, n_var: int = 2): """ - Calculate the Schwefel function value for a given list of variables. + Initialize the Schwefel function with the specified number of variables. Parameters ---------- - x : numpy.ndarray - A numpy array of float variables. - out : dict - Dictionary to store the output fitness values. + n_var : int, optional + The number of variables (dimensions) for the problem, by default 2. """ - d = len(x) - fitness = sum(xi * sin(sqrt(abs(xi))) for xi in x) - out["F"] = round((418.9829 * d) - fitness, 3) + gen_type = GeneType.REAL + xl = -500.0 + xu = 500.0 - def f(self, x): + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + + def f(self, x: list) -> float: """ - Alias for the evaluate method to maintain compatibility with the rest of the codebase. + Compute the Schwefel function value for a single solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Schwefel function value. + The computed fitness value for the given solution. """ - result = {} - self.evaluate(x, result) - return result["F"] + d = len(x) + fitness = sum(xi * sin(sqrt(abs(xi))) for xi in x) + return round((418.9829 * d) - fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/sphere.py b/pycellga/problems/single_objective/continuous/sphere.py index 0d44b94..6866d4a 100644 --- a/pycellga/problems/single_objective/continuous/sphere.py +++ b/pycellga/problems/single_objective/continuous/sphere.py @@ -1,53 +1,66 @@ from problems.abstract_problem import AbstractProblem +from common import GeneType class Sphere(AbstractProblem): """ Sphere function implementation for optimization problems. - The Sphere function is commonly used for testing optimization algorithms. + The Sphere function is a simple and commonly used benchmark for optimization algorithms. It is defined on a hypercube where each variable typically lies within [-5.12, 5.12]. + + Attributes + ---------- + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bounds for the variables (fixed to -5.12). + xu : float + Upper bounds for the variables (fixed to 5.12). + + Methods + ------- + f(x: list) -> float + Compute the Sphere function value for a single solution. + + Notes + ----- + -5.12 ≤ xi ≤ 5.12 for i = 1,…,n + Global minimum at f(0,...,0) = 0 """ - def __init__(self, design_variables=10): + def __init__(self, n_var: int = 10): """ - Initializes the Sphere function with specified design variables and bounds. - + Initialize the Sphere function with the specified number of variables. + Parameters ---------- - design_variables : int, optional - Number of variables for the function, by default 10. + n_var : int, optional + Number of variables (dimensions) in the problem, by default 10. """ - super().__init__(design_variables=design_variables, bounds=[(-5.12, 5.12)] * design_variables, objectives=["minimize"]) + gen_type = GeneType.REAL + xl = -5.12 + xu = 5.12 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: list) -> float: """ - Calculate the Sphere function value for a given list of variables. + Compute the Sphere function value for a single solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Sphere function value. + The computed fitness value for the given solution. """ if len(x) != self.n_var: raise ValueError(f"Input must have exactly {self.n_var} variables.") - - fitness = sum([xi**2 for xi in x]) - return round(fitness, 3) - def evaluate(self, x, out, *args, **kwargs): - """ - Evaluate function for compatibility with pymoo's optimizer. - - Parameters - ---------- - x : numpy.ndarray - Array of input variables. - out : dict - Dictionary to store the output fitness values. - """ - out["F"] = self.f(x) + fitness = sum(xi**2 for xi in x) + return round(fitness, 3) diff --git a/pycellga/problems/single_objective/continuous/styblinskitang.py b/pycellga/problems/single_objective/continuous/styblinskitang.py index 2cfc797..8a8a015 100644 --- a/pycellga/problems/single_objective/continuous/styblinskitang.py +++ b/pycellga/problems/single_objective/continuous/styblinskitang.py @@ -1,50 +1,77 @@ from problems.abstract_problem import AbstractProblem -from mpmath import power as pw +from common import GeneType class StyblinskiTang(AbstractProblem): """ Styblinski-Tang function implementation for optimization problems. - The Styblinski-Tang function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-5, 5], for all i = 1, 2, ..., n. + The Styblinski-Tang function is commonly used to test optimization algorithms. + It is defined over the range [-5, 5] for each variable and has a global minimum. + + Attributes + ---------- + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bounds for the variables (fixed to -5). + xu : float + Upper bounds for the variables (fixed to 5). + + Methods + ------- + f(x: list) -> float + Compute the Styblinski-Tang function value for a given solution. + + Notes + ----- + -5 ≤ xi ≤ 5 for i = 1,…,n + Global minimum at f(-2.903534, ..., -2.903534) ≈ -39.16599 * n_var """ - def __init__(self, design_variables=2): + def __init__(self, n_var: int = 2): """ - Initializes the Styblinski-Tang function with specified design variables and bounds. - + Initialize the Styblinski-Tang function with the specified number of variables. + Parameters ---------- - design_variables : int, optional - Number of variables for the function, by default 2. + n_var : int, optional + Number of variables (dimensions) in the problem, by default 2. """ - super().__init__(design_variables=design_variables, bounds=[(-5, 5)] * design_variables, objectives=["minimize"]) + gen_type = GeneType.REAL + xl = -5.0 + xu = 5.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: list) -> float: """ - Calculate the Styblinski-Tang function value for a given list of variables. + Compute the Styblinski-Tang function value for a given solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Styblinski-Tang function value. + The computed fitness value for the given solution. """ if len(x) != self.n_var: raise ValueError(f"Input must have exactly {self.n_var} variables.") - - fitness = sum(pw(xi, 4) - 16 * pw(xi, 2) + 5 * xi for xi in x) - fitness /= self.n_var + + fitness = sum(xi**4 - 16 * xi**2 + 5 * xi for xi in x) / self.n_var return round(fitness, 3) def evaluate(self, x, out, *args, **kwargs): """ Evaluate function for compatibility with pymoo's optimizer. + This method wraps the `f` method and allows pymoo to handle batch evaluations + by storing the computed fitness values in the output dictionary. + Parameters ---------- x : numpy.ndarray diff --git a/pycellga/problems/single_objective/continuous/sumofdifferentpowers.py b/pycellga/problems/single_objective/continuous/sumofdifferentpowers.py index 633764c..90c7a4f 100644 --- a/pycellga/problems/single_objective/continuous/sumofdifferentpowers.py +++ b/pycellga/problems/single_objective/continuous/sumofdifferentpowers.py @@ -1,27 +1,29 @@ +from problems.abstract_problem import AbstractProblem +from common import GeneType import numpy as np from mpmath import power as pw -from problems.abstract_problem import AbstractProblem - class Sumofdifferentpowers(AbstractProblem): """ Sum of Different Powers function implementation for optimization problems. - The Sum of Different Powers function is often used for testing optimization algorithms. - It is usually evaluated within the bounds x_i ∈ [-1, 1] for each variable. + The Sum of Different Powers function is commonly used to test optimization algorithms. + It is defined over the range [-1, 1] for each variable, with a global minimum of 0 at the origin. Attributes ---------- n_var : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-1, 1), (-1, 1), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (fixed to REAL). + xl : float + Lower bounds for the variables (fixed to -1). + xu : float + Upper bounds for the variables (fixed to 1). Methods ------- f(x: list) -> float - Calculates the Sum of Different Powers function value for a given list of variables. + Compute the Sum of Different Powers function value for a given solution. Notes ----- @@ -29,24 +31,34 @@ class Sumofdifferentpowers(AbstractProblem): Global minimum at f(0,...,0) = 0. """ - def __init__(self, design_variables=2): - bounds = [(-1, 1) for _ in range(design_variables)] - objectives = ["minimize"] - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var: int = 2): + """ + Initialize the Sum of Different Powers problem. + + Parameters + ---------- + n_var : int, optional + Number of variables (dimensions) in the problem, by default 2. + """ + gen_type = GeneType.REAL + xl = -1.0 + xu = 1.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: list) -> float: """ - Calculate the Sum of Different Powers function value for a given list of variables. + Compute the Sum of Different Powers function value for a given solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Sum of Different Powers function value. + The computed fitness value for the given solution. """ if len(x) != self.n_var: raise ValueError(f"Input must have exactly {self.n_var} variables.") @@ -56,7 +68,7 @@ def f(self, x: list) -> float: def evaluate(self, x, out, *args, **kwargs): """ - Evaluate method for compatibility with pymoo's framework. + Evaluate function for compatibility with pymoo's optimizer. Parameters ---------- diff --git a/pycellga/problems/single_objective/continuous/threehumps.py b/pycellga/problems/single_objective/continuous/threehumps.py index ddc13bb..164826c 100644 --- a/pycellga/problems/single_objective/continuous/threehumps.py +++ b/pycellga/problems/single_objective/continuous/threehumps.py @@ -1,44 +1,56 @@ from mpmath import power as pw from problems.abstract_problem import AbstractProblem +from common import GeneType + class Threehumps(AbstractProblem): """ Three Hump Camel function implementation for optimization problems. - The Three Hump Camel function is widely used for testing optimization algorithms. - The function is usually evaluated on the hypercube x_i ∈ [-5, 5], for all i = 1, 2, ..., n. + The Three Hump Camel function is commonly used for testing optimization algorithms. + It is defined for two variables within the bounds [-5, 5]. Attributes ---------- - bounds : list of tuple - Bounds for each variable, set to [(-5, 5), (-5, 5)] for this function. - design_variables : int - Number of variables for this problem, which is 2. - objectives : int - Number of objectives, which is 1 for single-objective optimization. + n_var : int + Number of variables (dimensions) for the problem, fixed to 2. + gen_type : GeneType + Type of genes used in the problem (REAL). + xl : float + Lower bounds for the variables, fixed to -5. + xu : float + Upper bounds for the variables, fixed to 5. Methods ------- f(x: list) -> float - Calculates the Three Hump Camel function value for a given list of variables. + Compute the Three Hump Camel function value for a given solution. """ def __init__(self): - super().__init__(design_variables=2, bounds=[(-5, 5), (-5, 5)], objectives=["minimize"]) + """ + Initialize the Three Hump Camel problem. + """ + gen_type = GeneType.REAL + n_var = 2 + xl = -5.0 + xu = 5.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: list) -> float: """ - Calculate the Three Hump Camel function value for a given list of variables. + Compute the Three Hump Camel function value for a given solution. Parameters ---------- - x : list - A list of float variables. + x : list or numpy.ndarray + Array of input variables. Returns ------- float - The Three Hump Camel function value. + The computed fitness value for the given solution. """ if len(x) != self.n_var: raise ValueError(f"Input must have exactly {self.n_var} variables.") diff --git a/pycellga/problems/single_objective/continuous/zakharov.py b/pycellga/problems/single_objective/continuous/zakharov.py index d7dce70..f24d34f 100644 --- a/pycellga/problems/single_objective/continuous/zakharov.py +++ b/pycellga/problems/single_objective/continuous/zakharov.py @@ -1,37 +1,53 @@ -from problems.abstract_problem import AbstractProblem from mpmath import power as pw -from typing import List, Any +from typing import List +from problems.abstract_problem import AbstractProblem +from common import GeneType + class Zakharov(AbstractProblem): """ Zakharov function implementation for optimization problems. - The Zakharov function is commonly used to test optimization algorithms. - It evaluates inputs over the hypercube x_i ∈ [-5, 10]. + The Zakharov function is widely used for testing optimization algorithms. + It is evaluated on the hypercube x_i ∈ [-5, 10] for all variables. Attributes ---------- - design_variables : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-5, 10), (-5, 10), ...]. - objectives : list - Objectives for the problem, set to ["minimize"] for single-objective optimization. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (REAL). + xl : float + Lower bounds for the variables, fixed to -5. + xu : float + Upper bounds for the variables, fixed to 10. Methods ------- f(x: list) -> float - Calculates the Zakharov function value for a given list of variables. + Compute the Zakharov function value for a given solution. + evaluate(x: list, out: dict, *args, **kwargs) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=2): - bounds = [(-5, 10) for _ in range(design_variables)] - objectives = ["minimize"] - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var: int = 2): + """ + Initialize the Zakharov problem. + + Parameters + ---------- + n_var : int, optional + Number of variables (dimensions) for the problem, by default 2. + """ + gen_type = GeneType.REAL + xl = -5.0 + xu = 10.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: List[float]) -> float: """ - Calculate the Zakharov function value for a given list of variables. + Compute the Zakharov function value for a given solution. Parameters ---------- @@ -43,15 +59,18 @@ def f(self, x: List[float]) -> float: float The Zakharov function value. """ + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") + fitness1 = sum(pw(xi, 2) for xi in x) fitness2 = pw(sum(0.5 * (i + 1) * xi for i, xi in enumerate(x)), 2) fitness3 = pw(sum(0.5 * (i + 1) * xi for i, xi in enumerate(x)), 4) fitness = fitness1 + fitness2 + fitness3 return round(fitness, 3) - def evaluate(self, x: List[float], out: dict, *args: Any, **kwargs: Any) -> None: + def evaluate(self, x: List[float], out: dict, *args, **kwargs) -> None: """ - Evaluate function for compatibility with pymoo's optimizer. + Evaluate method for compatibility with pymoo's framework. Parameters ---------- diff --git a/pycellga/problems/single_objective/continuous/zettle.py b/pycellga/problems/single_objective/continuous/zettle.py index 5796380..450bb65 100644 --- a/pycellga/problems/single_objective/continuous/zettle.py +++ b/pycellga/problems/single_objective/continuous/zettle.py @@ -1,41 +1,53 @@ -from problems.abstract_problem import AbstractProblem from mpmath import power as pw +from typing import List +from problems.abstract_problem import AbstractProblem +from common import GeneType + class Zettle(AbstractProblem): """ Zettle function implementation for optimization problems. The Zettle function is widely used for testing optimization algorithms. - It is usually evaluated on the hypercube x_i ∈ [-5, 5] for all i = 1, 2, ..., n. + It is typically evaluated on the hypercube x_i ∈ [-5, 5]. Attributes ---------- - design_variables : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable, typically [(-5, 5), (-5, 5), ...]. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + n_var : int + Number of variables (dimensions) in the problem. + gen_type : GeneType + Type of genes used in the problem (REAL). + xl : float + Lower bounds for the variables, fixed to -5. + xu : float + Upper bounds for the variables, fixed to 5. Methods ------- f(x: list) -> float - Calculates the Zettle function value for a given list of variables. - - Notes - ----- - -5 ≤ xi ≤ 5 for i = 1,…,n - Global minimum at f(-0.0299, 0) = -0.003791 + Compute the Zettle function value for a given solution. + evaluate(x: list, out: dict, *args, **kwargs) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=2): - super().__init__(design_variables=design_variables, - bounds=[(-5, 5) for _ in range(design_variables)], - objectives=["minimize"]) + def __init__(self, n_var: int = 2): + """ + Initialize the Zettle problem. + + Parameters + ---------- + n_var : int, optional + Number of variables (dimensions) for the problem, by default 2. + """ + gen_type = GeneType.REAL + xl = -5.0 + xu = 5.0 + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def f(self, x: list) -> float: + def f(self, x: List[float]) -> float: """ - Calculate the Zettle function value for a given list of variables. + Compute the Zettle function value for a given solution. Parameters ---------- @@ -53,5 +65,18 @@ def f(self, x: list) -> float: fitness = 0.0 for i in range(len(x) - 1): fitness += pw((pw(x[i], 2) + pw(x[i + 1], 2)) - 2 * x[i], 2) + 0.25 * x[i] - - return round(fitness, 6) \ No newline at end of file + + return round(fitness, 6) + + def evaluate(self, x: List[float], out: dict, *args, **kwargs) -> None: + """ + Evaluate method for compatibility with pymoo's framework. + + Parameters + ---------- + x : numpy.ndarray + Array of input variables. + out : dict + Dictionary to store the output fitness values. + """ + out["F"] = self.f(x) diff --git a/pycellga/problems/single_objective/discrete/binary/count_sat.py b/pycellga/problems/single_objective/discrete/binary/count_sat.py index 39b4c0d..c498e17 100644 --- a/pycellga/problems/single_objective/discrete/binary/count_sat.py +++ b/pycellga/problems/single_objective/discrete/binary/count_sat.py @@ -1,4 +1,6 @@ from problems.abstract_problem import AbstractProblem +from common import GeneType +from typing import List class CountSat(AbstractProblem): """ @@ -8,25 +10,39 @@ class CountSat(AbstractProblem): Attributes ---------- - design_variables : int + n_var : int The number of variables (chromosome length) for the problem. - bounds : list of tuple - The bounds for each binary variable, typically [(0, 1), (0, 1), ...] for binary inputs. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + gen_type : GeneType + The type of genes used in the problem, set to BINARY. + xl : int + Lower bounds for binary variables, fixed to 0. + xu : int + Upper bounds for binary variables, fixed to 1. Methods ------- - f(x: list) -> float + f(x: List[int]) -> float Calculates the CountSat function value for a given list of binary variables. + evaluate(x: List[int], out: dict, *args, **kwargs) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=20): - super().__init__(design_variables=design_variables, - bounds=[(0, 1) for _ in range(design_variables)], - objectives=["maximize"]) + def __init__(self, n_var: int = 20): + """ + Initialize the CountSat problem. + + Parameters + ---------- + n_var : int, optional + Number of binary variables (chromosome length) for the problem, by default 20. + """ + gen_type = GeneType.BINARY + xl = 0 # Lower bound for binary variables + xu = 1 # Upper bound for binary variables + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def f(self, x: list) -> float: + def f(self, x: List[int]) -> float: """ Calculate the CountSat function value for a given list of binary variables. @@ -47,16 +63,18 @@ def f(self, x: list) -> float: variables = len(x) # Calculate the fitness based on the CountSat formula - fitness = (total_ones + - (variables * (variables - 1) * (variables - 2)) - - ((variables - 2) * total_ones * (total_ones - 1)) + - (total_ones * (total_ones - 1) * (total_ones - 2))) + fitness = ( + total_ones + + (variables * (variables - 1) * (variables - 2)) + - ((variables - 2) * total_ones * (total_ones - 1)) + + (total_ones * (total_ones - 1) * (total_ones - 2)) + ) # Normalize the fitness value fitness_normalized = fitness / 6860 return round(fitness_normalized, 3) - def evaluate(self, x, out, *args, **kwargs): + def evaluate(self, x: List[int], out: dict, *args, **kwargs) -> None: """ Evaluate function for compatibility with pymoo's optimizer. diff --git a/pycellga/problems/single_objective/discrete/binary/ecc.py b/pycellga/problems/single_objective/discrete/binary/ecc.py index 86b2928..3d90cda 100644 --- a/pycellga/problems/single_objective/discrete/binary/ecc.py +++ b/pycellga/problems/single_objective/discrete/binary/ecc.py @@ -1,4 +1,6 @@ from problems.abstract_problem import AbstractProblem +from common import GeneType +from typing import List class Ecc(AbstractProblem): """ @@ -9,21 +11,41 @@ class Ecc(AbstractProblem): Attributes ---------- - design_variables : int + n_var : int Number of binary variables, typically 144. - bounds : list of tuple - Bounds for each variable, typically [(0, 1)] * 144 for binary inputs. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + gen_type : GeneType + The type of genes used in the problem, set to BINARY. + xl : int + Lower bounds for binary variables, fixed to 0. + xu : int + Upper bounds for binary variables, fixed to 1. + + Methods + ------- + f(x: List[int]) -> float + Calculates the ECC function value for a given list of binary variables. + evaluate(x: List[int], out: dict, *args, **kwargs) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=144): - bounds = [(0, 1) for _ in range(design_variables)] # Binary bounds for each variable - super().__init__(design_variables=design_variables, bounds=bounds, objectives=[1]) + def __init__(self, n_var: int = 144): + """ + Initialize the ECC problem. + + Parameters + ---------- + n_var : int, optional + Number of binary variables for the problem, by default 144. + """ + gen_type = GeneType.BINARY + xl = 0 # Lower bound for binary variables + xu = 1 # Upper bound for binary variables + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def f(self, x: list) -> float: + def f(self, x: List[int]) -> float: """ - Calculate the ECC function value for a given list of variables. + Calculate the ECC function value for a given list of binary variables. Parameters ---------- @@ -35,19 +57,39 @@ def f(self, x: list) -> float: float The ECC function value, rounded to four decimal places. """ + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") + individual_length = 12 # Length of individual code segments - half_code = 12 # Number of code segments to compare + half_code = self.n_var // individual_length # Number of code segments partial_fitness = 0.0 # Accumulated partial fitness value for i in range(half_code): for j in range(i + 1, half_code): # Avoid double-counting pairs - hamming = sum(x[i * individual_length + k] != x[j * individual_length + k] for k in range(individual_length)) + hamming = sum( + x[i * individual_length + k] != x[j * individual_length + k] + for k in range(individual_length) + ) if 0 < hamming < individual_length: - partial_fitness += (1.0 / (hamming * hamming) + - 1.0 / ((individual_length - hamming) * - (individual_length - hamming))) + partial_fitness += ( + 1.0 / (hamming * hamming) + + 1.0 / ((individual_length - hamming) * (individual_length - hamming)) + ) # Calculate final fitness value fitness = 1.0 / (2 * partial_fitness) if partial_fitness != 0 else 0.0 return round(fitness, 4) + + def evaluate(self, x: List[int], out: dict, *args, **kwargs) -> None: + """ + Evaluate function for compatibility with pymoo's optimizer. + + Parameters + ---------- + x : numpy.ndarray + Array of input variables. + out : dict + Dictionary to store the output fitness values. + """ + out["F"] = self.f(x) diff --git a/pycellga/problems/single_objective/discrete/binary/fms.py b/pycellga/problems/single_objective/discrete/binary/fms.py index 04c0f0f..a34b222 100644 --- a/pycellga/problems/single_objective/discrete/binary/fms.py +++ b/pycellga/problems/single_objective/discrete/binary/fms.py @@ -1,5 +1,7 @@ from problems.abstract_problem import AbstractProblem from numpy import pi, sin, random +from common import GeneType +from typing import List class Fms(AbstractProblem): """ @@ -9,31 +11,41 @@ class Fms(AbstractProblem): Attributes ---------- - design_variables : int - The number of variables for the problem. - bounds : list of tuple - The bounds for each variable. - objectives : int - Number of objectives, set to 1 for single-objective optimization. + n_var : int + The number of binary variables for the problem, typically 192. + gen_type : GeneType + The type of genes used in the problem, set to BINARY. + xl : int + Lower bounds for binary variables, fixed to 0. + xu : int + Upper bounds for binary variables, fixed to 1. Methods ------- - f(x: list) -> float - Calculates the FMS function value for a given list of variables. - - Notes - ----- - Length of chromosomes = 192 - Maximum Fitness Value = 0.01 - Maximum Fitness Value Error = 10^-2 + f(x: List[int]) -> float + Calculates the FMS function value for a given list of binary variables. + evaluate(x: List[int], out: dict, *args, **kwargs) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=192, bounds=[(0, 1)] * 192, objectives=1): - super().__init__(design_variables, bounds, objectives) + def __init__(self, n_var: int = 192): + """ + Initialize the FMS problem. + + Parameters + ---------- + n_var : int, optional + Number of binary variables for the problem, by default 192. + """ + gen_type = GeneType.BINARY + xl = 0 # Lower bound for binary variables + xu = 1 # Upper bound for binary variables + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - def f(self, x: list) -> float: + def f(self, x: List[int]) -> float: """ - Calculate the FMS function value for a given list of variables. + Calculate the FMS function value for a given list of binary variables. Parameters ---------- @@ -45,42 +57,37 @@ def f(self, x: list) -> float: float The FMS function value. """ + if len(x) != self.n_var: + raise ValueError(f"Input must have exactly {self.n_var} variables.") + theta = (2.0 * pi) / 100.0 random.seed(100) - # Initialize integer values for parameters - a1_int, w1_int, a2_int, w2_int, a3_int, w3_int = 0, 0, 0, 0, 0, 0 - - # Convert segments of x to integer parameters - for i in range(32): - a1_int = (a1_int << 1) | x[i] - w1_int = (w1_int << 1) | x[i + 32] - a2_int = (a2_int << 1) | x[i + 64] - w2_int = (w2_int << 1) | x[i + 96] - a3_int = (a3_int << 1) | x[i + 128] - w3_int = (w3_int << 1) | x[i + 160] - - # Map integer values to continuous values for each parameter - a1 = -6.4 + (12.75 * (a1_int / 4294967295.0)) - w1 = -6.4 + (12.75 * (w1_int / 4294967295.0)) - a2 = -6.4 + (12.75 * (a2_int / 4294967295.0)) - w2 = -6.4 + (12.75 * (w2_int / 4294967295.0)) - a3 = -6.4 + (12.75 * (a3_int / 4294967295.0)) - w3 = -6.4 + (12.75 * (w3_int / 4294967295.0)) - - # Generate target signal based on predefined parameters - target = [sin((5.0 * theta * i) - (1.5 * sin((4.8 * theta * i) + (2.0 * sin(4.9 * theta * i))))) - for i in range(101)] - - # Generate signal y based on the parameters derived from x - y = [a1 * sin((w1 * theta * j) - (a2 * sin((w2 * theta * j) + (a3 * sin(w3 * theta * j))))) - for j in range(101)] - - # Calculate fitness as the mean squared error between target and y + # Decode binary variables into continuous parameters + def decode_segment(segment): + value = 0 + for bit in segment: + value = (value << 1) | bit + return -6.4 + (12.75 * (value / 4294967295.0)) + + a1 = decode_segment(x[:32]) + w1 = decode_segment(x[32:64]) + a2 = decode_segment(x[64:96]) + w2 = decode_segment(x[96:128]) + a3 = decode_segment(x[128:160]) + w3 = decode_segment(x[160:192]) + + # Generate target signal + target = [sin((5.0 * theta * i) - (1.5 * sin((4.8 * theta * i) + (2.0 * sin(4.9 * theta * i))))) for i in range(101)] + + # Generate predicted signal + y = [a1 * sin((w1 * theta * j) - (a2 * sin((w2 * theta * j) + (a3 * sin(w3 * theta * j))))) for j in range(101)] + + # Compute mean squared error fitness = sum((target[k] - y[k]) ** 2 for k in range(101)) return round(fitness, 3) - def evaluate(self, x, out, *args, **kwargs): + def evaluate(self, x: List[int], out: dict, *args, **kwargs) -> None: """ Evaluate function for compatibility with pymoo's optimizer. diff --git a/pycellga/problems/single_objective/discrete/binary/maxcut100.py b/pycellga/problems/single_objective/discrete/binary/maxcut100.py index 849628c..9946fea 100644 --- a/pycellga/problems/single_objective/discrete/binary/maxcut100.py +++ b/pycellga/problems/single_objective/discrete/binary/maxcut100.py @@ -1,21 +1,17 @@ - from problems.abstract_problem import AbstractProblem +from common import GeneType + class Maxcut100(AbstractProblem): """ A class to represent the Maximum Cut (MAXCUT) problem for 100 nodes. - + Attributes ---------- problema : list of list of float A matrix representing the weights between nodes in the MAXCUT problem. - - Methods - ------- - f(x: list) -> float - Calculates the fitness value of a given chromosome for the Maxcut problem. """ - + def __init__(self): self.problema = [ @@ -221,19 +217,28 @@ def __init__(self): 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 10.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 10.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000], ] - def f(self, x: list) -> float: + + n_var = 100 + xl = 0 + xu = 1 + gen_type = GeneType.BINARY + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + + def f(self, x): """ - Calculates the fitness value of a given chromosome for the Maxcut problem. + Fitness function for the Maxcut problem. + Calculates the cut value of the given binary solution vector `x`. Parameters ---------- - x : list - A list representing a chromosome. + x : list of int + Binary vector representing the solution. Returns ------- float - The fitness value of the chromosome. + The fitness value (cut value). """ fitness = 0.0 n = len(self.problema) @@ -244,3 +249,17 @@ def f(self, x: list) -> float: fitness += self.problema[i][j] return fitness + + def evaluate(self, x, out, *args, **kwargs): + """ + Evaluates the Maxcut problem for a given solution `x`. + + Parameters + ---------- + x : ndarray + Decision variable matrix (each row is a solution vector). + out : dict + Output dictionary where results will be stored. + """ + # Fitness değerlerini hesapla ve çıktıya ekle + out["F"] = [self.f(ind) for ind in x] diff --git a/pycellga/problems/single_objective/discrete/binary/maxcut20_01.py b/pycellga/problems/single_objective/discrete/binary/maxcut20_01.py index f01846a..b7ac31d 100644 --- a/pycellga/problems/single_objective/discrete/binary/maxcut20_01.py +++ b/pycellga/problems/single_objective/discrete/binary/maxcut20_01.py @@ -1,4 +1,6 @@ from problems.abstract_problem import AbstractProblem +from common import GeneType +from typing import List class Maxcut20_01(AbstractProblem): """ @@ -16,12 +18,20 @@ class Maxcut20_01(AbstractProblem): ------- f(x: list) -> float Calculates the MAXCUT function value for a given list of binary variables. + evaluate(x: list, out: dict) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=20, bounds=None, objectives=1): - if bounds is None: - bounds = [(0, 1)] * design_variables # Binary bounds for each variable - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self): + """ + Initialize the MAXCUT problem with binary variables and adjacency matrix. + """ + n_var = 20 # Number of binary variables (nodes) + xl = 0 # Lower bound for binary variables + xu = 1 # Upper bound for binary variables + gen_type = GeneType.BINARY + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) # Define adjacency matrix (20x20 matrix of edge weights) self.problema = [ @@ -67,7 +77,7 @@ def __init__(self, design_variables=20, bounds=None, objectives=1): 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000] ] - def f(self, x: list) -> float: + def f(self, x: List[int]) -> float: """ Calculate the MAXCUT function value for a given list of binary variables. @@ -91,7 +101,7 @@ def f(self, x: list) -> float: return round(fitness, 6) - def evaluate(self, x, out, *args, **kwargs): + def evaluate(self, x: List[int], out: dict, *args, **kwargs) -> None: """ Evaluate function for compatibility with pymoo's optimizer. @@ -103,6 +113,3 @@ def evaluate(self, x, out, *args, **kwargs): Dictionary to store the output fitness values. """ out["F"] = self.f(x) - - - diff --git a/pycellga/problems/single_objective/discrete/binary/maxcut20_09.py b/pycellga/problems/single_objective/discrete/binary/maxcut20_09.py index 2a45c20..1d7b775 100644 --- a/pycellga/problems/single_objective/discrete/binary/maxcut20_09.py +++ b/pycellga/problems/single_objective/discrete/binary/maxcut20_09.py @@ -1,10 +1,13 @@ from problems.abstract_problem import AbstractProblem +from common import GeneType +from typing import List + class Maxcut20_09(AbstractProblem): """ Maximum Cut (MAXCUT) function for optimization on a 20-node graph. - This class is used to evaluate the cut value by summing weights of edges + This class evaluates the cut value by summing weights of edges between nodes in different partitions defined by binary variables. Attributes @@ -14,15 +17,23 @@ class Maxcut20_09(AbstractProblem): Methods ------- - f(x: list) -> float + f(x: List[int]) -> float Calculates the MAXCUT function value for a given list of binary variables. + evaluate(x: List[int], out: dict) -> None + Pymoo-compatible evaluation method for batch processing. """ - def __init__(self, design_variables=20, bounds=None, objectives=1): - if bounds is None: - bounds = [(0, 1) for _ in range(design_variables)] - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) - + def __init__(self): + """ + Initialize the MAXCUT problem with binary variables and adjacency matrix. + """ + n_var = 20 # Number of binary variables (nodes) + xl = 0 # Lower bound for binary variables + xu = 1 # Upper bound for binary variables + gen_type = GeneType.BINARY + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + # Define adjacency matrix (20x20 matrix of edge weights) self.problema = [ [0.000000, 0.130622, 0.694577, 0.922028, 0.335786, 0.359902, 0.279580, 0.880418, 0.201529, 0.313702, @@ -67,13 +78,13 @@ def __init__(self, design_variables=20, bounds=None, objectives=1): 0.978178, 0.424806, 0.651577, 0.690291, 0.159191, 0.955731, 0.067050, 0.752926, 0.224879, 0.000000] ] - def f(self, x: list) -> float: + def f(self, x: List[int]) -> float: """ Calculate the MAXCUT function value for a given list of binary variables. - + Parameters ---------- - x : list + x : List[int] A list of binary variables representing node partitions. Returns @@ -91,10 +102,10 @@ def f(self, x: list) -> float: return round(fitness, 6) - def evaluate(self, x, out, *args, **kwargs): + def evaluate(self, x: List[int], out: dict, *args, **kwargs) -> None: """ Evaluate function for compatibility with pymoo's optimizer. - + Parameters ---------- x : numpy.ndarray diff --git a/pycellga/problems/single_objective/discrete/binary/mmdp.py b/pycellga/problems/single_objective/discrete/binary/mmdp.py index c2017c6..414f947 100644 --- a/pycellga/problems/single_objective/discrete/binary/mmdp.py +++ b/pycellga/problems/single_objective/discrete/binary/mmdp.py @@ -1,5 +1,7 @@ from problems.abstract_problem import AbstractProblem -from typing import List, Tuple +from typing import List +from common import GeneType + class Mmdp(AbstractProblem): """ @@ -11,37 +13,32 @@ class Mmdp(AbstractProblem): Attributes ---------- - design_variables : List[str] - Names of the design variables (in this case, binary chromosome genes). - bounds : List[Tuple[float, float]] - Bounds for each design variable (0 or 1). - objectives : List[str] - Objectives for optimization, e.g., "maximize" in this case. - constraints : List[str] - Any constraints for the optimization problem. + gen_type : GeneType + Type of genes used in the problem (binary in this case). + n_var : int + The number of design variables (240 for MMDP). + xl : float + The lower bound for the design variables (0 for binary genes). + xu : float + The upper bound for the design variables (1 for binary genes). Methods ------- f(x: list) -> float Evaluates the fitness of a given chromosome. - - Notes - ----- - # Length of chromosomes = 240 - # Maximum Fitness Value = 40 """ def __init__(self): """ - Initializes the MMDP problem with predefined design variables, bounds, - objectives, and constraints. + Initializes the MMDP problem with binary genes, 240 design variables, + and predefined bounds. """ - design_variables = ["gene" + str(i) for i in range(240)] - bounds = [(0, 1) for _ in range(240)] - objectives = ["maximize"] - constraints = [] + n_var = 240 + xl = 0 + xu = 1 + gen_type=GeneType.BINARY - super().__init__(design_variables, bounds, objectives, constraints) + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: List[int]) -> float: """ diff --git a/pycellga/problems/single_objective/discrete/binary/one_max.py b/pycellga/problems/single_objective/discrete/binary/one_max.py index 442e49b..961ae88 100644 --- a/pycellga/problems/single_objective/discrete/binary/one_max.py +++ b/pycellga/problems/single_objective/discrete/binary/one_max.py @@ -1,5 +1,7 @@ from problems.abstract_problem import AbstractProblem -from typing import List, Tuple +from typing import List +from common import GeneType + class OneMax(AbstractProblem): """ @@ -10,14 +12,14 @@ class OneMax(AbstractProblem): Attributes ---------- - design_variables : int - Number of design variables (chromosome length). - bounds : List[Tuple[float, float]] - Bounds for each design variable as (min, max). - objectives : List[str] - Objectives for optimization, e.g., "maximize". - constraints : List[str] - Any constraints for the optimization problem. + gen_type : GeneType + Type of genes used in the problem (binary in this case). + n_var : int + The number of design variables (default is 100). + xl : float + The lower bound for the design variables (0 for binary genes). + xu : float + The upper bound for the design variables (1 for binary genes). Methods ------- @@ -25,27 +27,21 @@ class OneMax(AbstractProblem): Evaluates the fitness of a given chromosome. """ - def __init__(self, - design_variables: int = 100, - bounds: List[Tuple[float, float]] = [(0, 1)] * 100, - objectives: List[str] = ["maximize"], - constraints: List[str] = []): + def __init__(self, n_var: int = 100): """ - Initialize the OneMax problem with default design variables, bounds, - objectives, and optional constraints. + Initialize the OneMax problem with a default number of variables (100) + and binary gene bounds. Parameters ---------- - design_variables : int, optional + n_var : int, optional Number of design variables (default is 100). - bounds : List[Tuple[float, float]], optional - Bounds for each design variable in (min, max) format (default is [(0, 1)] * 100). - objectives : List[str], optional - Objectives for optimization, e.g., "maximize" (default is ["maximize"]). - constraints : List[str], optional - Constraints for the problem (default is an empty list). """ - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives, constraints=constraints) + xl = 0 + xu = 1 + gen_type=GeneType.BINARY + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) def f(self, x: List[int]) -> float: """ diff --git a/pycellga/problems/single_objective/discrete/binary/peak.py b/pycellga/problems/single_objective/discrete/binary/peak.py index 41b55bf..b6347db 100644 --- a/pycellga/problems/single_objective/discrete/binary/peak.py +++ b/pycellga/problems/single_objective/discrete/binary/peak.py @@ -1,6 +1,8 @@ from problems.abstract_problem import AbstractProblem from numpy import random -from typing import List, Tuple +from typing import List +from common import GeneType + class Peak(AbstractProblem): """ @@ -11,36 +13,41 @@ class Peak(AbstractProblem): Attributes ---------- - design_variables : List[str] - Names of the design variables. - bounds : List[Tuple[float, float]] - Bounds for each design variable as (min, max). - objectives : List[str] - Objectives for optimization, e.g., "minimize" or "maximize". - constraints : List[str] - Any constraints for the optimization problem. + gen_type : GeneType + Type of genes used in the problem (binary in this case). + n_var : int + The number of design variables (chromosome length, default is 100). + xl : float + The lower bounds for the design variables (0 for binary genes). + xu : float + The upper bounds for the design variables (1 for binary genes). Methods ------- f(x: list) -> float Evaluates the fitness of a given chromosome. - - Notes - ----- - # Length of chromosomes = 100 - # Maximum Fitness Value = 1.0 """ - def __init__(self): + def __init__(self, n_var: int = 100): """ - Initializes the Peak problem with default values. + Initialize the Peak problem with a default number of variables (100) + and binary gene bounds. + + Parameters + ---------- + n_var : int, optional + Number of design variables (default is 100). """ - design_variables = ["x" + str(i) for i in range(100)] - bounds = [(0, 1) for _ in range(100)] # Each gene is binary (0 or 1) - objectives = ["maximize"] # Aim to maximize fitness value - constraints = [] # No additional constraints + xl = 0 + xu = 1 + gen_type=GeneType.BINARY + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) - super().__init__(design_variables, bounds, objectives, constraints) + # Seed the random number generator for reproducibility + random.seed(100) + self.p_target = [ + [random.randint(2) for _ in range(n_var)] for _ in range(100) + ] # 100 target peaks def f(self, x: List[int]) -> float: """ @@ -60,37 +67,14 @@ def f(self, x: List[int]) -> float: float The fitness value of the chromosome, normalized to a range of 0.0 to 1.0. """ - - # Seed the random number generator for reproducibility - random.seed(100) - problem_length = len(x) - number_of_peaks = 100 - - # Generate target peaks - p_target = [[random.randint(2) for _ in range(problem_length)] for _ in range(number_of_peaks)] + min_distance = float("inf") - # Calculate the distance to the nearest peak - min_distance = float('inf') - for peak in p_target: + for peak in self.p_target: distance = sum(1 for xi, pi in zip(x, peak) if xi != pi) if distance < min_distance: min_distance = distance # Normalize the fitness value - fitness = min_distance / problem_length - + fitness = 1 - (min_distance / problem_length) # 1 - normalized distance return round(fitness, 3) - - def evaluate(self, x, out, *args, **kwargs): - """ - Evaluate function for compatibility with pymoo's optimizer. - - Parameters - ---------- - x : numpy.ndarray - Array of input variables. - out : dict - Dictionary to store the output fitness values. - """ - out["F"] = self.f(x) diff --git a/pycellga/problems/single_objective/discrete/permutation/tsp.py b/pycellga/problems/single_objective/discrete/permutation/tsp.py index d79fabf..0f1884d 100644 --- a/pycellga/problems/single_objective/discrete/permutation/tsp.py +++ b/pycellga/problems/single_objective/discrete/permutation/tsp.py @@ -3,7 +3,9 @@ from math import sqrt from geopy.distance import geodesic import os -from typing import List, Tuple +from typing import List +from common import GeneType + class Tsp(AbstractProblem): """ @@ -13,33 +15,55 @@ class Tsp(AbstractProblem): Attributes ---------- - design_variables : list - Names of the design variables (nodes in this case). - bounds : list of tuples - Bounds for each design variable as (min, max). - objectives : list - Objectives for optimization, e.g., "minimize" or "maximize". - constraints : list - Constraints for the optimization problem. - - Notes - ----- - - Uses geographical distance function (GEO) for evaluating routes. - - Example problem: burma14.tsp, with a known minimum distance. + gen_type : GeneType + The type of genes used in the problem (permutation in this case). + n_var : int + The number of nodes in the TSP problem. + xl : int + The minimum value for each variable (1 in this case, node index starts at 1). + xu : int + The maximum value for each variable (number of nodes). """ - def __init__(self): + def __init__(self, n_var: int = 14): """ Initialize the TSP problem with default attributes. - Uses the 'burma14' TSP dataset as an example with 14 nodes. + Parameters + ---------- + n_var : int, optional + Number of nodes in the TSP problem (default is 14). + """ + xl = [1] + xu = [14] + gen_type=GeneType.PERMUTATION + + super().__init__(gen_type=gen_type, n_var=n_var, xl=xl, xu=xu) + + # Load TSP data file + file_path = os.path.join(os.path.dirname(__file__), "burma14.tsp.txt") + with open(file_path) as fl: + self.problem = tsplib95.read(fl) + self.node_coords = list(self.problem.node_coords.values()) + + # Precompute distances + self.distances = self._compute_distances() + + def _compute_distances(self): """ - design_variables = ["node" + str(i) for i in range(1, 15)] - bounds = [(1, 14) for _ in range(14)] # Nodes range from 1 to 14 - objectives = ["minimize"] - constraints = [] + Precomputes the geographical distances between all node pairs. - super().__init__(design_variables, bounds, objectives, constraints) + Returns + ------- + dict + A dictionary with distances between all node pairs. + """ + distances = {} + for i, coord_a in enumerate(self.node_coords): + distances[i + 1] = {} + for j, coord_b in enumerate(self.node_coords): + distances[i + 1][j + 1] = self.geographical_dist(coord_a, coord_b) + return distances def f(self, x: List[int]) -> float: """ @@ -55,55 +79,14 @@ def f(self, x: List[int]) -> float: float The total distance of the route, rounded to one decimal place. """ - # Load TSP data file - file_path = os.path.join(os.path.dirname(__file__), 'burma14.tsp.txt') - with open(file_path) as fl: - problem = tsplib95.read(fl) - - nodes = list(problem.node_coords.values()) - - # Compute distances between all pairs of nodes - temp = [] - for i in nodes: - temp_row = [] - for j in nodes: - temp_row.append(self.gographical_dist(i, j)) - temp.append(temp_row) - - # Dictionary of distances between nodes - node_name = list(range(1, 15)) - Dist = {row_name: {col_name: temp[i][j] for j, col_name in enumerate(node_name)} - for i, row_name in enumerate(node_name)} - - # Calculate total route distance fitness = 0.0 for i in range(len(x)): start_node = x[i] end_node = x[(i + 1) % len(x)] - fitness += Dist[start_node][end_node] - + fitness += self.distances[start_node][end_node] return round(fitness, 1) - def euclidean_dist(self, a: List[float], b: List[float]) -> float: - """ - Computes the Euclidean distance between two nodes. - - Parameters - ---------- - a : list - Coordinates of the first node. - b : list - Coordinates of the second node. - - Returns - ------- - float - The Euclidean distance between the two nodes, rounded to one decimal place. - """ - dist = sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) - return round(dist, 1) - - def gographical_dist(self, a: List[float], b: List[float]) -> float: + def geographical_dist(self, a: List[float], b: List[float]) -> float: """ Computes the geographical distance between two nodes using the geodesic distance. diff --git a/pycellga/tests/test_ackley.py b/pycellga/tests/test_ackley.py index d7c3f44..8f1f937 100644 --- a/pycellga/tests/test_ackley.py +++ b/pycellga/tests/test_ackley.py @@ -21,22 +21,22 @@ def test_ackley(): If any of the computed values do not match the expected values. """ # Initialize the Ackley problem instance with appropriate dimensions - theproblem = Ackley(dimension=4) # Set dimension to match the length of test inputs + theproblem = Ackley(n_var=4) # Set number of variables to match the length of test inputs # Test cases with expected results assert np.isclose(theproblem.f([15, 2.5, -25.502, -30.120]), 21.493, atol=1e-3), \ - "Ackley function value at [15, 2.5, -25.502, -30.120] does not match expected result." + "Ackley function value at [15, 2.5, -25.502, -30.120] does not match the expected result." assert np.isclose(theproblem.f([-13.75, -1.2, 4.20, 2.3]), 16.998, atol=1e-3), \ - "Ackley function value at [-13.75, -1.2, 4.20, 2.3] does not match expected result." + "Ackley function value at [-13.75, -1.2, 4.20, 2.3] does not match the expected result." # Initialize another instance of Ackley with dimension 2 for the next test case - theproblem_2d = Ackley(dimension=2) + theproblem_2d = Ackley(n_var=2) assert np.isclose(theproblem_2d.f([-15.2, -30.1]), 20.8, atol=1e-1), \ - "Ackley function value at [-15.2, -30.1] does not match expected result." + "Ackley function value at [-15.2, -30.1] does not match the expected result." assert np.isclose(theproblem_2d.f([0, 0]), 0.0, atol=1e-6), \ - "Ackley function value at [0, 0] does not match expected result." + "Ackley function value at [0, 0] does not match the expected result." if __name__ == "__main__": test_ackley() diff --git a/pycellga/tests/test_arithmetic_crossover.py b/pycellga/tests/test_arithmetic_crossover.py index d51d4a5..e40e712 100644 --- a/pycellga/tests/test_arithmetic_crossover.py +++ b/pycellga/tests/test_arithmetic_crossover.py @@ -1,6 +1,8 @@ import pytest import random -from individual import Individual, GeneType + +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.arithmetic_crossover import ArithmeticCrossover @@ -8,12 +10,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Set example values for required arguments - design_variables = 5 # Number of design variables - bounds = [(0.0, 10.0)] * design_variables # Bounds for each variable - objectives = 1 # Number of objectives - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -59,7 +63,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_arithmetic_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_bentcigar_function.py b/pycellga/tests/test_bentcigar_function.py index d9b8924..2c60274 100644 --- a/pycellga/tests/test_bentcigar_function.py +++ b/pycellga/tests/test_bentcigar_function.py @@ -11,8 +11,8 @@ def setup_bentcigar(): Bentcigar An instance of the Bentcigar problem. """ - dimension = 10 - return Bentcigar(dimension=dimension) + n_var = 10 + return Bentcigar(n_var=n_var) def test_bentcigar_function(setup_bentcigar): """ @@ -38,8 +38,9 @@ def test_bentcigar_function(setup_bentcigar): for variables, expected_fitness in test_cases: fitness_value = setup_bentcigar.f(variables) print(f"Variables: {variables[:5]}... (truncated) => Fitness: {fitness_value}") - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), f"Expected {expected_fitness}, got {fitness_value}" + assert isinstance(fitness_value, float), "Fitness value should be of type float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), \ + f"Expected {expected_fitness}, but got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_bit_flip_mutation.py b/pycellga/tests/test_bit_flip_mutation.py index 2c1f4db..154bf63 100644 --- a/pycellga/tests/test_bit_flip_mutation.py +++ b/pycellga/tests/test_bit_flip_mutation.py @@ -2,7 +2,9 @@ import pytest from mutation.bit_flip_mutation import BitFlipMutation from problems.single_objective.discrete.binary.one_max import OneMax -from individual import Individual, GeneType +from individual import Individual +from common import GeneType + def test_bit_flip_mutation(): """ diff --git a/pycellga/tests/test_blxalpha_crossover.py b/pycellga/tests/test_blxalpha_crossover.py index d49fabb..3738f1d 100644 --- a/pycellga/tests/test_blxalpha_crossover.py +++ b/pycellga/tests/test_blxalpha_crossover.py @@ -1,6 +1,7 @@ import pytest import random -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.blxalpha_crossover import BlxalphaCrossover @@ -8,12 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Set example values for required arguments - design_variables = 5 # Number of design variables - bounds = [(0.0, 10.0)] * design_variables # Bounds for each variable - objectives = 1 # Number of objectives - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -59,7 +62,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_blxalpha_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_bohachevsky.py b/pycellga/tests/test_bohachevsky.py index f29d32a..a28df83 100644 --- a/pycellga/tests/test_bohachevsky.py +++ b/pycellga/tests/test_bohachevsky.py @@ -11,8 +11,8 @@ def setup_bohachevsky(): Bohachevsky An instance of the Bohachevsky problem. """ - dimension = 4 - return Bohachevsky(dimension=dimension) + n_var = 4 # Number of variables + return Bohachevsky(n_var=n_var) def test_bohachevsky(setup_bohachevsky): """ @@ -26,15 +26,18 @@ def test_bohachevsky(setup_bohachevsky): setup_bohachevsky : fixture The fixture providing the Bohachevsky problem instance. """ - # Test cases with expected results - assert pytest.approx(setup_bohachevsky.f([2.305, -4.025, 3.805, -1.505]), rel=1e-3) == 103.584, \ - "Bohachevsky function value at [2.305, -4.025, 3.805, -1.505] does not match expected result." - - assert pytest.approx(setup_bohachevsky.f([-4.995, -2.230, -3.706, 2.305]), rel=1e-3) == 95.582, \ - "Bohachevsky function value at [-4.995, -2.230, -3.706, 2.305] does not match expected result." - - assert pytest.approx(setup_bohachevsky.f([0, 0, 0, 0]), rel=1e-3) == 0.0, \ - "Bohachevsky function value at [0, 0, 0, 0] does not match expected result." + # Define test cases with expected results + test_cases = [ + ([2.305, -4.025, 3.805, -1.505], 103.584), # Test case 1 + ([-4.995, -2.230, -3.706, 2.305], 95.582), # Test case 2 + ([0, 0, 0, 0], 0.0) # Global minimum + ] + + for variables, expected_fitness in test_cases: + fitness_value = setup_bohachevsky.f(variables) + print(f"Variables: {variables} => Fitness: {fitness_value}") + assert pytest.approx(fitness_value, rel=1e-3) == expected_fitness, \ + f"Bohachevsky function value at {variables} does not match expected result. Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_byte_mutation.py b/pycellga/tests/test_byte_mutation.py index 7c3026a..ac44e02 100644 --- a/pycellga/tests/test_byte_mutation.py +++ b/pycellga/tests/test_byte_mutation.py @@ -1,6 +1,7 @@ import pytest import numpy as np -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from mutation.byte_mutation import ByteMutation @@ -9,12 +10,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Set example values for required arguments - design_variables = 5 # Number of design variables - bounds = [(0.0, 10.0)] * design_variables # Bounds for each variable - objectives = 1 # Number of objectives - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -57,7 +60,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_byte_mutation(setup_individual, setup_problem): """ diff --git a/pycellga/tests/test_byte_mutation_random.py b/pycellga/tests/test_byte_mutation_random.py index 0011428..ddf3af9 100644 --- a/pycellga/tests/test_byte_mutation_random.py +++ b/pycellga/tests/test_byte_mutation_random.py @@ -1,6 +1,7 @@ import pytest import numpy as np -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from mutation.byte_mutation_random import ByteMutationRandom @@ -8,12 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Set example values for required arguments - design_variables = 5 # Number of design variables - bounds = [(0.0, 10.0)] * design_variables # Bounds for each variable - objectives = 1 # Number of objectives - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -56,7 +59,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_byte_mutation_random(setup_individual, setup_problem): """ diff --git a/pycellga/tests/test_byte_one_point_crossover.py b/pycellga/tests/test_byte_one_point_crossover.py index a6dc7a2..b73fc81 100644 --- a/pycellga/tests/test_byte_one_point_crossover.py +++ b/pycellga/tests/test_byte_one_point_crossover.py @@ -1,6 +1,7 @@ import pytest import numpy as np -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.byte_one_point_crossover import ByteOnePointCrossover @@ -9,12 +10,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Set example values for required arguments - design_variables = 5 # Number of design variables - bounds = [(0.0, 10.0)] * design_variables # Bounds for each variable - objectives = 1 # Number of objectives - super().__init__(design_variables=design_variables, bounds=bounds, objectives=objectives) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -60,7 +63,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_byte_one_point_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_byte_uniform_crossover.py b/pycellga/tests/test_byte_uniform_crossover.py index 3195764..64bfff7 100644 --- a/pycellga/tests/test_byte_uniform_crossover.py +++ b/pycellga/tests/test_byte_uniform_crossover.py @@ -1,6 +1,7 @@ import pytest import numpy.random as randomgenerator -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.byte_uniform_crossover import ByteUniformCrossover @@ -8,9 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Set bounds as a list of tuples for each design variable - super().__init__(design_variables=5, bounds=[(0, 10)] * 5, objectives=1) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -56,7 +62,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_byte_uniform_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_chichinadze_function.py b/pycellga/tests/test_chichinadze_function.py index ff5a51b..6c88143 100644 --- a/pycellga/tests/test_chichinadze_function.py +++ b/pycellga/tests/test_chichinadze_function.py @@ -9,25 +9,10 @@ def setup_chichinadze(): Returns ------- Chichinadze - An instance of the Chichinadze problem with specified design variables and bounds. + An instance of the Chichinadze problem. """ return Chichinadze() -def test_chichinadze_attributes(setup_chichinadze): - """ - Test the attributes of the Chichinadze problem to ensure they match expected values. - - Parameters - ---------- - setup_chichinadze : fixture - The fixture providing the Chichinadze problem instance. - """ - problem = setup_chichinadze - assert problem.design_variables == ["x", "y"], "Design variables do not match expected values." - assert problem.bounds == [(-30, 30), (-30, 30)], "Bounds do not match expected values." - assert problem.objectives == ["minimize"], "Objectives do not match expected values." - assert problem.constraints == [], "Constraints should be empty." - def test_chichinadze_function(setup_chichinadze): """ Test the Chichinadze function implementation. @@ -51,8 +36,10 @@ def test_chichinadze_function(setup_chichinadze): for variables, expected_fitness in test_cases: fitness_value = setup_chichinadze.f(variables) - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-4), f"Expected {expected_fitness}, got {fitness_value}" + print(f"Variables: {variables} => Fitness: {fitness_value}") + assert isinstance(fitness_value, float), "Fitness value should be a float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-4), \ + f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_dropwave_function.py b/pycellga/tests/test_dropwave_function.py index 2f97cba..1326198 100644 --- a/pycellga/tests/test_dropwave_function.py +++ b/pycellga/tests/test_dropwave_function.py @@ -36,13 +36,14 @@ def test_dropwave_function(setup_dropwave): for variables, expected_fitness in test_cases: # Ensure the input length matches the number of variables - assert len(variables) == setup_dropwave.num_variables, \ - f"Input length does not match the expected number of variables ({setup_dropwave.num_variables})." + assert len(variables) == setup_dropwave.n_var, \ + f"Input length does not match the expected number of variables ({setup_dropwave.n_var})." fitness_value = setup_dropwave.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}") - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), f"Expected {expected_fitness}, got {fitness_value}" + assert isinstance(fitness_value, float), "Fitness value should be of type float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), \ + f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_flat_crossover.py b/pycellga/tests/test_flat_crossover.py index 592276c..292a2ba 100644 --- a/pycellga/tests/test_flat_crossover.py +++ b/pycellga/tests/test_flat_crossover.py @@ -1,6 +1,7 @@ import pytest import random -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.flat_crossover import FlatCrossover @@ -8,9 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Define design variables, bounds, and objectives according to the test requirements - super().__init__(design_variables=5, bounds=[(0, 10)] * 5, objectives=1) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -56,7 +62,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_flat_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_float_uniform_mutation.py b/pycellga/tests/test_float_uniform_mutation.py index 1107be9..b496f1b 100644 --- a/pycellga/tests/test_float_uniform_mutation.py +++ b/pycellga/tests/test_float_uniform_mutation.py @@ -1,6 +1,7 @@ import pytest import random -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from mutation.float_uniform_mutation import FloatUniformMutation @@ -8,9 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Define design variables, bounds, and objectives according to the test requirements - super().__init__(design_variables=5, bounds=[(0, 10)] * 5, objectives=1) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -53,7 +59,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_float_uniform_mutation(setup_individual, setup_problem): """ diff --git a/pycellga/tests/test_fms.py b/pycellga/tests/test_fms.py index 756fc47..06d5093 100644 --- a/pycellga/tests/test_fms.py +++ b/pycellga/tests/test_fms.py @@ -1,5 +1,5 @@ import pytest -from problems.single_objective.continuous.fms import Fms +from problems.single_objective.continuous.fms import Fms import numpy as np @pytest.fixture @@ -20,35 +20,33 @@ def test_fms(fms_instance): """ Test the Fms function implementation. - This test checks the calculation of the Fms function value for a given list of float variables. - It uses a predefined input and compares the output to ensure it's in the correct format. + This test checks the calculation of the Fms function value for given lists of float variables. + It uses multiple predefined inputs to validate the function's output. Parameters ---------- fms_instance : Fms An instance of the Fms class. - Notes - ----- - The test generates a sample input chromosome of length 6 within the valid bounds and checks: - - If the function output is a float. - - If the fitness value is non-negative, as it represents a sum of squares of differences. - Assertions ---------- - The fitness value should be a float. - The fitness value should be non-negative. """ - # Define a sample input chromosome within bounds [-6.4, 6.35] for each variable - sample_chromosome = np.random.uniform(-6.4, 6.35, size=6) - - # Calculate the FMS function value for the sample input - fitness_value = fms_instance.f(sample_chromosome) - - # Assertions to check correctness - assert isinstance(fitness_value, float), "Fitness value should be a float." - assert fitness_value >= 0, "Fitness value should be non-negative." - print(f"Sample chromosome: {sample_chromosome}, Fitness: {fitness_value}") + # Define sample input chromosomes and their expected properties + test_cases = [ + np.array([1.0, 2.0, -1.5, 0.5, 2.5, -0.75]), # Valid input within bounds + np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), # All zeros + np.array([-6.4, 6.35, -6.4, 6.35, -6.4, 6.35]), # Boundary values + np.random.uniform(-6.4, 6.35, size=6) # Random input within bounds + ] + + for sample_chromosome in test_cases: + fitness_value = fms_instance.f(sample_chromosome) + # Assertions + assert isinstance(fitness_value, float), "Fitness value should be a float." + assert fitness_value >= 0, "Fitness value should be non-negative." + print(f"Sample chromosome: {sample_chromosome}, Fitness: {fitness_value}") if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_griewank_function.py b/pycellga/tests/test_griewank_function.py index a0e1c5c..1684bfe 100644 --- a/pycellga/tests/test_griewank_function.py +++ b/pycellga/tests/test_griewank_function.py @@ -11,7 +11,7 @@ def setup_griewank(): Griewank An instance of the Griewank problem. """ - return Griewank(dimensions=2) + return Griewank(n_var=2) # Initialize with 2 dimensions def test_griewank_function(setup_griewank): """ diff --git a/pycellga/tests/test_holzman_function.py b/pycellga/tests/test_holzman_function.py index c0b3a70..6c698ee 100644 --- a/pycellga/tests/test_holzman_function.py +++ b/pycellga/tests/test_holzman_function.py @@ -11,7 +11,7 @@ def setup_holzman(): Holzman An instance of the Holzman problem with 2 design variables. """ - return Holzman(design_variables=2) + return Holzman(n_var=2) def test_holzman_function(setup_holzman): """ @@ -37,8 +37,9 @@ def test_holzman_function(setup_holzman): for variables, expected_fitness in test_cases: fitness_value = setup_holzman.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}") - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), f"Expected {expected_fitness}, got {fitness_value}" + assert isinstance(fitness_value, float), "Fitness value should be a float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), \ + f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_individual.py b/pycellga/tests/test_individual.py index f59ad40..58142ae 100644 --- a/pycellga/tests/test_individual.py +++ b/pycellga/tests/test_individual.py @@ -1,7 +1,8 @@ import pytest from numpy import random import random as rd -from individual import Individual, GeneType +from individual import Individual +from common import GeneType @pytest.fixture diff --git a/pycellga/tests/test_insertion_mutation.py b/pycellga/tests/test_insertion_mutation.py index 91683e2..8e8c194 100644 --- a/pycellga/tests/test_insertion_mutation.py +++ b/pycellga/tests/test_insertion_mutation.py @@ -1,6 +1,7 @@ from mutation.insertion_mutation import InsertionMutation from problems.single_objective.discrete.permutation.tsp import Tsp -from individual import Individual, GeneType +from individual import Individual +from common import GeneType import random def test_insertion_mutation(): diff --git a/pycellga/tests/test_levy_function.py b/pycellga/tests/test_levy_function.py index e2121df..34740a7 100644 --- a/pycellga/tests/test_levy_function.py +++ b/pycellga/tests/test_levy_function.py @@ -11,7 +11,7 @@ def setup_levy(): Levy An instance of the Levy problem with the default dimensions. """ - return Levy(dimension=2) + return Levy(n_var=2) def test_levy_function(setup_levy): """ diff --git a/pycellga/tests/test_linear_crossover.py b/pycellga/tests/test_linear_crossover.py index 867d0a1..904fea1 100644 --- a/pycellga/tests/test_linear_crossover.py +++ b/pycellga/tests/test_linear_crossover.py @@ -1,6 +1,7 @@ import pytest import random from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.linear_crossover import LinearCrossover @@ -8,9 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - # Initialize with necessary parameters - super().__init__(design_variables=5, bounds=[(0, 10)] * 5, objectives=1) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -56,7 +62,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_linear_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_matyas_function.py b/pycellga/tests/test_matyas_function.py index 8e3f75a..fe15fad 100644 --- a/pycellga/tests/test_matyas_function.py +++ b/pycellga/tests/test_matyas_function.py @@ -38,19 +38,22 @@ def test_matyas_function(setup_matyas): AssertionError If the actual fitness value does not match the expected fitness value. """ + # Define test cases with expected outputs test_cases = [ ([0.0, 0.0], 0.0), ([1.0, 1.0], 0.04), ([-1.0, -1.0], 0.04), ([5.0, -5.0], 25.0), - ([10.0, 10.0], 4.0) + ([10.0, 10.0], 4.0) ] + # Loop through each test case and validate the fitness value for variables, expected_fitness in test_cases: fitness_value = setup_matyas.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}, Expected: {expected_fitness}") - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-2), f"Expected {expected_fitness}, got {fitness_value}" + assert isinstance(fitness_value, float), "Fitness value should be a float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-2), \ + f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_one_point_crossover.py b/pycellga/tests/test_one_point_crossover.py index 8b7e5f1..69c2320 100644 --- a/pycellga/tests/test_one_point_crossover.py +++ b/pycellga/tests/test_one_point_crossover.py @@ -1,5 +1,6 @@ from recombination.one_point_crossover import OnePointCrossover -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.single_objective.discrete.binary.one_max import OneMax def test_one_point_crossover(): diff --git a/pycellga/tests/test_optimizer_alpha_cga.py b/pycellga/tests/test_optimizer_alpha_cga.py index 2a00171..b0ea0ec 100644 --- a/pycellga/tests/test_optimizer_alpha_cga.py +++ b/pycellga/tests/test_optimizer_alpha_cga.py @@ -3,38 +3,26 @@ from typing import List from optimizer import alpha_cga -from individual import GeneType +from common import GeneType from recombination import ByteOnePointCrossover, OnePointCrossover, PMXCrossover from mutation import ByteMutationRandom, BitFlipMutation, SwapMutation from selection import TournamentSelection +from problems import AbstractProblem -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. - """ +class RealProblem(AbstractProblem): - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) 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(mp.power(xi, 2) for xi in x) def test_optimizer_alpha_cga_real(): @@ -44,112 +32,69 @@ def test_optimizer_alpha_cga_real(): n_rows=5, n_gen=50, ch_size=5, - gen_type=GeneType.REAL, p_crossover=0.9, p_mutation=0.2, - problem=RealProblem(), + problem=RealProblem(n_var=5), selection=TournamentSelection, recombination=ByteOnePointCrossover, - mutation=ByteMutationRandom, - mins=[-32.768] * 5, - maxs=[32.768] * 5 + mutation=ByteMutationRandom ) assert result.fitness_value == 0.0, "The alpha_cga did not find the global minimum." assert result.chromosome == [0.0] * 5, "The chromosome does not match the global minimum." -class BinaryProblem: - """ - Example problem class to be maximized for binary chromosomes. +class BinaryProblem(AbstractProblem): - 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 __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) 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(), + problem=BinaryProblem(n_var=10), selection=TournamentSelection, recombination=OnePointCrossover, mutation=BitFlipMutation, - mins=[0] * 10, - maxs=[1] * 10 ) assert result.fitness_value == -10, "The alpha_cga did not maximize the number of 1s." assert result.chromosome == [1] * 10, "The chromosome does not match the optimal binary sequence." +class PermutationProblem(AbstractProblem): + + def __init__(self, n_var, target: List[int]): - -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. - """ + super().__init__( + gen_type=GeneType.PERMUTATION, + n_var=n_var, + xl=1, + xu=10 + ) 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) @@ -158,15 +103,12 @@ def test_optimizer_alpha_cga_permutation(self): 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 + mutation=SwapMutation ) # Assert that the alpha_cga finds the global minimum @@ -176,25 +118,5 @@ def test_optimizer_alpha_cga_permutation(self): assert result.chromosome == 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.fitness_value != 0.0, "With no crossover or mutation, the solution should not reach the global minimum." - - if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_optimizer_ccga.py b/pycellga/tests/test_optimizer_ccga.py index 6c37ed2..e1cf342 100644 --- a/pycellga/tests/test_optimizer_ccga.py +++ b/pycellga/tests/test_optimizer_ccga.py @@ -1,52 +1,36 @@ import pytest -from typing import List from optimizer import ccga -from individual import GeneType from selection import TournamentSelection -from individual import GeneType +from common import GeneType +from problems import AbstractProblem -class BinaryProblem: - """ - Example problem class to be maximized for binary chromosomes. +class BinaryProblem(AbstractProblem): - 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 __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) 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 + problem=BinaryProblem(n_var=5), + selection=TournamentSelection ) + assert result.fitness_value == 5, "The ccga did not maximize the number of 1s." assert result.chromosome == [1] * 5, "The chromosome does not match the optimal binary sequence." diff --git a/pycellga/tests/test_optimizer_cga.py b/pycellga/tests/test_optimizer_cga.py index 4e58239..8b1090b 100644 --- a/pycellga/tests/test_optimizer_cga.py +++ b/pycellga/tests/test_optimizer_cga.py @@ -3,152 +3,98 @@ from typing import List from optimizer import cga -from individual import GeneType +from common import GeneType from recombination import OnePointCrossover, ByteOnePointCrossover, PMXCrossover from mutation import BitFlipMutation, ByteMutationRandom, SwapMutation from selection import TournamentSelection +from problems import AbstractProblem -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. - """ +class RealProblem(AbstractProblem): - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) 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(mp.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(), + problem=RealProblem(n_var=5), selection=TournamentSelection, recombination=ByteOnePointCrossover, - mutation=ByteMutationRandom, - mins=[-32.768] * 5, - maxs=[32.768] * 5 + mutation=ByteMutationRandom ) assert result.fitness_value == 0.0, "The CGA did not find the global minimum." assert result.chromosome == [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. - """ +class BinaryProblem(AbstractProblem): - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) 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(), + problem=BinaryProblem(n_var=10), selection=TournamentSelection, recombination=OnePointCrossover, - mutation=BitFlipMutation, - mins=[0] * 10, - maxs=[1] * 10 + mutation=BitFlipMutation ) assert result.fitness_value == -10, "The CGA did not maximize the number of 1s." assert result.chromosome == [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. - """ +class PermutationProblem(AbstractProblem): - 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. - """ + def __init__(self, n_var, target: List[int]): + + super().__init__( + gen_type=GeneType.PERMUTATION, + n_var=n_var, + xl=1, + xu=10 + ) 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. - """ + def f(self, x: List[int]) -> float: + 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) @@ -157,15 +103,12 @@ def test_optimizer_cga_permutation(self): 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 + mutation=SwapMutation ) # Assert that the CGA finds the global minimum @@ -174,26 +117,5 @@ def test_optimizer_cga_permutation(self): assert result.fitness_value == 0.0, "The CGA did not find the global minimum." assert result.chromosome == 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.fitness_value != 0.0, "With no crossover or mutation, the solution should not reach the global minimum." - - if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_optimizer_mccga.py b/pycellga/tests/test_optimizer_mccga.py index a1361a4..ef6cb19 100644 --- a/pycellga/tests/test_optimizer_mccga.py +++ b/pycellga/tests/test_optimizer_mccga.py @@ -1,52 +1,35 @@ import pytest import mpmath as mp -from typing import List from optimizer import mcccga -from individual import GeneType +from common import GeneType from selection import TournamentSelection +from problems import AbstractProblem -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. - """ +class RealProblem(AbstractProblem): - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-3.768, + xu=3.768 + ) 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(mp.power(xi, 2) for xi in x) -def test_optimizer_mcccga_binary(): - """Test mcccga on a binary OneMax problem.""" +def test_optimizer_mccga_binary(): + 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 + problem=RealProblem(n_var=5), + selection=TournamentSelection ) assert result.fitness_value == 0.0, "The mcccga did not find the global minimum." assert result.chromosome == [0.0] * 5, "The chromosome does not match the global minimum." diff --git a/pycellga/tests/test_optimizer_sync_cga.py b/pycellga/tests/test_optimizer_sync_cga.py index 5611185..83f19c1 100644 --- a/pycellga/tests/test_optimizer_sync_cga.py +++ b/pycellga/tests/test_optimizer_sync_cga.py @@ -1,154 +1,98 @@ import pytest from optimizer import sync_cga -from individual import GeneType +from common import GeneType from recombination import ByteOnePointCrossover, OnePointCrossover, PMXCrossover from mutation import ByteMutationRandom, BitFlipMutation, SwapMutation from selection import TournamentSelection +from problems import AbstractProblem + import mpmath as mp 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. - """ +class RealProblem(AbstractProblem): - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.REAL, + n_var=n_var, + xl=-100, + xu=100 + ) 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(mp.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(), + problem=RealProblem(n_var=5), selection=TournamentSelection, recombination=ByteOnePointCrossover, - mutation=ByteMutationRandom, - mins=[-32.768] * 5, - maxs=[32.768] * 5 + mutation=ByteMutationRandom ) assert result.fitness_value == 0.0, "The sync_cga did not find the global minimum." assert result.chromosome == [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. - """ +class BinaryProblem(AbstractProblem): - def __init__(self): - pass + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) 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(), + problem=BinaryProblem(n_var=10), selection=TournamentSelection, recombination=OnePointCrossover, - mutation=BitFlipMutation, - mins=[0] * 10, - maxs=[1] * 10 + mutation=BitFlipMutation ) assert result.fitness_value == -10, "The sync_cga did not maximize the number of 1s." assert result.chromosome == [1] * 10, "The chromosome does not match the optimal binary sequence." +class PermutationProblem(AbstractProblem): -class PermutationProblem: - """ - Example problem class to be minimized using a permutation-based approach. + def __init__(self, n_var, target: List[int]): - 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. - """ + super().__init__( + gen_type=GeneType.PERMUTATION, + n_var=n_var, + xl=1, + xu=10 + ) 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) @@ -157,15 +101,12 @@ def test_optimizer_sync_cga_permutation(self): 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 + mutation=SwapMutation ) # Assert that the sync_cga finds the global minimum @@ -174,25 +115,5 @@ def test_optimizer_sync_cga_permutation(self): assert result.fitness_value == 0.0, "The sync_cga did not find the global minimum." assert result.chromosome == 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.fitness_value != 0.0, "With no crossover or mutation, the solution should not reach the global minimum." - if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_pmx_crossover.py b/pycellga/tests/test_pmx_crossover.py index c35636b..570be68 100644 --- a/pycellga/tests/test_pmx_crossover.py +++ b/pycellga/tests/test_pmx_crossover.py @@ -1,6 +1,7 @@ from problems.single_objective.discrete.permutation.tsp import Tsp from recombination.pmx_crossover import PMXCrossover -from individual import Individual, GeneType +from individual import Individual +from common import GeneType import random def test_pmx_crossover(): diff --git a/pycellga/tests/test_population.py b/pycellga/tests/test_population.py index 66e150f..8cdbca5 100644 --- a/pycellga/tests/test_population.py +++ b/pycellga/tests/test_population.py @@ -1,5 +1,5 @@ import pytest -from individual import Individual, GeneType +from common import GeneType from grid import Grid from neighborhoods.linear_9 import Linear9 from byte_operators import bits_to_floats @@ -17,8 +17,14 @@ class MockProblem(AbstractProblem): f(chromosome : List[float]) -> float Returns the sum of the chromosome as the fitness value. """ - def __init__(self): - super().__init__(design_variables=10, bounds=[(0, 1)] * 10, objectives=1) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, chromosome: List[float]) -> float: return sum(chromosome) @@ -34,7 +40,8 @@ def setup_population(): Population A population instance with a mock problem. """ - mock_problem = MockProblem() + mock_problem = MockProblem(n_var=10) + return Population( method_name=OptimizationMethod.CGA, ch_size=10, diff --git a/pycellga/tests/test_pow_function.py b/pycellga/tests/test_pow_function.py index 27ac0d1..b833882 100644 --- a/pycellga/tests/test_pow_function.py +++ b/pycellga/tests/test_pow_function.py @@ -5,8 +5,13 @@ def setup_pow(): """ Fixture to provide an instance of the Pow problem. + + Returns + ------- + Pow + An instance of the Pow optimization problem. """ - return Pow() + return Pow(n_var=5) def test_pow_function(setup_pow): """ @@ -17,10 +22,17 @@ def test_pow_function(setup_pow): Parameters ---------- - setup_pow : fixture - The fixture providing the Pow problem instance. + setup_pow : Pow + An instance of the Pow function problem for testing. + + Test Cases + ---------- + - [5.0, 7.0, 9.0, 3.0, 2.0]: Expected result is 0.0, representing the global minimum. + - [0.0, 0.0, 0.0, 0.0, 0.0]: All variables set to zero, expected result is 168.0. + - [10.0, 10.0, 10.0, 10.0, 10.0]: All variables set to upper bounds, expected result is 148.0. + - [-5.0, -5.0, -5.0, -5.0, -5.0]: All variables set to lower bounds, expected result is 553.0. + - [6.0, 8.0, 10.0, 4.0, 3.0]: Near the global minimum, expected result is 5.0. """ - # Define sample input variables and their expected Pow function values test_cases = [ ([5.0, 7.0, 9.0, 3.0, 2.0], 0.0), # Global minimum ([0.0, 0.0, 0.0, 0.0, 0.0], 168.0), # All zeros @@ -32,7 +44,7 @@ def test_pow_function(setup_pow): for variables, expected_fitness in test_cases: fitness_value = setup_pow.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}, Expected: {expected_fitness}") - assert isinstance(fitness_value, float) + assert isinstance(fitness_value, float), "Fitness value should be a float." assert fitness_value == pytest.approx(expected_fitness, rel=1e-2), f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": diff --git a/pycellga/tests/test_powell_function.py b/pycellga/tests/test_powell_function.py index 45f8398..b40c3f7 100644 --- a/pycellga/tests/test_powell_function.py +++ b/pycellga/tests/test_powell_function.py @@ -4,32 +4,47 @@ @pytest.fixture def setup_powell(): """ - Fixture to provide an instance of the Powell problem. + Fixture to provide an instance of the Powell problem with a default number of variables. + + Returns + ------- + Powell + An instance of the Powell optimization problem. """ - return Powell(design_variables=8) + return Powell(n_var=8) # Default number of variables def test_powell_function(setup_powell): """ Test the Powell function implementation. - Bu test, verilen değişken listeleri için Powell fonksiyonunun doğruluğunu kontrol eder. - Belirli girişler ve beklenen çıktılarla çalışır. + This test verifies the accuracy of the Powell function by evaluating it at predefined + input points and comparing the results to the expected fitness values. Parameters ---------- - setup_powell : fixture - Powell problem örneğini sağlayan fixture. + setup_powell : Powell + The fixture providing an instance of the Powell problem. + + Test Cases + ---------- + - [0.0, 0.0, 0.0, 0.0]: Expected result is 0.0. + - [1.0, 2.0, 3.0, 4.0]: Expected result is 1512.0. + - [1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0]: Expected result is 3024.0. + - [5.0, -4.0, 0.0, 0.0, 2.0, -3.0, 4.0, -5.0]: Expected result is 47571.0. """ test_cases = [ - ([0.0, 0.0, 0.0, 0.0], 0.0), - ([1.0, 2.0, 3.0, 4.0], 1512.0), - ([1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0], 3024.0), - ([5.0, -4.0, 0.0, 0.0, 2.0, -3.0, 4.0, -5.0], 47571.0) + ([0.0, 0.0, 0.0, 0.0], 0.0), + ([1.0, 2.0, 3.0, 4.0], 1512.0), + ([1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0], 3024.0), + ([5.0, -4.0, 0.0, 0.0, 2.0, -3.0, 4.0, -5.0], 47571.0), ] for variables, expected_fitness in test_cases: - setup_powell.design_variables = len(variables) fitness_value = setup_powell.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}, Expected: {expected_fitness}") - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-1), f"Expected {expected_fitness}, got {fitness_value}" + assert isinstance(fitness_value, float), "Fitness value should be a float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-1), \ + f"Expected {expected_fitness}, got {fitness_value}" + +if __name__ == "__main__": + pytest.main() diff --git a/pycellga/tests/test_rastrigin.py b/pycellga/tests/test_rastrigin.py index 71eea4e..94ca744 100644 --- a/pycellga/tests/test_rastrigin.py +++ b/pycellga/tests/test_rastrigin.py @@ -5,8 +5,13 @@ def setup_rastrigin(): """ Fixture to provide an instance of the Rastrigin problem. + + Returns + ------- + Rastrigin + An instance of the Rastrigin problem with 4 design variables. """ - return Rastrigin(design_variables=4) + return Rastrigin(n_var=4) def test_rastrigin(setup_rastrigin): """ @@ -30,8 +35,9 @@ def test_rastrigin(setup_rastrigin): for variables, expected_fitness in test_cases: fitness_value = setup_rastrigin.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}, Expected: {expected_fitness}") - assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), f"Expected {expected_fitness}, got {fitness_value}" + assert isinstance(fitness_value, float), "Fitness value should be a float." + assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), \ + f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_rosenbrock.py b/pycellga/tests/test_rosenbrock.py index 4729776..7644c7e 100644 --- a/pycellga/tests/test_rosenbrock.py +++ b/pycellga/tests/test_rosenbrock.py @@ -6,7 +6,7 @@ def setup_rosenbrock(): """ Fixture to provide an instance of the Rosenbrock problem. """ - return Rosenbrock(design_variables=4) + return Rosenbrock(n_var=4) # Ensure compatibility with the new AbstractProblem structure def test_rosenbrock(setup_rosenbrock): """ @@ -20,30 +20,24 @@ def test_rosenbrock(setup_rosenbrock): setup_rosenbrock : fixture The fixture providing the Rosenbrock problem instance. - Notes - ----- - The Rosenbrock function, also known as Rosenbrock's valley or banana function, - is a common test problem for optimization algorithms. The global minimum is at (1, ..., 1), where the function value is 0. - Assertions ---------- - The function should return the correct values for predefined inputs. - The function should return 0 for an input list of all ones. """ - theproblem = setup_rosenbrock - - # Test cases with known values + # Define test cases with known values test_cases = [ ([2.305, -4.025, 3.805, -1.505], 49665.553), ([-4.995, -2.230, -3.706, 2.305], 94539.427), - ([1, 1, 1, 1], 0.0) # Global minimum + ([1.0, 1.0, 1.0, 1.0], 0.0) # Global minimum ] for variables, expected_fitness in test_cases: - fitness_value = theproblem.f(variables) + fitness_value = setup_rosenbrock.f(variables) print(f"Variables: {variables} => Fitness: {fitness_value}, Expected: {expected_fitness}") assert isinstance(fitness_value, float) - assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), f"Expected {expected_fitness}, got {fitness_value}" + assert fitness_value == pytest.approx(expected_fitness, rel=1e-3), \ + f"Expected {expected_fitness}, got {fitness_value}" if __name__ == "__main__": pytest.main() diff --git a/pycellga/tests/test_rothellipsoid_function.py b/pycellga/tests/test_rothellipsoid_function.py index b57990a..8992609 100644 --- a/pycellga/tests/test_rothellipsoid_function.py +++ b/pycellga/tests/test_rothellipsoid_function.py @@ -6,7 +6,7 @@ def setup_rothellipsoid(): """ Fixture to provide an instance of the Rotated Hyper-Ellipsoid problem. """ - return Rothellipsoid(design_variables=3) + return Rothellipsoid(n_var=3) def test_rothellipsoid_function(setup_rothellipsoid): """ diff --git a/pycellga/tests/test_roulette_wheel_selection.py b/pycellga/tests/test_roulette_wheel_selection.py index 75f9524..db82740 100644 --- a/pycellga/tests/test_roulette_wheel_selection.py +++ b/pycellga/tests/test_roulette_wheel_selection.py @@ -1,7 +1,7 @@ from problems.single_objective.discrete.binary.one_max import OneMax from selection.roulette_wheel_selection import RouletteWheelSelection from population import Population, OptimizationMethod -from individual import GeneType +from common import GeneType def test_roulette_wheel_selection(): """ diff --git a/pycellga/tests/test_schaffer2_function.py b/pycellga/tests/test_schaffer2_function.py index f98cb03..baf9901 100644 --- a/pycellga/tests/test_schaffer2_function.py +++ b/pycellga/tests/test_schaffer2_function.py @@ -6,7 +6,7 @@ def setup_schaffer2(): """ Fixture to provide an instance of the Schaffer2 problem. """ - return Schaffer2(design_variables=2) + return Schaffer2(n_var=2) def test_schaffer2_function(setup_schaffer2): """ diff --git a/pycellga/tests/test_schaffer_function.py b/pycellga/tests/test_schaffer_function.py index 93f1402..f70f001 100644 --- a/pycellga/tests/test_schaffer_function.py +++ b/pycellga/tests/test_schaffer_function.py @@ -6,7 +6,7 @@ def setup_schaffer(): """ Fixture to provide an instance of the Schaffer problem with the default number of design variables. """ - return Schaffer(design_variables=2) + return Schaffer(n_var=2) def test_schaffer_function(setup_schaffer): """ diff --git a/pycellga/tests/test_schwefel.py b/pycellga/tests/test_schwefel.py index 67399c4..7c210fa 100644 --- a/pycellga/tests/test_schwefel.py +++ b/pycellga/tests/test_schwefel.py @@ -6,7 +6,7 @@ def setup_schwefel(): """ Fixture to provide an instance of the Schwefel problem. """ - return Schwefel(design_variables=4) + return Schwefel(n_var=4) def test_schwefel(setup_schwefel): """ diff --git a/pycellga/tests/test_shuffle_mutation.py b/pycellga/tests/test_shuffle_mutation.py index 14a297a..9244530 100644 --- a/pycellga/tests/test_shuffle_mutation.py +++ b/pycellga/tests/test_shuffle_mutation.py @@ -1,6 +1,7 @@ from mutation.shuffle_mutation import ShuffleMutation from problems.single_objective.discrete.permutation.tsp import Tsp -from individual import Individual, GeneType +from individual import Individual +from common import GeneType import random def test_shuffle_mutation(): diff --git a/pycellga/tests/test_sphere.py b/pycellga/tests/test_sphere.py index 21c2cda..7fd8365 100644 --- a/pycellga/tests/test_sphere.py +++ b/pycellga/tests/test_sphere.py @@ -6,7 +6,7 @@ def setup_sphere(): """ Fixture to provide an instance of the Sphere problem. """ - return Sphere(design_variables=10) + return Sphere(n_var=10) def test_sphere(setup_sphere): """ diff --git a/pycellga/tests/test_styblinskitang_function.py b/pycellga/tests/test_styblinskitang_function.py index a904a41..4700921 100644 --- a/pycellga/tests/test_styblinskitang_function.py +++ b/pycellga/tests/test_styblinskitang_function.py @@ -6,7 +6,7 @@ def setup_styblinski_tang(): """ Fixture to provide an instance of the StyblinskiTang problem. """ - return StyblinskiTang(design_variables=2) + return StyblinskiTang(n_var=2) def test_styblinskitang_function(setup_styblinski_tang): """ diff --git a/pycellga/tests/test_sumofdifferentpowers_function.py b/pycellga/tests/test_sumofdifferentpowers_function.py index 9fccd61..d7a35cc 100644 --- a/pycellga/tests/test_sumofdifferentpowers_function.py +++ b/pycellga/tests/test_sumofdifferentpowers_function.py @@ -7,7 +7,7 @@ def setup_sumofdifferentpowers(): Fixture to create an instance of the Sumofdifferentpowers problem. """ # Instantiate with a specific number of design variables if needed - return Sumofdifferentpowers(design_variables=3) + return Sumofdifferentpowers(n_var=3) def test_sumofdifferentpowers_function(setup_sumofdifferentpowers): """ diff --git a/pycellga/tests/test_swap_mutation.py b/pycellga/tests/test_swap_mutation.py index 32402f8..f1a9fae 100644 --- a/pycellga/tests/test_swap_mutation.py +++ b/pycellga/tests/test_swap_mutation.py @@ -1,6 +1,7 @@ from mutation.swap_mutation import SwapMutation from problems.single_objective.discrete.permutation.tsp import Tsp -from individual import Individual, GeneType +from individual import Individual +from common import GeneType import random def test_swap_mutation(): diff --git a/pycellga/tests/test_tournament_selection.py b/pycellga/tests/test_tournament_selection.py index 8be62e2..889867c 100644 --- a/pycellga/tests/test_tournament_selection.py +++ b/pycellga/tests/test_tournament_selection.py @@ -1,7 +1,7 @@ from problems.single_objective.discrete.binary.one_max import OneMax from selection.tournament_selection import TournamentSelection from population import Population, OptimizationMethod -from individual import GeneType +from common import GeneType def test_tournament_selection(): """ diff --git a/pycellga/tests/test_two_opt_mutation.py b/pycellga/tests/test_two_opt_mutation.py index 59fa0a5..e89ed75 100644 --- a/pycellga/tests/test_two_opt_mutation.py +++ b/pycellga/tests/test_two_opt_mutation.py @@ -1,6 +1,7 @@ from mutation.two_opt_mutation import TwoOptMutation from problems.single_objective.discrete.permutation.tsp import Tsp -from individual import Individual, GeneType +from individual import Individual +from common import GeneType import random def test_two_opt_mutation(): diff --git a/pycellga/tests/test_two_point_crossover.py b/pycellga/tests/test_two_point_crossover.py index 09e343c..5b1d88d 100644 --- a/pycellga/tests/test_two_point_crossover.py +++ b/pycellga/tests/test_two_point_crossover.py @@ -1,7 +1,8 @@ import numpy as np from problems.single_objective.discrete.binary.one_max import OneMax from recombination.two_point_crossover import TwoPointCrossover -from individual import Individual, GeneType +from individual import Individual +from common import GeneType def test_two_point_crossover(): """ diff --git a/pycellga/tests/test_unfair_average_crossover.py b/pycellga/tests/test_unfair_average_crossover.py index 9ddfd7d..9e65500 100644 --- a/pycellga/tests/test_unfair_average_crossover.py +++ b/pycellga/tests/test_unfair_average_crossover.py @@ -1,6 +1,7 @@ import pytest import random -from individual import Individual, GeneType +from individual import Individual +from common import GeneType from problems.abstract_problem import AbstractProblem from recombination.unfair_avarage_crossover import UnfairAvarageCrossover @@ -8,8 +9,14 @@ class MockProblem(AbstractProblem): """ A mock problem class for testing purposes. """ - def __init__(self): - super().__init__(design_variables=5, bounds=[(0, 10)] * 5, objectives=1) + def __init__(self, n_var): + + super().__init__( + gen_type=GeneType.BINARY, + n_var=n_var, + xl=0, + xu=1 + ) def f(self, x: list) -> float: """ @@ -55,7 +62,7 @@ def setup_problem(): MockProblem An instance of the mock problem. """ - return MockProblem() + return MockProblem(n_var=5) def test_unfair_average_crossover(setup_parents, setup_problem): """ diff --git a/pycellga/tests/test_uniform_crossover.py b/pycellga/tests/test_uniform_crossover.py index 52b03fa..a331a86 100644 --- a/pycellga/tests/test_uniform_crossover.py +++ b/pycellga/tests/test_uniform_crossover.py @@ -1,6 +1,7 @@ from problems.single_objective.discrete.binary.one_max import OneMax from recombination.uniform_crossover import UniformCrossover -from individual import Individual, GeneType +from individual import Individual +from common import GeneType def test_uniform_crossover(): """ diff --git a/pycellga/tests/test_zakharov_function.py b/pycellga/tests/test_zakharov_function.py index f880351..c3594ff 100644 --- a/pycellga/tests/test_zakharov_function.py +++ b/pycellga/tests/test_zakharov_function.py @@ -6,7 +6,7 @@ def setup_zakharov(): """ Fixture to provide an instance of the Zakharov problem. """ - return Zakharov(design_variables=2) + return Zakharov(n_var=2) def test_zakharov_function(setup_zakharov): """ @@ -24,7 +24,7 @@ def test_zakharov_function(setup_zakharov): ] # Testing 3 variables requires initializing with design_variables=3 - three_var_problem = Zakharov(design_variables=3) + three_var_problem = Zakharov(n_var=3) extended_test_case = ([1.0, 2.0, 3.0], 2464.0) # Run test cases with default 2-variable problem