Library / SDK
jMetal/jMetalPy avatar
jMetal/jMetalPy

jMetalPy: metaheuristic optimization in Python, with the algorithm catalogue already written

A framework for single/multi-objective optimization with metaheuristics

616 stars157 forksJupyter NotebookMIT

At a glance

What is it?
jMetalPy is a Python port of the jMetal framework for single and multi-objective optimization, shipping around twenty algorithms, a set of standard benchmark problems and the statistical tooling to compare runs. It is a library for people who would rather not implement NSGA-II from scratch.
Who is it for?
Adopt jMetalPy if you are running multi-objective experiments in Python and want a working NSGA-II, SMPSO or MOEA/D implementation, standard benchmark problems and hypothesis testing in the same library, without writing the operators yourself. Do not adopt it if you need a general purpose hyperparameter tuner for a single objective model, since its design is centred on evolutionary search over populations and Pareto fronts.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Jupyter Notebook, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

jMetalPy is the Python face of the jMetal framework

jMetalPy is described as a Python version of the jMetal framework, and the project description calls it a framework for single and multi-objective optimization with metaheuristics. The Java original is the reference point: the project states that an Experiment class exists for performing studies either alone or alongside jMetal, which tells you the two implementations are meant to be comparable rather than competing.

That framing matters for who should use it. Metaheuristics are algorithms that search for good solutions without a gradient or a convex objective, and they are the tool of choice when your problem is a simulation, a scheduling problem, a multi-objective trade-off, or a combinatorial mess. jMetalPy hands you a catalogue of them, the operators they need, and a set of standard benchmark problems to check your setup against before you trust a result.

The primary language recorded for the repository is Jupyter Notebook, and there is a notebooks folder at the root, which fits a project meant to be explored interactively as much as scripted.

The catalogue covers evolutionary, swarm and hypervolume based search

The features list for v1.9.0 is the fastest way to understand the scope. The algorithm line includes local search, genetic algorithm, evolution strategy, simulated annealing and random search on the single objective side, and NSGA-II, NSGA-III, SMPSO, OMOPSO, MOEA/D, SMS-EMOA, MOEA/D-DRA, MOEA/D-IEpsilon, GDE3, SPEA2, HYPE, IBEA and MOCell for multi-objective work. There are also preference articulation variants, named G-NSGA-II, G-GDE3 and G-SPEA2, plus SMPSO/RP, and dynamic versions of NSGA-II, SMPSO and GDE3.

The operators underneath are shared across those algorithms: selection by binary tournament, ranking and crowding distance, random, n-ary random and best solution; crossover including single-point, SBX, PMX, CX, BLX-Alpha, BLX-Alpha-Beta, arithmetic, UNDX and differential evolution; mutation by bit-flip, polynomial, uniform, random, non-uniform, Levy flight and power-law.

Encodings are real, integer, binary and permutation. This is the part most often missing from a hand rolled optimizer: an algorithm is only half the work, and the other half is representing a solution so the operators can move it around without breaking its constraints.

The API is problem, algorithm, operators, termination

The documented hello world configures NSGA-II on the ZDT1 benchmark, which is the standard two-objective test problem. The object graph is worth reading closely, because it is the pattern you will repeat for every experiment.

python
from jmetal.algorithm.multiobjective import NSGAII
from jmetal.operator.crossover import SBXCrossover
from jmetal.operator.mutation import PolynomialMutation
from jmetal.problem import ZDT1
from jmetal.util.termination_criterion import StoppingByEvaluations

problem = ZDT1()

The algorithm is then built from the problem, a population size, an offspring population size, a mutation operator and a crossover operator, with the mutation probability derived from the problem's own variable count:

python
algorithm = NSGAII(
    problem=problem,
    population_size=100,
    offspring_population_size=100,
    mutation=PolynomialMutation(probability=1.0 / problem.number_of_variables(), distribution_index=20),
    crossover=SBXCrossover(probability=1.0, distribution_index=20),
    termination_criterion=StoppingByEvaluations(max_evaluations=25000)
)

algorithm.run()

Note what the termination criterion does and does not say. StoppingByEvaluations counts objective function evaluations, not wall clock time, which is the right default for comparing algorithms fairly, and the wrong default when a run must finish before a deadline.

Results come back as a set of non dominated solutions

After a run, the results are read from algorithm.result() and filtered for the non dominated set, which is the Pareto front approximation for the run. The utilities write the front to files in the format the jMetal ecosystem expects, which is what makes a Python run comparable with a Java one.

python
from jmetal.util.solution import get_non_dominated_solutions, print_function_values_to_file, print_variables_to_file

front = get_non_dominated_solutions(algorithm.result())

# save to files
print_function_values_to_file(front, 'FUN.NSGAII.ZDT1')
print_variables_to_file(front, 'VAR.NSGAII.ZDT1')

Visualisation is a first class part of the library rather than a separate script. The lab module provides a Plot class, and the README shows a two-objective front plotted with axis labels and a filename and format, which produces a png of the front. The features list also mentions real time, static and interactive Pareto front plotting, and the documentation shows scatter plots in two and three dimensions, parallel coordinates and an interactive chord plot.

For a multi-objective study, being able to look at the front is not decoration. A scalar score tells you almost nothing about the shape of the trade-off you are choosing between.

pip install jmetalpy leaves out the distributed machinery on purpose

Installation is a single pip command, and the choice of extras is where the first real decision sits:

bash
pip install jmetalpy  # or "jmetalpy[distributed]"

The details panel spells out the reasoning. The core install comprises the core functionality of the framework, described as enough for most users, and is equivalent to running pip install "jmetalpy[core]". The parallel and distributed computing features are based on pySpark and Dask, and those extra dependencies are not installed automatically.

The other supported variants are:

bash
pip install "jmetalpy[dev]"  # Install requirements for development
pip install "jmetalpy[distributed]"  # Install requirements for parallel/distributed computing
pip install "jmetalpy[complete]"  # Install all requirements

So the default install is a deliberate choice by the maintainers, not an oversight. If your study needs to spread evaluations across a cluster, you have to ask for it explicitly.

The core dependencies are numpy, pandas, scipy, matplotlib, plotly, statsmodels, tqdm and moocore, and the manifest requires Python 3.11 or newer, with classifiers for 3.11 and 3.12.

Quality indicators and hypothesis testing are what make the results defensible

The features list includes quality indicators for hypervolume, normalized hypervolume, additive epsilon, GD, IGD, IGD+ and average Hausdorff distance. These are the standard measures used to compare approximation sets, and the presence of normalized hypervolume and the IGD family tells you the library handles both the indicator and the reference front, which is where a lot of home grown comparison code goes wrong.

There is also an Experiment class for running studies, and support for pairwise and multiple hypothesis testing for statistical analysis, covering several frequentist and Bayesian methods, critical distance plots and posterior diagrams. That combination matters: critical distance plots are how you show which of several algorithms is statistically distinguishable from which, rather than reporting a mean and a standard deviation and hoping the reader infers significance.

A console entry point is declared in the manifest, jmetalpy-quality-indicator, so indicators can be computed from the command line as well as from Python. The project also links a journal paper introducing jMetalPy at doi 10.1016/j.swevo.2019.100598, and ships a CITATION.cff for citation tooling.

Where the library stops and your own code begins

Two boundaries are worth understanding before you adopt it. The first is that jMetalPy is an algorithmic framework, not a general purpose hyperparameter tuner. It searches populations of solutions over a problem object you implement, and it expects you to supply the problem evaluation. If you want a tuner for a single objective model with a handful of parameters, a different category of tool fits better, and jMetalPy's machinery would be overhead.

The second boundary is the statistical gap between a library result and a published claim. The project gives you indicators and hypothesis testing, which is more than most optimisation libraries do, but the benchmark problems, including ZDT1-6, DTLZ1-7, WFG1-9, ZCAT1-20, eqDTLZ, FDA, LZ09, UF, LIR-CMOP, RWA and RE, are test problems with known structure. Success on ZDT1 says little about your own problem, and the framework will not tell you otherwise.

The counterargument is real as well. Reimplementing SBX crossover, polynomial mutation, crowding distance selection and hypervolume calculation correctly takes weeks and is a common source of silent bugs, and this library is the maintained Python side of a framework with a paper behind it.

Active maintenance, a permissive licence, and a toolchain to match

The repository is not archived and the last push was on 2026-09-11. Releases are recent too: v1.9.0 on 2025-10-29, v1.8.0 on 2025-10-09 and v1.7.0 on 2024-05-29. Two releases close together in October 2025 suggests an active period after a long gap, and CHANGELOG.md is the place to see what changed between them.

The licence is MIT, which is permissive with no copyleft obligation, and the project is published on PyPI under the name jmetalpy. The manifest also classifies the package as Development Status :: 5 - Production/Stable and Intended Audience :: Science/Research, which is a useful signal about who the maintainers think it is for.

For contributors, the Makefile is the map of the development tasks. The interesting ones are the strict ones: docs builds with mkdocs build --strict, so a documentation warning fails the build, and packaging runs python -m build followed by python -m twine check dist/*. Tests are marked with smoke, slow and integration markers, and lint and format both run ruff. There is also an AGENTS.md with guidelines for people using AI assistants on the codebase, which is unusual to see in a research framework and says something about how the project expects contributions to be made.

Editorial conclusion

Adopt jMetalPy if you are running multi-objective experiments in Python and want a working NSGA-II, SMPSO or MOEA/D implementation, standard benchmark problems and hypothesis testing in the same library, without writing the operators yourself. Do not adopt it if you need a general purpose hyperparameter tuner for a single objective model, since its design is centred on evolutionary search over populations and Pareto fronts. Before you install, check the extras you actually need: a plain pip install jmetalpy gives you the core, while pip install "jmetalpy[distributed]" adds the pySpark and Dask machinery that the base install leaves out.

Frequently asked questions

How do I install jMetalPy?

Run `pip install jmetalpy` for the core framework, which the project says is enough for most users. Use `pip install "jmetalpy[distributed]"` to add the parallel and distributed computing features based on pySpark and Dask, `pip install "jmetalpy[dev]"` for development requirements, or `pip install "jmetalpy[complete]"` for everything. Python 3.11 or newer is required.

How do I run NSGA-II in jMetalPy?

Create the problem, for example ZDT1(), then build the algorithm with a population size, an offspring population size, a mutation operator such as PolynomialMutation, a crossover operator such as SBXCrossover, and a termination criterion such as StoppingByEvaluations, then call algorithm.run(). Results are read from algorithm.result() and filtered with get_non_dominated_solutions.

Does jMetalPy support parallel and distributed computing?

Yes, based on Apache Spark and Dask. Those extra dependencies are not installed by a plain pip install jmetalpy, so you have to install the distributed extra to use them.

Which algorithms and benchmark problems does jMetalPy include?

The v1.9.0 feature list names algorithms including NSGA-II, NSGA-III, SMPSO, OMOPSO, MOEA/D, SMS-EMOA, GDE3, SPEA2, HYPE, IBEA and MOCell, plus preference articulation and dynamic variants. Benchmark problems include ZDT1-6, DTLZ1-7, WFG1-9, ZCAT1-20, eqDTLZ, FDA, LZ09, UF, LIR-CMOP, RWA and RE.

Can I compare the results of different algorithms statistically in jMetalPy?

The project includes an Experiment class for running studies, quality indicators such as hypervolume, GD, IGD and IGD+, and pairwise and multiple hypothesis testing with critical distance plots and posterior diagrams.

Official sources

  1. jMetal/jMetalPy on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jmetal-jmetalpy.svg)](https://hysenlabs.com/projects/jmetal-jmetalpy)

Was this useful?