(weights: &mut [f64], bounds: &[(f64, f64)])
| 246 | } |
| 247 | |
| 248 | fn project_to_bounds(weights: &mut [f64], bounds: &[(f64, f64)]) -> Result<(), AllocError> { |
| 249 | for (w, (lo, hi)) in weights.iter_mut().zip(bounds.iter()) { |
| 250 | *w = w.clamp(*lo, hi.min(1.0)); |
| 251 | } |
| 252 | let sum: f64 = weights.iter().sum(); |
| 253 | if (sum - 1.0).abs() < 1e-12 { |
| 254 | return Ok(()); |
| 255 | } |
| 256 | if sum < 1.0 { |
| 257 | let deficit = 1.0 - sum; |
| 258 | let capacities: Vec<f64> = |
| 259 | bounds.iter().zip(weights.iter()).map(|(b, w)| b.1.min(1.0) - *w).collect(); |
| 260 | let total_cap: f64 = capacities.iter().sum(); |
| 261 | if total_cap <= 1e-12 { |
| 262 | return Err(AllocError::InfeasibleBounds { lower_sum: 1.0, upper_sum: 0.0 }); |
| 263 | } |
| 264 | for i in 0..weights.len() { |
| 265 | weights[i] += deficit * capacities[i] / total_cap; |
| 266 | } |
| 267 | } else { |
| 268 | let excess = sum - 1.0; |
| 269 | let removable: Vec<f64> = |
| 270 | bounds.iter().zip(weights.iter()).map(|(b, w)| (w - b.0).max(0.0)).collect(); |
| 271 | let total_rm: f64 = removable.iter().sum(); |
| 272 | if total_rm <= 1e-12 { |
| 273 | return Err(AllocError::InfeasibleBounds { lower_sum: 1.0, upper_sum: 0.0 }); |
| 274 | } |
| 275 | for i in 0..weights.len() { |
| 276 | weights[i] -= excess * removable[i] / total_rm; |
| 277 | } |
| 278 | } |
| 279 | Ok(()) |
| 280 | } |
| 281 | |
| 282 | fn inverse_variance(cov: &DMatrix<f64>, bounds: &[(f64, f64)]) -> Result<Vec<f64>, AllocError> { |
| 283 | check_bounds_feasible(bounds)?; |
no test coverage detected