refactor: replace TpeError with Error in the optimizer library

This commit is contained in:
Manuel Raimann
2026-01-30 19:24:58 +01:00
parent eb57519506
commit d8ef4352c3
9 changed files with 158 additions and 170 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
study study
.optimize_with_sampler(20, |trial| { .optimize_with_sampler(20, |trial| {
let x = trial.suggest_float("x", -10.0, 10.0)?; let x = trial.suggest_float("x", -10.0, 10.0)?;
Ok::<_, optimizer::TpeError>(x * x) Ok::<_, optimizer::Error>(x * x)
}) })
.unwrap(); .unwrap();
+3 -9
View File
@@ -1,10 +1,5 @@
//! Error types for the optimizer library. #[derive(Debug, thiserror::Error)]
pub enum Error {
use thiserror::Error;
/// The error type for TPE operations.
#[derive(Debug, Error)]
pub enum TpeError {
/// Returned when the lower bound is greater than the upper bound. /// Returned when the lower bound is greater than the upper bound.
#[error("invalid bounds: low ({low}) must be less than or equal to high ({high})")] #[error("invalid bounds: low ({low}) must be less than or equal to high ({high})")]
InvalidBounds { InvalidBounds {
@@ -61,5 +56,4 @@ pub enum TpeError {
TaskError(String), TaskError(String),
} }
/// A specialized Result type for TPE operations. pub type Result<T> = core::result::Result<T, Error>;
pub type Result<T> = core::result::Result<T, TpeError>;
+10 -10
View File
@@ -5,7 +5,7 @@
use rand::Rng; use rand::Rng;
use crate::error::{Result, TpeError}; use crate::error::{Error, Result};
/// A Gaussian kernel density estimator for continuous distributions. /// A Gaussian kernel density estimator for continuous distributions.
/// ///
@@ -45,10 +45,10 @@ impl KernelDensityEstimator {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::EmptySamples` if `samples` is empty. /// Returns `Error::EmptySamples` if `samples` is empty.
pub(crate) fn new(samples: Vec<f64>) -> Result<Self> { pub(crate) fn new(samples: Vec<f64>) -> Result<Self> {
if samples.is_empty() { if samples.is_empty() {
return Err(TpeError::EmptySamples); return Err(Error::EmptySamples);
} }
let bandwidth = Self::scotts_rule(&samples); let bandwidth = Self::scotts_rule(&samples);
@@ -61,14 +61,14 @@ impl KernelDensityEstimator {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::EmptySamples` if `samples` is empty. /// Returns `Error::EmptySamples` if `samples` is empty.
/// Returns `TpeError::InvalidBandwidth` if `bandwidth` is not positive. /// Returns `Error::InvalidBandwidth` if `bandwidth` is not positive.
pub(crate) fn with_bandwidth(samples: Vec<f64>, bandwidth: f64) -> Result<Self> { pub(crate) fn with_bandwidth(samples: Vec<f64>, bandwidth: f64) -> Result<Self> {
if samples.is_empty() { if samples.is_empty() {
return Err(TpeError::EmptySamples); return Err(Error::EmptySamples);
} }
if bandwidth <= 0.0 { if bandwidth <= 0.0 {
return Err(TpeError::InvalidBandwidth(bandwidth)); return Err(Error::InvalidBandwidth(bandwidth));
} }
Ok(Self { samples, bandwidth }) Ok(Self { samples, bandwidth })
@@ -261,20 +261,20 @@ mod tests {
fn test_kde_empty_samples() { fn test_kde_empty_samples() {
let samples: Vec<f64> = vec![]; let samples: Vec<f64> = vec![];
let result = KernelDensityEstimator::new(samples); let result = KernelDensityEstimator::new(samples);
assert!(matches!(result, Err(TpeError::EmptySamples))); assert!(matches!(result, Err(Error::EmptySamples)));
} }
#[test] #[test]
fn test_kde_zero_bandwidth() { fn test_kde_zero_bandwidth() {
let samples = vec![1.0, 2.0, 3.0]; let samples = vec![1.0, 2.0, 3.0];
let result = KernelDensityEstimator::with_bandwidth(samples, 0.0); let result = KernelDensityEstimator::with_bandwidth(samples, 0.0);
assert!(matches!(result, Err(TpeError::InvalidBandwidth(_)))); assert!(matches!(result, Err(Error::InvalidBandwidth(_))));
} }
#[test] #[test]
fn test_kde_negative_bandwidth() { fn test_kde_negative_bandwidth() {
let samples = vec![1.0, 2.0, 3.0]; let samples = vec![1.0, 2.0, 3.0];
let result = KernelDensityEstimator::with_bandwidth(samples, -1.0); let result = KernelDensityEstimator::with_bandwidth(samples, -1.0);
assert!(matches!(result, Err(TpeError::InvalidBandwidth(_)))); assert!(matches!(result, Err(Error::InvalidBandwidth(_))));
} }
} }
+3 -3
View File
@@ -33,7 +33,7 @@
//! study //! study
//! .optimize_with_sampler(20, |trial| { //! .optimize_with_sampler(20, |trial| {
//! let x = trial.suggest_float("x", -10.0, 10.0)?; //! let x = trial.suggest_float("x", -10.0, 10.0)?;
//! Ok::<_, optimizer::TpeError>(x * x) //! Ok::<_, optimizer::Error>(x * x)
//! }) //! })
//! .unwrap(); //! .unwrap();
//! //!
@@ -86,7 +86,7 @@
//! let optimizer = trial.suggest_categorical("optimizer", &["sgd", "adam", "rmsprop"])?; //! let optimizer = trial.suggest_categorical("optimizer", &["sgd", "adam", "rmsprop"])?;
//! //!
//! // Return objective value //! // Return objective value
//! Ok::<_, optimizer::TpeError>(x * n as f64) //! Ok::<_, optimizer::Error>(x * n as f64)
//! }) //! })
//! .unwrap(); //! .unwrap();
//! ``` //! ```
@@ -140,7 +140,7 @@ mod study;
mod trial; mod trial;
mod types; mod types;
pub use error::{Result, TpeError}; pub use error::{Error, Result};
pub use param::ParamValue; pub use param::ParamValue;
pub use study::Study; pub use study::Study;
pub use trial::Trial; pub use trial::Trial;
+12 -12
View File
@@ -9,7 +9,7 @@ use rand::rngs::StdRng;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use crate::distribution::Distribution; use crate::distribution::Distribution;
use crate::error::{Result, TpeError}; use crate::error::{Error, Result};
use crate::kde::KernelDensityEstimator; use crate::kde::KernelDensityEstimator;
use crate::param::ParamValue; use crate::param::ParamValue;
use crate::sampler::{CompletedTrial, Sampler}; use crate::sampler::{CompletedTrial, Sampler};
@@ -106,8 +106,8 @@ impl TpeSampler {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::InvalidGamma` if gamma is not in (0.0, 1.0). /// Returns `Error::InvalidGamma` if gamma is not in (0.0, 1.0).
/// Returns `TpeError::InvalidBandwidth` if `kde_bandwidth` is Some but not positive. /// Returns `Error::InvalidBandwidth` if `kde_bandwidth` is Some but not positive.
pub fn with_config( pub fn with_config(
gamma: f64, gamma: f64,
n_startup_trials: usize, n_startup_trials: usize,
@@ -116,12 +116,12 @@ impl TpeSampler {
seed: Option<u64>, seed: Option<u64>,
) -> Result<Self> { ) -> Result<Self> {
if gamma <= 0.0 || gamma >= 1.0 { if gamma <= 0.0 || gamma >= 1.0 {
return Err(TpeError::InvalidGamma(gamma)); return Err(Error::InvalidGamma(gamma));
} }
if let Some(bw) = kde_bandwidth if let Some(bw) = kde_bandwidth
&& bw <= 0.0 && bw <= 0.0
{ {
return Err(TpeError::InvalidBandwidth(bw)); return Err(Error::InvalidBandwidth(bw));
} }
let rng = match seed { let rng = match seed {
@@ -484,7 +484,7 @@ impl TpeSamplerBuilder {
/// # Note /// # Note
/// ///
/// Validation happens at `build()` time. If gamma is not in (0.0, 1.0), /// Validation happens at `build()` time. If gamma is not in (0.0, 1.0),
/// `build()` will return `Err(TpeError::InvalidGamma)`. /// `build()` will return `Err(Error::InvalidGamma)`.
#[must_use] #[must_use]
pub fn gamma(mut self, gamma: f64) -> Self { pub fn gamma(mut self, gamma: f64) -> Self {
self.gamma = gamma; self.gamma = gamma;
@@ -569,7 +569,7 @@ impl TpeSamplerBuilder {
/// # Note /// # Note
/// ///
/// Validation happens at `build()` time. If bandwidth is not positive, /// Validation happens at `build()` time. If bandwidth is not positive,
/// `build()` will return `Err(TpeError::InvalidBandwidth)`. /// `build()` will return `Err(Error::InvalidBandwidth)`.
#[must_use] #[must_use]
pub fn kde_bandwidth(mut self, bandwidth: f64) -> Self { pub fn kde_bandwidth(mut self, bandwidth: f64) -> Self {
self.kde_bandwidth = Some(bandwidth); self.kde_bandwidth = Some(bandwidth);
@@ -602,8 +602,8 @@ impl TpeSamplerBuilder {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::InvalidGamma` if gamma is not in (0.0, 1.0). /// Returns `Error::InvalidGamma` if gamma is not in (0.0, 1.0).
/// Returns `TpeError::InvalidBandwidth` if `kde_bandwidth` is Some but not positive. /// Returns `Error::InvalidBandwidth` if `kde_bandwidth` is Some but not positive.
/// ///
/// # Examples /// # Examples
/// ///
@@ -817,13 +817,13 @@ mod tests {
#[test] #[test]
fn test_tpe_sampler_invalid_gamma_zero() { fn test_tpe_sampler_invalid_gamma_zero() {
let result = TpeSampler::with_config(0.0, 10, 24, None, None); let result = TpeSampler::with_config(0.0, 10, 24, None, None);
assert!(matches!(result, Err(TpeError::InvalidGamma(_)))); assert!(matches!(result, Err(Error::InvalidGamma(_))));
} }
#[test] #[test]
fn test_tpe_sampler_invalid_gamma_one() { fn test_tpe_sampler_invalid_gamma_one() {
let result = TpeSampler::with_config(1.0, 10, 24, None, None); let result = TpeSampler::with_config(1.0, 10, 24, None, None);
assert!(matches!(result, Err(TpeError::InvalidGamma(_)))); assert!(matches!(result, Err(Error::InvalidGamma(_))));
} }
#[test] #[test]
@@ -1077,7 +1077,7 @@ mod tests {
#[test] #[test]
fn test_tpe_sampler_builder_invalid_gamma() { fn test_tpe_sampler_builder_invalid_gamma() {
let result = TpeSamplerBuilder::new().gamma(1.5).build(); let result = TpeSamplerBuilder::new().gamma(1.5).build();
assert!(matches!(result, Err(TpeError::InvalidGamma(_)))); assert!(matches!(result, Err(Error::InvalidGamma(_))));
} }
#[test] #[test]
+38 -38
View File
@@ -275,7 +275,7 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if no trials have been completed. /// Returns `Error::NoCompletedTrials` if no trials have been completed.
/// ///
/// # Examples /// # Examples
/// ///
@@ -305,7 +305,7 @@ where
let trials = self.completed_trials.read(); let trials = self.completed_trials.read();
if trials.is_empty() { if trials.is_empty() {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
let best = trials let best = trials
@@ -325,7 +325,7 @@ where
} }
} }
}) })
.ok_or(crate::TpeError::NoCompletedTrials)?; .ok_or(crate::Error::NoCompletedTrials)?;
Ok(best.clone()) Ok(best.clone())
} }
@@ -338,7 +338,7 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if no trials have been completed. /// Returns `Error::NoCompletedTrials` if no trials have been completed.
/// ///
/// # Examples /// # Examples
/// ///
@@ -387,7 +387,7 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if all trials failed (no successful trials). /// Returns `Error::NoCompletedTrials` if all trials failed (no successful trials).
/// ///
/// # Examples /// # Examples
/// ///
@@ -402,7 +402,7 @@ where
/// study /// study
/// .optimize(10, |trial| { /// .optimize(10, |trial| {
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// Ok::<_, optimizer::TpeError>(x * x) /// Ok::<_, optimizer::Error>(x * x)
/// }) /// })
/// .unwrap(); /// .unwrap();
/// ///
@@ -431,7 +431,7 @@ where
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -455,7 +455,7 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if all trials failed (no successful trials). /// Returns `Error::NoCompletedTrials` if all trials failed (no successful trials).
/// ///
/// # Examples /// # Examples
/// ///
@@ -474,7 +474,7 @@ where
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// // Simulate async work (e.g., network request) /// // Simulate async work (e.g., network request)
/// let value = x * x; /// let value = x * x;
/// Ok::<_, optimizer::TpeError>((trial, value)) /// Ok::<_, optimizer::Error>((trial, value))
/// }) /// })
/// .await?; /// .await?;
/// ///
@@ -511,7 +511,7 @@ where
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -536,8 +536,8 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if all trials failed (no successful trials). /// Returns `Error::NoCompletedTrials` if all trials failed (no successful trials).
/// Returns `TpeError::TaskError` if the semaphore is closed or a spawned task panics. /// Returns `Error::TaskError` if the semaphore is closed or a spawned task panics.
/// ///
/// # Examples /// # Examples
/// ///
@@ -556,7 +556,7 @@ where
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// // Async objective function (e.g., network request) /// // Async objective function (e.g., network request)
/// let value = x * x; /// let value = x * x;
/// Ok::<_, optimizer::TpeError>((trial, value)) /// Ok::<_, optimizer::Error>((trial, value))
/// }) /// })
/// .await?; /// .await?;
/// ///
@@ -590,7 +590,7 @@ where
.clone() .clone()
.acquire_owned() .acquire_owned()
.await .await
.map_err(|e| crate::TpeError::TaskError(e.to_string()))?; .map_err(|e| crate::Error::TaskError(e.to_string()))?;
let trial = self.create_trial(); let trial = self.create_trial();
let objective = Arc::clone(&objective); let objective = Arc::clone(&objective);
@@ -607,7 +607,7 @@ where
for handle in handles { for handle in handles {
match handle match handle
.await .await
.map_err(|e| crate::TpeError::TaskError(e.to_string()))? .map_err(|e| crate::Error::TaskError(e.to_string()))?
{ {
Ok((trial, value)) => { Ok((trial, value)) => {
self.complete_trial(trial, value); self.complete_trial(trial, value);
@@ -620,7 +620,7 @@ where
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -643,9 +643,9 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if no trials completed successfully /// Returns `Error::NoCompletedTrials` if no trials completed successfully
/// before optimization stopped (either by completing all trials or early stopping). /// before optimization stopped (either by completing all trials or early stopping).
/// Returns `TpeError::Internal` if a completed trial is not found after adding (internal invariant violation). /// Returns `Error::Internal` if a completed trial is not found after adding (internal invariant violation).
/// ///
/// # Examples /// # Examples
/// ///
@@ -664,7 +664,7 @@ where
/// 100, /// 100,
/// |trial| { /// |trial| {
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// Ok::<_, optimizer::TpeError>(x * x) /// Ok::<_, optimizer::Error>(x * x)
/// }, /// },
/// |_study, completed_trial| { /// |_study, completed_trial| {
/// // Stop early if we find a value less than 1.0 /// // Stop early if we find a value less than 1.0
@@ -702,7 +702,7 @@ where
// Get the just-completed trial for the callback // Get the just-completed trial for the callback
let trials = self.completed_trials.read(); let trials = self.completed_trials.read();
let Some(completed) = trials.last() else { let Some(completed) = trials.last() else {
return Err(crate::TpeError::Internal( return Err(crate::Error::Internal(
"completed trial not found after adding", "completed trial not found after adding",
)); ));
}; };
@@ -725,7 +725,7 @@ where
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -783,7 +783,7 @@ impl Study<f64> {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if all trials failed (no successful trials). /// Returns `Error::NoCompletedTrials` if all trials failed (no successful trials).
/// ///
/// # Examples /// # Examples
/// ///
@@ -798,7 +798,7 @@ impl Study<f64> {
/// study /// study
/// .optimize_with_sampler(10, |trial| { /// .optimize_with_sampler(10, |trial| {
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// Ok::<_, optimizer::TpeError>(x * x) /// Ok::<_, optimizer::Error>(x * x)
/// }) /// })
/// .unwrap(); /// .unwrap();
/// ///
@@ -829,7 +829,7 @@ impl Study<f64> {
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -851,8 +851,8 @@ impl Study<f64> {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if no trials completed successfully. /// Returns `Error::NoCompletedTrials` if no trials completed successfully.
/// Returns `TpeError::Internal` if a completed trial is not found after adding (internal invariant violation). /// Returns `Error::Internal` if a completed trial is not found after adding (internal invariant violation).
/// ///
/// # Examples /// # Examples
/// ///
@@ -871,7 +871,7 @@ impl Study<f64> {
/// 100, /// 100,
/// |trial| { /// |trial| {
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// Ok::<_, optimizer::TpeError>(x * x) /// Ok::<_, optimizer::Error>(x * x)
/// }, /// },
/// |study, _completed_trial| { /// |study, _completed_trial| {
/// // Stop after finding 5 good trials /// // Stop after finding 5 good trials
@@ -907,7 +907,7 @@ impl Study<f64> {
// Get the just-completed trial for the callback // Get the just-completed trial for the callback
let trials = self.completed_trials.read(); let trials = self.completed_trials.read();
let Some(completed) = trials.last() else { let Some(completed) = trials.last() else {
return Err(crate::TpeError::Internal( return Err(crate::Error::Internal(
"completed trial not found after adding", "completed trial not found after adding",
)); ));
}; };
@@ -930,7 +930,7 @@ impl Study<f64> {
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -953,7 +953,7 @@ impl Study<f64> {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if all trials failed (no successful trials). /// Returns `Error::NoCompletedTrials` if all trials failed (no successful trials).
/// ///
/// # Examples /// # Examples
/// ///
@@ -972,7 +972,7 @@ impl Study<f64> {
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// // Simulate async work (e.g., network request) /// // Simulate async work (e.g., network request)
/// let value = x * x; /// let value = x * x;
/// Ok::<_, optimizer::TpeError>((trial, value)) /// Ok::<_, optimizer::Error>((trial, value))
/// }) /// })
/// .await?; /// .await?;
/// ///
@@ -1009,7 +1009,7 @@ impl Study<f64> {
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
@@ -1034,8 +1034,8 @@ impl Study<f64> {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `TpeError::NoCompletedTrials` if all trials failed (no successful trials). /// Returns `Error::NoCompletedTrials` if all trials failed (no successful trials).
/// Returns `TpeError::TaskError` if the semaphore is closed or a spawned task panics. /// Returns `Error::TaskError` if the semaphore is closed or a spawned task panics.
/// ///
/// # Examples /// # Examples
/// ///
@@ -1054,7 +1054,7 @@ impl Study<f64> {
/// let x = trial.suggest_float("x", -10.0, 10.0)?; /// let x = trial.suggest_float("x", -10.0, 10.0)?;
/// // Async objective function (e.g., network request) /// // Async objective function (e.g., network request)
/// let value = x * x; /// let value = x * x;
/// Ok::<_, optimizer::TpeError>((trial, value)) /// Ok::<_, optimizer::Error>((trial, value))
/// }) /// })
/// .await?; /// .await?;
/// ///
@@ -1087,7 +1087,7 @@ impl Study<f64> {
.clone() .clone()
.acquire_owned() .acquire_owned()
.await .await
.map_err(|e| crate::TpeError::TaskError(e.to_string()))?; .map_err(|e| crate::Error::TaskError(e.to_string()))?;
let trial = self.create_trial_with_sampler(); let trial = self.create_trial_with_sampler();
let objective = Arc::clone(&objective); let objective = Arc::clone(&objective);
@@ -1104,7 +1104,7 @@ impl Study<f64> {
for handle in handles { for handle in handles {
match handle match handle
.await .await
.map_err(|e| crate::TpeError::TaskError(e.to_string()))? .map_err(|e| crate::Error::TaskError(e.to_string()))?
{ {
Ok((trial, value)) => { Ok((trial, value)) => {
self.complete_trial(trial, value); self.complete_trial(trial, value);
@@ -1117,7 +1117,7 @@ impl Study<f64> {
// Return error if no trials succeeded // Return error if no trials succeeded
if self.n_trials() == 0 { if self.n_trials() == 0 {
return Err(crate::TpeError::NoCompletedTrials); return Err(crate::Error::NoCompletedTrials);
} }
Ok(()) Ok(())
+26 -32
View File
@@ -8,7 +8,7 @@ use parking_lot::RwLock;
use crate::distribution::{ use crate::distribution::{
CategoricalDistribution, Distribution, FloatDistribution, IntDistribution, CategoricalDistribution, Distribution, FloatDistribution, IntDistribution,
}; };
use crate::error::{Result, TpeError}; use crate::error::{Error, Result};
use crate::param::ParamValue; use crate::param::ParamValue;
use crate::sampler::{CompletedTrial, Sampler}; use crate::sampler::{CompletedTrial, Sampler};
use crate::types::TrialState; use crate::types::TrialState;
@@ -190,7 +190,7 @@ impl Trial {
/// ``` /// ```
pub fn suggest_float(&mut self, name: impl Into<String>, low: f64, high: f64) -> Result<f64> { pub fn suggest_float(&mut self, name: impl Into<String>, low: f64, high: f64) -> Result<f64> {
if low > high { if low > high {
return Err(TpeError::InvalidBounds { low, high }); return Err(Error::InvalidBounds { low, high });
} }
let name = name.into(); let name = name.into();
@@ -216,7 +216,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different bounds or type" reason: "parameter was previously sampled with different bounds or type"
.to_string(), .to_string(),
@@ -226,7 +226,7 @@ impl Trial {
// Sample using the sampler // Sample using the sampler
let dist = Distribution::Float(distribution); let dist = Distribution::Float(distribution);
let ParamValue::Float(value) = self.sample_value(&dist) else { let ParamValue::Float(value) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal(
"Float distribution should return Float value", "Float distribution should return Float value",
)); ));
}; };
@@ -283,11 +283,11 @@ impl Trial {
high: f64, high: f64,
) -> Result<f64> { ) -> Result<f64> {
if low <= 0.0 { if low <= 0.0 {
return Err(TpeError::InvalidLogBounds); return Err(Error::InvalidLogBounds);
} }
if low > high { if low > high {
return Err(TpeError::InvalidBounds { low, high }); return Err(Error::InvalidBounds { low, high });
} }
let name = name.into(); let name = name.into();
@@ -313,7 +313,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different bounds or type" reason: "parameter was previously sampled with different bounds or type"
.to_string(), .to_string(),
@@ -323,7 +323,7 @@ impl Trial {
// Sample using the sampler (sampler handles log-scale transformation) // Sample using the sampler (sampler handles log-scale transformation)
let dist = Distribution::Float(distribution); let dist = Distribution::Float(distribution);
let ParamValue::Float(value) = self.sample_value(&dist) else { let ParamValue::Float(value) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal(
"Float distribution should return Float value", "Float distribution should return Float value",
)); ));
}; };
@@ -380,11 +380,11 @@ impl Trial {
step: f64, step: f64,
) -> Result<f64> { ) -> Result<f64> {
if step <= 0.0 { if step <= 0.0 {
return Err(TpeError::InvalidStep); return Err(Error::InvalidStep);
} }
if low > high { if low > high {
return Err(TpeError::InvalidBounds { low, high }); return Err(Error::InvalidBounds { low, high });
} }
let name = name.into(); let name = name.into();
@@ -410,7 +410,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different bounds or type" reason: "parameter was previously sampled with different bounds or type"
.to_string(), .to_string(),
@@ -420,7 +420,7 @@ impl Trial {
// Sample using the sampler (sampler handles step-grid) // Sample using the sampler (sampler handles step-grid)
let dist = Distribution::Float(distribution); let dist = Distribution::Float(distribution);
let ParamValue::Float(value) = self.sample_value(&dist) else { let ParamValue::Float(value) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal(
"Float distribution should return Float value", "Float distribution should return Float value",
)); ));
}; };
@@ -466,7 +466,7 @@ impl Trial {
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
pub fn suggest_int(&mut self, name: impl Into<String>, low: i64, high: i64) -> Result<i64> { pub fn suggest_int(&mut self, name: impl Into<String>, low: i64, high: i64) -> Result<i64> {
if low > high { if low > high {
return Err(TpeError::InvalidBounds { return Err(Error::InvalidBounds {
low: low as f64, low: low as f64,
high: high as f64, high: high as f64,
}); });
@@ -495,7 +495,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different bounds or type" reason: "parameter was previously sampled with different bounds or type"
.to_string(), .to_string(),
@@ -505,9 +505,7 @@ impl Trial {
// Sample using the sampler // Sample using the sampler
let dist = Distribution::Int(distribution); let dist = Distribution::Int(distribution);
let ParamValue::Int(value) = self.sample_value(&dist) else { let ParamValue::Int(value) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal("Int distribution should return Int value"));
"Int distribution should return Int value",
));
}; };
// Store distribution and value // Store distribution and value
@@ -554,11 +552,11 @@ impl Trial {
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
pub fn suggest_int_log(&mut self, name: impl Into<String>, low: i64, high: i64) -> Result<i64> { pub fn suggest_int_log(&mut self, name: impl Into<String>, low: i64, high: i64) -> Result<i64> {
if low < 1 { if low < 1 {
return Err(TpeError::InvalidLogBounds); return Err(Error::InvalidLogBounds);
} }
if low > high { if low > high {
return Err(TpeError::InvalidBounds { return Err(Error::InvalidBounds {
low: low as f64, low: low as f64,
high: high as f64, high: high as f64,
}); });
@@ -587,7 +585,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different bounds or type" reason: "parameter was previously sampled with different bounds or type"
.to_string(), .to_string(),
@@ -597,9 +595,7 @@ impl Trial {
// Sample using the sampler (sampler handles log-scale transformation) // Sample using the sampler (sampler handles log-scale transformation)
let dist = Distribution::Int(distribution); let dist = Distribution::Int(distribution);
let ParamValue::Int(value) = self.sample_value(&dist) else { let ParamValue::Int(value) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal("Int distribution should return Int value"));
"Int distribution should return Int value",
));
}; };
// Store distribution and value // Store distribution and value
@@ -659,11 +655,11 @@ impl Trial {
step: i64, step: i64,
) -> Result<i64> { ) -> Result<i64> {
if step <= 0 { if step <= 0 {
return Err(TpeError::InvalidStep); return Err(Error::InvalidStep);
} }
if low > high { if low > high {
return Err(TpeError::InvalidBounds { return Err(Error::InvalidBounds {
low: low as f64, low: low as f64,
high: high as f64, high: high as f64,
}); });
@@ -692,7 +688,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different bounds or type" reason: "parameter was previously sampled with different bounds or type"
.to_string(), .to_string(),
@@ -702,9 +698,7 @@ impl Trial {
// Sample using the sampler (sampler handles step-grid) // Sample using the sampler (sampler handles step-grid)
let dist = Distribution::Int(distribution); let dist = Distribution::Int(distribution);
let ParamValue::Int(value) = self.sample_value(&dist) else { let ParamValue::Int(value) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal("Int distribution should return Int value"));
"Int distribution should return Int value",
));
}; };
// Store distribution and value // Store distribution and value
@@ -760,7 +754,7 @@ impl Trial {
choices: &[T], choices: &[T],
) -> Result<T> { ) -> Result<T> {
if choices.is_empty() { if choices.is_empty() {
return Err(TpeError::EmptyChoices); return Err(Error::EmptyChoices);
} }
let name = name.into(); let name = name.into();
@@ -779,7 +773,7 @@ impl Trial {
} }
} }
// Distribution exists but doesn't match // Distribution exists but doesn't match
return Err(TpeError::ParameterConflict { return Err(Error::ParameterConflict {
name, name,
reason: "parameter was previously sampled with different number of choices or type" reason: "parameter was previously sampled with different number of choices or type"
.to_string(), .to_string(),
@@ -789,7 +783,7 @@ impl Trial {
// Sample using the sampler // Sample using the sampler
let dist = Distribution::Categorical(distribution); let dist = Distribution::Categorical(distribution);
let ParamValue::Categorical(index) = self.sample_value(&dist) else { let ParamValue::Categorical(index) = self.sample_value(&dist) else {
return Err(TpeError::Internal( return Err(Error::Internal(
"Categorical distribution should return Categorical value", "Categorical distribution should return Categorical value",
)); ));
}; };
+13 -13
View File
@@ -6,7 +6,7 @@
use optimizer::sampler::random::RandomSampler; use optimizer::sampler::random::RandomSampler;
use optimizer::sampler::tpe::TpeSampler; use optimizer::sampler::tpe::TpeSampler;
use optimizer::{Direction, Study, TpeError}; use optimizer::{Direction, Error, Study};
#[tokio::test] #[tokio::test]
async fn test_optimize_async_basic() { async fn test_optimize_async_basic() {
@@ -16,7 +16,7 @@ async fn test_optimize_async_basic() {
study study
.optimize_async(10, |mut trial| async move { .optimize_async(10, |mut trial| async move {
let x = trial.suggest_float("x", -10.0, 10.0)?; let x = trial.suggest_float("x", -10.0, 10.0)?;
Ok::<_, TpeError>((trial, x * x)) Ok::<_, Error>((trial, x * x))
}) })
.await .await
.expect("async optimization should succeed"); .expect("async optimization should succeed");
@@ -39,7 +39,7 @@ async fn test_optimize_async_with_sampler() {
study study
.optimize_async_with_sampler(15, |mut trial| async move { .optimize_async_with_sampler(15, |mut trial| async move {
let x = trial.suggest_float("x", -5.0, 5.0)?; let x = trial.suggest_float("x", -5.0, 5.0)?;
Ok::<_, TpeError>((trial, x * x)) Ok::<_, Error>((trial, x * x))
}) })
.await .await
.expect("async optimization with sampler should succeed"); .expect("async optimization with sampler should succeed");
@@ -57,7 +57,7 @@ async fn test_optimize_parallel() {
study study
.optimize_parallel(20, 4, |mut trial| async move { .optimize_parallel(20, 4, |mut trial| async move {
let x = trial.suggest_float("x", -10.0, 10.0)?; let x = trial.suggest_float("x", -10.0, 10.0)?;
Ok::<_, TpeError>((trial, x * x)) Ok::<_, Error>((trial, x * x))
}) })
.await .await
.expect("parallel optimization should succeed"); .expect("parallel optimization should succeed");
@@ -79,7 +79,7 @@ async fn test_optimize_parallel_with_sampler() {
.optimize_parallel_with_sampler(15, 3, |mut trial| async move { .optimize_parallel_with_sampler(15, 3, |mut trial| async move {
let x = trial.suggest_float("x", -5.0, 5.0)?; let x = trial.suggest_float("x", -5.0, 5.0)?;
let y = trial.suggest_float("y", -5.0, 5.0)?; let y = trial.suggest_float("y", -5.0, 5.0)?;
Ok::<_, TpeError>((trial, x * x + y * y)) Ok::<_, Error>((trial, x * x + y * y))
}) })
.await .await
.expect("parallel optimization with sampler should succeed"); .expect("parallel optimization with sampler should succeed");
@@ -99,7 +99,7 @@ async fn test_optimize_async_all_failures() {
.await; .await;
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -116,7 +116,7 @@ async fn test_optimize_async_with_sampler_all_failures() {
.await; .await;
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -133,7 +133,7 @@ async fn test_optimize_parallel_all_failures() {
.await; .await;
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -150,7 +150,7 @@ async fn test_optimize_parallel_with_sampler_all_failures() {
.await; .await;
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -168,9 +168,9 @@ async fn test_optimize_async_partial_failures() {
async move { async move {
if count.is_multiple_of(2) { if count.is_multiple_of(2) {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>((trial, x)) Ok::<_, Error>((trial, x))
} else { } else {
Err(TpeError::NoCompletedTrials) // Use as error type Err(Error::NoCompletedTrials) // Use as error type
} }
} }
}) })
@@ -190,7 +190,7 @@ async fn test_optimize_parallel_high_concurrency() {
study study
.optimize_parallel(5, 10, |mut trial| async move { .optimize_parallel(5, 10, |mut trial| async move {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>((trial, x)) Ok::<_, Error>((trial, x))
}) })
.await .await
.expect("should handle high concurrency"); .expect("should handle high concurrency");
@@ -207,7 +207,7 @@ async fn test_optimize_parallel_single_concurrency() {
study study
.optimize_parallel(10, 1, |mut trial| async move { .optimize_parallel(10, 1, |mut trial| async move {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>((trial, x)) Ok::<_, Error>((trial, x))
}) })
.await .await
.expect("should work with single concurrency"); .expect("should work with single concurrency");
+52 -52
View File
@@ -8,7 +8,7 @@
use optimizer::sampler::random::RandomSampler; use optimizer::sampler::random::RandomSampler;
use optimizer::sampler::tpe::TpeSampler; use optimizer::sampler::tpe::TpeSampler;
use optimizer::{Direction, Study, TpeError, Trial}; use optimizer::{Direction, Error, Study, Trial};
// ============================================================================= // =============================================================================
// Test: optimize simple quadratic function with TPE, finds near-optimal // Test: optimize simple quadratic function with TPE, finds near-optimal
@@ -30,7 +30,7 @@ fn test_tpe_optimizes_quadratic_function() {
study study
.optimize_with_sampler(50, |trial| { .optimize_with_sampler(50, |trial| {
let x = trial.suggest_float("x", -10.0, 10.0)?; let x = trial.suggest_float("x", -10.0, 10.0)?;
Ok::<_, TpeError>((x - 3.0).powi(2)) Ok::<_, Error>((x - 3.0).powi(2))
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -61,7 +61,7 @@ fn test_tpe_optimizes_multivariate_function() {
.optimize_with_sampler(100, |trial| { .optimize_with_sampler(100, |trial| {
let x = trial.suggest_float("x", -5.0, 5.0)?; let x = trial.suggest_float("x", -5.0, 5.0)?;
let y = trial.suggest_float("y", -5.0, 5.0)?; let y = trial.suggest_float("y", -5.0, 5.0)?;
Ok::<_, TpeError>(x * x + y * y) Ok::<_, Error>(x * x + y * y)
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -90,7 +90,7 @@ fn test_tpe_maximization() {
study study
.optimize_with_sampler(50, |trial| { .optimize_with_sampler(50, |trial| {
let x = trial.suggest_float("x", -10.0, 10.0)?; let x = trial.suggest_float("x", -10.0, 10.0)?;
Ok::<_, TpeError>(-(x - 2.0).powi(2) + 10.0) Ok::<_, Error>(-(x - 2.0).powi(2) + 10.0)
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -123,7 +123,7 @@ fn test_random_sampler_uniform_float_distribution() {
.optimize(n_samples, |trial| { .optimize(n_samples, |trial| {
let x = trial.suggest_float("x", 0.0, 1.0)?; let x = trial.suggest_float("x", 0.0, 1.0)?;
samples.push(x); samples.push(x);
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -160,7 +160,7 @@ fn test_random_sampler_uniform_int_distribution() {
let n = trial.suggest_int("n", 1, 10)?; let n = trial.suggest_int("n", 1, 10)?;
assert!((1..=10).contains(&n), "sample {n} out of range [1, 10]"); assert!((1..=10).contains(&n), "sample {n} out of range [1, 10]");
counts[(n - 1) as usize] += 1; counts[(n - 1) as usize] += 1;
Ok::<_, TpeError>(n as f64) Ok::<_, Error>(n as f64)
}) })
.unwrap(); .unwrap();
@@ -192,7 +192,7 @@ fn test_random_sampler_uniform_categorical_distribution() {
let choice = trial.suggest_categorical("cat", &choices)?; let choice = trial.suggest_categorical("cat", &choices)?;
let idx = choices.iter().position(|&c| c == choice).unwrap(); let idx = choices.iter().position(|&c| c == choice).unwrap();
counts[idx] += 1; counts[idx] += 1;
Ok::<_, TpeError>(idx as f64) Ok::<_, Error>(idx as f64)
}) })
.unwrap(); .unwrap();
@@ -227,7 +227,7 @@ fn test_random_sampler_reproducibility() {
.optimize_with_sampler(100, |trial| { .optimize_with_sampler(100, |trial| {
let x = trial.suggest_float("x", 0.0, 100.0)?; let x = trial.suggest_float("x", 0.0, 100.0)?;
values1.push(x); values1.push(x);
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -235,7 +235,7 @@ fn test_random_sampler_reproducibility() {
.optimize_with_sampler(100, |trial| { .optimize_with_sampler(100, |trial| {
let x = trial.suggest_float("x", 0.0, 100.0)?; let x = trial.suggest_float("x", 0.0, 100.0)?;
values2.push(x); values2.push(x);
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -367,7 +367,7 @@ fn test_parameter_conflict_float_different_bounds() {
trial.suggest_float("x", 0.0, 1.0).unwrap(); trial.suggest_float("x", 0.0, 1.0).unwrap();
let result = trial.suggest_float("x", 0.0, 2.0); // Different upper bound let result = trial.suggest_float("x", 0.0, 2.0); // Different upper bound
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -377,7 +377,7 @@ fn test_parameter_conflict_float_vs_log() {
trial.suggest_float("x", 0.1, 1.0).unwrap(); trial.suggest_float("x", 0.1, 1.0).unwrap();
let result = trial.suggest_float_log("x", 0.1, 1.0); // Same bounds but log scale let result = trial.suggest_float_log("x", 0.1, 1.0); // Same bounds but log scale
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -387,7 +387,7 @@ fn test_parameter_conflict_float_vs_step() {
trial.suggest_float("x", 0.0, 1.0).unwrap(); trial.suggest_float("x", 0.0, 1.0).unwrap();
let result = trial.suggest_float_step("x", 0.0, 1.0, 0.1); // Same bounds but with step let result = trial.suggest_float_step("x", 0.0, 1.0, 0.1); // Same bounds but with step
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -397,7 +397,7 @@ fn test_parameter_conflict_int_different_bounds() {
trial.suggest_int("n", 1, 10).unwrap(); trial.suggest_int("n", 1, 10).unwrap();
let result = trial.suggest_int("n", 1, 20); // Different upper bound let result = trial.suggest_int("n", 1, 20); // Different upper bound
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -407,7 +407,7 @@ fn test_parameter_conflict_int_vs_log() {
trial.suggest_int("n", 1, 100).unwrap(); trial.suggest_int("n", 1, 100).unwrap();
let result = trial.suggest_int_log("n", 1, 100); // Same bounds but log scale let result = trial.suggest_int_log("n", 1, 100); // Same bounds but log scale
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -417,7 +417,7 @@ fn test_parameter_conflict_categorical_different_n_choices() {
trial.suggest_categorical("opt", &["a", "b", "c"]).unwrap(); trial.suggest_categorical("opt", &["a", "b", "c"]).unwrap();
let result = trial.suggest_categorical("opt", &["x", "y"]); // Different number of choices let result = trial.suggest_categorical("opt", &["x", "y"]); // Different number of choices
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -427,7 +427,7 @@ fn test_parameter_conflict_float_vs_int() {
trial.suggest_float("x", 0.0, 10.0).unwrap(); trial.suggest_float("x", 0.0, 10.0).unwrap();
let result = trial.suggest_int("x", 0, 10); // Different type let result = trial.suggest_int("x", 0, 10); // Different type
assert!(matches!(result, Err(TpeError::ParameterConflict { .. }))); assert!(matches!(result, Err(Error::ParameterConflict { .. })));
} }
#[test] #[test]
@@ -438,7 +438,7 @@ fn test_parameter_conflict_returns_name() {
let result = trial.suggest_float("my_param", 0.0, 2.0); let result = trial.suggest_float("my_param", 0.0, 2.0);
match result { match result {
Err(TpeError::ParameterConflict { name, .. }) => { Err(Error::ParameterConflict { name, .. }) => {
assert_eq!(name, "my_param"); assert_eq!(name, "my_param");
} }
_ => panic!("expected ParameterConflict error"), _ => panic!("expected ParameterConflict error"),
@@ -456,7 +456,7 @@ fn test_empty_categorical_returns_error() {
let result = trial.suggest_categorical("opt", empty); let result = trial.suggest_categorical("opt", empty);
assert!(matches!(result, Err(TpeError::EmptyChoices))); assert!(matches!(result, Err(Error::EmptyChoices)));
} }
#[test] #[test]
@@ -466,7 +466,7 @@ fn test_empty_categorical_vec_returns_error() {
let result = trial.suggest_categorical("numbers", &empty); let result = trial.suggest_categorical("numbers", &empty);
assert!(matches!(result, Err(TpeError::EmptyChoices))); assert!(matches!(result, Err(Error::EmptyChoices)));
} }
// ============================================================================= // =============================================================================
@@ -480,7 +480,7 @@ fn test_study_basic_workflow() {
study study
.optimize(10, |trial| { .optimize(10, |trial| {
let x = trial.suggest_float("x", -5.0, 5.0)?; let x = trial.suggest_float("x", -5.0, 5.0)?;
Ok::<_, TpeError>(x * x) Ok::<_, Error>(x * x)
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -517,7 +517,7 @@ fn test_no_completed_trials_error() {
let study: Study<f64> = Study::new(Direction::Minimize); let study: Study<f64> = Study::new(Direction::Minimize);
let result = study.best_trial(); let result = study.best_trial();
assert!(matches!(result, Err(TpeError::NoCompletedTrials))); assert!(matches!(result, Err(Error::NoCompletedTrials)));
} }
#[test] #[test]
@@ -526,11 +526,11 @@ fn test_invalid_bounds_errors() {
// low > high for float // low > high for float
let result = trial.suggest_float("x", 10.0, 5.0); let result = trial.suggest_float("x", 10.0, 5.0);
assert!(matches!(result, Err(TpeError::InvalidBounds { .. }))); assert!(matches!(result, Err(Error::InvalidBounds { .. })));
// low > high for int // low > high for int
let result = trial.suggest_int("n", 100, 50); let result = trial.suggest_int("n", 100, 50);
assert!(matches!(result, Err(TpeError::InvalidBounds { .. }))); assert!(matches!(result, Err(Error::InvalidBounds { .. })));
} }
#[test] #[test]
@@ -539,14 +539,14 @@ fn test_invalid_log_bounds_errors() {
// low <= 0 for log float // low <= 0 for log float
let result = trial.suggest_float_log("x", 0.0, 1.0); let result = trial.suggest_float_log("x", 0.0, 1.0);
assert!(matches!(result, Err(TpeError::InvalidLogBounds))); assert!(matches!(result, Err(Error::InvalidLogBounds)));
let result = trial.suggest_float_log("y", -1.0, 1.0); let result = trial.suggest_float_log("y", -1.0, 1.0);
assert!(matches!(result, Err(TpeError::InvalidLogBounds))); assert!(matches!(result, Err(Error::InvalidLogBounds)));
// low < 1 for log int // low < 1 for log int
let result = trial.suggest_int_log("n", 0, 100); let result = trial.suggest_int_log("n", 0, 100);
assert!(matches!(result, Err(TpeError::InvalidLogBounds))); assert!(matches!(result, Err(Error::InvalidLogBounds)));
} }
#[test] #[test]
@@ -555,14 +555,14 @@ fn test_invalid_step_errors() {
// step <= 0 for float // step <= 0 for float
let result = trial.suggest_float_step("x", 0.0, 1.0, 0.0); let result = trial.suggest_float_step("x", 0.0, 1.0, 0.0);
assert!(matches!(result, Err(TpeError::InvalidStep))); assert!(matches!(result, Err(Error::InvalidStep)));
let result = trial.suggest_float_step("y", 0.0, 1.0, -0.1); let result = trial.suggest_float_step("y", 0.0, 1.0, -0.1);
assert!(matches!(result, Err(TpeError::InvalidStep))); assert!(matches!(result, Err(Error::InvalidStep)));
// step <= 0 for int // step <= 0 for int
let result = trial.suggest_int_step("n", 0, 100, 0); let result = trial.suggest_int_step("n", 0, 100, 0);
assert!(matches!(result, Err(TpeError::InvalidStep))); assert!(matches!(result, Err(Error::InvalidStep)));
} }
#[test] #[test]
@@ -588,7 +588,7 @@ fn test_tpe_with_categorical_parameter() {
"cubic" => -((x - 1.0).powi(2)) + 10.0, // peak at x=1, max value 10 "cubic" => -((x - 1.0).powi(2)) + 10.0, // peak at x=1, max value 10
_ => unreachable!(), _ => unreachable!(),
}; };
Ok::<_, TpeError>(value) Ok::<_, Error>(value)
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -615,7 +615,7 @@ fn test_tpe_with_integer_parameters() {
study study
.optimize_with_sampler(30, |trial| { .optimize_with_sampler(30, |trial| {
let n = trial.suggest_int("n", 1, 10)?; let n = trial.suggest_int("n", 1, 10)?;
Ok::<_, TpeError>(((n - 7) as f64).powi(2)) Ok::<_, Error>(((n - 7) as f64).powi(2))
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -643,7 +643,7 @@ fn test_callback_early_stopping() {
|trial| { |trial| {
trials_run.set(trials_run.get() + 1); trials_run.set(trials_run.get() + 1);
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}, },
|_study, _trial| { |_study, _trial| {
// Stop after 5 trials // Stop after 5 trials
@@ -666,7 +666,7 @@ fn test_study_trials_iteration() {
study study
.optimize(5, |trial| { .optimize(5, |trial| {
let x = trial.suggest_float("x", 0.0, 1.0)?; let x = trial.suggest_float("x", 0.0, 1.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -758,7 +758,7 @@ fn test_best_value() {
study study
.optimize(10, |trial| { .optimize(10, |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -792,7 +792,7 @@ fn test_study_set_sampler() {
study study
.optimize_with_sampler(10, |trial| { .optimize_with_sampler(10, |trial| {
let x = trial.suggest_float("x", -5.0, 5.0)?; let x = trial.suggest_float("x", -5.0, 5.0)?;
Ok::<_, TpeError>(x * x) Ok::<_, Error>(x * x)
}) })
.expect("optimization should succeed with new sampler"); .expect("optimization should succeed with new sampler");
@@ -807,7 +807,7 @@ fn test_study_with_i32_value_type() {
study study
.optimize(10, |trial| { .optimize(10, |trial| {
let x = trial.suggest_int("x", -10, 10)?; let x = trial.suggest_int("x", -10, 10)?;
Ok::<_, TpeError>(x.abs() as i32) Ok::<_, Error>(x.abs() as i32)
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -824,7 +824,7 @@ fn test_optimize_all_trials_fail() {
let result = study.optimize(5, |_trial| Err::<f64, &str>("always fails")); let result = study.optimize(5, |_trial| Err::<f64, &str>("always fails"));
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -842,7 +842,7 @@ fn test_optimize_with_callback_all_trials_fail() {
); );
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -854,7 +854,7 @@ fn test_optimize_with_sampler_all_trials_fail() {
let result = study.optimize_with_sampler(5, |_trial| Err::<f64, &str>("always fails")); let result = study.optimize_with_sampler(5, |_trial| Err::<f64, &str>("always fails"));
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -872,7 +872,7 @@ fn test_optimize_with_callback_sampler_all_trials_fail() {
); );
assert!( assert!(
matches!(result, Err(TpeError::NoCompletedTrials)), matches!(result, Err(Error::NoCompletedTrials)),
"should return NoCompletedTrials when all trials fail" "should return NoCompletedTrials when all trials fail"
); );
} }
@@ -902,7 +902,7 @@ fn test_tpe_sampler_builder_default_trait() {
study study
.optimize_with_sampler(5, |trial| { .optimize_with_sampler(5, |trial| {
let x = trial.suggest_float("x", 0.0, 1.0)?; let x = trial.suggest_float("x", 0.0, 1.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -917,7 +917,7 @@ fn test_tpe_sampler_default_trait() {
study study
.optimize_with_sampler(5, |trial| { .optimize_with_sampler(5, |trial| {
let x = trial.suggest_float("x", 0.0, 1.0)?; let x = trial.suggest_float("x", 0.0, 1.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -938,7 +938,7 @@ fn test_tpe_with_fixed_kde_bandwidth() {
study study
.optimize_with_sampler(20, |trial| { .optimize_with_sampler(20, |trial| {
let x = trial.suggest_float("x", -5.0, 5.0)?; let x = trial.suggest_float("x", -5.0, 5.0)?;
Ok::<_, TpeError>(x * x) Ok::<_, Error>(x * x)
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -949,7 +949,7 @@ fn test_tpe_with_fixed_kde_bandwidth() {
#[test] #[test]
fn test_tpe_sampler_invalid_kde_bandwidth() { fn test_tpe_sampler_invalid_kde_bandwidth() {
let result = TpeSampler::with_config(0.25, 10, 24, Some(-1.0), None); let result = TpeSampler::with_config(0.25, 10, 24, Some(-1.0), None);
assert!(matches!(result, Err(TpeError::InvalidBandwidth(_)))); assert!(matches!(result, Err(Error::InvalidBandwidth(_))));
} }
#[test] #[test]
@@ -966,7 +966,7 @@ fn test_tpe_split_trials_with_two_trials() {
study study
.optimize_with_sampler(5, |trial| { .optimize_with_sampler(5, |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.expect("optimization should succeed with small history"); .expect("optimization should succeed with small history");
@@ -987,7 +987,7 @@ fn test_tpe_with_log_scale_int() {
.optimize_with_sampler(20, |trial| { .optimize_with_sampler(20, |trial| {
let batch_size = trial.suggest_int_log("batch_size", 1, 1024)?; let batch_size = trial.suggest_int_log("batch_size", 1, 1024)?;
// Optimal around batch_size = 32 // Optimal around batch_size = 32
Ok::<_, TpeError>(((batch_size as f64).log2() - 5.0).powi(2)) Ok::<_, Error>(((batch_size as f64).log2() - 5.0).powi(2))
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -1009,7 +1009,7 @@ fn test_tpe_with_step_distributions() {
.optimize_with_sampler(20, |trial| { .optimize_with_sampler(20, |trial| {
let x = trial.suggest_float_step("x", 0.0, 10.0, 0.5)?; let x = trial.suggest_float_step("x", 0.0, 10.0, 0.5)?;
let n = trial.suggest_int_step("n", 0, 100, 10)?; let n = trial.suggest_int_step("n", 0, 100, 10)?;
Ok::<_, TpeError>((x - 5.0).powi(2) + ((n - 50) as f64).powi(2)) Ok::<_, Error>((x - 5.0).powi(2) + ((n - 50) as f64).powi(2))
}) })
.expect("optimization should succeed"); .expect("optimization should succeed");
@@ -1088,7 +1088,7 @@ fn test_tpe_empty_good_or_bad_values_fallback() {
study study
.optimize_with_sampler(10, |trial| { .optimize_with_sampler(10, |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();
@@ -1096,7 +1096,7 @@ fn test_tpe_empty_good_or_bad_values_fallback() {
study study
.optimize_with_sampler(5, |trial| { .optimize_with_sampler(5, |trial| {
let y = trial.suggest_float("y", 0.0, 10.0)?; let y = trial.suggest_float("y", 0.0, 10.0)?;
Ok::<_, TpeError>(y) Ok::<_, Error>(y)
}) })
.unwrap(); .unwrap();
@@ -1114,7 +1114,7 @@ fn test_callback_early_stopping_on_first_trial() {
100, 100,
|trial| { |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}, },
|_study, _trial| { |_study, _trial| {
// Stop immediately after first trial // Stop immediately after first trial
@@ -1138,7 +1138,7 @@ fn test_callback_sampler_early_stopping() {
100, 100,
|trial| { |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}, },
|study, _trial| { |study, _trial| {
if study.n_trials() >= 3 { if study.n_trials() >= 3 {
@@ -1174,7 +1174,7 @@ fn test_best_trial_with_nan_values() {
study study
.optimize(5, |trial| { .optimize(5, |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?; let x = trial.suggest_float("x", 0.0, 10.0)?;
Ok::<_, TpeError>(x) Ok::<_, Error>(x)
}) })
.unwrap(); .unwrap();