#![allow(clippy::needless_range_loop)]
use bcinr_cmca::generated::consequence_mass::case_studies::{
LensSpec, PackedSemanticState, K, N, Q,
};
pub fn compute_measures_f64(state: &PackedSemanticState) -> [f64; K] {
let factors: Vec<f64> = state
.factors
.iter()
.map(|f| f.to_bits() as f64 / 65536.0)
.collect();
let recomp = factors[0];
let verify = factors[1];
let standing = factors[2];
let access = factors[4];
let search = factors[5];
let retrieval = factors[6];
let sched = factors[7];
let bval = factors[8];
let conseq = factors[9];
let m0 = (recomp * 5.0 + verify) * access * standing;
let m1 = (bval + conseq) * search * standing;
let m2 = bval * retrieval;
let m3 = bval * sched;
[m0, m1, m2, m3]
}
#[allow(clippy::too_many_arguments)] pub fn allocate_f64(
states: &[PackedSemanticState; N],
lenses: &[LensSpec; Q],
lambda: &[[f64; Q]; K],
eta: f64,
parent: &[i32; N],
weights: &mut [[f64; 2 * Q]; N],
payoffs: &[[f64; 2 * Q]; N],
zeta: f64,
epsilon_kappa: f64,
mu: &[f64; N],
costs: &[f64; N],
t: u32,
last_switch_t: &mut u32,
prev_mode: &mut u32,
tau_d: u32,
) -> [f64; N] {
let mut is_leaf = [true; N];
for (i, leaf) in is_leaf.iter_mut().enumerate() {
for &p in parent.iter() {
if p == i as i32 {
*leaf = false;
}
}
}
let mut is_descendant = [[false; N]; N];
#[allow(clippy::needless_range_loop)] for i in 0..N {
is_descendant[i][i] = true;
}
for _ in 0..N {
for j in 0..N {
let p = parent[j];
if p != -1 {
#[allow(clippy::needless_range_loop)]
for k in 0..N {
if is_descendant[j][k] {
is_descendant[p as usize][k] = true;
}
}
}
}
}
let mut is_subtree_leaf = [[false; N]; N];
for i in 0..N {
for k in 0..N {
is_subtree_leaf[i][k] = is_leaf[k] && is_descendant[i][k];
}
}
let mut raw_masses = [[0.0; N]; K];
for i in 0..N {
let m = compute_measures_f64(&states[i]);
for k in 0..K {
raw_masses[k][i] = m[k];
}
}
let mut node_masses = raw_masses;
#[allow(clippy::needless_range_loop)] for k in 0..K {
for i in 0..N {
node_masses[k][i] = node_masses[k][i].clamp(0.0001, 1000.0);
}
}
let beta = if zeta > 0.1 { 0.1 } else { zeta };
let mut root_idx = 0usize;
for (i, &p) in parent.iter().enumerate() {
if p == -1 {
root_idx = i;
}
}
let mut max_w = -1.0;
let mut dom_mode = 0u32;
for (e, &w) in weights[root_idx].iter().enumerate() {
if w > max_w {
max_w = w;
dom_mode = e as u32;
}
}
let switch_wanted = dom_mode != *prev_mode;
let can_switch = t.wrapping_sub(*last_switch_t) >= tau_d;
let switch_rejected = switch_wanted && !can_switch;
let update_allowed = !switch_rejected;
for v in 0..N {
if !is_leaf[v] {
for q_idx in 0..Q {
let q_val = lenses[q_idx].q.to_bits() as f64 / 65536.0;
let mut sum_meas_den = 0.0;
let mut sum_leaf_den = 0.0;
for i in 0..N {
if parent[i] == v as i32 {
sum_meas_den += node_masses[0][i].powf(q_val);
}
if is_subtree_leaf[v][i] {
sum_leaf_den += node_masses[0][i].powf(q_val);
}
}
let mut kappa = 0.0;
for c in 0..N {
if parent[c] == v as i32 {
let s_meas = node_masses[0][c].powf(q_val) / sum_meas_den;
let mut l_q_c = 0.0;
for x in 0..N {
if is_subtree_leaf[c][x] {
l_q_c += node_masses[0][x].powf(q_val);
}
}
let s_leaf = l_q_c / sum_leaf_den;
if s_leaf > 0.0 {
kappa += s_leaf * (s_leaf / s_meas).log2();
}
}
}
let update_active = kappa > epsilon_kappa;
if update_active && update_allowed {
let w_flat = weights[v][2 * q_idx];
let p_flat = payoffs[v][2 * q_idx];
weights[v][2 * q_idx] = w_flat * (beta * p_flat).exp();
let w_desc = weights[v][2 * q_idx + 1];
let p_desc = payoffs[v][2 * q_idx + 1];
weights[v][2 * q_idx + 1] = w_desc * (beta * p_desc).exp();
}
}
for q_idx in 0..Q {
let w_flat = weights[v][2 * q_idx];
let w_desc = weights[v][2 * q_idx + 1];
let sum = w_flat + w_desc;
if sum > 0.0 {
weights[v][2 * q_idx] /= sum;
weights[v][2 * q_idx + 1] /= sum;
}
}
}
}
let mut new_dom_mode = 0u32;
let mut new_max_w = -1.0;
for (e, &w) in weights[root_idx].iter().enumerate() {
if w > new_max_w {
new_max_w = w;
new_dom_mode = e as u32;
}
}
if new_dom_mode != *prev_mode && can_switch {
*last_switch_t = t;
*prev_mode = new_dom_mode;
}
let mut pi_kq = [[[0.0; N]; Q]; K];
for k in 0..K {
for q_idx in 0..Q {
let q_val = lenses[q_idx].q.to_bits() as f64 / 65536.0;
let mut root_sum = 0.0;
for i in 0..N {
if parent[i] == -1 {
root_sum += node_masses[k][i].powf(q_val);
}
}
let mut alloc_flow = [0.0; N];
for i in 0..N {
if parent[i] == -1 {
alloc_flow[i] = node_masses[k][i].powf(q_val) / root_sum;
}
}
let mut flat_alloc = [0.0; N];
for _ in 0..N {
for v in 0..N {
if !is_leaf[v] {
let w_flat = weights[v][2 * q_idx];
let w_desc = weights[v][2 * q_idx + 1];
let sum = w_flat + w_desc;
let rho = if sum > 0.0 { w_desc / sum } else { 0.5 };
let flat_coeff = 1.0 - rho;
let desc_coeff = rho;
let flat_part = flat_coeff * alloc_flow[v];
let desc_part = desc_coeff * alloc_flow[v];
let mut child_sum = 0.0;
let mut leaf_sum = 0.0;
for c in 0..N {
if parent[c] == v as i32 {
child_sum += node_masses[k][c].powf(q_val);
}
}
for x in 0..N {
if is_subtree_leaf[v][x] {
leaf_sum += node_masses[k][x].powf(q_val);
}
}
for x in 0..N {
if is_subtree_leaf[v][x] {
flat_alloc[x] +=
flat_part * (node_masses[k][x].powf(q_val) / leaf_sum);
}
}
for c in 0..N {
if parent[c] == v as i32 {
alloc_flow[c] +=
desc_part * (node_masses[k][c].powf(q_val) / child_sum);
}
}
alloc_flow[v] = 0.0;
}
}
}
for x in 0..N {
pi_kq[k][q_idx][x] = flat_alloc[x] + alloc_flow[x];
}
}
}
let mut pi_combined = [0.0; N];
for k in 0..K {
for q_idx in 0..Q {
let lam = lambda[k][q_idx];
for x in 0..N {
pi_combined[x] += lam * pi_kq[k][q_idx][x];
}
}
}
let mut priced_val = [0.0; N];
for x in 0..N {
let mu_clamped = mu[x].clamp(0.0, 100.0);
let dot_prod = mu_clamped * costs[x];
priced_val[x] = pi_combined[x] * (-dot_prod).exp();
}
let mut priced_sum = 0.0;
for i in 0..N {
if is_leaf[i] {
priced_sum += priced_val[i];
}
}
let mut pi_mu = [0.0; N];
for x in 0..N {
if priced_sum > 0.0 {
pi_mu[x] = priced_val[x] / priced_sum;
}
}
let mut num_leaves = 0;
for &leaf in is_leaf.iter() {
if leaf {
num_leaves += 1;
}
}
let u_sharp = 1.0 / num_leaves as f64;
let mut pi_res = [0.0; N];
for i in 0..N {
if is_leaf[i] {
pi_res[i] = eta * u_sharp + (1.0 - eta) * pi_mu[i];
}
}
pi_res
}