Skip to content

Fix NSGA-II generation cache sync by introducing Incremental Indexing - #244

Draft
yen-0 wants to merge 1 commit into
optuna:mainfrom
yen-0:feature/nsgaii_incremental_indexing_rework
Draft

Fix NSGA-II generation cache sync by introducing Incremental Indexing#244
yen-0 wants to merge 1 commit into
optuna:mainfrom
yen-0:feature/nsgaii_incremental_indexing_rework

Conversation

@yen-0

@yen-0 yen-0 commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Motivation

After #238, parent population cache lookup is refreshed from storage, but the child-generation index is still local to each sampler instance.

As a result, one worker can miss trials that were already completed by another worker and persisted to storage, leaving generation_to_numbers stale when assigning the next child generation.

This patch incrementally resyncs the generation cache from storage while keeping the fast path from #238.

Description of change

  • Incrementally resync the local child-generation cache from storage before assigning the next generation.
  • Preserve the local fast path in after_trial.
  • Add regression tests for stale cache and cross-worker completion pickup.

Benchmark

This is primarily a correctness patch for stale generation-cache synchronization. It keeps the #238 fast path and stayed faster than main in local runs.

Rust benchmarks

Median of 3 runs on this machine:

Benchmark main #179 current
(x, 1-x) simple 0.284s 0.339s 0.297s
(x, 1-x) dense40 1.426s 1.322s 1.407s
WFG 1.663s N/A 1.651s

Benchmark scripts

Rust benchmark

Used a local temporary example:

use std::env;
use std::time::Instant;

use rustuna_core::storage::InMemoryStorage;
use rustuna_core::study::{create_study, Direction};
use rustuna_sampler::nsgaii::NSGAIISampler;

fn main() -> rustuna_core::Result<()> {
    let mode = env::args()
        .nth(1)
        .unwrap_or_else(|| "simple".to_string());
    let n_trials = 10_000;
    let study = create_study(
        "nsgaii-bench",
        InMemoryStorage::new(),
        NSGAIISampler::seed_from_u64(1, 50, None, 0.9, 0.5),
        vec![Direction::Minimize, Direction::Maximize],
    )?;

    let start = Instant::now();
    match mode.as_str() {
        "simple" => {
            study.optimize(
                |mut trial| {
                    let x = trial.suggest_float("x", 0.0, 1.0)?;
                    Ok(vec![x, 1.0 - x])
                },
                n_trials,
            )?;
        }
        "dense40" => {
            study.optimize(
                |mut trial| {
                    let mut v0 = 0.0;
                    let mut v1 = 0.0;
                    for i in 0..40 {
                        let name = format!("x{i}");
                        let x = trial.suggest_float(&name, 0.0, 1.0)?;
                        v0 += x * x;
                        v1 += (1.0 - x) * (1.0 - x);
                    }
                    Ok(vec![v0, v1])
                },
                n_trials,
            )?;
        }
        _ => panic!("unknown mode: {mode}"),
    }
    println!("mode={mode} elapsed={:.3}", start.elapsed().as_secs_f64());
    Ok(())
}

Commands:

WFG benchmark

Used a local Python benchmark:

import time

import optunahub
import rustuna

wfg = optunahub.load_module("benchmarks/wfg")
problem = wfg.Problem(function_id=4, n_objectives=2, dimension=40, k=2)

sampler = rustuna.samplers.NSGAIISampler(seed=1)
study = rustuna.create_study(sampler=sampler, directions=["minimize", "minimize"])

start = time.perf_counter()
study.optimize(
    lambda trial: list(
        problem.evaluate(
            {
                name: trial.suggest_float(name, dist.low, dist.high)
                for name, dist in problem.search_space.items()
            }
        )
    ),
    n_trials=10000,
)
print(f"elapsed={time.perf_counter() - start:.3f}")

Co-authored-by: shemmi <shemmi@preferred.jp>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant