pyVolutionary is a package collecting metaheuristic algorithms, available for free usage and able to solve a wide range of optimization problems.
Project description
pyVolutionary
Introduction
pyVolutionary stands as a versatile Python library dedicated to metaheuristic algorithms within the realm of evolutionary computation. Engineered for ease of use, flexibility, and speed, it exhibits robustness and efficiency, having undergone rigorous testing on large-scale problem instances. The primary objectives encompass the implementation of both classical and cutting-edge nature-inspired algorithms. The library is conceived as a user-friendly resource facilitating rapid access to optimization algorithms for researchers, fostering the dissemination of optimization knowledge to a broad audience without financial barriers.
Nature-inspired algorithms constitute a widely embraced tool for addressing optimization challenges. Over the course of their evolution, a plethora of variants have emerged (paper 1, paper 2), showcasing their adaptability and versatility across diverse domains and applications. Noteworthy advancements have been achieved through hybridization, modification, and adaptation of these algorithms. However, the implementation of nature-inspired algorithms can often pose a formidable challenge, characterized by complexity and tedium. pyVolutionary is specifically crafted to surmount this challenge, offering a streamlined and expedited approach to leveraging these algorithms without the need for arduous, time-consuming implementations from scratch.
The list of algorithms currently implemented in pyVolutionary can be consulted in the Algorithms section. The library is continuously updated with new algorithms and problems, and contributions are welcome.
Installation
pyVolutionary is available on PyPI, and can be installed via pip:
pip install pyvolutionary
Usage
Once installed, pyVolutionary can be imported into your Python scripts as follows:
import pyvolutionary
Now, you can access the algorithms and problems included in the library. With pyVolutionary, you can solve both
continuous and discrete optimization problems. It is also possible to solve mixed problems, i.e., problems with both
continuous and discrete variables. In order to do so, you need to define a Task
class, which inherits from the
Task
class of the library. The list of variables in the problem must be specified in the constructor of the class
inheriting from Task
. The variables can be either continuous or discrete. The following table describes the types of
variables currently implemented in the library.
Variable type | Class name | Description | Example |
---|---|---|---|
Continuous | ContinuousVariable |
A continuous variable | ContinuousVariable(name="x0", lower_bound=-100.0, upper_bound=100.0) |
Discrete | DiscreteVariable |
A discrete variable | DiscreteVariable(choices=["scale", "auto", 0.01, 0.1, 0.5, 1.0], name="gamma") |
Permutation | PermutationVariable |
A permutation of the specified choices | PermutationVariable(items=[[60, 200], [180, 200], [80, 180]], name="routes") |
An example of a custom Task
class is the following:
from pyvolutionary import ContinuousVariable, Task
class Sphere(Task):
def objective_function(self, x: list[float]) -> float:
x1, x2 = x
f1 = x1 - 2 * x2 + 3
f2 = 2 * x1 + x2 - 8
return f1 ** 2 + f2 ** 2
# Define the task with the bounds and the configuration of the optimizer
task = Sphere(
variables=[
ContinuousVariable(name="x1", lower_bound=-100.0, upper_bound=100.0),
ContinuousVariable(name="x2", lower_bound=-100.0, upper_bound=100.0),
],
)
You can pass the minmax
parameter to the Task
class to specify whether you want to minimize or maximize the function.
Therefore, if you want to maximize the function, you can write:
from pyvolutionary import ContinuousVariable, Task
class Sphere(Task):
def objective_function(self, x: list[float]) -> float:
x1, x2 = x
f1 = x1 - 2 * x2 + 3
f2 = 2 * x1 + x2 - 8
return -(f1 ** 2 + f2 ** 2)
task = Sphere(
variables=[
ContinuousVariable(name="x1", lower_bound=-100.0, upper_bound=100.0),
ContinuousVariable(name="x2", lower_bound=-100.0, upper_bound=100.0),
],
minmax="max",
)
By default, the minmax
parameter is set to min
. If necessary (e.g., in the implementation of the objective function),
additional data can be injected into the Task
class by using the data
parameter of the constructor. This data can
be accessed by using the data
attribute of the Task
class (see combinatorial example below).
Finally, you can also specify the seed of the random number generator by using the seed
parameter of the definition
of the Task
:
from pyvolutionary import ContinuousVariable, Task
class Sphere(Task):
def objective_function(self, x: list[float]) -> float:
x1, x2 = x
f1 = x1 - 2 * x2 + 3
f2 = 2 * x1 + x2 - 8
return -(f1 ** 2 + f2 ** 2)
task = Sphere(
variables=[
ContinuousVariable(name="x1", lower_bound=-100.0, upper_bound=100.0),
ContinuousVariable(name="x2", lower_bound=-100.0, upper_bound=100.0),
],
minmax="max",
seed=42,
)
Continuous problems
For example, let us inspect how you can solve the continuous sphere problem with the Particle Swarm Optimization algorithm.
from pyvolutionary import ContinuousVariable, ParticleSwarmOptimization, ParticleSwarmOptimizationConfig, Task
# Define the problem, you can replace the following class with your custom problem to optimize
class Sphere(Task):
def objective_function(self, x: list[float]) -> float:
x1, x2 = x
f1 = x1 - 2 * x2 + 3
f2 = 2 * x1 + x2 - 8
return f1 ** 2 + f2 ** 2
# Define the task with the bounds and the configuration of the optimizer
population = 200
dimension = 2
position_min = -100.0
position_max = 100.0
generation = 400
fitness_error = 10e-4
task = Sphere(
variables=[ContinuousVariable(
name=f"x{i}", lower_bound=position_min, upper_bound=position_max
) for i in range(dimension)],
)
configuration = ParticleSwarmOptimizationConfig(
population_size=population,
fitness_error=fitness_error,
max_cycles=generation,
c1=0.1,
c2=0.1,
w=[0.35, 1],
)
optimization_result = ParticleSwarmOptimization(configuration).optimize(task)
You can also specify the mode of the solver by using the mode
argument of the optimize
method.
For instance, if you want to run the Particle Swarm Optimization algorithm in parallel with threads, you can write:
optimization_result = ParticleSwarmOptimization(configuration).optimize(task, mode="thread")
The possible values of the mode
parameter are:
serial
: the algorithm is run in serial mode;process
: the algorithm is run in parallel with processes;thread
: the algorithm is run in parallel with threads.
In case of process
and thread
modes, you can also specify the number of processes or threads to use by using the
n_jobs
argument of the optimize
method:
optimization_result = ParticleSwarmOptimization(configuration).optimize(task, mode="thread", jobs=4)
The optimization result is a dictionary containing the following keys:
evolution
: a list of the agents found at each generationrates
: a list of the fitness values of the agents found at each generationbest_solution
: the best agent found by the algorithm
Explicitly, the evolution
key contains a list of Population
, i.e. a dictionary which agents
key contains a list of
Agent
. The latter is a dictionary composed by the following basic keys:
position
: the position of the agentfitness
: the fitness value of the agentcost
: the cost of the agent
from pydantic import BaseModel
class Agent(BaseModel):
position: list[float]
cost: float
fitness: float
These are the basic information, but each algorithm can add more information to the agent, such as the velocity in the case of PSO.
Discrete problem
A typical problem involving discrete variables is the optimization of the hyperparameters of a Machine Learning model, such as a Support Vector Classifier (SVC). You can use pyVolutionary to accomplish this task. In the following, we provide an example using the Particle Swarm Optimization (PSO) as the optimizer.
from typing import Any
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn import datasets, metrics
from pyvolutionary import (
best_agent,
ContinuousVariable,
DiscreteVariable,
ParticleSwarmOptimization,
ParticleSwarmOptimizationConfig,
Task,
)
# Load the data set; In this example, the breast cancer dataset is loaded.
X, y = datasets.load_breast_cancer(return_X_y=True)
# Create training and test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1, stratify=y)
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)
class SvmOptimizedProblem(Task):
def objective_function(self, x: list[Any]):
x_transformed = self.transform_position(x)
C, kernel = x_transformed["C"], x_transformed["kernel"]
degree, gamma = x_transformed["degree"], x_transformed["gamma"]
svc = SVC(C=C, kernel=kernel, degree=degree, gamma=gamma, probability=True, random_state=1)
svc.fit(X_train_std, y_train)
y_predict = svc.predict(X_test_std)
return metrics.accuracy_score(y_test, y_predict)
task = SvmOptimizedProblem(
variables=[
ContinuousVariable(lower_bound=0.01, upper_bound=1000., name="C"),
DiscreteVariable(choices=["linear", "poly", "rbf", "sigmoid"], name="kernel"),
DiscreteVariable(choices=[*range(1, 6)], name="degree"),
DiscreteVariable(choices=["scale", "auto", 0.01, 0.05, 0.1, 0.5, 1.0], name="gamma"),
],
minmax="max",
)
configuration = ParticleSwarmOptimizationConfig(
population_size=200,
fitness_error=10e-4,
max_cycles=100,
c1=0.1,
c2=0.1,
w=[0.35, 1],
)
result = ParticleSwarmOptimization(configuration).optimize(task)
best = best_agent(result.evolution[-1].agents, task.minmax)
print(f"Best parameters: {task.transform_position(best.position)}")
print(f"Best accuracy: {best.cost}")
You can replace the PSO with any other algorithm implemented in the library.
Combinatorial problem
Within the framework of pyVolutionary for addressing the Traveling Salesman Problem (TSP), a solution is a plausible route signifying a tour that encompasses visiting all cities precisely once and returning to the initial city. Typically, this solution is articulated as a permutation of the cities, wherein each city features exactly once in the permutation.
As an illustration, consider a TSP scenario involving 5 cities denoted as A, B, C, D, and E. A potential solution might be denoted by the permutation [A, B, D, E, C], illustrating the order in which the cities are visited. This interpretation indicates that the tour initiates at city A, proceeds to city B, then D, E, and ultimately C before looping back to city A.
The following code snippet illustrates how to solve the TSP with the Virus Colony Search Optimization algorithm.
from typing import Any
import numpy as np
from pyvolutionary import (
best_agent,
Task,
PermutationVariable,
VirusColonySearchOptimization,
VirusColonySearchOptimizationConfig,
)
from pyvolutionary.helpers import distance
class TspProblem(Task):
def objective_function(self, x: list[Any]) -> float:
x_transformed = self.transform_position(x)
routes = x_transformed["routes"]
city_pos = self.data["city_positions"]
n_routes = len(routes)
return np.sum([distance(
city_pos[route], city_pos[routes[(idx + 1) % n_routes]]
) for idx, route in enumerate(routes)])
city_positions = [
[60, 200], [180, 200], [80, 180], [140, 180], [20, 160],
[100, 160], [200, 160], [140, 140], [40, 120], [100, 120],
[180, 100], [60, 80], [120, 80], [180, 60], [20, 40],
[100, 40], [200, 40], [20, 20], [60, 20], [160, 20]
]
task = TspProblem(
variables=[PermutationVariable(name="routes", items=list(range(0, len(city_positions))))],
data={"city_positions": city_positions},
)
configuration = VirusColonySearchOptimizationConfig(
population_size=10,
fitness_error=0.01,
max_cycles=100,
lamda=0.1,
sigma=2.5,
)
result = VirusColonySearchOptimization(configuration).optimize(task)
best = best_agent(result.evolution[-1].agents, task.minmax)
print(f"Best real scheduling: {task.transform_position(best.position)}")
print(f"Best fitness: {best.cost}")
Utilities
pyVolutionary provides a set of utilities to facilitate the use of the library. For example, you can use the
plot
function to plot the evolution of the algorithm. Its usage is as follows:
plot(function: callable, pos_min: float, pos_max: float, evolution: list[Population])
where:
function
: the function to plot, i.e., the function to optimizepos_min
: the minimum possible coordinates in the search spacepos_max
: the maximum possible coordinates in the search spaceevolution
: the evolution of the algorithm, i.e., the list of the agents found at each generation
It is also possible to inspect an animation of the evolution of the algorithm by using the animate
function:
animate(function: callable, optimization_result: OptimizationResult, pos_min: float, pos_max: float, filename: str)
where:
function
: the same as aboveoptimization_result
: the result of the optimization, i.e., the dictionary returned by theoptimize
methodpos_min
: the same as abovepos_max
: the same as abovefilename
: the name of the file where to save the animation
Furthermore, you can extract the trend of the best agent found by the algorithm by using the best_agent_trend
function:
best_agent_trend(optimization_result: OptimizationResult, iters: list[int] | None = None) -> list[float]
where:
optimization_result
: the same as aboveiters
: a list of the iterations to consider. IfNone
, all the iterations are considered It returns a list of the cost values of the best agent found at each iteration.
If you prefer, you can extract the trend of a specific agent by using the agent_trend
function:
agent_trend(optimization_result: OptimizationResult, idx: int, iters: list[int] | None = None) -> list[float]
where:
optimization_result
: the same as aboveidx
: the index of the agent to consideriters
: the same as above It returns a list of the cost values of the agent at each iteration.
Extending the library
pyVolutionary is designed to be easily extensible. You can add your own algorithms and problems to the library by following the instructions below.
Adding a new algorithm
To add a new algorithm, you need to create a new class that inherits from the OptimizationAbstract
class. The new
class must implement the optimization_step
method, where you can implement your new metaheuristic algorithm.
The constructor of the new class must accept a config
parameter, which is a Pydantic model extending the BaseOptimizationConfig
class. This class contains the parameters of the algorithm, such as the population size, the number of generations, etc.
from pydantic import BaseModel
class BaseOptimizationConfig(BaseModel):
population_size: int
fitness_error: float | None = None
max_cycles: int
The examples listed in the following section can be used as a reference for the implementation of a new algorithm.
Once you created your new classes, you can run the algorithm by calling the optimize
method, which takes as input a
Task
object and returns a dictionary as above described.
Algorithms
The following algorithms are currently implemented in pyVolutionary:
Algorithm | Class | Year | Paper | Example |
---|---|---|---|---|
African Vulture Optimization | AfricanVultureOptimization |
2022 | paper | example |
Ant Colony Optimization | AntColonyOptimization |
2008 | paper | example |
Aquila Optimization | AquilaOptimization |
2021 | paper | example |
Artificial Bee Colony Optimization | BeeColonyOptimization |
2007 | paper | example |
Bacterial Foraging Optimization | BacterialForagingOptimization |
2002 | paper | example |
Bat Optimization | BatOptimization |
2010 | paper | example |
Biogeography-Based Optimization | BiogeographyBasedOptimization |
2008 | paper | example |
Brown-Bear Optimization | BrownBearOptimization |
2023 | paper | example |
Camel Caravan Optimization | CamelCaravanOptimization |
2016 | paper | example |
Cat Swarm Optimization | CatSwarmOptimization |
2006 | paper | example |
Chernobyl Disaster Optimization | ChernobylDisasterOptimization |
2023 | paper | example |
Coral Reef Optimization | CoralReefOptimization |
2014 | paper | example |
Cuckoo Search Optimization | CuckooSearchOptimization |
2009 | paper | example |
Coyotes Optimization | CoyotesOptimization |
2018 | paper | example |
Earthworms Optimization | EarthwormsOptimization |
2015 | paper | example |
Electromagnetic Field Optimization | ElectromagneticFieldOptimization |
2016 | paper | example |
Elephant Herd Optimization | ElephantHerdOptimization |
2015 | paper | example |
Energy Valley Optimization | EnergyValleyOptimization |
2023 | paper | example |
Firefly Swarm Optimization | FireflySwarmOptimization |
2009 | paper | example |
Fire Hawk Optimization | FireHawkOptimization |
2022 | paper | example |
Fireworks Optimization | FireworksOptimization |
2010 | paper | example |
Fish School Search Optimization | FishSchoolSearchOptimization |
2008 | paper | example |
Flower Pollination Algorithm Optimization | FlowerPollinationAlgorithmOptimization |
2012 | paper | example |
Forest Optimization Algorithm | ForestOptimizationAlgorithm |
2014 | paper | example |
Fox Optimization | FoxOptimization |
2023 | paper | example |
Genetic Algorithm Optimization | GeneticAlgorithmOptimization |
1989 | paper | example |
Giza Pyramid Construction Optimization | GizaPyramidConstructionOptimization |
2021 | paper | example |
Grasshopper Optimization Algorithm | GrasshopperOptimization |
2017 | paper | example |
Grey Wolf Optimization | GreyWolfOptimization |
2014 | paper | example |
Harmony Search Optimization | HarmonySearchOptimization |
2001 | paper | example |
Imperialist Competitive Optimization | ImperialistCompetitiveOptimization |
2013 | paper | example |
Invasive Weed Optimization | InvasiveWeedOptimization |
2006 | paper | example |
Krill Herd Optimization | KrillHerdOptimization |
2012 | paper | example |
Levy Flight Jaya Swarm Optimization | LeviFlightJayaSwarmOptimization |
2021 | paper | example |
Monarch Butterfly Optimization | MonarchButterflyOptimization |
2019 | paper | example |
Mountain Gazelle Optimization | MountainGazelleOptimization |
2022 | paper | example |
Osprey Optimization | OspreyOptimization |
2023 | paper | example |
Particle Swarm Optimization | ParticleSwarmOptimization |
1995 | paper | example |
Pathfinder Algorithm Optimization | PathfinderAlgorithmOptimization |
2019 | paper | example |
Pelican Optimization | PelicanOptimization |
2022 | paper | example |
Seagull Optimization | SeagullOptimization |
2019 | paper | example |
Siberian Tiger Optimization | SiberianTigerOptimization |
2022 | paper | example |
Tasmanian Devil Optimization | TasmanianDevilOptimization |
2022 | paper | example |
Virus Colony Search Optimization | VirusColonySearchOptimization |
2016 | paper | example |
Walrus Optimization | WalrusOptimization |
2022 | paper | example |
Whales Optimization | WhalesOptimization |
2016 | paper | example |
Wildebeest Herd Optimization | WildebeestHerdOptimization |
2019 | paper | example |
Zebra Optimization | ZebraOptimization |
2022 | paper | example |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file pyvolutionary-1.1.0.tar.gz
.
File metadata
- Download URL: pyvolutionary-1.1.0.tar.gz
- Upload date:
- Size: 87.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.6.1 CPython/3.10.12 Linux/5.15.0-91-generic
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 39a4cb88777ee427fa592bb6beb80223343eb6598e6da5a828862653c6e273b5 |
|
MD5 | d7d375707713a52b41fbd0e942707d25 |
|
BLAKE2b-256 | 32fe1e6b4064d743829be56d63a1b4dab141e2b0daec1b4ad04974247e4073ca |
File details
Details for the file pyvolutionary-1.1.0-py3-none-any.whl
.
File metadata
- Download URL: pyvolutionary-1.1.0-py3-none-any.whl
- Upload date:
- Size: 155.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.6.1 CPython/3.10.12 Linux/5.15.0-91-generic
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | ed1d5f54792dd0da202d9c68a8da73e3c8c966d740640d9768cd6d44e32d8009 |
|
MD5 | 4d341643c8f130a4d23730211ee219c3 |
|
BLAKE2b-256 | 4da3f894214eac2396d3843836a120ca619ee0f7f0ca2ced6da3da425661576e |