When two parameters share the same Distribution (e.g. two
FloatParam::new(-5.0, 5.0) in one study), TpeSampler iterated
trial.distributions (a HashMap) with find_map to extract a value
matching the target distribution. HashMap iteration order varies
across runs, so x's history could be conflated with y's history
non-deterministically — making seeded TPE runs flaky in parallel
test execution.
Pick the smallest-ParamId match instead, so the choice is stable
across runs.
Also:
- src/sampler/genetic.rs: collapse if into match guard (clippy)
- src/lib.rs: drop std_instead_of_core lint (the only remaining
hits are std::io::Error/ErrorKind, which have no stable core
equivalent yet)
- Cargo.toml: relax minimum versions for fastrand/tokio/tracing/
optimizer-derive; bump dev-dep tokio to 1.50 to match in-tree usage
- Clamp KDE candidates to parameter bounds before evaluating l(x)/g(x),
matching the univariate TPE behavior; without this, candidates scored
well at out-of-bounds locations but became suboptimal when clamped
- Sort HashMap iterations by ParamId before consuming the seeded RNG to
eliminate non-deterministic sampling caused by global ParamId counter
- Replace wall-clock timing assertion in async concurrency test with
atomic max-active counter to avoid CI flakiness
- Gate unused HashSet import behind cfg(feature = "async")
* fix: address 18 bugs found during codebase audit
High severity:
- TPE/MOTPE: match parameters by exact distribution equality instead of
flat-mapping over all param values, preventing cross-parameter mixing
- MultivariateTpeSampler: find_matching_param now uses search space
distributions for exact matching instead of type+range heuristic
- JournalStorage: write_to_file no longer advances file_offset (left to
refresh), both operations serialized under single io_lock mutex,
refresh uses fetch_max and deduplicates by trial ID
Medium severity:
- NSGA-III: use actual Pareto front ranks for tournament selection
instead of artificial cyclic indices
- sample_random: apply step quantization after log-scale sampling
- internal_bounds: return None for non-positive log-scale bounds
- SobolSampler: use per-trial dimension HashMap for concurrent safety
- JournalStorage refresh: protect with io_lock mutex, use fetch_max
- n_trials(): filter by TrialState::Complete as documented
- FloatParam: reject NaN/Infinity in validate()
- Pruners: assert n_min_trials >= 1, guard compute_percentile on empty
- Visualization: escape_js for importance chart parameter names
Low severity:
- save(): use peek_next_trial_id() from Storage trait
- csv_escape: handle carriage return per RFC 4180
- from_internal: use saturating arithmetic for stepped Int distributions
- BoolParam: bounds-check categorical index < 2
- min_max: skip NaN values with safe fallback
* ci: trigger CI on pull requests targeting any branch
- Rename stale "GridSearchSampler" panic message to "GridSampler"
- Assert concurrency > 0 in optimize_parallel to prevent deadlock
- Fix inaccurate comment in CSV export (empty for all non-complete trials, not just pruned)
Resolve symlinks and `../` traversals best-effort in `new()` and
`open()`, falling back to the original path if canonicalization fails
(e.g. file doesn't exist yet).
- Timing-based proof that trials run in parallel, not sequentially
- Atomic counter proof that multiple workers overlap simultaneously
- Panic propagation returns TaskError with panic message
- Partial failures with parallel path store only successful trials
- Remove stale #[allow(dead_code)] and #[allow(too_many_lines)] attributes
- Replace #[allow(dead_code)] with #[cfg(test)] for test-only methods
- Delete unused fill_remaining_independent() and ParamMeta.dist field
- Extract sample_float/int/categorical helpers from TpeSampler, MotpeSampler,
and SamplingEngine to shorten match-heavy sample() methods
- Extract try_fit_kdes() helper to consolidate repeated fallback blocks
- Refactor sample_tpe_float/int to take &FloatDistribution/&IntDistribution
instead of destructured args, removing #[allow(too_many_arguments)]
Fix direct path references for in-scope items (GammaStrategy,
CompletedTrial) and convert feature-gated items to plain backtick
formatting so docs build cleanly without --all-features.
- Add sampler/common.rs with distribution helpers (internal_bounds,
from_internal, to_internal, sample_random) used by 8 samplers
- Add sampler/tpe/common.rs with TPE sampling functions
(sample_tpe_float, sample_tpe_int, sample_tpe_categorical)
shared by TpeSampler, MultivariateTpeSampler, and MotpeSampler
- Remove ~940 lines of near-identical code across sampler modules
- Use Vec::with_capacity() in param_importance() and fanova_with_config()
where iteration count is known or bounded
- Fix flaky MultivariateTpeSampler doctest by increasing trials from 30
to 50
- Add Trial::into_completed() and into_multi_objective_trial() to move
fields instead of cloning 5 HashMaps/Vecs per trial completion
- Fire after_trial callback before pushing to storage, eliminating the
clone-from-storage pattern at all 4 call sites
- Optimize top_trials(n) to sort indices and clone only N trials instead
of cloning all completed trials
- Remove unused set_complete/set_pruned methods
- Replace Mutex<fastrand::Rng> with a stored seed + MurmurHash3 mixer
in RandomSampler, TpeSampler, and MotpeSampler so parallel workers
no longer serialize on a shared lock
- Add mix_seed() and distribution_fingerprint() to rng_util for
deterministic per-call RNG derivation from (seed, trial_id, distribution)
- Include an AtomicU64 call counter to disambiguate parameters that
share the same distribution within a trial
Remove the conditional guard on pruned trial assertions since the test
explicitly prunes 2 trials. Assert the expected count first, then check
summary content unconditionally to catch regressions.
- Add `CompletedTrial::validate()` checking all f64 fields are finite
- Call validate after deserializing each trial in journal storage
- Make `distribution` and `param` modules public (types already in public fields)
- Add blanket `impl Objective<V> for Fn(&mut Trial) -> Result<V, E>`
so closures work directly with `optimize`
- Rewrite optimize, optimize_async, optimize_parallel to accept
`impl Objective<V>` with before_trial/after_trial hooks
- Remove optimize_with, optimize_with_async, optimize_with_parallel
- Remove max_retries and retry logic from Objective trait
- Add explicit closure type annotations for HRTB inference
- Convert FnMut test closures to Fn via RefCell/Cell
- Delete 20 duplicate tests already covered by parameter_tests.rs
- Move 11 pure Trial unit tests into src/trial.rs
- Split remaining 84 integration tests into tests/study/ (9 modules)
- Group sampler tests into tests/sampler/ (7 modules)
- Group pruner tests into tests/pruner/ (2 modules)