Skip to content

CI coverage tests docs-build-status PyPI Python License code style: ruff OpenSSF Scorecard

max-div logo

max-div

Configurable solver for Maximum Diversity Problems with optional fairness constraints: given n items — vectors or precomputed distances — select the k most diverse, optionally subject to per-group minimum/maximum quotas.

max-div is an anytime heuristic: it returns a good selection quickly and keeps improving it for as long as you allow (wall-clock time or iteration count). Two things set it apart among freely available tools: it is the only dedicated diversity solver with native support for overlapping fairness constraints, and the only one offering a geometric-mean separation objective — one of four diversity metrics it provides (minimum, mean, and geometric-mean separation, plus mean pairwise distance).

Feature comparison of max-div against exact solvers and one-shot pickers: distance metrics, diversity objectives, constraint handling, time budgets and practical scale

It fills the gap between exact MIP/CP solvers, which prove optimal answers but do not scale, and single-shot pickers, which are fast but at best have very limited support for constraints. The benchmarks show where it leads and where it does not.

Installation

pip install max-div

Python 3.11+; free-threaded builds (3.14t) are supported and CI-tested (see the installation notes for the numba version they require).

Quick start

import numpy as np
from max_div import MaxDivProblem, MaxDivSolverBuilder, seconds

rng = np.random.default_rng(42)
vectors = rng.random((200, 5))               # 200 points in 5 dimensions

# select the 20 most diverse, improving for up to 5 seconds
problem = MaxDivProblem.new(vectors, k=20)
solution = MaxDivSolverBuilder(problem).with_preset(seconds(5)).build().solve()

print(solution.i_selected)                   # indices of the selected items

With fairness constraints

Require a minimum and/or maximum number of selected items from given subsets — useful for fair representation across groups. Groups may overlap, and infeasible constraints degrade gracefully to the least-infeasible selection rather than failing.

from max_div import Constraint

# require between 8 and 12 of the selected items from each half of the data
constraints = [
    Constraint(int_set=set(range(0, 100)),   min_count=8, max_count=12),
    Constraint(int_set=set(range(100, 200)), min_count=8, max_count=12),
]
problem = MaxDivProblem.new(vectors, k=20, constraints=constraints)

Documentation

Full documentation lives at max-div.readthedocs.io, including:

License

Licensed under the Apache License 2.0.