Developer Guide
Welcome to the cilpy developer guide! We're excited that you're interested in
contributing. This document provides everything you need to know to get your
development environment set up and contribute to the project effectively.
Our goal is to create a library that is powerful yet easy to maintain and extend. Following these guidelines helps us achieve that.
1. Setting Up Your Development Environment
To contribute code, you first need to set up a local development environment.
1. Fork and Clone the Repository:
First, fork the repository
on the GitLab instance. Then, clone your fork to your local machine:
git clone git@git.cs.sun.ac.za:Computer-Science/rw771/2025/24717274-AE4-src.git
cd 24717274-AE4-src
2. Install in Editable Mode:
Installing the package in "editable" mode allows you to test your changes
live without having to reinstall the package after every modification.
pip install -e .
2. Core Design Philosophy
cilpy is built on three core principles. When developing new features, please
keep these in mind:
- Genericity: The library's interfaces should be abstract enough to support a wide variety of CI paradigms, from single-objective GAs to multi- population co-evolutionary systems. Avoid making assumptions that would limit the framework to a specific type of problem or solver.
- Extendability: The primary goal is to make it easy for researchers to
add their own components. This is achieved through the core
ProblemandSolverabstract base classes. A user should be able to add a new algorithm and immediately test it on all existing problems, and vice-versa. - Maintainability: Clean, readable, and well-documented code is essential. We enforce this through mandatory type hinting, adherence to style guides, and a requirement for unit tests.
3. The Contribution Workflow
We follow a standard fork-and-pull-request workflow.
- Find an Issue: Start by looking at the To-Do List or the issue tracker for tasks. If you have a new idea, consider creating an issue first to discuss it with the maintainers.
- Create a Branch: From the
mainbranch, create a new feature branch for your work. Please use the branch prefixes outlined below.bash # Example for a new feature git checkout -b ft/add-new-pso-variant main - Write Code: Make your changes, following the coding style guidelines below.
- Write Tests: All new features must be accompanied by tests. If you add a
new problem, add a test case for it in
test/test_problems.py. If you add a new solver, create a test for it. Tests are crucial for ensuring the long- term stability of the library. Learn more about our testing strategy here - Ensure All Tests Pass: Run the test suite to make sure your changes haven't broken existing functionality.
- Submit a Merge Request: Push your branch to your fork and open a Merge
Request to the main
cilpyrepository. Provide a clear description of the changes you have made.
Branch Naming Prefixes
ft/: Feature development (e.g.,ft/quantum-pso)fx/: Bug fixes (e.g.,fx/fix-ga-selection-bug)ch/: Maintenance tasks (e.g.,ch/update-dependencies)rf/: Code refactoring (e.g.,rf/optimize-runner-loop)dc/: Documentation updates (e.g.,dc/clarify-solver-api)ts/: Testing-related work (e.g.,ts/add-tests-for-mpb)
4. How to Add New Components
This is the most common way to contribute. The key is to correctly implement the required interface.
Adding a New Problem
- Create the File: Add a new Python file in the
cilpy/problem/directory. - Implement the Interface: Your new class must inherit from
cilpy.problem.Problemand implement all of its abstract methods. - Write a Test: Add a test case to
test/test_problems.pythat instantiates your problem and evaluates a known solution to ensure the fitness calculation is correct.
Template for a new problem:
# In cilpy/problem/my_new_problem.py
from typing import List, Tuple
from cilpy.problem import Problem, Evaluation
class MyNewProblem(Problem[List[float], float]):
def __init__(self, dimension: int):
# Always call super().__init__()
super().__init__(
dimension=dimension,
bounds=([-10.0] * dimension, [10.0] * dimension),
name="MyNewProblem"
)
def evaluate(self, solution: List[float]) -> Evaluation[float]:
# Your fitness logic here
fitness = ...
return Evaluation(fitness=fitness)
def is_dynamic(self) -> Tuple[bool, bool]:
# Return True for the first element if the objective is dynamic
return (False, False)
Adding a New Solver
- Create the File: Add a new Python file in the
cilpy/solver/solvers/directory. - Implement the Interface: Your new class must inherit from
cilpy.solver.Solverand implement all of its abstract methods. - Write a Test: Add a test that runs your solver on a simple, known problem (like Sphere) and asserts that it finds a reasonably good solution.
Template for a new solver:
# In cilpy/solver/solvers/my_new_solver.py
from typing import List, Tuple
from cilpy.problem import Problem, Evaluation
from cilpy.solver import Solver
class MyNewSolver(Solver[List[float], float]):
def __init__(self, problem: Problem, name: str, **kwargs):
# Always call super().__init__()
super().__init__(problem, name)
# Your initialization logic here (e.g., population)
self.best_solution = None
self.best_evaluation = Evaluation(fitness=float('inf'))
def step(self) -> None:
# Your core algorithm logic for one iteration
# ... update self.best_solution and self.best_evaluation
pass
def get_result(self) -> List[Tuple[List[float], Evaluation[float]]]:
# Return the best result found so far
return [(self.best_solution, self.best_evaluation)]
5. Coding Style and Conventions
Consistency is key. Please adhere to the following standards.
- Docstrings: All public modules, classes, and functions must have Google-style docstrings.
- Style Guide: All code must adhere to PEP 8.
We recommend using an autoformatter like
black. - Line Length: Maximum 80 characters for code and docstrings.
- Type Hinting: All function and method signatures must include type hints.
- Imports: Group imports in this order: (1) Python standard library, (2)
third-party libraries, (3)
cilpyimports. - Dependencies: The core library should have minimal dependencies. Please discuss with a maintainer before adding a new third-party dependency.
Where to Start?
A great place to start contributing is by looking at our To-Do List or picking up an unassigned issue from the issue tracker. We look forward to your contributions!