use crate::cognition::adaptive_thresholds::{
get_adaptive_threshold, threshold_arousal_intervention, threshold_body_budget_conservation,
threshold_resource_pressure, ThresholdContext,
};
use crate::math::vector::clamp;
use crate::types::belief::AffectValence;
use crate::types::intervention::{CognitiveState, SamplingOverride};
use crate::types::world::{GainMode, WorldModel};
const TEMP_PHASIC: f64 = 0.3;
const TEMP_TONIC: f64 = 0.9;
const TEMP_NEUTRAL: f64 = 0.5;
const TOP_P_PHASIC: f64 = 0.8;
const TOP_P_TONIC: f64 = 0.95;
const TOP_P_NEUTRAL: f64 = 0.9;
const CONSERVATION_TEMP_REDUCTION: f64 = 0.15;
const CONSERVATION_TOP_P_REDUCTION: f64 = 0.1;
const VOLATILITY_HIGH_THRESHOLD: f64 = 0.6;
const VOLATILITY_TEMP_BOOST: f64 = 0.2;
const AROUSAL_FREQUENCY_PENALTY: f64 = 0.3;
const PRESSURE_PRESENCE_PENALTY: f64 = 0.2;
const TEMP_MIN: f64 = 0.1;
const TEMP_MAX: f64 = 1.0;
const TOP_P_MIN: f64 = 0.5;
const TOP_P_MAX: f64 = 1.0;
pub fn compute_sampling_override(state: &CognitiveState) -> SamplingOverride {
let threshold_ctx = ThresholdContext {
sensory_pe: state.sensory_pe,
arousal: state.arousal,
gate_confidence: state.gate_confidence,
pe_volatility: state.pe_volatility,
};
let budget_threshold =
get_adaptive_threshold(&threshold_body_budget_conservation(), &threshold_ctx);
let arousal_threshold =
get_adaptive_threshold(&threshold_arousal_intervention(), &threshold_ctx);
let pressure_threshold =
get_adaptive_threshold(&threshold_resource_pressure(), &threshold_ctx);
let (base_temp, base_top_p) = match state.gain_mode {
GainMode::Phasic => (TEMP_PHASIC, TOP_P_PHASIC),
GainMode::Tonic => (TEMP_TONIC, TOP_P_TONIC),
GainMode::Neutral => (TEMP_NEUTRAL, TOP_P_NEUTRAL),
};
let mut temperature = base_temp;
let mut top_p = base_top_p;
let mut frequency_penalty = 0.0;
let mut presence_penalty = 0.0;
if state.body_budget < budget_threshold {
let conservation = (budget_threshold - state.body_budget) / budget_threshold.max(0.01);
temperature -= conservation * CONSERVATION_TEMP_REDUCTION;
top_p -= conservation * CONSERVATION_TOP_P_REDUCTION;
}
if state.pe_volatility > VOLATILITY_HIGH_THRESHOLD {
let explore_drive =
(state.pe_volatility - VOLATILITY_HIGH_THRESHOLD) / (1.0 - VOLATILITY_HIGH_THRESHOLD);
temperature += explore_drive * VOLATILITY_TEMP_BOOST;
}
if state.arousal > arousal_threshold && state.valence == AffectValence::Negative {
let intensity =
(state.arousal - arousal_threshold) / (1.0 - arousal_threshold).max(0.01);
frequency_penalty += intensity * AROUSAL_FREQUENCY_PENALTY;
}
if state.resource_pressure > pressure_threshold {
let pressure_drive = (state.resource_pressure - pressure_threshold)
/ (1.0 - pressure_threshold).max(0.01);
presence_penalty += pressure_drive * PRESSURE_PRESENCE_PENALTY;
}
SamplingOverride {
temperature: clamp(temperature, TEMP_MIN, TEMP_MAX),
top_p: clamp(top_p, TOP_P_MIN, TOP_P_MAX),
frequency_penalty: clamp(frequency_penalty, 0.0, 1.0),
presence_penalty: clamp(presence_penalty, 0.0, 1.0),
logit_biases: Vec::new(), }
}
pub fn build_cognitive_state(model: &WorldModel, gain_mode: GainMode) -> CognitiveState {
CognitiveState {
arousal: model.belief.affect.arousal,
valence: model.belief.affect.valence,
certainty: model.belief.affect.certainty,
sustained_arousal: model.belief.affect.sustained,
gain_mode,
body_budget: model.body_budget,
sensory_pe: model.sensory_pe,
resource_pressure: model.resource_pressure,
pe_volatility: model.pe_volatility,
gate_confidence: model.gate.confidence,
gate_type: model.gate.gate,
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn phasic_gain_lowers_temperature() {
let state = CognitiveState {
gain_mode: GainMode::Phasic,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(s.temperature <= 0.3, "Phasic should produce low temperature");
assert!(s.top_p <= 0.8, "Phasic should produce low top_p");
}
#[test]
fn tonic_gain_raises_temperature() {
let state = CognitiveState {
gain_mode: GainMode::Tonic,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(s.temperature >= 0.7, "Tonic should produce high temperature");
assert!(s.top_p >= 0.9, "Tonic should produce high top_p");
}
#[test]
fn neutral_state_produces_balanced_params() {
let state = CognitiveState::default();
let s = compute_sampling_override(&state);
assert_relative_eq!(s.temperature, TEMP_NEUTRAL, epsilon = 0.01);
assert_relative_eq!(s.top_p, TOP_P_NEUTRAL, epsilon = 0.01);
assert_eq!(s.frequency_penalty, 0.0);
assert_eq!(s.presence_penalty, 0.0);
}
#[test]
fn high_arousal_negative_raises_frequency_penalty() {
let state = CognitiveState {
arousal: 0.8,
valence: AffectValence::Negative,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(
s.frequency_penalty > 0.0,
"High arousal + negative valence should raise frequency penalty"
);
}
#[test]
fn high_arousal_positive_no_frequency_penalty() {
let state = CognitiveState {
arousal: 0.8,
valence: AffectValence::Positive,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert_eq!(
s.frequency_penalty, 0.0,
"High arousal + positive valence should NOT raise frequency penalty"
);
}
#[test]
fn low_body_budget_conserves() {
let state = CognitiveState {
body_budget: 0.1,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
let neutral = compute_sampling_override(&CognitiveState::default());
assert!(
s.temperature < neutral.temperature,
"Low body budget should lower temperature"
);
assert!(
s.top_p < neutral.top_p,
"Low body budget should lower top_p"
);
}
#[test]
fn high_volatility_explores() {
let state = CognitiveState {
pe_volatility: 0.9,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
let neutral = compute_sampling_override(&CognitiveState::default());
assert!(
s.temperature > neutral.temperature,
"High PE volatility should raise temperature for exploration"
);
}
#[test]
fn high_resource_pressure_raises_presence_penalty() {
let state = CognitiveState {
resource_pressure: 0.9,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(
s.presence_penalty > 0.0,
"High resource pressure should raise presence penalty"
);
}
#[test]
fn temperature_clamped_to_safe_range() {
let state = CognitiveState {
gain_mode: GainMode::Phasic,
body_budget: 0.0,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(
s.temperature >= TEMP_MIN,
"Temperature must not go below minimum"
);
let state = CognitiveState {
gain_mode: GainMode::Tonic,
pe_volatility: 1.0,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(
s.temperature <= TEMP_MAX,
"Temperature must not exceed maximum"
);
}
#[test]
fn multiple_signals_compose() {
let state = CognitiveState {
arousal: 0.9,
valence: AffectValence::Negative,
resource_pressure: 0.9,
..CognitiveState::default()
};
let s = compute_sampling_override(&state);
assert!(s.frequency_penalty > 0.0);
assert!(s.presence_penalty > 0.0);
}
#[test]
fn build_cognitive_state_from_world_model() {
let model = WorldModel::new("test".into());
let state = build_cognitive_state(&model, GainMode::Phasic);
assert_eq!(state.arousal, model.belief.affect.arousal);
assert_eq!(state.valence, model.belief.affect.valence);
assert_eq!(state.body_budget, model.body_budget);
assert_eq!(state.sensory_pe, model.sensory_pe);
assert_eq!(state.gain_mode, GainMode::Phasic);
assert_eq!(state.gate_confidence, model.gate.confidence);
}
}