Development documentation — 0.3.0a4 / main
This is the development branch, not a published stable release. Use the source installation guide. Functional checks cover Ubuntu, Windows and macOS; interpreter, artifact and optional-feature limits still apply. Read support, release gates and known limitations before relying on results.
What RKO guarantees about convergence
Documentation index · Limitations · Parameter reference · Scientific references
This page states the one convergence property the library can defend with a proof and a test, and what it does not tell you. It is a safety property, not a speed or quality claim.
The guarantee
Let the decoder map a uniform random key vector to a solution with objective value at most
f* + e (a target you accept) with probability mu. We call mu the target mass: it is a
property of your decoder, not of the algorithm. If the search
- draws at least one fresh uniform key vector per generation or restart, and
- never discards the best evaluated solution,
then, for every tolerance e whose target has positive mass, the best cost found reaches
f* + e almost surely. The best cost therefore converges to f* when every e > 0 has positive
mass, and equals f* exactly only if an optimal solution itself has positive mass. After k fresh
uniform draws the chance of having missed a target of mass mu is at most (1 - mu)^k.
For BRKGA the draws are the p keys of the initial population plus m = int(p * pm) random
mutants in every generation, so after G generations
P(miss) <= (1 - mu) ** (p + m * G)
The bound ignores crossover and the elite local step, which can only help.
Both conditions hold in the library:
- Fresh uniform keys. BRKGA rejects settings where
int(p * pm)is zero in every engine, so the mutant channel cannot be switched off by an override. See the parameter reference. - No lost improvement. The reported cost equals the lowest objective value any evaluation
returned, and every history is non-increasing, on the Python, Rust and C++ engines
(
test_best_cost_monotonic.py).
The convergence statement is about the best evaluated solution, not about the keys. Keys keep moving and the population need not collapse; that is not a defect.
What it does not say
- Nothing about speed. The bound uses only the uniform draws. For a sort decoder over
nitems a single permutation has mass1/n!, and a symmetric TSP cycle has2/(n-1)!; the bound is correct and useless atn = 30. A threshold decoder with one target bit pattern has mass2**-n. Practical quality comes from the operators exploiting structure, which this page does not analyze. muis unknown for real problems. You can compute it only for toy decoders. Do not read the bound as a confidence level for your instance.- No certificate of optimality. A found solution is never proved optimal.
- No finite-time quality. Elapsed time, evaluation budgets and a hard objective all break nothing in the statement; they just stop the search before the guarantee matters.
- Only the mutant channel is covered. Other algorithms in a portfolio add search power but are not part of this bound. A portfolio keeps the best of everything it ran.
- A decoder that cannot produce a solution has
mu = 0. Then nothing can be guaranteed, for example when feasibility repair always maps to the same few solutions.
A misconfiguration the library now refuses
from rko_solver import RKO
try:
RKO(
decode, objective, n_variables, algorithms=["BRKGA"], adaptation="fixed",
hyperparameters={"BRKGA": {"p": 15, "pm": 0.05}}, execution="sequential",
).solve(0.1)
except ValueError as error:
print(error)
With p = 15 and pm = 0.05 the mutant count rounds down to zero, so the call raises
ValueError: BRKGA: p * pm must give at least one mutant per generation. With the default
pm = 0.10 the population must be at least 10.
How this is checked
test_brkga_survival_bound.py uses a five-bit threshold target, mu = 1/32, which is the only
kind of target whose mass is exactly known.
- On the Python algorithm, an injected local step costs exactly one evaluation, so a budget of
p + G * (p - elite + 1)holds exactlyGgenerations. Over 2000 seeded runs per budget the miss count must not exceed the bound by an exact one-sided binomial test at1e-6.
Generations G |
Evaluations | Miss rate (measured) | Bound |
|---|---|---|---|
| 0 | 10 | 0.707 | 0.728 |
| 2 | 30 | 0.470 | 0.530 |
| 4 | 50 | 0.321 | 0.386 |
| 6 | 70 | 0.223 | 0.281 |
| 10 | 110 | 0.108 | 0.149 |
Population 10, one elite, five mutants, 2000 seeds. The observed misses sit below the bound, as
expected, because crossover also reaches the target.
- On all three engines through the public API, the test requires that every one of 60 seeds finds
the target within 6000 evaluations. The Rust and C++ local step spends an unreported number of
evaluations per generation, so no exact bound is claimed there; even at 30 evaluations per
generation the guaranteed draws would put a miss below 1e-10 per seed.
Limits of that check:
- Removing the mutants, or drawing them where they cannot hit the target, makes the test fail (1252 and 1331 misses of 2000 against a bound of 0.53, in two temporary mutations).
- Losing a single mutant is not detected. Crossover compensates, and the margin between the measured rate and the bound is larger than the effect. The exact mutant count is protected by the validation tests instead.
- It is a smoke test of the library's behaviour on one toy decoder, not a proof for your problem.