Skip to content

michaelmillar/pareto-front

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pareto

Lightweight Pareto toolkit for Python data workflows

Non-dominated sorting, crowding distance, and ranking with per-objective min/max directions.
NumPy-only core. Fully typed. No framework lock-in.


demo.mp4

What it does

Pareto takes an array of multi-objective data and sorts it into Pareto fronts. Each objective can independently minimise or maximise. It handles ranking, diversity selection, and weighted scoring without pulling in an optimisation framework.

import numpy as np
from pareto import non_dominated_sort, ParetoRanker

candidates = np.array([
    [0.95, 120, 0.02],   # accuracy, latency_ms, cost
    [0.91, 45,  0.08],
    [0.88, 30,  0.15],
    [0.93, 80,  0.05],
    [0.80, 200, 0.25],
])

fronts = non_dominated_sort(
    candidates,
    senses=["max", "min", "min"],
)

ranker = ParetoRanker(senses=["max", "min", "min"])
top_3 = ranker.select(candidates, k=3)
fronts[0] = [0, 1, 2, 3]     # non-dominated set
fronts[1] = [4]               # dominated

top_3     = [0, 2, 1]         # best trade-offs by rank + crowding distance

Install

pip install pareto-front

For visualisation helpers:

pip install pareto-front[viz]

Quick start

from pareto import non_dominated_sort, crowding_distance

fronts = non_dominated_sort(objectives)
distances = crowding_distance(objectives[fronts[0]])

Every function accepts an optional senses parameter. Pass "min" or "max" per objective, or use the Sense enum:

from pareto import Sense

fronts = non_dominated_sort(objectives, senses=[Sense.MIN, Sense.MAX])

Rankers store senses on construction:

from pareto import ParetoRanker, WeightedRanker

ranker = ParetoRanker(senses=["min", "max", "min"])
result = ranker.rank(objectives)
top_k  = ranker.select(objectives, k=5)

weighted = WeightedRanker(weights=[1.0, 2.0, 0.5], senses=["min", "max", "min"])
scores = weighted.scores(objectives)

How it compares

Every existing tool is either a full optimisation framework that happens to include sorting, or a narrow utility that only extracts the non-dominated set. Nothing sits in between with typed APIs, per-objective directions, and ranking out of the box.

Tool Sorting Crowding Ranking Senses Typed Dependencies
pymoo Yes Yes NSGA-II only Framework config Partial scipy, matplotlib, many
pygmo Yes Yes sort_population_mo All-minimise No (C++ bindings) pagmo2 (C++)
DEAP Yes Yes NSGA-II selection All-minimise No None heavy, but no types
Optuna Yes Partial Internal only Framework config Partial sqlalchemy, many
paretoset Pareto set only No No Yes No numpy
fast-pareto Pareto set only No No No Partial numpy
pareto Yes Yes ParetoRanker + WeightedRanker Per-objective Full (py.typed) numpy

Where pareto is stronger. Single dependency (numpy). Full typing with py.typed marker. Per-objective min/max on every function. Combined ranking (front + crowding distance) and weighted scoring as first-class features, not buried inside an optimiser. O(N log N) fast path for 2D problems. Clean API that works directly with numpy arrays.

Where pareto is weaker. pymoo and pygmo have far more algorithms (NSGA-III, reference-point methods, hypervolume indicators, decomposition). DEAP has evolutionary operators. Optuna handles the entire hyperparameter loop. Pareto has none of these and is not trying to. If you need a full multi-objective optimiser, use one of those.

The closest alternative is paretoset. It extracts the Pareto-optimal set from a DataFrame with per-objective directions. But it stops there: no crowding distance, no front ranking beyond the first front, no weighted scoring, no visualisation. Pareto picks up where paretoset leaves off.

Benchmarks

non_dominated_sort on random uniform data (median of 5 runs, seed 42). 2D uses an O(N log N) sweep; 3D+ uses vectorised pairwise comparison.

Points Objectives Time (ms)
100 2 0.8
1,000 2 11.6
5,000 2 42.1
100 5 14.1
1,000 5 203.4
5,000 5 4,018.2

Status

73 tests. Pure Python + NumPy. Zero compiled extensions.

Not yet implemented:

  • pandas/polars DataFrame integration (accepts via __array__ protocol, but no native returns)
  • Hypervolume indicator
  • Epsilon-dominance
  • NSGA-III reference-point sorting
  • Parallel/threaded sorting for large N

Licence

Apache-2.0

About

Lightweight Pareto toolkit for Python data workflows. Non-dominated sorting, crowding distance, and ranking with per-objective min/max directions.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors