Skip to main content

Evo‑NOMAD

Evo‑NOMAD is a flexible evolutionary optimizer that couples global search (crossover + mutation) with local, derivative-free refinement via the NOMAD solver, exposed through PyNomad.

Why Evo-NOMAD?

  • Derivative‑free continuous control – no action discretisation, no back‑prop through time; handles dense recurrence gracefully.
  • Prior‑aware search – starts from a biological connectome (or any strong prior).
    • Pure mode: many tiny edits → minimal drift.
    • Hybrid mode: < 50 edits → structural fidelity.
  • Embarrassingly parallel – flip on Ray to scale linearly with CPU cores.

Installation

pip install EVO-NOMAD

# Development version (clone + editable install)
git clone https://github.com/greenfire0/Evo-NOMAD.git
cd Evo-NOMAD
pip install -e .[ray]  # optional: Ray for parallel NOMAD calls

Requirements: Python >= 3.9 · NumPy >= 1.23 · tqdm · PyNomad >= 0.9 · (optional: ray)


Quick start

import numpy as np
from EVONOMAD import EVONOMAD

obj = lambda x: -np.sum(x**2)  # maximise => global optimum at x = 0

opt = EVONOMAD(
    "pure",                          # or "hybrid"
    population_size=32,
    dimension=10,
    objective_fn=obj,
    subset_size=5,
    bounds=0.2,
    max_bb_eval=100,
    n_mutate_coords=2,
)

best_x, best_fit = opt.run(generations=100)
print(f"Best fitness: {best_fit:.4f}")

API

EVONOMAD(
    optimizer_type: Literal["pure", "hybrid"],
    population_size: int,
    dimension: int,
    objective_fn: Callable[[np.ndarray], float],
    subset_size: int = 20,
    bounds: float = 0.1,
    max_bb_eval: int = 200,
    n_elites: int | None = None,
    n_mutate_coords: int = 5,
    crossover_rate: float = 0.5,
    crossover_type: Literal["uniform", "fitness"] = "uniform",
    crossover_exponent: float = 1.0,
    init_pop: np.ndarray | None = None,
    init_vec: np.ndarray | None = None,
    low: float = -1.0,
    high: float = 1.0,
    use_ray: bool | None = None,
    seed: int | None = None,
)

Methods

Evo‑NOMAD offers two training strategies that differ only in when and how NOMAD is invoked within the evolutionary loop.

Pure mode

Every generation, each individual in the population is passed to NOMAD for local refinement:

  1. Slice selection – Pick subset_size coordinates at random (≤ 49, per NOMAD’s convergence guarantees).
  2. Local search – Run PyNomad with a ±bounds hyper‑rectangle around that slice and a budget of max_bb_eval evaluations.
  3. Replacement – If the refined individual improves its fitness, it replaces the original.
  4. Reproduction – Select the top n_elites by fitness, then fill the rest of the population via fitness‑proportional crossover (probability crossover_rate) followed by random‑reset mutation (n_mutate_coords coordinates).

Pure mode tends to make many small synaptic adjustments, keeping the overall L2 distance to the original connectome low while steadily improving reward.

Hybrid mode

An evolutionary mutation proposes a sparse change‑set first; NOMAD then fine‑tunes only those altered weights:

  1. Mutation – Each offspring mutates a random subset of weights (usually < 50).
  2. Targeted NOMAD – If the diff mask is novel and < 50 coords, run PyNomad only on that mask.
  3. Evaluation & elitism – Update fitness, retain best individuals, proceed with crossover/mutation.

Hybrid mode yields comparable rewards to Pure mode while changing far fewer synapses – ideal when biological plausibility demands minimal rewiring.

Key hyper‑parameters (shared):

name effect
subset_size # parameters NOMAD refines per call (≤ 49)
bounds half‑width of the NOMAD search box
max_bb_eval NOMAD evaluations per call
n_mutate_coords coordinates reset per mutation

Testing

pip install -e .[dev]  # includes pytest, ruff, black, etc.
pytest -q              # run smoke + reproducibility tests

Contributing

  1. Fork + create a feature branch
  2. Run pre-commit install
  3. Add unit tests for new behavior
  4. PR + short summary of the change

License

MIT License — see LICENSE file


Acknowledgements

  • Hi my name is miles, I hope you enjoy these algorithms and optimize some cool shit using them <3
  • PyNomad
  • NOMAD team at Polytechnique Montréal / GERAD

Metadata

Release files for EVONOMAD 0.1.1

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

Source distribution (sdist)

Source distribution for EVONOMAD 0.1.1
File Size Uploaded
evonomad-0.1.1.tar.gz 31.6 kB Details

Built distribution (wheel)

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

Total release size: 61.5 kB

Release files / evonomad-0.1.1.tar.gz

Download URL evonomad-0.1.1.tar.gz
Size 31.6 kB
Tags Source
SHA-256 checksum
How to use checksums
41572359836dceaf302764be8276fd3699a3922d6ac58a1db63ca0dd6701cc90
BLAKE2b-256 checksum
How to use checksums
9f8561678dcd706ea45daeb2713e548e8af38fc3ee6552e063c6d1cbc765f687
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / evonomad-0.1.1-py3-none-any.whl

Download URL evonomad-0.1.1-py3-none-any.whl
Size 29.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
64f599f4a2aac4dbe5e3b0309517910ef41e252b736742bdb198bf8bed045801
BLAKE2b-256 checksum
How to use checksums
953f99d15df6fa68ee64be37c5eef1274ab395371f8f7123811aef377ea98738
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

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