AiDotNet.Evolution
0.1.0
dotnet add package AiDotNet.Evolution --version 0.1.0
NuGet\Install-Package AiDotNet.Evolution -Version 0.1.0
<PackageReference Include="AiDotNet.Evolution" Version="0.1.0" />
<PackageVersion Include="AiDotNet.Evolution" Version="0.1.0" />
<PackageReference Include="AiDotNet.Evolution" />
paket add AiDotNet.Evolution --version 0.1.0
#r "nuget: AiDotNet.Evolution, 0.1.0"
#:package AiDotNet.Evolution@0.1.0
#addin nuget:?package=AiDotNet.Evolution&version=0.1.0
#tool nuget:?package=AiDotNet.Evolution&version=0.1.0
<h1 align="center">AiDotNet.Evolution</h1>
<p align="center"> <strong>Evolutionary search for .NET that keeps the receipts — deterministic, budgeted, and resumable.</strong> </p>
<p align="center"> <a href="https://www.nuget.org/packages/AiDotNet.Evolution"><img src="https://img.shields.io/nuget/vpre/AiDotNet.Evolution?logo=nuget" alt="NuGet"></a> <a href="https://github.com/ooples/AiDotNet.Evolution/actions/workflows/build.yml"><img src="https://github.com/ooples/AiDotNet.Evolution/actions/workflows/build.yml/badge.svg?branch=main" alt="Build and Test"></a> <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue.svg" alt="Apache 2.0"></a> <img src="https://img.shields.io/badge/.NET-10%20%7C%208%20%7C%20Framework%204.7.1-512BD4?logo=dotnet&logoColor=white" alt=".NET 10, .NET 8, .NET Framework 4.7.1"> </p>
<p align="center"> <img src="https://img.shields.io/badge/deps-none%20on%20.NET%208%20%2F%2010-2ea043" alt="No third-party dependencies on .NET 8 and .NET 10"> <img src="https://img.shields.io/badge/LLM-optional-8b5cf6" alt="LLM optional"> <img src="https://img.shields.io/badge/replay-byte--identical-3b82f6" alt="Deterministic replay"> <img src="https://img.shields.io/badge/telemetry-none-2ea043" alt="No telemetry"> <img src="https://img.shields.io/badge/12-runnable%20examples-0d9488" alt="12 runnable examples"> </p>
You have something you can score — a model config, a GPU kernel, a compiler schedule, a prompt, a piece of generated code — and too many possible versions of it to try by hand. This searches that space for you, in .NET.
It is a quality-diversity engine, so it returns a map of the best candidate of each kind rather than a thousand variations of one local optimum. It is a library: no service, no account, no telemetry.
Quick start
dotnet add package AiDotNet.Evolution --prerelease
using AiDotNet.Evolution;
// 1. Describe the space you want to search.
EvolutionSearchSpace space = new EvolutionSearchSpaceBuilder()
.Add(EvolutionParameter.Integer("depth", 1, 20))
.Add(EvolutionParameter.Logarithmic("rate", 0.0001, 1.0))
.Build();
// 2. Say how good a candidate is. Anything you can score works here.
var task = new EvolutionSearchTask(space, "quickstart", "v1", "score-v1", (genome, _, _) =>
new ValueTask<EvolutionTaskResult>(EvolutionTaskResult.Completed(
quality: -Math.Abs(genome.Number("depth") - 7) - Math.Abs(Math.Log10(genome.Number("rate") / 0.01)),
descriptors: new Dictionary<string, double> { ["depth"] = genome.Number("depth") },
costUnits: 1)));
// 3. Run the search.
var engine = new EvolutionEngine<EvolutionSearchGenome>(
task,
EvolutionSearchPresets.CreateAdaptiveMixed(space),
_ => new MapElitesArchive<EvolutionSearchGenome>(new[] { new EvolutionDescriptorDefinition("depth", 1, 21, 10) }),
new EvolutionEngineOptions { RunId = "quickstart", Seed = 42, MaxEvaluationAttempts = 200 });
EvolutionRunResult<EvolutionSearchGenome> result = await engine.RunAsync(
new[] { space.Sample(StableRandom.CreateStream(42, 0)) });
Console.WriteLine($"best quality : {result.Best!.Evaluation.Quality:F4}");
Console.WriteLine($"depth : {result.Best.Candidate.CanonicalGenome.Genome.Number("depth")}");
best quality : -0.0111
depth : 7
Three objects: a space, a task that scores a candidate, and an engine. Everything below is optional.
The three ids on
EvolutionSearchTask— task id, task version, evaluator version — are how the engine refuses a checkpoint or a cached score produced by a different scoring function. Change how you score, bump the evaluator version, and stale results stop being reused.
What it gives you
- Deterministic runs. Same seed, same result, and every run emits a
StateHashyou can compare. One example runs a search across three processes, kills them mid-flight, and asserts the resumed report is byte-identical to the uninterrupted one. - Budgets you cannot overrun. Every evaluation is admitted against a resource ledger before it runs and charged after. A worker that dies holding a reservation leaves a retained liability, not a silent refund.
- Real checkpoint/resume. Archives, operator learning, island topology, random streams and the ledger restore together; an incompatible checkpoint is refused rather than reinterpreted.
- LLMs optional. The engine knows nothing about models or prompts. Supply model-driven mutation through a typed contract and it is metered like any other operator, or leave it out and pay nothing per evaluation.
- Runs where you already are. .NET 10, .NET 8, and .NET Framework 4.7.1,
with no third-party dependencies on .NET 8 and 10. Only 4.7.1 pulls one in,
System.Text.Json, because it is not in-box there. - Evaluation can leave the process. Hand work to external workers over a durable coordinator that persists work, leases and receipts together, and survives a crash on either side. TypeScript, Python and C ABI bindings ship with it.
What people search with it
| You want to tune | Your genome is | Your score is |
|---|---|---|
| Model hyperparameters | depth, learning rate, family | held-out metric |
| A GPU kernel or compiler schedule | tile sizes, unroll factors, fusion | measured runtime |
| Generated code or programs | the program itself | tests passed, then speed |
| A prompt or agent policy | template and parameter choices | task success rate |
| Feature subsets | which columns are in | cross-validated score |
| Deployment configs | replicas, batch size, cache sizes | cost under an SLA |
The engine knows nothing about any of these. You implement
IEvolutionTask<TGenome> and it stays out of your domain.
Twelve examples you can run
dotnet run --project examples/TypedParameterSearch -c Release
| Example | What it shows |
|---|---|
| TypedParameterSearch | Mixed integer/real/categorical space with conditional parameters |
| ParetoSearch | Competing objectives, keeping the tradeoff front instead of one winner |
| AdaptiveIslandSearch | Parallel islands, migration, restarting stalled populations |
| SurrogateSearch | A cheap learned model ranks candidates before you pay for real evaluation |
| MultiFidelitySearch | Cheap-first screening with successive halving and promotion |
| ReplicatedEvaluation | Noisy scores, repeated sampling, confirmation before believing a win |
| PersistentEvaluation | Reusing earlier evaluations across runs, with freshness and force-fresh |
| OperatorCreditSearch | Learning which mutation strategies earn their cost |
| ProposalPipeline | Overlapping proposal generation with evaluation, bounded in flight |
| CompilerGuidedSearch | Using compiler feedback to steer program improvement |
| DurableWork | External workers, crash recovery, and exactly-once receipts |
| DurableSession | A live engine session driven across a durable host process |
Measured, not asserted
Results that were not measured are labeled as not measured. The repository ships retained evidence — including failed and discarded campaigns — each with an offline verifier you can run without trusting us.
| Evidence | Scale |
|---|---|
| Pareto search | 180 matched-budget runs, reproduced from the recorded state hashes in CI |
| Proposal pipeline | 576 live executions and 576 offline replays; 13,824 proposal calls, 18,432 evaluations |
| Surrogate ranking | 240 runs, 27,248 pilot calls |
| Adaptive islands | 62,720 local objective calls |
| Engine performance | Isolated per-core measurement, with the failed campaigns retained |
The external harness runs SciPy differential evolution and upstream pyribs CMA-ME against the same C# evaluator — no Python reimplementation of the objective to tilt the result. See the comparison contract and run it yourself.
main enforces a coverage ratchet (≥89.8% line, ≥74.51% branch), builds all
three target frameworks, and runs 1,400+ tests with zero skips. A pull request
that skips a test fails the build rather than reporting green.
Going further
| Capability | Doc |
|---|---|
| Mixed, conditional and log-scaled spaces | Typed search spaces |
| Competing objectives | Pareto search |
| Budgets and liabilities | Resource accounting |
| Noisy scores and confirmation | Replicated evaluation |
| Cheap screening first | Screening · Multi-fidelity |
| Learned candidate ranking | Surrogate selection |
| Which operators earn their keep | Operator credit · Portfolios |
| Parallel islands and restarts | Adaptive islands |
| Overlapping proposal and evaluation | Proposal pipeline |
| Reusing past evaluations safely | Persistent reuse · Warm starts |
| Where a measurement came from | Measurement origin |
| High-dimensional archives | Centroid archives |
| Work that runs outside the process | Durable external work · Worker protocol · Work identity |
Contracts you implement
IEvolutionTask<TGenome> (identity, validation, evaluation) ·
IVariationOperator<TGenome> (proposes immutable genomes) ·
IEvolutionArchive<TGenome> (what to keep; MapElitesArchive<TGenome> ships) ·
IEvolutionGenomeCodec<TGenome> (checkpointing without imposing a serializer) ·
ISelectionPolicy<TGenome>, ICandidateRefiner<TGenome>, IMigrationPolicy<TGenome>.
Reference-typed genomes implement IImmutableEvolutionGenome<TGenome> and return
an independently owned copy from CreateOwnedSnapshot. The engine takes that
snapshot once at canonicalization, so archives, migration and selection never
clone on their hot paths.
Related packages
AiDotNet.Evolution.Programs and AiDotNet.Evolution.CSharp add standalone
program contracts, process execution, script metrics, model judging and novelty
screening. The core package depends on neither AiDotNet nor AiDotNet.Tensors.
Contributing
See CONTRIBUTING.md for local validation, and repository setup for CI, security and release configuration.
License
Apache License 2.0. This is an independent .NET implementation with its own engine, orchestration, persistence and integration contracts; no OpenEvolve runtime code is copied or ported. OpenEvolve was reviewed as prior art during design, and THIRD-PARTY-NOTICES.md records that review under its Apache-2.0 terms.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net471 is compatible. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.1
- System.Text.Json (>= 10.0.12)
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on AiDotNet.Evolution:
| Package | Downloads |
|---|---|
|
AiDotNet.Evolution.Surrogates
Optional bounded numeric surrogate adapters with explicit reliability diagnostics for AiDotNet.Evolution. |
|
|
AiDotNet.Evolution.Programs
Compiler-neutral bounded program improvement and artifact-bound promotion. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 72 | 9/19/2026 |
| 0.1.0-preview.2 | 63 | 9/11/2026 |
| 0.1.0-preview.1 | 1,216 | 9/9/2026 |