vanth/crates/varo/src/lib.rs
Markus Scully 87957bfbf8
♻️📝 vanth, varo: Refactor storage backend, fix distribution sampling, and enhance type handling
- Refactored `Store` to use a trait-based backend with type-specific tables, replacing the previous enum approach.
- Implemented `Sqlite` and `Memory` backends with proper table management and type-aware operations.
- Added serialization/deserialization handling in `Store` using `serde_json`.
- Implemented `ToString` for `Ty` and improved equality comparisons using string representation.
- Fixed `Distribution::sample` in `varo` to correctly handle 0, 1, and 2+ moments cases.
- Updated store integration tests to verify type-specific storage operations including write, read, and deletion.
- Commented out unfinished `EntityContents` and related structs pending future implementation.
2025-08-07 12:05:55 +03:00

99 lines
2.7 KiB
Rust

use rand_core::{RngCore, SeedableRng};
use rand_chacha::ChaCha8Rng;
use serde::{Deserialize, Serialize};
use std::f32::consts::PI;
use vanth_derive::Vanth;
#[derive(Clone, Debug, Deserialize, Serialize, Vanth)]
pub struct Rng {
inner: ChaCha8Rng,
}
pub fn rng_new() -> Rng {
Rng { inner: ChaCha8Rng::from_seed([0u8; 32]) }
}
pub fn rng_from_seed(seed: [u8; 32]) -> Rng {
Rng { inner: ChaCha8Rng::from_seed(seed) }
}
pub fn rng_set_stream(rng: &mut Rng, stream: u64) {
rng.inner.set_stream(stream);
}
pub fn rng_next_u32(rng: &mut Rng) -> u32 {
rng.inner.next_u32()
}
pub fn rng_next_u64(rng: &mut Rng) -> u64 {
rng.inner.next_u64()
}
pub fn rng_fill_bytes(rng: &mut Rng, dest: &mut [u8]) {
rng.inner.fill_bytes(dest)
}
pub fn rng_gen_f32(rng: &mut Rng) -> f32 {
rng_next_u32(rng) as f32 / u32::MAX as f32
}
pub fn rng_gen_gaussian(rng: &mut Rng, mean: f32, std_dev: f32) -> f32 {
let uniform_for_radius_calc = rng_gen_f32(rng);
let uniform_for_angle = rng_gen_f32(rng);
let radius = (-2.0 * (1.0 - uniform_for_radius_calc).ln()).sqrt();
let theta = 2.0 * PI * uniform_for_angle;
mean + std_dev * radius * theta.cos()
}
pub trait Varo {
/// Produce a random instance of `Self` using the provided RNG.
fn next(digest: &mut Rng) -> Self;
}
#[derive(Clone, Debug, Deserialize, Serialize, Vanth)]
pub struct Distribution {
/// 0th element is the mean, 1st is the variance etc.
pub moments: Vec<f32>,
}
impl Distribution {
pub fn sample(&self, digest: &mut Rng) -> f32 {
if self.moments.is_empty() {
rng_gen_f32(digest)
} else if self.moments.len() == 1 {
rng_gen_gaussian(digest, self.moments[0], 1.0)
} else {
rng_gen_gaussian(digest, self.moments[0], self.moments[1].sqrt())
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize, Vanth)]
pub struct Score {
pub value: f32,
}
impl From<f32> for Score {
fn from(value: f32) -> Self {
Score { value }
}
}
#[derive(Clone, Debug, Deserialize, Serialize, Vanth)]
pub struct OptimizationResult {
/// List of pairs of evaluation score and Rng used to generate the value.
pub values: Vec<(Rng, f32)>
}
pub fn optimize<T: Varo>(evaluator: impl Fn(T) -> Score, rng: &mut Rng, rounds: u32) -> OptimizationResult {
let mut values: Vec<(Rng, f32)> = Vec::with_capacity(rounds as usize);
for i in 0..rounds {
let mut child = rng.clone();
rng_set_stream(&mut child, i as u64);
let t = T::next(&mut child);
let score = evaluator(t).value;
values.push((child, score));
}
values.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
OptimizationResult { values }
}