| 92 | } |
| 93 | |
| 94 | pub fn classification_score( |
| 95 | y_true: &[f64], |
| 96 | probabilities: &[f64], |
| 97 | sample_weight: Option<&[f64]>, |
| 98 | scoring: SearchScoring, |
| 99 | ) -> Result<f64, String> { |
| 100 | if y_true.is_empty() { |
| 101 | return Err("y_true cannot be empty".to_string()); |
| 102 | } |
| 103 | if probabilities.len() != y_true.len() { |
| 104 | return Err("probabilities/y_true length mismatch".to_string()); |
| 105 | } |
| 106 | if let Some(sw) = sample_weight { |
| 107 | if sw.len() != y_true.len() { |
| 108 | return Err("sample_weight length mismatch".to_string()); |
| 109 | } |
| 110 | if sw.iter().any(|w| *w < 0.0) { |
| 111 | return Err("sample_weight cannot contain negative values".to_string()); |
| 112 | } |
| 113 | } |
| 114 | if probabilities.iter().any(|p| !p.is_finite() || *p < 0.0 || *p > 1.0) { |
| 115 | return Err("probabilities must be finite and in [0,1]".to_string()); |
| 116 | } |
| 117 | if y_true.iter().any(|y| (*y - 0.0).abs() > 1e-12 && (*y - 1.0).abs() > 1e-12) { |
| 118 | return Err("y_true must contain only binary labels in {0,1}".to_string()); |
| 119 | } |
| 120 | |
| 121 | let mut sum_w = 0.0; |
| 122 | let mut weighted_correct = 0.0; |
| 123 | let mut weighted_loss = 0.0; |
| 124 | |
| 125 | let mut pos_total = 0.0; |
| 126 | let mut neg_total = 0.0; |
| 127 | let mut pos_correct = 0.0; |
| 128 | let mut neg_correct = 0.0; |
| 129 | |
| 130 | let eps = 1e-15; |
| 131 | for i in 0..y_true.len() { |
| 132 | let w = sample_weight.map(|sw| sw[i]).unwrap_or(1.0); |
| 133 | if w == 0.0 { |
| 134 | continue; |
| 135 | } |
| 136 | let y = y_true[i]; |
| 137 | let p = probabilities[i].max(eps).min(1.0 - eps); |
| 138 | let pred = if probabilities[i] >= 0.5 { 1.0 } else { 0.0 }; |
| 139 | |
| 140 | sum_w += w; |
| 141 | if (pred - y).abs() < 1e-12 { |
| 142 | weighted_correct += w; |
| 143 | } |
| 144 | |
| 145 | weighted_loss += -w * (y * p.ln() + (1.0 - y) * (1.0 - p).ln()); |
| 146 | |
| 147 | if y == 1.0 { |
| 148 | pos_total += w; |
| 149 | if pred == 1.0 { |
| 150 | pos_correct += w; |
| 151 | } |