Implement Parameters API

- Add `.name()` builder method on all 5 parameter types for custom labels
- Add `CompletedTrial::get(&param)` for typed parameter access
- Add `Display` impl on `ParamValue`
- Add prelude module at `optimizer::prelude::*`
- Shadow `_with_sampler` methods on `Study<f64>` so `optimize()` auto-uses
  the configured sampler; deprecate `_with_sampler` variants
- Use runtime `Any` downcasting with `trial_factory` to avoid E0592
- Update all examples and tests to use the new API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Manuel Raimann
2026-02-06 18:54:55 +01:00
parent 55e50e6afb
commit 5dc81fa0ab
11 changed files with 479 additions and 499 deletions
+83 -1
View File
@@ -145,6 +145,7 @@ pub struct FloatParam {
high: f64,
log_scale: bool,
step: Option<f64>,
name: Option<String>,
}
impl FloatParam {
@@ -157,6 +158,7 @@ impl FloatParam {
high,
log_scale: false,
step: None,
name: None,
}
}
@@ -173,6 +175,16 @@ impl FloatParam {
self.step = Some(step);
self
}
/// Sets a human-readable name for this parameter.
///
/// When set, this name is used as the parameter's label instead of
/// the default `Debug` output.
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
impl Parameter for FloatParam {
@@ -217,6 +229,10 @@ impl Parameter for FloatParam {
}
Ok(())
}
fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| format!("{self:?}"))
}
}
/// An integer parameter with optional log-scale and step size.
@@ -248,6 +264,7 @@ pub struct IntParam {
high: i64,
log_scale: bool,
step: Option<i64>,
name: Option<String>,
}
impl IntParam {
@@ -260,6 +277,7 @@ impl IntParam {
high,
log_scale: false,
step: None,
name: None,
}
}
@@ -276,6 +294,16 @@ impl IntParam {
self.step = Some(step);
self
}
/// Sets a human-readable name for this parameter.
///
/// When set, this name is used as the parameter's label instead of
/// the default `Debug` output.
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
impl Parameter for IntParam {
@@ -320,6 +348,10 @@ impl Parameter for IntParam {
}
Ok(())
}
fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| format!("{self:?}"))
}
}
/// A categorical parameter that selects from a list of choices.
@@ -339,6 +371,7 @@ impl Parameter for IntParam {
pub struct CategoricalParam<T: Clone> {
id: ParamId,
choices: Vec<T>,
name: Option<String>,
}
impl<T: Clone> CategoricalParam<T> {
@@ -348,8 +381,19 @@ impl<T: Clone> CategoricalParam<T> {
Self {
id: ParamId::new(),
choices,
name: None,
}
}
/// Sets a human-readable name for this parameter.
///
/// When set, this name is used as the parameter's label instead of
/// the default `Debug` output.
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
impl<T: Clone + Debug> Parameter for CategoricalParam<T> {
@@ -380,6 +424,10 @@ impl<T: Clone + Debug> Parameter for CategoricalParam<T> {
}
Ok(())
}
fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| format!("{self:?}"))
}
}
/// A boolean parameter (equivalent to a categorical with `[false, true]`).
@@ -396,13 +444,27 @@ impl<T: Clone + Debug> Parameter for CategoricalParam<T> {
#[derive(Clone, Debug)]
pub struct BoolParam {
id: ParamId,
name: Option<String>,
}
impl BoolParam {
/// Creates a new boolean parameter.
#[must_use]
pub fn new() -> Self {
Self { id: ParamId::new() }
Self {
id: ParamId::new(),
name: None,
}
}
/// Sets a human-readable name for this parameter.
///
/// When set, this name is used as the parameter's label instead of
/// the default `Debug` output.
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
@@ -431,6 +493,10 @@ impl Parameter for BoolParam {
)),
}
}
fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| format!("{self:?}"))
}
}
/// A trait for enum types that can be used as categorical parameters.
@@ -529,6 +595,7 @@ pub trait Categorical: Sized + Clone {
#[derive(Clone, Debug)]
pub struct EnumParam<T: Categorical> {
id: ParamId,
name: Option<String>,
_marker: core::marker::PhantomData<T>,
}
@@ -538,9 +605,20 @@ impl<T: Categorical> EnumParam<T> {
pub fn new() -> Self {
Self {
id: ParamId::new(),
name: None,
_marker: core::marker::PhantomData,
}
}
/// Sets a human-readable name for this parameter.
///
/// When set, this name is used as the parameter's label instead of
/// the default `Debug` output.
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
impl<T: Categorical> Default for EnumParam<T> {
@@ -570,6 +648,10 @@ impl<T: Categorical + Debug> Parameter for EnumParam<T> {
)),
}
}
fn label(&self) -> String {
self.name.clone().unwrap_or_else(|| format!("{self:?}"))
}
}
#[cfg(test)]