Remove Serde

This commit is contained in:
Manuel Raimann
2026-01-30 18:38:43 +01:00
committed by Manuel
parent aaf880e1c7
commit daab3ea202
11 changed files with 0 additions and 443 deletions
-252
View File
@@ -7,19 +7,11 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use parking_lot::RwLock;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::sampler::{CompletedTrial, RandomSampler, Sampler};
use crate::trial::Trial;
use crate::types::Direction;
/// Helper function to create default sampler for serde deserialization.
#[cfg(feature = "serde")]
fn default_sampler() -> Arc<dyn Sampler> {
Arc::new(RandomSampler::new())
}
/// A study manages the optimization process, tracking trials and their results.
///
/// The study is parameterized by the objective value type `V`, which defaults to `f64`.
@@ -29,13 +21,6 @@ fn default_sampler() -> Arc<dyn Sampler> {
/// When `V = f64`, the study passes trial history to the sampler for informed
/// parameter suggestions (e.g., TPE sampler uses history to guide sampling).
///
/// # Serialization
///
/// When the `serde` feature is enabled, the study can be serialized and deserialized.
/// The completed trials and trial ID counter are preserved, allowing optimization to
/// continue after deserialization. The sampler is not serialized; upon deserialization,
/// a default `RandomSampler` is used. Use `Study::set_sampler()` to restore a custom sampler.
///
/// # Examples
///
/// ```
@@ -115,9 +100,6 @@ where
/// Sets a new sampler for the study.
///
/// This method is useful after deserializing a study when you want to use
/// a custom sampler (e.g., TPE) instead of the default `RandomSampler`.
///
/// # Arguments
///
/// * `sampler` - The sampler to use for parameter sampling.
@@ -127,7 +109,6 @@ where
/// ```
/// use optimizer::{Direction, Study, TpeSampler};
///
/// // After deserializing a study, restore the TPE sampler
/// let mut study: Study<f64> = Study::new(Direction::Minimize);
/// study.set_sampler(TpeSampler::new());
/// ```
@@ -1105,236 +1086,3 @@ impl Study<f64> {
Ok(())
}
}
// Manual Serialize implementation for Study<V> when serde feature is enabled.
#[cfg(feature = "serde")]
impl<V> Serialize for Study<V>
where
V: PartialOrd + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("Study", 3)?;
state.serialize_field("direction", &self.direction)?;
// Serialize the Vec inside the Arc<RwLock<>>
let trials = self.completed_trials.read();
state.serialize_field("completed_trials", &*trials)?;
state.serialize_field("next_trial_id", &self.next_trial_id.load(Ordering::SeqCst))?;
state.end()
}
}
// Manual Deserialize implementation for Study<V> when serde feature is enabled.
#[cfg(feature = "serde")]
impl<'de, V> Deserialize<'de> for Study<V>
where
V: PartialOrd + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use std::fmt;
use std::marker::PhantomData;
use serde::de::{self, MapAccess, Visitor};
#[derive(serde::Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
Direction,
CompletedTrials,
NextTrialId,
}
struct StudyVisitor<V>(PhantomData<V>);
impl<'de, V> Visitor<'de> for StudyVisitor<V>
where
V: PartialOrd + Deserialize<'de>,
{
type Value = Study<V>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Study")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut direction = None;
let mut completed_trials: Option<Vec<CompletedTrial<V>>> = None;
let mut next_trial_id = None;
while let Some(key) = map.next_key()? {
match key {
Field::Direction => {
if direction.is_some() {
return Err(de::Error::duplicate_field("direction"));
}
direction = Some(map.next_value()?);
}
Field::CompletedTrials => {
if completed_trials.is_some() {
return Err(de::Error::duplicate_field("completed_trials"));
}
completed_trials = Some(map.next_value()?);
}
Field::NextTrialId => {
if next_trial_id.is_some() {
return Err(de::Error::duplicate_field("next_trial_id"));
}
next_trial_id = Some(map.next_value()?);
}
}
}
let direction = direction.ok_or_else(|| de::Error::missing_field("direction"))?;
let completed_trials =
completed_trials.ok_or_else(|| de::Error::missing_field("completed_trials"))?;
let next_trial_id: u64 =
next_trial_id.ok_or_else(|| de::Error::missing_field("next_trial_id"))?;
Ok(Study {
direction,
sampler: default_sampler(),
completed_trials: Arc::new(RwLock::new(completed_trials)),
next_trial_id: AtomicU64::new(next_trial_id),
})
}
}
const FIELDS: &[&str] = &["direction", "completed_trials", "next_trial_id"];
deserializer.deserialize_struct("Study", FIELDS, StudyVisitor(PhantomData))
}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
use super::*;
#[test]
fn test_study_serde_round_trip() {
// Create a study and add some trials
let study: Study<f64> = Study::new(Direction::Minimize);
// Run some optimization
study
.optimize(5, |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?;
let y = trial.suggest_int("y", 1, 5)?;
Ok::<_, crate::TpeError>(x + y as f64)
})
.unwrap();
// Serialize to JSON
let serialized = serde_json::to_string(&study).unwrap();
// Deserialize from JSON
let deserialized: Study<f64> = serde_json::from_str(&serialized).unwrap();
// Verify the data is preserved
assert_eq!(deserialized.direction(), study.direction());
assert_eq!(deserialized.n_trials(), study.n_trials());
// Verify the best trial is the same
let original_best = study.best_trial().unwrap();
let deserialized_best = deserialized.best_trial().unwrap();
assert_eq!(original_best.id, deserialized_best.id);
// Use approximate comparison for floats due to JSON serialization precision
assert!((original_best.value - deserialized_best.value).abs() < 1e-10);
// Check that all param keys match
assert_eq!(original_best.params.len(), deserialized_best.params.len());
for (key, original_val) in &original_best.params {
let deserialized_val = deserialized_best.params.get(key).unwrap();
match (original_val, deserialized_val) {
(crate::param::ParamValue::Float(a), crate::param::ParamValue::Float(b)) => {
assert!((a - b).abs() < 1e-10, "Float param {key} differs");
}
_ => assert_eq!(original_val, deserialized_val),
}
}
// Verify we can continue optimization on the deserialized study
let initial_count = deserialized.n_trials();
deserialized
.optimize(3, |trial| {
let x = trial.suggest_float("x", 0.0, 10.0)?;
let y = trial.suggest_int("y", 1, 5)?;
Ok::<_, crate::TpeError>(x + y as f64)
})
.unwrap();
// Verify new trials were added
assert_eq!(deserialized.n_trials(), initial_count + 3);
}
#[test]
fn test_study_serde_preserves_trial_ids() {
let study: Study<f64> = Study::new(Direction::Maximize);
// Add 5 trials
study
.optimize(5, |trial| {
let x = trial.suggest_float("x", -1.0, 1.0)?;
Ok::<_, crate::TpeError>(x * x)
})
.unwrap();
// Serialize and deserialize
let serialized = serde_json::to_string(&study).unwrap();
let deserialized: Study<f64> = serde_json::from_str(&serialized).unwrap();
// Create a new trial - its ID should continue from where we left off
let new_trial = deserialized.create_trial();
assert_eq!(new_trial.id(), 5); // Next trial should be ID 5
}
#[test]
fn test_completed_trial_serde() {
use std::collections::HashMap;
use crate::distribution::{Distribution, FloatDistribution, IntDistribution};
use crate::param::ParamValue;
let mut params = HashMap::new();
params.insert("x".to_string(), ParamValue::Float(0.5));
params.insert("n".to_string(), ParamValue::Int(42));
let mut distributions = HashMap::new();
distributions.insert(
"x".to_string(),
Distribution::Float(FloatDistribution {
low: 0.0,
high: 1.0,
log_scale: false,
step: None,
}),
);
distributions.insert(
"n".to_string(),
Distribution::Int(IntDistribution {
low: 1,
high: 100,
log_scale: false,
step: None,
}),
);
let completed = CompletedTrial::new(42, params.clone(), distributions.clone(), 0.75);
// Serialize and deserialize
let serialized = serde_json::to_string(&completed).unwrap();
let deserialized: CompletedTrial<f64> = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.id, 42);
assert_eq!(deserialized.value, 0.75);
assert_eq!(deserialized.params, params);
assert_eq!(deserialized.distributions, distributions);
}
}