use serde::{Deserialize, Serialize};
use crate::{Error, Result};
use super::NoiseLevel;
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "GuidanceScheduleRepr", into = "GuidanceScheduleRepr")]
#[non_exhaustive]
pub enum GuidanceSchedule {
#[default]
Constant,
Interval(GuidanceInterval),
Monotone,
}
impl GuidanceSchedule {
pub fn guidance_at(&self, level: NoiseLevel, guidance: f32) -> f32 {
match self {
Self::Constant => guidance,
Self::Interval(band) => {
if band.contains(level.signal_variance()) {
guidance
} else {
1.0
}
}
Self::Monotone => 1.0 + (guidance - 1.0) * level.signal_variance(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GuidanceInterval {
lo: f32,
hi: f32,
}
impl GuidanceInterval {
pub fn new(lo: f32, hi: f32) -> Result<Self> {
for (name, value) in [("lo", lo), ("hi", hi)] {
if !value.is_finite() || !(0.0..=1.0).contains(&value) {
return Err(Error::validation(format!(
"guidance interval {name} must be a finite signal variance in [0, 1], got {value}"
)));
}
}
if lo >= hi {
return Err(Error::validation(format!(
"guidance interval requires lo < hi, got [{lo}, {hi}]"
)));
}
Ok(Self { lo, hi })
}
pub fn lo(&self) -> f32 {
self.lo
}
pub fn hi(&self) -> f32 {
self.hi
}
fn contains(&self, gamma: f32) -> bool {
(self.lo..=self.hi).contains(&gamma)
}
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(
tag = "type",
content = "band",
rename_all = "snake_case",
deny_unknown_fields
)]
enum GuidanceScheduleRepr {
Constant,
Interval(GuidanceIntervalRepr),
Monotone,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct GuidanceIntervalRepr {
lo: f32,
hi: f32,
}
impl TryFrom<GuidanceScheduleRepr> for GuidanceSchedule {
type Error = Error;
fn try_from(repr: GuidanceScheduleRepr) -> Result<Self> {
Ok(match repr {
GuidanceScheduleRepr::Constant => Self::Constant,
GuidanceScheduleRepr::Interval(band) => {
Self::Interval(GuidanceInterval::new(band.lo, band.hi)?)
}
GuidanceScheduleRepr::Monotone => Self::Monotone,
})
}
}
impl From<GuidanceSchedule> for GuidanceScheduleRepr {
fn from(schedule: GuidanceSchedule) -> Self {
match schedule {
GuidanceSchedule::Constant => Self::Constant,
GuidanceSchedule::Interval(band) => Self::Interval(GuidanceIntervalRepr {
lo: band.lo(),
hi: band.hi(),
}),
GuidanceSchedule::Monotone => Self::Monotone,
}
}
}