Skip to main content

DEFoundry — Differential Evolution

This repository now provides a small, explicit Differential Evolution package with independently composable mutation strategies and crossover operators.

What changed

  • The optimizer is now a real Python package: differential_evolution/
  • Mutation and crossover are separate components
  • Randomness is explicit and reproducible
  • A deterministic test suite verifies formulas and DE invariants
  • The original main.py, population_initialization.py, and testing_functions.py remain as compatibility shims
  • Full SHADE and LSHADE optimizers are available for success-history adaptive DE

Installation

DEFoundry requires Python 3.11 or later and has no mandatory runtime dependencies. The distribution name is defoundry; the Python import is differential_evolution.

python3 -m pip install defoundry

For development, install from a source checkout with python3 -m pip install -e .. Maintainers can follow the release guide to validate and publish distributions.

Core concepts

  • target vector: the current population member being evolved
  • donor vector: the output of a mutation strategy
  • trial vector: the result of applying a crossover operator to the target and donor

The optimizer orchestrates:

  1. population initialization
  2. donor-vector mutation
  3. target/donor crossover
  4. boundary handling
  5. objective evaluation
  6. greedy one-to-one selection

Built-in mutation strategies

  • Rand1
  • Rand2
  • Best1
  • Best2
  • CurrentToBest1
  • CurrentToBest2
  • CurrentToRand1
  • CurrentToRand2
  • TrigonometricMutation
  • DirectedMutation
  • NeighborhoodSearchMutation

Built-in scale-factor controllers

  • ConstantScaleFactor
  • RandomizedScaleFactor
  • AdaptiveScaleFactor

Built-in crossover-rate controllers

  • ConstantCrossoverRate
  • AdaptiveCrossoverRate

These can be passed anywhere a mutation strategy expects scale or difference_scale.

from differential_evolution import AdaptiveScaleFactor, Rand1

mutation = Rand1(scale=AdaptiveScaleFactor(initial=0.5, tau=0.1, lower=0.1, upper=0.9))

For the Brest et al. paper's full jDE control-parameter scheme, pair AdaptiveScaleFactor with AdaptiveCrossoverRate, or use the convenience helper:

from differential_evolution import DifferentialEvolution, jde_rand_1_bin, sphere_function

components = jde_rand_1_bin()
optimizer = DifferentialEvolution(
    objective=sphere_function,
    bounds=[(-5.0, 5.0), (-5.0, 5.0)],
    population_size=20,
    mutation=components.mutation,
    crossover=components.crossover,
    max_generations=100,
    seed=123,
)

Built-in crossover operators

  • BinomialCrossover
  • ExponentialCrossover
  • IdentityCrossover

IdentityCrossover is useful for canonical current-to-rand style runs where no extra crossover is desired.

Built-in diversity measures

  • PopulationDiameter
  • PopulationRadius
  • AverageDistanceAroundPopulationCenter
  • AverageDistanceAroundAllIndividuals
  • PopulationCoherence
  • DimensionalVariance
  • AggregatedDistribution
  • AveragePairwiseDistance

You can disable diversity tracking by leaving diversity_measures=None, or track several measures at once:

from differential_evolution import (
    AveragePairwiseDistance,
    DifferentialEvolution,
    PopulationDiameter,
    Rand1,
    BinomialCrossover,
    sphere_function,
)

optimizer = DifferentialEvolution(
    objective=sphere_function,
    bounds=[(-5.0, 5.0), (-5.0, 5.0)],
    population_size=20,
    mutation=Rand1(scale=0.8),
    crossover=BinomialCrossover(crossover_rate=0.9),
    diversity_measures=[
        PopulationDiameter(),
        AveragePairwiseDistance(),
        "population_coherence",
    ],
    max_generations=50,
    seed=123,
)
result = optimizer.run()
print(result.diversity_history["population_diameter"])

PopulationCoherence compares the movement of the population center between consecutive generations with the average movement of individuals. Its first recorded value is 0.0 because there is no previous generation yet.

AggregatedDistribution is implemented here as a project-specific marginal dispersion statistic: each coordinate is normalized to [0, 1], binned, and summarized by the variance-to-mean ratio of occupancy counts.

Built-in initializers

  • RandomInitializer
  • TentInitializer
  • OppositionInitializer
  • QuasiOppositionInitializer
  • SobolInitializer

SobolInitializer supports up to 40 dimensions and 2**30 sequence points. It applies a seed-dependent digital shift by default; pass scramble=False for the unshifted sequence. See the current algorithm reference.

Population reduction

The optimizer can optionally reduce population size during the run:

  • LinearPopulationReduction
  • HyperbolicTangentPopulationReduction
from differential_evolution import (
    BinomialCrossover,
    DifferentialEvolution,
    LinearPopulationReduction,
    Rand1,
    sphere_function,
)

optimizer = DifferentialEvolution(
    objective=sphere_function,
    bounds=[(-5.0, 5.0), (-5.0, 5.0)],
    population_size=20,
    mutation=Rand1(scale=0.8),
    crossover=BinomialCrossover(crossover_rate=0.9),
    population_schedule=LinearPopulationReduction(min_population_size=4),
    max_evaluations=2000,
    seed=123,
)

LinearPopulationReduction follows the L-SHADE idea of shrinking linearly with used function-evaluation budget when max_evaluations is provided. If max_evaluations is omitted, it falls back to generation progress. HyperbolicTangentPopulationReduction is a project-specific smooth nonlinear schedule.

Supply at least one stopping limit. max_generations defaults to None, so an evaluation budget alone runs until that budget is exhausted. If both limits are supplied, the first one reached stops the run.

When the population is reduced, the worst individuals by current fitness are removed. Users must choose a minimum population size that remains compatible with the mutation strategy in use.

SHADE and L-SHADE

The package also includes dedicated optimizers for the full success-history adaptive algorithms:

  • SHADE
  • LSHADE

SHADE includes:

  • current-to-pbest/1
  • external archive
  • Cauchy sampling of F
  • Gaussian sampling of CR
  • success-history memories M_F and M_CR

LSHADE extends SHADE with linear population size reduction.

from differential_evolution import LSHADE, sphere_function

optimizer = LSHADE(
    objective=sphere_function,
    bounds=[(-5.0, 5.0), (-5.0, 5.0)],
    population_size=18,
    min_population_size=4,
    memory_size=6,
    max_evaluations=2000,
    seed=123,
)
result = optimizer.run()

Saving runs

For post-run investigation, enable record_snapshots=True and save the result:

result = optimizer.run()
result.save_json("shade_run.json")
result.save_pickle("shade_run.pkl")

Each snapshot stores the generation number, evaluation count, best solution, population size, optional population and fitness arrays, diversity values, and algorithm-specific extra state. For SHADE and LSHADE, the extra state includes the external archive and the parameter memories.

Example

from differential_evolution import (
    BinomialCrossover,
    DifferentialEvolution,
    ExponentialCrossover,
    Rand1,
    Best1,
    sphere_function,
)

optimizer = DifferentialEvolution(
    objective=sphere_function,
    bounds=[(-5.0, 5.0), (-5.0, 5.0)],
    population_size=20,
    mutation=Rand1(scale=0.8),
    crossover=BinomialCrossover(crossover_rate=0.9),
    max_generations=50,
    seed=123,
)
result = optimizer.run()

optimizer_alt = DifferentialEvolution(
    objective=sphere_function,
    bounds=[(-5.0, 5.0), (-5.0, 5.0)],
    population_size=20,
    mutation=Best1(scale=0.5),
    crossover=ExponentialCrossover(crossover_rate=0.8),
    max_generations=50,
    seed=123,
)
result_alt = optimizer_alt.run()

Custom components

Any callable with the right signature can be injected.

class MyMutation:
    def __call__(self, context):
        target = context.population[context.target_index]
        return [value * 0.5 for value in target]


class MyCrossover:
    def __call__(self, target_vector, donor_vector, rng):
        trial = list(target_vector)
        trial[0] = donor_vector[0]
        return trial

Reproducibility

Use seed= or pass a custom RNG object. The optimizer does not use module-level global random state.

Compatibility

The original constructor remains available from a source checkout only:

from main import DifferentialEvolution

Installed users can use from differential_evolution.compat import DifferentialEvolution. Both paths emit a deprecation warning and forward into the new implementation. Details are in MIGRATION.md.

Verification

  • algorithm audit: ALGORITHM_AUDIT.md
  • migration notes: MIGRATION.md
  • tests: python3 -m unittest discover -s tests -v
  • examples: python3 -m examples.basic_usage, python3 -m examples.custom_components, python3 -m examples.diversity_measures, and python3 -m examples.population_initialization

Review corrections and compatibility

See review fixes for changes and the disposition of the reported findings. Plain DE now clips to bounds by default; SHADE/L-SHADE use parent-aware midpoint repair. Mutation and crossover components are copied per optimizer; inspect adapted state on optimizer.mutation and optimizer.crossover. run() is single-use. result.success means a usable objective value was found, not numerical convergence; result.message reports the exhausted limit.

Sobol initialization uses a seeded digital shift by default. Use SobolInitializer(scramble=False) for the original unshifted sequence. All fixed-dimensional benchmarks, including Ackley, require exactly two coordinates.

Metadata

Release files for defoundry 0.1.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 defoundry 0.1.0
File Size Uploaded Provenance
defoundry-0.1.0.tar.gz 98.7 kB With provenance Details

Built distribution (wheel)

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

Total release size: 139.9 kB

Release files / defoundry-0.1.0.tar.gz

Download URL defoundry-0.1.0.tar.gz
Size 98.7 kB
Tags Source
SHA-256 checksum
How to use checksums
fd7c53f5143cd2dc488e6e253b289938b792cfecef688bfe6df85aec88b7dab9
BLAKE2b-256 checksum
How to use checksums
56550cc82c9bad10f7ce6b7984c4d87d0eb1e57f1603382f1a399f8d6ca69a91
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 9, 2026.

Transparency log

Release files / defoundry-0.1.0-py3-none-any.whl

Download URL defoundry-0.1.0-py3-none-any.whl
Size 41.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2a17fc7ea3adf37b5253a76219d723327bf8c9696a38f5603e0901dbb6393bae
BLAKE2b-256 checksum
How to use checksums
42dc93cff778f286a1e17d6f871395b8a9a4f2a795501a98ab71c6f7d4e8843f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 9, 2026.

Transparency log

Security for defoundry 0.1.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for defoundry-0.1.0.tar.gz

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 Oct 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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