183 Commits

Author SHA1 Message Date
dependabot[bot] 47f63b2461 chore(deps): bump the all-actions group across 1 directory with 2 updates
Bumps the all-actions group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [codecov/codecov-action](https://github.com/codecov/codecov-action).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

Updates `codecov/codecov-action` from 6 to 7
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all-actions
- dependency-name: codecov/codecov-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-27 01:53:11 +02:00
Manuel f60b2e9a9e Update deps 2026-06-24 13:51:45 +02:00
dependabot[bot] 2c41412c77 chore(deps): bump the all-actions group across 1 directory with 2 updates
Bumps the all-actions group with 2 updates in the / directory: [codecov/codecov-action](https://github.com/codecov/codecov-action) and [bnjbvr/cargo-machete](https://github.com/bnjbvr/cargo-machete).


Updates `codecov/codecov-action` from 5 to 6
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5...v6)

Updates `bnjbvr/cargo-machete` from 0.9.1 to 0.9.2
- [Release notes](https://github.com/bnjbvr/cargo-machete/releases)
- [Changelog](https://github.com/bnjbvr/cargo-machete/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bnjbvr/cargo-machete/compare/7959c845782fed02ee69303126d4a12d64f1db18...ac30a525c0a8d163a92d727b3ff079ee3f6ecb08)

---
updated-dependencies:
- dependency-name: bnjbvr/cargo-machete
  dependency-version: 0.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-actions
- dependency-name: codecov/codecov-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-01 12:15:27 +02:00
Manuel Raimann da2d367fb7 chore: release v1.0.1 v1.0.1 2026-05-01 12:06:45 +02:00
Manuel Raimann ab74468603 fix(deps): set tracing minimum to 0.1.26 for Span::entered() 2026-05-01 12:06:45 +02:00
Manuel Raimann 4225d61027 fix(sampler): deterministic parameter matching in TPE
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
2026-04-30 09:27:06 +02:00
Manuel Raimann db192eb1cb chore: release v1.0.0 v1.0.0 2026-02-13 10:36:16 +01:00
Manuel Raimann 80a7c27551 ci: publish optimizer-derive before optimizer to fix crates.io dependency 2026-02-13 10:36:16 +01:00
Manuel Raimann 86db6361d6 fix(sampler): clamp multivariate TPE candidates to parameter bounds
- 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")
2026-02-13 10:21:39 +01:00
Manuel 11a8534b38 fix: address 18 bugs found during codebase audit (#8)
* 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
2026-02-13 10:03:37 +01:00
Manuel Raimann 1cd18c16f9 fix: address review findings in grid sampler, parallel optimization, and CSV export
- 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)
2026-02-12 17:57:29 +01:00
Manuel Raimann 9f2ce2a482 fix(docs): remove private intra-doc link from JournalStorage doc comment 2026-02-12 17:05:12 +01:00
Manuel Raimann 8c025b4525 fix(journal): canonicalize file paths in JournalStorage constructors
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).
2026-02-12 16:34:27 +01:00
Manuel Raimann cfa8426312 docs(sampler,pruner): add implementation guides for custom samplers and pruners
- Expand sampler module docs with available samplers tables, custom
  sampler walkthrough, stateless/stateful patterns, cold start handling,
  history reading, thread safety, and testing guidance
- Expand pruner module docs with stateful/stateless classification,
  warmup parameters, decorator composition, thread safety, and testing
- Add code examples to both trait docs (NoisySampler, StalePruner)
2026-02-12 16:31:25 +01:00
Manuel Raimann 8d06d213db docs(journal): document file-size DoS potential in JournalStorage 2026-02-12 16:26:41 +01:00
Manuel Raimann 479554ffb2 fix(docs): convert JournalStorage intra-doc link to plain backtick text 2026-02-12 16:24:26 +01:00
Manuel Raimann fd5c5453e5 test(async): add concurrency verification tests for optimize_parallel
- 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
2026-02-12 16:22:38 +01:00
Manuel Raimann 7c0cce160a test(stress): add ignored stress tests for large-scale scenarios
- 10k sequential trials with RandomSampler
- 128 parameters with TPE sampler
- 128 concurrent workers with optimize_parallel
- 5k trials with TPE and 32 parallel workers
2026-02-12 16:18:55 +01:00
Manuel Raimann 8394a747a5 test(journal): add corrupted and malicious JSONL file tests 2026-02-12 16:07:31 +01:00
Manuel Raimann 0e0e9ff38f test(sobol): add integration tests for SobolSampler via Study API 2026-02-12 16:02:46 +01:00
Manuel Raimann 12efbaabab fix(sampler): return None instead of panicking on parameter type mismatch in CompletedTrial::get() 2026-02-12 16:00:05 +01:00
Manuel Raimann 3d3dcb4c26 feat(error): mark Error enum as #[non_exhaustive] 2026-02-12 15:56:16 +01:00
Manuel Raimann cc50e1ad4b refactor: reduce #[allow] attributes and extract per-distribution helpers
- 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)]
2026-02-12 15:54:29 +01:00
Manuel Raimann 38a20bbc42 fix(docs): resolve all broken rustdoc intra-doc links
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.
2026-02-12 15:47:53 +01:00
Manuel Raimann f0798ca58a refactor(study): move export_json() from persistence to export module 2026-02-12 15:42:34 +01:00
Manuel Raimann a502d7e1b8 refactor(tpe): split multivariate sampler into focused submodules
- Extract engine.rs (core sampling logic) and trials.rs (trial processing)
- Deduplicate TPE sampling functions by delegating to tpe::common
- Gate persistence.rs imports with #[cfg(feature = "serde")]
2026-02-12 15:38:23 +01:00
Manuel Raimann d80d2bb1fb refactor(sampler): extract shared utilities to reduce duplication
- 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
2026-02-12 15:25:27 +01:00
Manuel Raimann 4781107ede refactor(study): split monolithic study.rs into focused submodules
- mod.rs: core struct (pub(crate) fields), constructors, trial management
- builder.rs: StudyBuilder fluent API
- optimize.rs: sync optimization loop
- async_impl.rs: optimize_async/optimize_parallel (feature-gated)
- analysis.rs: best_trial, top_trials, param_importance, fanova
- export.rs: CSV, summary, Display, export_html
- persistence.rs: StudySnapshot, save/load, with_journal
- iter.rs: iter(), IntoIterator
2026-02-12 15:03:02 +01:00
Manuel Raimann 9ab94002d9 perf(tpe): eliminate unnecessary Vec allocations in KDE/TPE sampling
- In-place log transformation in sample_tpe_float (saves 2 allocs per log-scale float sample)
- Pass owned Vec<i64> to sample_tpe_int to reduce peak memory during int-to-float conversion
- Stack-allocate categorical count arrays for <=32 choices in sample_tpe_categorical
2026-02-12 14:51:03 +01:00
Manuel Raimann 1b158e4d1b perf: pre-allocate vectors in analysis methods
- 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
2026-02-12 14:46:46 +01:00
Manuel Raimann ddbbe294bc perf(journal): use incremental refresh instead of re-reading entire file
Track a byte offset so refresh() only parses lines appended since the
last read, reducing total I/O from O(n²) to O(n) over an n-trial run.
2026-02-12 14:44:19 +01:00
Manuel Raimann 5da63d8976 perf: reduce CompletedTrial cloning overhead
- 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
2026-02-12 14:44:19 +01:00
Manuel Raimann 74cf0643eb perf: replace RNG mutex with per-call seed derivation in stateless samplers
- 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
2026-02-12 14:44:19 +01:00
Manuel Raimann baebbae64c docs: add pre-commit guidelines for formatting and testing 2026-02-12 14:44:19 +01:00
Manuel Raimann 750bbad0d8 test: assert pruned count unconditionally in summary test
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.
2026-02-12 14:44:19 +01:00
Manuel Raimann 271748700f perf(tpe): use quickselect instead of full sort in split_trials 2026-02-12 14:44:19 +01:00
Manuel Raimann c561caaa34 fix: validate NaN/Inf in deserialized trials during journal loading
- 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)
2026-02-12 14:44:19 +01:00
Manuel 04d822479e Update Cargo.toml
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-12 14:12:52 +01:00
Manuel Raimann 8713a93636 refactor: adjust indentation for better readability in pruning.rs 2026-02-12 13:59:14 +01:00
Manuel Raimann 3723f91849 refactor: update rustfmt configuration by commenting out unused options 2026-02-12 13:56:09 +01:00
Manuel Raimann 968eb67d07 fix: return error instead of panicking on out-of-bounds categorical index 2026-02-12 13:55:00 +01:00
Manuel 2d88526d8d Update examples/pruning.rs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-12 13:52:26 +01:00
Manuel Raimann cc3937d4f7 Add LICENSE.md 2026-02-12 13:49:49 +01:00
Manuel Raimann 6f1c070b7d test: actually use NaN values in test_best_trial_with_nan_values 2026-02-12 13:38:54 +01:00
Manuel 12f7f25baa Update README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-12 13:29:44 +01:00
Manuel Raimann e68a3788be refactor: move RandomMultiObjectiveSampler into sampler::random 2026-02-12 13:21:37 +01:00
Manuel Raimann d20f09c66a refactor: shorten sampler type names
- GridSearchSampler → GridSampler
- DifferentialEvolutionSampler → DESampler
- DifferentialEvolutionStrategy → DEStrategy
- DifferentialEvolutionSamplerBuilder → DESamplerBuilder
2026-02-12 13:19:06 +01:00
Manuel Raimann 47b5f9cec8 feat: unify optimize and optimize_with via blanket Objective impl
- 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
2026-02-12 13:09:14 +01:00
Manuel Raimann c20a53dfba fix: bump minimum tokio version to 1.30 for JoinSet::len() 2026-02-12 12:52:02 +01:00
Manuel Raimann d81d1de4ff refactor(tests): split integration.rs into focused subfolders
- 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)
2026-02-12 12:49:24 +01:00