use cobre_solver::RowBatch;
use crate::cut::FutureCostFunction;
use crate::cut::row::push_scaled_coefficient;
use crate::indexer::StageIndexer;
pub fn build_delta_cut_row_batch_into(
batch: &mut RowBatch,
fcf: &FutureCostFunction,
stage: usize,
indexer: &StageIndexer,
col_scale: &[f64],
current_iteration: u64,
) {
batch.clear();
let n_state = indexer.n_state;
let theta_col = indexer.theta;
let mask = &indexer.nonzero_state_indices;
let is_sparse = !mask.is_empty();
let num_cuts: usize = fcf.pools[stage]
.active_delta_cuts(current_iteration)
.count();
if num_cuts == 0 {
batch.row_starts.push(0_i32);
return;
}
let nnz_per_cut = if is_sparse {
mask.len() + 1
} else {
n_state + 1
};
let total_nnz = num_cuts * nnz_per_cut;
let mut nz_offset = 0;
for (_slot, intercept, coefficients) in fcf.pools[stage].active_delta_cuts(current_iteration) {
debug_assert_eq!(
coefficients.len(),
n_state,
"cut coefficients length {got} != n_state {expected}",
got = coefficients.len(),
expected = n_state,
);
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
batch.row_starts.push(nz_offset as i32);
if is_sparse {
for &j in mask {
let lp_col = indexer.state_to_lp_column(j);
push_scaled_coefficient(batch, lp_col, coefficients[j], col_scale);
}
} else {
for (j, &c) in coefficients.iter().enumerate() {
let lp_col = indexer.state_to_lp_column(j);
debug_assert!(
!(lp_col == j
&& indexer.n_anticipated > 0
&& j >= indexer.anticipated_state.start
&& j < indexer.anticipated_state.start
+ indexer.n_anticipated * indexer.k_max)
|| c == 0.0,
"padding-slot j={j} has non-zero cut coefficient {c}; \
shift_anticipated_state must have seeded a non-zero into a padding slot"
);
push_scaled_coefficient(batch, lp_col, c, col_scale);
}
}
debug_assert!(
i32::try_from(theta_col).is_ok(),
"theta_col={theta_col} exceeds i32::MAX"
);
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
batch.col_indices.push(theta_col as i32);
let d_theta = if col_scale.is_empty() {
1.0
} else {
col_scale[theta_col]
};
batch.values.push(d_theta);
batch.row_lower.push(intercept);
batch.row_upper.push(f64::INFINITY);
nz_offset += nnz_per_cut;
}
#[allow(clippy::expect_used)]
batch.row_starts.push(
i32::try_from(total_nnz).expect("total_nnz exceeds i32::MAX; LP exceeds HiGHS API limit"),
);
batch.num_rows = num_cuts;
}