Skip to content

fix: address 18 bugs found during codebase audit#8

Merged
raimannma merged 3 commits intorefactorfrom
fix/found-issues
Feb 13, 2026
Merged

fix: address 18 bugs found during codebase audit#8
raimannma merged 3 commits intorefactorfrom
fix/found-issues

Conversation

@raimannma
Copy link
Owner

Summary

Fixes all 18 bugs identified in the codebase audit (3 high, 11 medium, 4 low severity).

High severity

  • TPE/MOTPE parameter mixingsample_float/sample_int/sample_categorical now match by exact Distribution equality via t.distributions instead of flat-mapping over all param values. Prevents cross-parameter corruption with overlapping ranges.
  • MultivariateTpeSampler find_matching_param — Uses search space distributions for exact matching instead of type+range heuristic. Cache now stores search space alongside joint sample.
  • JournalStorage trial losswrite_to_file no longer advances file_offset; both write_to_file and refresh 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 cyclic indices
  • sample_random: Apply step quantization after log-scale sampling
  • internal_bounds: Return None for non-positive log-scale bounds
  • SobolSampler: Per-trial dimension HashMap for concurrent safety
  • 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 input
  • Visualization: Apply escape_js to 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: Saturating arithmetic for stepped Int distributions
  • BoolParam: Bounds-check categorical index < 2
  • min_max: Skip NaN values with safe fallback

Test plan

  • All 923 tests pass (cargo test --all-features)
  • Clippy clean (cargo clippy --all-features)
  • cargo fmt clean
  • New tests added for NaN/Infinity validation and BoolParam bounds check

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
@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 81.08108% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.25%. Comparing base (1cd18c1) to head (f45f146).

Files with missing lines Patch % Lines
src/sampler/motpe.rs 52.63% 27 Missing ⚠️
src/sampler/common.rs 47.05% 9 Missing ⚠️
src/sampler/tpe/sampler.rs 89.47% 6 Missing ⚠️
src/storage/journal.rs 75.00% 4 Missing ⚠️
src/visualization.rs 80.00% 2 Missing ⚠️
src/sampler/nsga3.rs 93.33% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor       #8      +/-   ##
============================================
- Coverage     93.39%   93.25%   -0.15%     
============================================
  Files            53       53              
  Lines         14393    14522     +129     
============================================
+ Hits          13443    13542      +99     
- Misses          950      980      +30     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes 18 bugs identified in a codebase audit, ranging from high-severity correctness issues to low-severity edge cases. The changes primarily address parameter matching in TPE/MOTPE samplers, concurrency issues in JournalStorage, validation gaps, and various other correctness and robustness improvements.

Changes:

  • Fixed TPE/MOTPE parameter mixing by using exact Distribution equality instead of flat-mapping values
  • Fixed JournalStorage race conditions with unified io_lock and proper deduplication
  • Fixed NSGA-III to use actual Pareto front ranks instead of cyclic indices
  • Added validation for NaN/Infinity in FloatParam and bounds checking in BoolParam
  • Fixed SobolSampler to use per-trial dimension tracking for concurrent safety
  • Fixed n_trials() to filter by TrialState::Complete
  • Added step quantization after log-scale sampling
  • Various other validation and edge case fixes

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/visualization.rs Apply escape_js to importance chart parameter names; handle NaN values in min_max with safe fallback
src/study/persistence.rs Use peek_next_trial_id() to capture exact counter position including failed trials
src/study/mod.rs Filter n_trials() by TrialState::Complete to exclude pruned/failed trials
src/study/export.rs Add carriage return handling to csv_escape per RFC 4180
src/storage/mod.rs Add peek_next_trial_id() trait method for persistence
src/storage/memory.rs Implement peek_next_trial_id() with load instead of fetch_add
src/storage/journal.rs Rename write_lock to io_lock; remove file_offset advancement from write_to_file; add deduplication in refresh with fetch_max
src/sampler/tpe/sampler.rs Match parameters by exact Distribution equality via trial.distributions instead of flat-mapping values
src/sampler/tpe/multivariate/mod.rs Use search space distributions for exact matching; cache search space alongside joint sample
src/sampler/sobol.rs Use per-trial dimension HashMap for concurrent safety (note: potential unbounded growth)
src/sampler/nsga3.rs Use actual Pareto front ranks for tournament selection instead of cyclic indices
src/sampler/motpe.rs Match parameters by exact Distribution equality via trial.distributions
src/sampler/common.rs Return None for non-positive log-scale bounds; use saturating arithmetic in from_internal; apply step quantization after log-scale sampling
src/pruner/percentile.rs Assert n_min_trials >= 1; guard compute_percentile on empty input
src/pruner/median.rs Assert n_min_trials >= 1
src/parameter.rs Reject NaN/Infinity in FloatParam validate(); bounds-check BoolParam categorical index < 2

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +56 to 60
/// Internal state for tracking per-trial dimension counters.
struct SobolState {
/// The `trial_id` of the current trial (used to reset dimension counter).
current_trial: u64,
/// Next Sobol dimension to use for the current trial.
next_dimension: u32,
/// Next Sobol dimension for each in-flight trial.
dimensions: HashMap<u64, u32>,
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SobolSampler now uses a per-trial dimension HashMap to track dimensions, but this map grows unboundedly and is never cleaned up. In long-running studies with many trials, this could lead to significant memory usage as completed trial IDs accumulate in the HashMap forever. Consider adding a cleanup mechanism to remove entries for completed trials, or using a bounded cache structure.

Copilot uses AI. Check for mistakes.
@raimannma raimannma merged commit 11a8534 into refactor Feb 13, 2026
24 of 26 checks passed
@raimannma raimannma deleted the fix/found-issues branch February 13, 2026 09:03
@github-actions
Copy link

github-actions bot commented Feb 13, 2026

Benchmark Comparison

Base: 1cd18c16f915422b5342811cb86ff7d10bb2fb16 | Head: f45f146ef0451b5726a64db080e96499124530e7

Click to expand full results
group                         base                                   head
-----                         ----                                   ----
grid_sample/points/10         1.00    345.4±6.79ns        ? ?/sec    1.05    361.0±8.48ns        ? ?/sec
grid_sample/points/5          1.00    338.4±4.02ns        ? ?/sec    1.00    337.2±4.85ns        ? ?/sec
grid_sample/points/50         1.02    433.0±6.98ns        ? ?/sec    1.00    426.1±6.18ns        ? ?/sec
random_sample/history/10      1.00     14.7±0.15ns        ? ?/sec    1.00     14.8±0.27ns        ? ?/sec
random_sample/history/100     1.00     14.7±0.13ns        ? ?/sec    1.00     14.8±0.24ns        ? ?/sec
random_sample/history/1000    1.00     14.7±0.13ns        ? ?/sec    1.00     14.8±0.30ns        ? ?/sec
random_vs_tpe/random_5d       1.00    144.8±0.20µs        ? ?/sec    1.00    144.8±0.33µs        ? ?/sec
random_vs_tpe/tpe_5d          3.55     18.4±0.02ms        ? ?/sec    1.00      5.2±0.01ms        ? ?/sec
tpe_rosenbrock/dims/10        6.81     71.0±0.59ms        ? ?/sec    1.00     10.4±0.11ms        ? ?/sec
tpe_rosenbrock/dims/2         1.54      3.2±0.01ms        ? ?/sec    1.00      2.1±0.00ms        ? ?/sec
tpe_sample/history/10         1.41      4.2±0.09µs        ? ?/sec    1.00      3.0±0.04µs        ? ?/sec
tpe_sample/history/100        1.62     30.3±0.46µs        ? ?/sec    1.00     18.7±0.36µs        ? ?/sec
tpe_sample/history/1000       1.53    292.4±5.17µs        ? ?/sec    1.00    191.7±1.90µs        ? ?/sec
tpe_sphere/dims/10            6.80     70.8±0.07ms        ? ?/sec    1.00     10.4±0.03ms        ? ?/sec
tpe_sphere/dims/2             1.55      3.2±0.04ms        ? ?/sec    1.00      2.1±0.00ms        ? ?/sec
tpe_sphere/dims/50            33.33  1729.3±7.56ms        ? ?/sec    1.00     51.9±0.08ms        ? ?/sec

Benchmarks run on ubuntu-latest via Criterion + critcmp.
Results may vary due to shared CI runners. Look for consistent >5% changes.

raimannma added a commit that referenced this pull request Feb 13, 2026
* 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
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