use rust_decimal::Decimal;
use crate::functions::expression::{
InputValue, TaskOutput, TaskOutputOwned,
};
use crate::functions::Task;
const TRIALS: usize = 1000;
fn systematic_scalar(k: usize) -> Decimal {
Decimal::from_f64_retain(k as f64 / 999.0).unwrap_or(Decimal::ZERO)
}
fn systematic_vector(k: usize, len: usize) -> Vec<Decimal> {
if len == 0 {
return vec![];
}
if len == 1 {
return vec![Decimal::ONE];
}
if k == 0 {
return vec![Decimal::ZERO; len];
}
let k = k - 1; let focus = k % len;
let sub_k = k / len;
let sub_trials = (TRIALS - 1) / len;
let proportion = if sub_trials <= 1 {
0.5
} else {
(sub_k as f64 / (sub_trials - 1) as f64).clamp(0.0, 1.0)
};
let focus_weight = proportion;
let other_weight = (1.0 - focus_weight) / (len - 1) as f64;
(0..len)
.map(|p| {
let v = if p == focus {
focus_weight
} else {
other_weight
};
Decimal::from_f64_retain(v).unwrap_or(Decimal::ZERO)
})
.collect()
}
fn systematic_map_scalar(k: usize, len: usize) -> Vec<Decimal> {
if len == 0 {
return vec![];
}
if len == 1 {
let v = k as f64 / 999.0;
return vec![
Decimal::from_f64_retain(v).unwrap_or(Decimal::ZERO),
];
}
if k == 0 {
return vec![Decimal::ZERO; len];
}
let k = k - 1;
let focus = k % len;
let sub_k = k / len;
let sub_trials = (TRIALS - 1) / len;
let proportion = if sub_trials <= 1 {
0.5
} else {
(sub_k as f64 / (sub_trials - 1) as f64).clamp(0.0, 1.0)
};
let background = 0.001 / len as f64;
let focus_value = proportion;
(0..len)
.map(|i| {
let v = if i == focus { focus_value } else { background };
Decimal::from_f64_retain(v).unwrap_or(Decimal::ZERO)
})
.collect()
}
fn systematic_vc_scores(k: usize, n: usize) -> Vec<Decimal> {
systematic_vector(k, n)
}
pub(crate) enum ScalarOutputShape {
Scalar,
VectorCompletion(usize),
}
pub(crate) fn check_scalar_distribution(
task_index: usize,
input: &InputValue,
task: &Task,
shape: &ScalarOutputShape,
) -> Result<(), String> {
let mut min = Decimal::ONE;
let mut max = Decimal::ZERO;
let mut sum = Decimal::ZERO;
for k in 0..TRIALS {
let mock_output = match shape {
ScalarOutputShape::Scalar => TaskOutput::Owned(
TaskOutputOwned::Scalar(systematic_scalar(k)),
),
ScalarOutputShape::VectorCompletion(n) => TaskOutput::Owned(
TaskOutputOwned::Vector(systematic_vc_scores(k, *n)),
),
};
let result = task.compile_output(input, mock_output).map_err(|e| {
format!(
"OD01: Task [{}]: output expression evaluation failed during \
distribution check (trial {}): {}",
task_index, k, e
)
})?;
let value = match result {
TaskOutputOwned::Scalar(s) => s,
_ => {
return Ok(());
}
};
if value < min {
min = value;
}
if value > max {
max = value;
}
sum += value;
}
let avg =
sum / Decimal::from_f64_retain(TRIALS as f64).unwrap_or(Decimal::ONE);
if min > Decimal::new(1, 2) {
return Err(format!(
"OD02: Task [{}]: output expression is biased — when given raw outputs \
ranging from 0.0 to 1.0, the minimum compiled output was {} (expected \
near 0.0). The output expression should map the full range of raw task \
outputs to the full [0, 1] range.",
task_index, min
));
}
if max < Decimal::new(99, 2) {
return Err(format!(
"OD03: Task [{}]: output expression is biased — when given raw outputs \
ranging from 0.0 to 1.0, the maximum compiled output was {} (expected \
near 1.0). The output expression should map the full range of raw task \
outputs to the full [0, 1] range.",
task_index, max
));
}
if avg < Decimal::new(4, 1) || avg > Decimal::new(6, 1) {
return Err(format!(
"OD04: Task [{}]: output expression is non-uniform — the average compiled \
output was {} (expected near 0.5). The output expression should produce \
roughly uniform results across the [0, 1] range when given uniformly \
distributed raw outputs.",
task_index, avg
));
}
Ok(())
}
pub(crate) enum VectorOutputShape {
MapScalar(usize),
Vector(u64),
VectorCompletion(usize),
}
pub(crate) fn check_vector_distribution(
task_index: usize,
input: &InputValue,
task: &Task,
shape: &VectorOutputShape,
output_length: usize,
) -> Result<(), String> {
let mut per_element_min = vec![Decimal::ONE; output_length];
let mut per_element_max = vec![Decimal::ZERO; output_length];
let mut per_element_sum = vec![Decimal::ZERO; output_length];
for k in 0..TRIALS {
let mock_output = match shape {
VectorOutputShape::MapScalar(len) => TaskOutput::Owned(
TaskOutputOwned::Vector(systematic_map_scalar(k, *len)),
),
VectorOutputShape::Vector(n) => TaskOutput::Owned(
TaskOutputOwned::Vector(
systematic_vector(k, *n as usize),
),
),
VectorOutputShape::VectorCompletion(n) => TaskOutput::Owned(
TaskOutputOwned::Vector(systematic_vc_scores(k, *n)),
),
};
let result = task.compile_output(input, mock_output).map_err(|e| {
if k == 0 && matches!(shape, VectorOutputShape::MapScalar(_)) {
format!(
"OD09: Task [{}]: output expression fails when all mapped \
scalar outputs are zero (division by zero). The expression \
must handle the case where sum(output) == 0, e.g. use \
`x / sum(output) if sum(output) > 0 else 1.0 / len(output)`.",
task_index
)
} else {
format!(
"OD05: Task [{}]: output expression evaluation failed during \
distribution check (trial {}): {}",
task_index, k, e
)
}
})?;
let values = match result {
TaskOutputOwned::Vector(v) => v,
_ => {
return Ok(());
}
};
if values.len() != output_length {
return Ok(());
}
for (p, &v) in values.iter().enumerate() {
if v < per_element_min[p] {
per_element_min[p] = v;
}
if v > per_element_max[p] {
per_element_max[p] = v;
}
per_element_sum[p] += v;
}
}
let trials_dec =
Decimal::from_f64_retain(TRIALS as f64).unwrap_or(Decimal::ONE);
let expected_avg = Decimal::ONE
/ Decimal::from_f64_retain(output_length as f64)
.unwrap_or(Decimal::ONE);
let avg_low = expected_avg * Decimal::new(8, 1); let avg_high = expected_avg * Decimal::new(12, 1);
for p in 0..output_length {
let avg = per_element_sum[p] / trials_dec;
if per_element_min[p] > Decimal::new(1, 2) {
return Err(format!(
"OD06: Task [{}], element [{}]: output expression is biased — when \
given systematic raw outputs, the minimum compiled value for this \
element was {} (expected near 0.0). Each element of the output vector \
should be able to reach values near 0.",
task_index, p, per_element_min[p]
));
}
if per_element_max[p] < Decimal::new(99, 2) {
return Err(format!(
"OD07: Task [{}], element [{}]: output expression is biased — when \
given systematic raw outputs, the maximum compiled value for this \
element was {} (expected near 1.0). Each element of the output vector \
should be able to reach values near 1.",
task_index, p, per_element_max[p]
));
}
if avg < avg_low || avg > avg_high {
return Err(format!(
"OD08: Task [{}], element [{}]: output expression is non-uniform — \
the average compiled value for this element was {} (expected near \
{:.4}). Each element should average around 1/{} when given uniformly \
distributed raw outputs.",
task_index, p, avg, expected_avg, output_length
));
}
}
Ok(())
}