From 85b3f02af41c5a7b570ef8138ef9004be7606c6c Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Thu, 12 Mar 2026 21:58:11 +0100 Subject: [PATCH] fix(hyperopt): enforce minimum 5 trials, skip hyperopt when trials=0 The hyperopt binary was crashing with "trials (5) must be greater than n_initial (5)" when --trials 0 was passed. Root cause: the bump logic set trials = n_initial instead of max(5, n_initial + 1). Fix: both hyperopt_baseline_rl and hyperopt_baseline_supervised now clamp trials to max(5, n_initial + 1). Also add `when` condition to the compile-and-train DAG so hyperopt step is skipped when trials=0, and train-best gracefully handles missing hyperopt results. Co-Authored-By: Claude Opus 4.6 --- crates/ml/examples/hyperopt_baseline_rl.rs | 15 ++++++------- .../examples/hyperopt_baseline_supervised.rs | 14 +++++++------ .../k8s/argo/compile-and-train-template.yaml | 21 +++++++++++-------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/crates/ml/examples/hyperopt_baseline_rl.rs b/crates/ml/examples/hyperopt_baseline_rl.rs index a347c4f0d..f6600e327 100644 --- a/crates/ml/examples/hyperopt_baseline_rl.rs +++ b/crates/ml/examples/hyperopt_baseline_rl.rs @@ -509,15 +509,16 @@ fn main() -> Result<()> { ); } - // Enforce trials > n_initial (ArgminOptimizer needs at least n_initial - // LHS exploration rounds + 1 TPE-guided trial to be meaningful) - let min_trials = args.n_initial + 1; - if args.trials < min_trials { + // Enforce minimum 5 trials AND trials > n_initial + // (ArgminOptimizer needs at least n_initial LHS rounds + 1 TPE-guided trial) + const MIN_TRIALS: usize = 5; + let effective_min = MIN_TRIALS.max(args.n_initial + 1); + if args.trials < effective_min { info!( - "Trials {} below minimum (n_initial {} + 1) — bumping to {}", - args.trials, args.n_initial, min_trials + "Trials {} below minimum {} (floor={}, n_initial {}+1={}) — clamping", + args.trials, effective_min, MIN_TRIALS, args.n_initial, args.n_initial + 1 ); - args.trials = min_trials; + args.trials = effective_min; } // Create output directory diff --git a/crates/ml/examples/hyperopt_baseline_supervised.rs b/crates/ml/examples/hyperopt_baseline_supervised.rs index f003eb8d6..2a023f3d4 100644 --- a/crates/ml/examples/hyperopt_baseline_supervised.rs +++ b/crates/ml/examples/hyperopt_baseline_supervised.rs @@ -654,14 +654,16 @@ fn main() -> Result<()> { info!("Models to optimize: {:?}", models); - // Enforce trials > n_initial (ArgminOptimizer needs LHS exploration + ≥1 TPE trial) - let min_trials = args.n_initial + 1; - if args.trials < min_trials { + // Enforce minimum 5 trials AND trials > n_initial + // (ArgminOptimizer needs at least n_initial LHS rounds + 1 TPE-guided trial) + const MIN_TRIALS: usize = 5; + let effective_min = MIN_TRIALS.max(args.n_initial + 1); + if args.trials < effective_min { info!( - "Trials {} below minimum (n_initial {} + 1) — bumping to {}", - args.trials, args.n_initial, min_trials + "Trials {} below minimum {} (floor={}, n_initial {}+1={}) — clamping", + args.trials, effective_min, MIN_TRIALS, args.n_initial, args.n_initial + 1 ); - args.trials = min_trials; + args.trials = effective_min; } if let Some(parent) = args.output.parent() { diff --git a/infra/k8s/argo/compile-and-train-template.yaml b/infra/k8s/argo/compile-and-train-template.yaml index ef506ff60..6ed5f74c6 100644 --- a/infra/k8s/argo/compile-and-train-template.yaml +++ b/infra/k8s/argo/compile-and-train-template.yaml @@ -111,9 +111,10 @@ spec: - name: hyperopt template: hyperopt dependencies: [fetch-binary, gpu-warmup] + when: "{{workflow.parameters.hyperopt-trials}} != 0" - name: train-best template: train-best - dependencies: [hyperopt] + dependencies: [fetch-binary, gpu-warmup, hyperopt] - name: evaluate template: evaluate dependencies: [train-best] @@ -431,16 +432,18 @@ spec: # Determine binary and hyperopt flag case "$MODEL" in - dqn|ppo) - BINARY=train_baseline_rl - HYPEROPT_FLAG="--hyperopt-results /workspace/output/${MODEL}_hyperopt_results.json" - ;; - *) - BINARY=train_baseline_supervised - HYPEROPT_FLAG="--hyperopt-results /workspace/output/${MODEL}_hyperopt_results.json" - ;; + dqn|ppo) BINARY=train_baseline_rl ;; + *) BINARY=train_baseline_supervised ;; esac + HYPEROPT_FLAG="" + if [ -f "/workspace/output/${MODEL}_hyperopt_results.json" ]; then + HYPEROPT_FLAG="--hyperopt-results /workspace/output/${MODEL}_hyperopt_results.json" + echo " Using hyperopt results: ${MODEL}_hyperopt_results.json" + else + echo " No hyperopt results — training with default hyperparams" + fi + echo "=== Training best: $MODEL ({{workflow.parameters.train-epochs}} epochs) ===" /workspace/bin/${BINARY} \