helena 0.1.0

Core types and component interfaces for helena, a latent data-to-waveform generation platform.
Documentation
use crate::latent::Continuous;
use crate::{
    Conditioning, Error, GenerationParams, Latent, LatentGenerator, Result, Seed, SeedPolicy,
    Temperature,
};

/// Draw a condition-by-seed grid from `generator`: `grid[c][s]` is the latent for
/// `conditionings[c]` under `seeds[s]`. The raw measurement [`sample_grid`] scores;
/// expose it so a caller can derive several diagnostics (e.g. per-condition
/// [`sample_dispersion`] and the pooled [`GenerationDiagnostics`]) from one set of
/// draws rather than regenerating per metric. Every cell shares `params` save its
/// seed, so the grid is fully determined by `(conditionings, seeds, params)`.
///
/// Each cell is generated under a [`SeedPolicy::Seeded`] carrying that cell's
/// [`Seed`], so the draws are replayable per grid position (the old per-cell seed
/// budget, now a typed [`Seed`] value rather than a bare `u64`). The temperature is
/// taken from `params.seed` when it already carries one, else the neutral `1.0`, so
/// the base policy's spread is preserved while only the seed varies across the grid.
///
/// [`sample_grid`]: super::sample_grid
/// [`sample_dispersion`]: super::sample_dispersion
/// [`GenerationDiagnostics`]: super::GenerationDiagnostics
pub fn generate_grid<G: LatentGenerator<Continuous>>(
    generator: &G,
    conditionings: &[Conditioning],
    seeds: &[Seed],
    params: &GenerationParams,
) -> Result<Vec<Vec<Latent<Continuous>>>> {
    if conditionings.is_empty() {
        return Err(Error::validation(
            "bakeoff grid needs at least one conditioning",
        ));
    }
    if seeds.is_empty() {
        return Err(Error::validation("bakeoff grid needs at least one seed"));
    }
    // The base policy's temperature, or the neutral spread when it fixes none.
    let temperature = params
        .seed
        .temperature()
        .unwrap_or_else(|| Temperature::new(1.0).expect("1.0 is a valid temperature"));

    let mut grid = Vec::with_capacity(conditionings.len());
    for conditioning in conditionings {
        let mut row = Vec::with_capacity(seeds.len());
        for &seed in seeds {
            let cell_params = GenerationParams {
                seed: SeedPolicy::Seeded { seed, temperature },
                ..*params
            };
            row.push(generator.generate(conditioning, &cell_params)?);
        }
        grid.push(row);
    }
    Ok(grid)
}