(weights: &mut [f64], bounds: &[(f64, f64)])
| 463 | } |
| 464 | |
| 465 | fn project_to_bounds(weights: &mut [f64], bounds: &[(f64, f64)]) -> Result<(), ClaError> { |
| 466 | for (w, (lo, hi)) in weights.iter_mut().zip(bounds.iter()) { |
| 467 | *w = w.clamp(*lo, hi.min(1.0)); |
| 468 | } |
| 469 | let sum: f64 = weights.iter().sum(); |
| 470 | if (sum - 1.0).abs() < 1e-12 { |
| 471 | return Ok(()); |
| 472 | } |
| 473 | if sum < 1.0 { |
| 474 | let deficit = 1.0 - sum; |
| 475 | let capacities: Vec<f64> = |
| 476 | bounds.iter().zip(weights.iter()).map(|(b, w)| b.1.min(1.0) - *w).collect(); |
| 477 | let total_cap: f64 = capacities.iter().sum(); |
| 478 | if total_cap <= 1e-12 { |
| 479 | return Err(ClaError::DimensionMismatch); |
| 480 | } |
| 481 | for i in 0..weights.len() { |
| 482 | weights[i] += deficit * capacities[i] / total_cap; |
| 483 | } |
| 484 | } else { |
| 485 | let excess = sum - 1.0; |
| 486 | let removable: Vec<f64> = |
| 487 | bounds.iter().zip(weights.iter()).map(|(b, w)| (w - b.0).max(0.0)).collect(); |
| 488 | let total_rm: f64 = removable.iter().sum(); |
| 489 | if total_rm <= 1e-12 { |
| 490 | return Err(ClaError::DimensionMismatch); |
| 491 | } |
| 492 | for i in 0..weights.len() { |
| 493 | weights[i] -= excess * removable[i] / total_rm; |
| 494 | } |
| 495 | } |
| 496 | Ok(()) |
| 497 | } |
| 498 | |
| 499 | fn solve_min_vol(cov: &DMatrix<f64>, bounds: &[(f64, f64)]) -> Result<Vec<f64>, ClaError> { |
| 500 | check_bounds_feasible(bounds)?; |
no test coverage detected