Skip to main content

amcpso — Adaptive Mirror-Consensus Particle Swarm Optimization

amcpso is a gradient-assisted particle swarm optimizer that evolves particles in the dual space of a strongly convex mirror potential. This buys two things classical PSO doesn't have:

  • Feasibility by construction. With the entropic mirror, every iterate is automatically a valid point on the probability simplex (x_k >= 0, sum(x) == 1) — no clip-and-renormalize projection distorting the search.
  • Entropy-regulated annealing. A swarm-dispersion signal r_t in [0, 1] closes a feedback loop that cools exploration (noise, consensus pull) automatically as the swarm converges, instead of using a fixed schedule.

It combines five forces per iteration — inertia, natural-gradient descent, mirrored cognitive/social attraction, graph-Laplacian consensus over a time-varying neighborhood topology, and annealed Gaussian noise.

Installation

pip install amcpso

Requires Python >= 3.8 and NumPy >= 1.20.

When to use it (and when not to)

Use amcpso when your problem is constrained, geometry-sensitive, and/or multimodal:

  • Simplex-constrained problems — recovering or optimizing over a probability distribution, mixture weights, portfolio allocations, or any compositional/proportion data (h_type='entropic'). This is where the mirror machinery pays off most: feasibility is structural, not statistical, and the algorithm is significantly better than projection-based PSO in the reference benchmarks.
  • Rugged, multimodal landscapes in ordinary R^d (h_type='euclidean'), such as the Rastrigin function. The entropy-regulated schedule and local (rather than global-best) topology help delay premature convergence to a poor local minimum, and it is competitive-to-better than classical constriction PSO in the reference benchmarks.
  • You have (or can cheaply compute, e.g. via autodiff or finite differences) a gradient of the objective — amcpso is gradient-assisted, not a purely black-box method.

Don't expect a win when:

  • Your objective is smooth and unimodal (e.g. a plain quadratic bowl) — classical PSO's noiseless contraction is typically competitive or better here, consistent with the No-Free-Lunch theorem.
  • You have no usable gradient and computing one is expensive or impossible — consider a derivative-free method instead.

Quick start

import numpy as np
from amcpso import amc_pso

def sphere(x):
    return float(np.sum(x**2))

def sphere_grad(x):
    return 2 * x

x_best, f_best = amc_pso(sphere, sphere_grad, d=5, seed=0)
print(x_best, f_best)

The function: amc_pso(...)

from amcpso import amc_pso

x_best, f_best = amc_pso(
    f, grad_f, d,
    N=50, T=500, h_type='euclidean', init_range=(-5, 5),
    beta=0.7, eta0=0.05, c10=1.5, c20=1.5, lambda0=0.3,
    T0=0.5, alpha=5.0, gamma=1.0, bandwidth=2.0, epsilon=1e-10,
    kappa=1e-3, p=0.5, G_max=10.0, v_max=None,
    natural='diag', naive=False, entropy_feedback=True,
    return_history=False, seed=None,
)

Required arguments

Argument Type Description
f callable Objective function f(x) -> float, x is shape (d,). The function being minimized.
grad_f callable Gradient grad_f(x) -> array of shape (d,). Required — amcpso is gradient-assisted.
d int Problem dimension.

Key optional arguments

Argument Default Description
N 50 Swarm size.
T 500 Number of iterations (optimization budget).
h_type 'euclidean' 'euclidean' for unconstrained R^d; 'entropic' for the probability simplex (feasibility for free).
init_range (-5, 5) Per-coordinate init range, used only for h_type='euclidean'.
beta 0.7 Inertia weight, in [0, 1).
eta0, p 0.05, 0.5 Step-size schedule eta_t = eta0 / (1 + t) ** p.
c10, c20 1.5, 1.5 Initial cognitive / social gains (linearly annealed to 0).
lambda0 0.3 Base consensus gain; effective gain scales with the dispersion ratio r_t.
T0, alpha, gamma 0.5, 5.0, 1.0 Exploration temperature schedule.
bandwidth, kappa 2.0, 1e-3 Gaussian-kernel neighborhood topology bandwidth and cutoff.
G_max 10.0 Gradient-norm clip (rescales, doesn't truncate).
v_max None Optional dual-velocity norm clip for extra robustness.
natural 'diag' Natural-gradient preconditioner for the entropic mirror: 'diag' or 'fisher'.
return_history False If True, also return a dict of per-iteration traces (best, r, T, lam, eta).
seed None RNG seed for reproducibility.
naive, entropy_feedback False, True Ablation flags — leave at defaults for actual optimization; see the docstring for details.

Full parameter-by-parameter documentation is available in the function's docstring: help(amc_pso).

Returns

  • x_best (numpy.ndarray, shape (d,)) — best position found.
  • f_best (float) — objective value at x_best.
  • history (dict, only if return_history=True) — per-iteration traces.

Example: multimodal optimization (Rastrigin, R^d)

This is the best-use-case scenario for the Euclidean mirror: a rugged, multimodal landscape.

import numpy as np
from amcpso import amc_pso
from amcpso.benchmarks import rastrigin, rastrigin_grad

d = 10
x_best, f_best = amc_pso(
    rastrigin, rastrigin_grad, d=d,
    h_type='euclidean', init_range=(-5.12, 5.12),
    N=50, T=500, seed=7,
)
print(f"best objective: {f_best:.4f}")

Example: constrained optimization on the probability simplex

This is the best use case for amcpso: recovering a probability vector x* on the simplex from noisy linear measurements b = A @ x*, where every iterate must satisfy x >= 0 and sum(x) == 1.

import numpy as np
from amcpso import amc_pso

rng = np.random.default_rng(12345)
d = 20
A = rng.standard_normal((40, d))
x_star = rng.dirichlet(np.ones(d))   # ground truth on the simplex
b = A @ x_star

def loss(x):
    return float(np.sum((A @ x - b) ** 2))

def loss_grad(x):
    return 2.0 * A.T @ (A @ x - b)

x_best, f_best = amc_pso(loss, loss_grad, d=d, h_type='entropic', seed=0)

print("sum(x_best) =", x_best.sum())      # 1.0, always -- feasible by construction
print("min(x_best) =", x_best.min())      # >= 0, always
print("loss        =", f_best)

Unlike classical PSO with clip-and-renormalize projection, amc_pso with h_type='entropic' never produces an infeasible iterate at any point during the search — the softmax inverse mirror map guarantees it structurally.

Diagnosing convergence with return_history

x_best, f_best, hist = amc_pso(
    rastrigin, rastrigin_grad, d=10,
    h_type='euclidean', init_range=(-5.12, 5.12),
    seed=7, return_history=True,
)

# hist['best']: best-so-far objective per iteration
# hist['r']:    dispersion ratio r_t in [0, 1]
# hist['T']:    temperature T_t
# hist['lam']:  consensus gain lambda_t
# hist['eta']:  step size eta_t

Algorithm summary

Each particle keeps a primal position x, a dual variable y = grad_h(x), and a dual velocity v. Every iteration:

  1. Rebuilds a time-varying neighborhood topology from a thresholded Gaussian kernel on pairwise distances.
  2. Measures swarm dispersion S_t (half log-determinant of the swarm covariance) and derives a dispersion ratio r_t = min(1, exp((S_t - S_0) / d)), which drives the consensus gain and temperature schedules.
  3. Updates the dual velocity as the sum of inertia, natural-gradient descent, cognitive attraction, social attraction, Laplacian consensus, and annealed noise.
  4. Maps back to the primal space with the inverse mirror map (x = grad_h_star(y)), which is the identity for the Euclidean mirror and a softmax for the entropic mirror.

See the function docstring (help(amc_pso)) and the accompanying manuscript for the full derivation.

License

MIT — see LICENSE.

Metadata

Release files for amcpso 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for amcpso 1.0.0
File Size Uploaded
amcpso-1.0.0.tar.gz 15.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for amcpso 1.0.0
File Interpreter ABI Platform
amcpso-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.6 kB

Release files / amcpso-1.0.0.tar.gz

Download URL amcpso-1.0.0.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
956514d502219ccb6ca91091b65d53c12f9f673a14822f98370209566bf5dead
BLAKE2b-256 checksum
How to use checksums
bd285973b1d4b10fb6ebb972abb7e4d7f8429ba455d1a593374afe0db41fbe60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.

Transparency log

Release files / amcpso-1.0.0-py3-none-any.whl

Download URL amcpso-1.0.0-py3-none-any.whl
Size 12.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
21d267fa6bb65dd77da9cbee66fc4781488b9eb4f1ceae3a5c8a0a9b66f72eed
BLAKE2b-256 checksum
How to use checksums
5a4e96bbc52f10d307e5733e1a42d5a0e987e1cdf2d99881b94516880616b4bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page