pub const FORMAT_VERSION: u32 = 1;
pub const ENTITY_SLOT_DELIVERY_DATE_SENTINEL: i32 = i32::MIN;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EntitySlot {
pub entity_type: u8,
pub entity_id: i32,
pub subindex: u32,
pub was_active: bool,
pub delivery_date: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum StateFamily {
HydroStorage = 0,
HydroInflowLag = 1,
AnticipatedThermalState = 2,
HydroTransitBucket = 3,
}
impl StateFamily {
#[must_use]
pub const fn code(self) -> u8 {
self as u8
}
#[must_use]
pub const fn from_code(code: u8) -> Option<Self> {
match code {
0 => Some(Self::HydroStorage),
1 => Some(Self::HydroInflowLag),
2 => Some(Self::AnticipatedThermalState),
3 => Some(Self::HydroTransitBucket),
_ => None,
}
}
}
impl EntitySlot {
#[must_use]
pub fn family(&self) -> Option<StateFamily> {
StateFamily::from_code(self.entity_type)
}
}
#[derive(Debug, Clone)]
pub struct PolicyCutRecord<'a> {
pub cut_id: u64,
pub slot_index: u32,
pub iteration: u32,
pub forward_pass_index: u32,
pub intercept: f64,
pub coefficients: &'a [f64],
pub is_active: bool,
}
#[derive(Debug, Clone)]
pub struct PolicyBasisRecord<'a> {
pub stage_id: u32,
pub iteration: u32,
pub column_status: &'a [u8],
pub row_status: &'a [u8],
pub num_cut_rows: u32,
}
pub const STAGE_STATES_NODE_ID_SENTINEL: i32 = -1;
pub const STAGE_CUTS_NODE_ID_SENTINEL: i32 = -1;
pub const STAGE_CUTS_GRAPH_STAGE_ID_SENTINEL: i32 = -1;
#[derive(Debug, Clone)]
pub struct StageStatesPayload<'a> {
pub stage_id: u32,
pub node_id: i32,
pub state_dimension: u32,
pub count: u32,
pub data: &'a [f64],
pub entity_manifest: &'a [EntitySlot],
}
#[derive(Debug)]
pub struct StageCutsPayload<'a> {
pub stage_id: u32,
pub state_dimension: u32,
pub capacity: u32,
pub warm_start_count: u32,
pub cuts: &'a [PolicyCutRecord<'a>],
pub active_cut_indices: &'a [u32],
pub populated_count: u32,
pub entity_manifest: &'a [EntitySlot],
pub cost_scale_factor: f64,
pub node_id: i32,
pub graph_stage_id: i32,
}
#[derive(Debug, Clone)]
pub struct ManifestNode {
pub id: i32,
pub stage_id: i32,
pub pool_id: u32,
}
#[derive(Debug, Clone)]
pub struct ManifestEdge {
pub source_id: i32,
pub target_id: i32,
pub probability: f64,
}
#[derive(Debug, Clone, Default)]
pub struct GraphManifest {
pub n_pools: u32,
pub nodes: Vec<ManifestNode>,
pub edges: Vec<ManifestEdge>,
}
#[derive(Debug, Clone)]
pub struct ProducerBlock {
pub completed_iterations: u32,
pub final_lower_bound: f64,
pub best_upper_bound: Option<f64>,
pub max_iterations: u32,
pub forward_passes: u32,
pub warm_start_cuts: u32,
pub warm_start_counts: Vec<u32>,
pub rng_seed: u64,
pub total_visited_states: u64,
pub training_block_mode: String,
pub training_block_mode_per_stage: Vec<String>,
pub cost_scale_factor: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct CheckpointManifest {
pub format_version: u32,
pub cobre_version: String,
pub created_at: String,
pub num_stages: u32,
pub graph_manifest: GraphManifest,
pub producer: ProducerBlock,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct OwnedPolicyCutRecord {
pub cut_id: u64,
pub slot_index: u32,
pub iteration: u32,
pub forward_pass_index: u32,
pub intercept: f64,
pub coefficients: Vec<f64>,
pub is_active: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct OwnedPolicyBasisRecord {
pub stage_id: u32,
pub iteration: u32,
pub column_status: Vec<u8>,
pub row_status: Vec<u8>,
pub num_cut_rows: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StageCutsReadResult {
pub stage_id: u32,
pub state_dimension: u32,
pub capacity: u32,
pub warm_start_count: u32,
pub populated_count: u32,
pub cuts: Vec<OwnedPolicyCutRecord>,
pub entity_manifest: Vec<EntitySlot>,
pub cost_scale_factor: Option<f64>,
pub node_id: i32,
pub graph_stage_id: i32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StageStatesReadResult {
pub stage_id: u32,
pub node_id: i32,
pub state_dimension: u32,
pub count: u32,
pub data: Vec<f64>,
pub entity_manifest: Vec<EntitySlot>,
}
#[derive(Debug, Clone)]
pub struct PolicyCheckpoint {
pub metadata: CheckpointManifest,
pub stage_cuts: Vec<StageCutsReadResult>,
pub stage_bases: Vec<OwnedPolicyBasisRecord>,
pub stage_states: Vec<StageStatesReadResult>,
}
#[cfg(test)]
mod tests {
use super::{ENTITY_SLOT_DELIVERY_DATE_SENTINEL, EntitySlot, StateFamily};
#[test]
fn state_family_codes_match_policy_fbs_entity_type() {
assert_eq!(StateFamily::HydroStorage.code(), 0);
assert_eq!(StateFamily::HydroInflowLag.code(), 1);
assert_eq!(StateFamily::AnticipatedThermalState.code(), 2);
assert_eq!(StateFamily::HydroTransitBucket.code(), 3);
}
#[test]
fn state_family_from_code_round_trips_and_rejects_unknown() {
for family in [
StateFamily::HydroStorage,
StateFamily::HydroInflowLag,
StateFamily::AnticipatedThermalState,
StateFamily::HydroTransitBucket,
] {
assert_eq!(StateFamily::from_code(family.code()), Some(family));
}
assert_eq!(StateFamily::from_code(4), None);
assert_eq!(StateFamily::from_code(u8::MAX), None);
}
#[test]
fn entity_slot_family_reads_the_raw_byte() {
let slot = EntitySlot {
entity_type: StateFamily::AnticipatedThermalState.code(),
entity_id: 7,
subindex: 0,
was_active: true,
delivery_date: ENTITY_SLOT_DELIVERY_DATE_SENTINEL,
};
assert_eq!(slot.family(), Some(StateFamily::AnticipatedThermalState));
}
}