IsotonicRegression

Struct IsotonicRegression 

Source
pub struct IsotonicRegression { /* private fields */ }
Expand description

Isotonic Regression - non-parametric calibration using monotonic transformation More flexible than Platt scaling but requires more data

Implementations§

Source§

impl IsotonicRegression

Source

pub fn new() -> Self

Create a new isotonic regression calibrator

Examples found in repository?
examples/calibration_demo.rs (line 118)
103fn demo_isotonic_regression() -> Result<(), Box<dyn std::error::Error>> {
104    // Generate non-linearly separable scores
105    let scores = array![
106        0.1, 0.25, 0.2, // Low scores
107        0.4, 0.35, 0.55, // Mid-low scores
108        0.6, 0.75, 0.7, // Mid-high scores
109        0.85, 0.95, 0.9 // High scores
110    ];
111    // Non-linear relationship with labels
112    let labels = array![0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1];
113
114    println!("   Input scores: {scores:?}");
115    println!("   True labels:  {labels:?}\n");
116
117    // Fit isotonic regression
118    let mut iso = IsotonicRegression::new();
119    iso.fit(&scores, &labels)?;
120
121    println!("   Fitted isotonic regression (maintains monotonicity)");
122
123    // Transform scores
124    let calibrated_probs = iso.transform(&scores)?;
125    println!("\n   Calibrated probabilities:");
126    for (i, (&score, &prob)) in scores.iter().zip(calibrated_probs.iter()).enumerate() {
127        println!("   Sample {i}: score={score:.2} → P(class=1)={prob:.4}");
128    }
129
130    // Verify monotonicity
131    let mut is_monotonic = true;
132    for i in 0..calibrated_probs.len() - 1 {
133        if calibrated_probs[i] > calibrated_probs[i + 1] + 1e-6 {
134            is_monotonic = false;
135            break;
136        }
137    }
138    println!(
139        "\n   Monotonicity preserved: {}",
140        if is_monotonic { "✓" } else { "✗" }
141    );
142
143    Ok(())
144}
More examples
Hide additional examples
examples/calibration_drug_discovery.rs (line 331)
221fn main() {
222    println!("\n╔══════════════════════════════════════════════════════════╗");
223    println!("║  Quantum ML Calibration for Drug Discovery              ║");
224    println!("║  Molecular Binding Affinity Prediction                  ║");
225    println!("╚══════════════════════════════════════════════════════════╝\n");
226
227    // ========================================================================
228    // 1. Generate Drug Discovery Dataset
229    // ========================================================================
230
231    println!("📊 Generating drug discovery dataset...\n");
232
233    let n_train = 1000;
234    let n_cal = 300; // Calibration set
235    let n_test = 500; // Test set
236    let n_features = 20;
237
238    let mut all_molecules = generate_drug_dataset(n_train + n_cal + n_test, n_features);
239
240    // Split into train, calibration, and test sets
241    let test_molecules: Vec<_> = all_molecules.split_off(n_train + n_cal);
242    let cal_molecules: Vec<_> = all_molecules.split_off(n_train);
243    let train_molecules = all_molecules;
244
245    println!("Dataset statistics:");
246    println!("  Training set: {} molecules", train_molecules.len());
247    println!("  Calibration set: {} molecules", cal_molecules.len());
248    println!("  Test set: {} molecules", test_molecules.len());
249    println!("  Features per molecule: {n_features}");
250
251    let train_positive = train_molecules.iter().filter(|m| m.true_binding).count();
252    println!(
253        "  Training set binding ratio: {:.1}%",
254        train_positive as f64 / train_molecules.len() as f64 * 100.0
255    );
256
257    // ========================================================================
258    // 2. Train Quantum Neural Network (Simplified)
259    // ========================================================================
260
261    println!("\n🔬 Training quantum molecular predictor...\n");
262
263    let qnn = QuantumMolecularPredictor::new(n_features, 0.3);
264
265    // Get predictions on calibration set
266    let cal_probs = qnn.predict_batch(&cal_molecules);
267    let cal_labels = Array1::from_shape_fn(cal_molecules.len(), |i| {
268        usize::from(cal_molecules[i].true_binding)
269    });
270
271    // Get predictions on test set
272    let test_probs = qnn.predict_batch(&test_molecules);
273    let test_labels = Array1::from_shape_fn(test_molecules.len(), |i| {
274        usize::from(test_molecules[i].true_binding)
275    });
276
277    println!("Model trained! Evaluating uncalibrated performance...");
278
279    let test_preds = test_probs.mapv(|p| usize::from(p >= 0.5));
280    let acc = accuracy(&test_preds, &test_labels);
281    let prec = precision(&test_preds, &test_labels, 2).expect("Precision failed");
282    let rec = recall(&test_preds, &test_labels, 2).expect("Recall failed");
283    let f1 = f1_score(&test_preds, &test_labels, 2).expect("F1 failed");
284
285    println!("  Accuracy: {:.2}%", acc * 100.0);
286    println!("  Precision (class 1): {:.2}%", prec[1] * 100.0);
287    println!("  Recall (class 1): {:.2}%", rec[1] * 100.0);
288    println!("  F1 Score (class 1): {:.3}", f1[1]);
289
290    // ========================================================================
291    // 3. Analyze Uncalibrated Model
292    // ========================================================================
293
294    println!("\n📉 Analyzing uncalibrated model calibration...\n");
295
296    let uncalib_ece =
297        expected_calibration_error(&test_probs, &test_labels, 10).expect("ECE failed");
298
299    println!("Uncalibrated metrics:");
300    println!("  Expected Calibration Error (ECE): {uncalib_ece:.4}");
301
302    if uncalib_ece > 0.1 {
303        println!("  ⚠️  High ECE indicates poor calibration!");
304    }
305
306    // ========================================================================
307    // 4. Apply Calibration Methods
308    // ========================================================================
309
310    println!("\n🔧 Applying calibration methods...\n");
311
312    // Method 1: Platt Scaling
313    println!("1️⃣  Platt Scaling (parametric, fast)");
314    let mut platt = PlattScaler::new();
315    platt
316        .fit(&cal_probs, &cal_labels)
317        .expect("Platt fitting failed");
318    let platt_test_probs = platt
319        .transform(&test_probs)
320        .expect("Platt transform failed");
321    let platt_ece =
322        expected_calibration_error(&platt_test_probs, &test_labels, 10).expect("ECE failed");
323    println!(
324        "   ECE after Platt: {:.4} ({:.1}% improvement)",
325        platt_ece,
326        (uncalib_ece - platt_ece) / uncalib_ece * 100.0
327    );
328
329    // Method 2: Isotonic Regression
330    println!("\n2️⃣  Isotonic Regression (non-parametric, flexible)");
331    let mut isotonic = IsotonicRegression::new();
332    isotonic
333        .fit(&cal_probs, &cal_labels)
334        .expect("Isotonic fitting failed");
335    let isotonic_test_probs = isotonic
336        .transform(&test_probs)
337        .expect("Isotonic transform failed");
338    let isotonic_ece =
339        expected_calibration_error(&isotonic_test_probs, &test_labels, 10).expect("ECE failed");
340    println!(
341        "   ECE after Isotonic: {:.4} ({:.1}% improvement)",
342        isotonic_ece,
343        (uncalib_ece - isotonic_ece) / uncalib_ece * 100.0
344    );
345
346    // Method 3: Bayesian Binning into Quantiles (BBQ)
347    println!("\n3️⃣  Bayesian Binning into Quantiles (BBQ-10)");
348    let mut bbq = BayesianBinningQuantiles::new(10);
349    bbq.fit(&cal_probs, &cal_labels)
350        .expect("BBQ fitting failed");
351    let bbq_test_probs = bbq.transform(&test_probs).expect("BBQ transform failed");
352    let bbq_ece =
353        expected_calibration_error(&bbq_test_probs, &test_labels, 10).expect("ECE failed");
354    println!(
355        "   ECE after BBQ: {:.4} ({:.1}% improvement)",
356        bbq_ece,
357        (uncalib_ece - bbq_ece) / uncalib_ece * 100.0
358    );
359
360    // ========================================================================
361    // 5. Compare All Methods
362    // ========================================================================
363
364    println!("\n📊 Comprehensive method comparison...\n");
365
366    println!("Method Comparison (ECE on test set):");
367    println!("  Uncalibrated:      {uncalib_ece:.4}");
368    println!("  Platt Scaling:     {platt_ece:.4}");
369    println!("  Isotonic Regr.:    {isotonic_ece:.4}");
370    println!("  BBQ-10:            {bbq_ece:.4}");
371
372    // ========================================================================
373    // 6. Decision Impact Analysis
374    // ========================================================================
375
376    // Choose best method based on ECE
377    let (best_method, best_probs, best_ece) = if bbq_ece < isotonic_ece && bbq_ece < platt_ece {
378        ("BBQ-10", bbq_test_probs, bbq_ece)
379    } else if isotonic_ece < platt_ece {
380        ("Isotonic Regression", isotonic_test_probs, isotonic_ece)
381    } else {
382        ("Platt Scaling", platt_test_probs, platt_ece)
383    };
384
385    println!("\n🏆 Best calibration method: {best_method}");
386
387    // Demonstrate impact on different decision thresholds
388    for threshold in &[0.3, 0.5, 0.7, 0.9] {
389        demonstrate_decision_impact(&test_molecules, &test_probs, &best_probs, *threshold);
390    }
391
392    // ========================================================================
393    // 7. Regulatory Compliance Analysis
394    // ========================================================================
395
396    println!("\n\n╔═══════════════════════════════════════════════════════╗");
397    println!("║  Regulatory Compliance Analysis (FDA Guidelines)     ║");
398    println!("╚═══════════════════════════════════════════════════════╝\n");
399
400    println!("FDA requires ML/AI models to provide:\n");
401    println!("✓ Well-calibrated probability estimates");
402    println!("✓ Uncertainty quantification");
403    println!("✓ Transparency in decision thresholds");
404    println!("✓ Performance on diverse molecular scaffolds\n");
405
406    println!("Calibration status:");
407    if best_ece < 0.05 {
408        println!("  ✅ Excellent calibration (ECE < 0.05)");
409    } else if best_ece < 0.10 {
410        println!("  ✅ Good calibration (ECE < 0.10)");
411    } else if best_ece < 0.15 {
412        println!("  ⚠️  Acceptable calibration (ECE < 0.15) - consider improvement");
413    } else {
414        println!("  ❌ Poor calibration (ECE >= 0.15) - recalibration required");
415    }
416
417    println!("\nUncertainty quantification:");
418    println!("  📊 Calibration curve available: Yes");
419    println!("  📊 Confidence intervals: Yes (via BBQ method)");
420
421    // ========================================================================
422    // 8. Recommendations
423    // ========================================================================
424
425    println!("\n\n╔═══════════════════════════════════════════════════════╗");
426    println!("║  Recommendations for Production Deployment           ║");
427    println!("╚═══════════════════════════════════════════════════════╝\n");
428
429    println!("Based on the analysis:\n");
430    println!("1. 🎯 Use {best_method} for best calibration");
431    println!("2. 📊 Monitor ECE and NLL in production");
432    println!("3. 🔄 Recalibrate when data distribution shifts");
433    println!("4. 💰 Optimize decision threshold based on cost/benefit analysis");
434    println!("5. 🔬 Consider ensemble methods for critical decisions");
435    println!("6. 📈 Track calibration degradation over time");
436    println!("7. ⚗️  Validate on diverse molecular scaffolds");
437    println!("8. 🚨 Set up alerts for calibration drift (ECE > 0.15)");
438
439    println!("\n✨ Drug discovery calibration demonstration complete! ✨\n");
440}
examples/calibration_finance.rs (line 458)
343fn main() {
344    println!("\n╔══════════════════════════════════════════════════════════╗");
345    println!("║  Quantum ML Calibration for Financial Risk Prediction   ║");
346    println!("║  Credit Default & Portfolio Risk Assessment             ║");
347    println!("╚══════════════════════════════════════════════════════════╝\n");
348
349    // ========================================================================
350    // 1. Generate Credit Application Dataset
351    // ========================================================================
352
353    println!("📊 Generating credit application dataset...\n");
354
355    let n_train = 5000;
356    let n_cal = 1000;
357    let n_test = 2000;
358    let n_features = 15;
359
360    let mut all_applications = generate_credit_dataset(n_train + n_cal + n_test, n_features);
361
362    // Split into train, calibration, and test sets
363    let test_apps: Vec<_> = all_applications.split_off(n_train + n_cal);
364    let cal_apps: Vec<_> = all_applications.split_off(n_train);
365    let train_apps = all_applications;
366
367    println!("Dataset statistics:");
368    println!("  Training set: {} applications", train_apps.len());
369    println!("  Calibration set: {} applications", cal_apps.len());
370    println!("  Test set: {} applications", test_apps.len());
371    println!("  Features per application: {n_features}");
372
373    let train_default_rate =
374        train_apps.iter().filter(|a| a.true_default).count() as f64 / train_apps.len() as f64;
375    println!(
376        "  Historical default rate: {:.2}%",
377        train_default_rate * 100.0
378    );
379
380    let total_loan_volume: f64 = test_apps.iter().map(|a| a.loan_amount).sum();
381    println!(
382        "  Test portfolio size: ${:.2}M",
383        total_loan_volume / 1_000_000.0
384    );
385
386    // ========================================================================
387    // 2. Train Quantum Credit Risk Model
388    // ========================================================================
389
390    println!("\n🔬 Training quantum credit risk model...\n");
391
392    let qcrm = QuantumCreditRiskModel::new(n_features, 0.2);
393
394    // Get predictions
395    let cal_probs = qcrm.predict_batch(&cal_apps);
396    let cal_labels =
397        Array1::from_shape_fn(cal_apps.len(), |i| usize::from(cal_apps[i].true_default));
398
399    let test_probs = qcrm.predict_batch(&test_apps);
400    let test_labels =
401        Array1::from_shape_fn(test_apps.len(), |i| usize::from(test_apps[i].true_default));
402
403    println!("Model trained! Evaluating uncalibrated performance...");
404
405    let test_preds = test_probs.mapv(|p| usize::from(p >= 0.5));
406    let acc = accuracy(&test_preds, &test_labels);
407    let prec = precision(&test_preds, &test_labels, 2).expect("Precision failed");
408    let rec = recall(&test_preds, &test_labels, 2).expect("Recall failed");
409    let f1 = f1_score(&test_preds, &test_labels, 2).expect("F1 failed");
410    let auc = auc_roc(&test_probs, &test_labels).expect("AUC failed");
411
412    println!("  Accuracy: {:.2}%", acc * 100.0);
413    println!("  Precision (class 1): {:.2}%", prec[1] * 100.0);
414    println!("  Recall (class 1): {:.2}%", rec[1] * 100.0);
415    println!("  F1 Score (class 1): {:.3}", f1[1]);
416    println!("  AUC-ROC: {auc:.3}");
417
418    // ========================================================================
419    // 3. Analyze Uncalibrated Model
420    // ========================================================================
421
422    println!("\n📉 Analyzing uncalibrated model calibration...\n");
423
424    let uncalib_ece =
425        expected_calibration_error(&test_probs, &test_labels, 10).expect("ECE failed");
426    let uncalib_mce = maximum_calibration_error(&test_probs, &test_labels, 10).expect("MCE failed");
427    let uncalib_logloss = log_loss(&test_probs, &test_labels);
428
429    println!("Uncalibrated calibration metrics:");
430    println!("  Expected Calibration Error (ECE): {uncalib_ece:.4}");
431    println!("  Maximum Calibration Error (MCE): {uncalib_mce:.4}");
432    println!("  Log Loss: {uncalib_logloss:.4}");
433
434    if uncalib_ece > 0.10 {
435        println!("  ⚠️  High ECE - probabilities are poorly calibrated!");
436        println!("     This violates regulatory requirements for risk models.");
437    }
438
439    // ========================================================================
440    // 4. Apply Multiple Calibration Methods
441    // ========================================================================
442
443    println!("\n🔧 Applying advanced calibration methods...\n");
444
445    // Apply calibration methods
446    println!("🔧 Applying calibration methods...\n");
447
448    // Try different calibration methods
449    let mut platt = PlattScaler::new();
450    platt
451        .fit(&cal_probs, &cal_labels)
452        .expect("Platt fit failed");
453    let platt_probs = platt
454        .transform(&test_probs)
455        .expect("Platt transform failed");
456    let platt_ece = expected_calibration_error(&platt_probs, &test_labels, 10).expect("ECE failed");
457
458    let mut isotonic = IsotonicRegression::new();
459    isotonic
460        .fit(&cal_probs, &cal_labels)
461        .expect("Isotonic fit failed");
462    let isotonic_probs = isotonic
463        .transform(&test_probs)
464        .expect("Isotonic transform failed");
465    let isotonic_ece =
466        expected_calibration_error(&isotonic_probs, &test_labels, 10).expect("ECE failed");
467
468    let mut bbq = BayesianBinningQuantiles::new(10);
469    bbq.fit(&cal_probs, &cal_labels).expect("BBQ fit failed");
470    let bbq_probs = bbq.transform(&test_probs).expect("BBQ transform failed");
471    let bbq_ece = expected_calibration_error(&bbq_probs, &test_labels, 10).expect("ECE failed");
472
473    println!("Calibration Results:");
474    println!("  Platt Scaling: ECE = {platt_ece:.4}");
475    println!("  Isotonic Regression: ECE = {isotonic_ece:.4}");
476    println!("  BBQ-10: ECE = {bbq_ece:.4}");
477
478    // Choose best method
479    let (best_method_name, best_test_probs) = if bbq_ece < isotonic_ece && bbq_ece < platt_ece {
480        ("BBQ-10", bbq_probs)
481    } else if isotonic_ece < platt_ece {
482        ("Isotonic", isotonic_probs)
483    } else {
484        ("Platt", platt_probs)
485    };
486
487    println!("\n🏆 Best method: {best_method_name}\n");
488
489    let best_ece =
490        expected_calibration_error(&best_test_probs, &test_labels, 10).expect("ECE failed");
491
492    println!("Calibrated model performance:");
493    println!(
494        "  ECE: {:.4} ({:.1}% improvement)",
495        best_ece,
496        (uncalib_ece - best_ece) / uncalib_ece * 100.0
497    );
498
499    // ========================================================================
500    // 5. Economic Impact Analysis
501    // ========================================================================
502
503    println!("\n\n╔═══════════════════════════════════════════════════════╗");
504    println!("║  Economic Impact of Calibration                      ║");
505    println!("╚═══════════════════════════════════════════════════════╝\n");
506
507    let default_loss_rate = 0.60; // Lose 60% of principal on default
508    let profit_margin = 0.08; // 8% profit on successful loans
509
510    for threshold in &[0.3, 0.5, 0.7] {
511        println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
512        println!(
513            "Decision Threshold: {:.0}% default probability",
514            threshold * 100.0
515        );
516        println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
517
518        let (uncalib_value, uncalib_approved, uncalib_tp, uncalib_fp, uncalib_fn) =
519            calculate_lending_value(
520                &test_apps,
521                &test_probs,
522                *threshold,
523                default_loss_rate,
524                profit_margin,
525            );
526
527        let (calib_value, calib_approved, calib_tp, calib_fp, calib_fn) = calculate_lending_value(
528            &test_apps,
529            &best_test_probs,
530            *threshold,
531            default_loss_rate,
532            profit_margin,
533        );
534
535        println!("Uncalibrated Model:");
536        println!("  Loans approved: {}/{}", uncalib_approved, test_apps.len());
537        println!("  Correctly rejected defaults: {uncalib_tp}");
538        println!("  Missed profit opportunities: {uncalib_fp}");
539        println!("  Approved defaults (losses): {uncalib_fn}");
540        println!(
541            "  Net portfolio value: ${:.2}M",
542            uncalib_value / 1_000_000.0
543        );
544
545        println!("\nCalibrated Model:");
546        println!("  Loans approved: {}/{}", calib_approved, test_apps.len());
547        println!("  Correctly rejected defaults: {calib_tp}");
548        println!("  Missed profit opportunities: {calib_fp}");
549        println!("  Approved defaults (losses): {calib_fn}");
550        println!("  Net portfolio value: ${:.2}M", calib_value / 1_000_000.0);
551
552        let value_improvement = calib_value - uncalib_value;
553        println!("\n💰 Economic Impact:");
554        if value_improvement > 0.0 {
555            println!(
556                "  Additional profit: ${:.2}M ({:.1}% improvement)",
557                value_improvement / 1_000_000.0,
558                value_improvement / uncalib_value.abs() * 100.0
559            );
560        } else {
561            println!("  Value change: ${:.2}M", value_improvement / 1_000_000.0);
562        }
563
564        let default_reduction = uncalib_fn as i32 - calib_fn as i32;
565        if default_reduction > 0 {
566            println!(
567                "  Defaults avoided: {} ({:.1}% reduction)",
568                default_reduction,
569                default_reduction as f64 / uncalib_fn as f64 * 100.0
570            );
571        }
572    }
573
574    // ========================================================================
575    // 6. Basel III Capital Requirements
576    // ========================================================================
577
578    demonstrate_capital_impact(&test_apps, &test_probs, &best_test_probs);
579
580    // ========================================================================
581    // 7. Stress Testing
582    // ========================================================================
583
584    println!("\n\n╔═══════════════════════════════════════════════════════╗");
585    println!("║  Regulatory Stress Testing (CCAR/DFAST)              ║");
586    println!("╚═══════════════════════════════════════════════════════╝\n");
587
588    println!("Stress scenarios:");
589    println!("  📉 Severe economic downturn (unemployment +5%)");
590    println!("  📊 Market volatility increase (+200%)");
591    println!("  🏦 Credit spread widening (+300 bps)\n");
592
593    // Simulate stress by increasing default probabilities
594    let stress_factor = 2.5;
595    let stressed_probs = test_probs.mapv(|p| (p * stress_factor).min(0.95));
596    let stressed_calib_probs = best_test_probs.mapv(|p| (p * stress_factor).min(0.95));
597
598    let (stress_uncalib_value, _, _, _, _) = calculate_lending_value(
599        &test_apps,
600        &stressed_probs,
601        0.5,
602        default_loss_rate,
603        profit_margin,
604    );
605
606    let (stress_calib_value, _, _, _, _) = calculate_lending_value(
607        &test_apps,
608        &stressed_calib_probs,
609        0.5,
610        default_loss_rate,
611        profit_margin,
612    );
613
614    println!("Portfolio value under stress:");
615    println!(
616        "  Uncalibrated Model: ${:.2}M",
617        stress_uncalib_value / 1_000_000.0
618    );
619    println!(
620        "  Calibrated Model: ${:.2}M",
621        stress_calib_value / 1_000_000.0
622    );
623
624    let stress_resilience = stress_calib_value - stress_uncalib_value;
625    if stress_resilience > 0.0 {
626        println!(
627            "  ✅ Better stress resilience: +${:.2}M",
628            stress_resilience / 1_000_000.0
629        );
630    }
631
632    // ========================================================================
633    // 8. Recommendations
634    // ========================================================================
635
636    println!("\n\n╔═══════════════════════════════════════════════════════╗");
637    println!("║  Production Deployment Recommendations                ║");
638    println!("╚═══════════════════════════════════════════════════════╝\n");
639
640    println!("Based on the analysis:\n");
641    println!("1. 🎯 Deploy {best_method_name} calibration method");
642    println!("2. 📊 Implement monthly recalibration schedule");
643    println!("3. 🔍 Monitor ECE and backtest predictions quarterly");
644    println!("4. 💰 Optimize decision threshold for portfolio objectives");
645    println!("5. 📈 Track calibration drift using hold-out validation set");
646    println!("6. 🏛️  Document calibration methodology for regulators");
647    println!("7. ⚖️  Conduct annual model validation review");
648    println!("8. 🚨 Set up alerts for ECE > 0.10 (regulatory threshold)");
649    println!("9. 📉 Perform stress testing with calibrated probabilities");
650    println!("10. 💼 Integrate with capital allocation framework");
651
652    println!("\n\n╔═══════════════════════════════════════════════════════╗");
653    println!("║  Regulatory Compliance Checklist                      ║");
654    println!("╚═══════════════════════════════════════════════════════╝\n");
655
656    println!("✅ Model Validation:");
657    println!("   ✓ Calibration metrics documented (ECE, NLL, Brier)");
658    println!("   ✓ Backtesting performed on hold-out set");
659    println!("   ✓ Stress testing under adverse scenarios");
660    println!("   ✓ Uncertainty quantification available\n");
661
662    println!("✅ Basel III Compliance:");
663    println!("   ✓ Expected Loss calculated with calibrated probabilities");
664    println!("   ✓ Risk-weighted assets computed correctly");
665    println!("   ✓ Capital requirements meet regulatory minimums");
666    println!("   ✓ Model approved for internal ratings-based approach\n");
667
668    println!("✅ Ongoing Monitoring:");
669    println!("   ✓ Quarterly performance reviews scheduled");
670    println!("   ✓ Calibration drift detection in place");
671    println!("   ✓ Model governance framework established");
672    println!("   ✓ Audit trail for all predictions maintained");
673
674    println!("\n✨ Financial risk calibration demonstration complete! ✨\n");
675}
Source

pub fn fit( &mut self, scores: &Array1<f64>, labels: &Array1<usize>, ) -> Result<()>

Fit isotonic regression to scores and labels

Examples found in repository?
examples/calibration_demo.rs (line 119)
103fn demo_isotonic_regression() -> Result<(), Box<dyn std::error::Error>> {
104    // Generate non-linearly separable scores
105    let scores = array![
106        0.1, 0.25, 0.2, // Low scores
107        0.4, 0.35, 0.55, // Mid-low scores
108        0.6, 0.75, 0.7, // Mid-high scores
109        0.85, 0.95, 0.9 // High scores
110    ];
111    // Non-linear relationship with labels
112    let labels = array![0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1];
113
114    println!("   Input scores: {scores:?}");
115    println!("   True labels:  {labels:?}\n");
116
117    // Fit isotonic regression
118    let mut iso = IsotonicRegression::new();
119    iso.fit(&scores, &labels)?;
120
121    println!("   Fitted isotonic regression (maintains monotonicity)");
122
123    // Transform scores
124    let calibrated_probs = iso.transform(&scores)?;
125    println!("\n   Calibrated probabilities:");
126    for (i, (&score, &prob)) in scores.iter().zip(calibrated_probs.iter()).enumerate() {
127        println!("   Sample {i}: score={score:.2} → P(class=1)={prob:.4}");
128    }
129
130    // Verify monotonicity
131    let mut is_monotonic = true;
132    for i in 0..calibrated_probs.len() - 1 {
133        if calibrated_probs[i] > calibrated_probs[i + 1] + 1e-6 {
134            is_monotonic = false;
135            break;
136        }
137    }
138    println!(
139        "\n   Monotonicity preserved: {}",
140        if is_monotonic { "✓" } else { "✗" }
141    );
142
143    Ok(())
144}
More examples
Hide additional examples
examples/calibration_drug_discovery.rs (line 333)
221fn main() {
222    println!("\n╔══════════════════════════════════════════════════════════╗");
223    println!("║  Quantum ML Calibration for Drug Discovery              ║");
224    println!("║  Molecular Binding Affinity Prediction                  ║");
225    println!("╚══════════════════════════════════════════════════════════╝\n");
226
227    // ========================================================================
228    // 1. Generate Drug Discovery Dataset
229    // ========================================================================
230
231    println!("📊 Generating drug discovery dataset...\n");
232
233    let n_train = 1000;
234    let n_cal = 300; // Calibration set
235    let n_test = 500; // Test set
236    let n_features = 20;
237
238    let mut all_molecules = generate_drug_dataset(n_train + n_cal + n_test, n_features);
239
240    // Split into train, calibration, and test sets
241    let test_molecules: Vec<_> = all_molecules.split_off(n_train + n_cal);
242    let cal_molecules: Vec<_> = all_molecules.split_off(n_train);
243    let train_molecules = all_molecules;
244
245    println!("Dataset statistics:");
246    println!("  Training set: {} molecules", train_molecules.len());
247    println!("  Calibration set: {} molecules", cal_molecules.len());
248    println!("  Test set: {} molecules", test_molecules.len());
249    println!("  Features per molecule: {n_features}");
250
251    let train_positive = train_molecules.iter().filter(|m| m.true_binding).count();
252    println!(
253        "  Training set binding ratio: {:.1}%",
254        train_positive as f64 / train_molecules.len() as f64 * 100.0
255    );
256
257    // ========================================================================
258    // 2. Train Quantum Neural Network (Simplified)
259    // ========================================================================
260
261    println!("\n🔬 Training quantum molecular predictor...\n");
262
263    let qnn = QuantumMolecularPredictor::new(n_features, 0.3);
264
265    // Get predictions on calibration set
266    let cal_probs = qnn.predict_batch(&cal_molecules);
267    let cal_labels = Array1::from_shape_fn(cal_molecules.len(), |i| {
268        usize::from(cal_molecules[i].true_binding)
269    });
270
271    // Get predictions on test set
272    let test_probs = qnn.predict_batch(&test_molecules);
273    let test_labels = Array1::from_shape_fn(test_molecules.len(), |i| {
274        usize::from(test_molecules[i].true_binding)
275    });
276
277    println!("Model trained! Evaluating uncalibrated performance...");
278
279    let test_preds = test_probs.mapv(|p| usize::from(p >= 0.5));
280    let acc = accuracy(&test_preds, &test_labels);
281    let prec = precision(&test_preds, &test_labels, 2).expect("Precision failed");
282    let rec = recall(&test_preds, &test_labels, 2).expect("Recall failed");
283    let f1 = f1_score(&test_preds, &test_labels, 2).expect("F1 failed");
284
285    println!("  Accuracy: {:.2}%", acc * 100.0);
286    println!("  Precision (class 1): {:.2}%", prec[1] * 100.0);
287    println!("  Recall (class 1): {:.2}%", rec[1] * 100.0);
288    println!("  F1 Score (class 1): {:.3}", f1[1]);
289
290    // ========================================================================
291    // 3. Analyze Uncalibrated Model
292    // ========================================================================
293
294    println!("\n📉 Analyzing uncalibrated model calibration...\n");
295
296    let uncalib_ece =
297        expected_calibration_error(&test_probs, &test_labels, 10).expect("ECE failed");
298
299    println!("Uncalibrated metrics:");
300    println!("  Expected Calibration Error (ECE): {uncalib_ece:.4}");
301
302    if uncalib_ece > 0.1 {
303        println!("  ⚠️  High ECE indicates poor calibration!");
304    }
305
306    // ========================================================================
307    // 4. Apply Calibration Methods
308    // ========================================================================
309
310    println!("\n🔧 Applying calibration methods...\n");
311
312    // Method 1: Platt Scaling
313    println!("1️⃣  Platt Scaling (parametric, fast)");
314    let mut platt = PlattScaler::new();
315    platt
316        .fit(&cal_probs, &cal_labels)
317        .expect("Platt fitting failed");
318    let platt_test_probs = platt
319        .transform(&test_probs)
320        .expect("Platt transform failed");
321    let platt_ece =
322        expected_calibration_error(&platt_test_probs, &test_labels, 10).expect("ECE failed");
323    println!(
324        "   ECE after Platt: {:.4} ({:.1}% improvement)",
325        platt_ece,
326        (uncalib_ece - platt_ece) / uncalib_ece * 100.0
327    );
328
329    // Method 2: Isotonic Regression
330    println!("\n2️⃣  Isotonic Regression (non-parametric, flexible)");
331    let mut isotonic = IsotonicRegression::new();
332    isotonic
333        .fit(&cal_probs, &cal_labels)
334        .expect("Isotonic fitting failed");
335    let isotonic_test_probs = isotonic
336        .transform(&test_probs)
337        .expect("Isotonic transform failed");
338    let isotonic_ece =
339        expected_calibration_error(&isotonic_test_probs, &test_labels, 10).expect("ECE failed");
340    println!(
341        "   ECE after Isotonic: {:.4} ({:.1}% improvement)",
342        isotonic_ece,
343        (uncalib_ece - isotonic_ece) / uncalib_ece * 100.0
344    );
345
346    // Method 3: Bayesian Binning into Quantiles (BBQ)
347    println!("\n3️⃣  Bayesian Binning into Quantiles (BBQ-10)");
348    let mut bbq = BayesianBinningQuantiles::new(10);
349    bbq.fit(&cal_probs, &cal_labels)
350        .expect("BBQ fitting failed");
351    let bbq_test_probs = bbq.transform(&test_probs).expect("BBQ transform failed");
352    let bbq_ece =
353        expected_calibration_error(&bbq_test_probs, &test_labels, 10).expect("ECE failed");
354    println!(
355        "   ECE after BBQ: {:.4} ({:.1}% improvement)",
356        bbq_ece,
357        (uncalib_ece - bbq_ece) / uncalib_ece * 100.0
358    );
359
360    // ========================================================================
361    // 5. Compare All Methods
362    // ========================================================================
363
364    println!("\n📊 Comprehensive method comparison...\n");
365
366    println!("Method Comparison (ECE on test set):");
367    println!("  Uncalibrated:      {uncalib_ece:.4}");
368    println!("  Platt Scaling:     {platt_ece:.4}");
369    println!("  Isotonic Regr.:    {isotonic_ece:.4}");
370    println!("  BBQ-10:            {bbq_ece:.4}");
371
372    // ========================================================================
373    // 6. Decision Impact Analysis
374    // ========================================================================
375
376    // Choose best method based on ECE
377    let (best_method, best_probs, best_ece) = if bbq_ece < isotonic_ece && bbq_ece < platt_ece {
378        ("BBQ-10", bbq_test_probs, bbq_ece)
379    } else if isotonic_ece < platt_ece {
380        ("Isotonic Regression", isotonic_test_probs, isotonic_ece)
381    } else {
382        ("Platt Scaling", platt_test_probs, platt_ece)
383    };
384
385    println!("\n🏆 Best calibration method: {best_method}");
386
387    // Demonstrate impact on different decision thresholds
388    for threshold in &[0.3, 0.5, 0.7, 0.9] {
389        demonstrate_decision_impact(&test_molecules, &test_probs, &best_probs, *threshold);
390    }
391
392    // ========================================================================
393    // 7. Regulatory Compliance Analysis
394    // ========================================================================
395
396    println!("\n\n╔═══════════════════════════════════════════════════════╗");
397    println!("║  Regulatory Compliance Analysis (FDA Guidelines)     ║");
398    println!("╚═══════════════════════════════════════════════════════╝\n");
399
400    println!("FDA requires ML/AI models to provide:\n");
401    println!("✓ Well-calibrated probability estimates");
402    println!("✓ Uncertainty quantification");
403    println!("✓ Transparency in decision thresholds");
404    println!("✓ Performance on diverse molecular scaffolds\n");
405
406    println!("Calibration status:");
407    if best_ece < 0.05 {
408        println!("  ✅ Excellent calibration (ECE < 0.05)");
409    } else if best_ece < 0.10 {
410        println!("  ✅ Good calibration (ECE < 0.10)");
411    } else if best_ece < 0.15 {
412        println!("  ⚠️  Acceptable calibration (ECE < 0.15) - consider improvement");
413    } else {
414        println!("  ❌ Poor calibration (ECE >= 0.15) - recalibration required");
415    }
416
417    println!("\nUncertainty quantification:");
418    println!("  📊 Calibration curve available: Yes");
419    println!("  📊 Confidence intervals: Yes (via BBQ method)");
420
421    // ========================================================================
422    // 8. Recommendations
423    // ========================================================================
424
425    println!("\n\n╔═══════════════════════════════════════════════════════╗");
426    println!("║  Recommendations for Production Deployment           ║");
427    println!("╚═══════════════════════════════════════════════════════╝\n");
428
429    println!("Based on the analysis:\n");
430    println!("1. 🎯 Use {best_method} for best calibration");
431    println!("2. 📊 Monitor ECE and NLL in production");
432    println!("3. 🔄 Recalibrate when data distribution shifts");
433    println!("4. 💰 Optimize decision threshold based on cost/benefit analysis");
434    println!("5. 🔬 Consider ensemble methods for critical decisions");
435    println!("6. 📈 Track calibration degradation over time");
436    println!("7. ⚗️  Validate on diverse molecular scaffolds");
437    println!("8. 🚨 Set up alerts for calibration drift (ECE > 0.15)");
438
439    println!("\n✨ Drug discovery calibration demonstration complete! ✨\n");
440}
examples/calibration_finance.rs (line 460)
343fn main() {
344    println!("\n╔══════════════════════════════════════════════════════════╗");
345    println!("║  Quantum ML Calibration for Financial Risk Prediction   ║");
346    println!("║  Credit Default & Portfolio Risk Assessment             ║");
347    println!("╚══════════════════════════════════════════════════════════╝\n");
348
349    // ========================================================================
350    // 1. Generate Credit Application Dataset
351    // ========================================================================
352
353    println!("📊 Generating credit application dataset...\n");
354
355    let n_train = 5000;
356    let n_cal = 1000;
357    let n_test = 2000;
358    let n_features = 15;
359
360    let mut all_applications = generate_credit_dataset(n_train + n_cal + n_test, n_features);
361
362    // Split into train, calibration, and test sets
363    let test_apps: Vec<_> = all_applications.split_off(n_train + n_cal);
364    let cal_apps: Vec<_> = all_applications.split_off(n_train);
365    let train_apps = all_applications;
366
367    println!("Dataset statistics:");
368    println!("  Training set: {} applications", train_apps.len());
369    println!("  Calibration set: {} applications", cal_apps.len());
370    println!("  Test set: {} applications", test_apps.len());
371    println!("  Features per application: {n_features}");
372
373    let train_default_rate =
374        train_apps.iter().filter(|a| a.true_default).count() as f64 / train_apps.len() as f64;
375    println!(
376        "  Historical default rate: {:.2}%",
377        train_default_rate * 100.0
378    );
379
380    let total_loan_volume: f64 = test_apps.iter().map(|a| a.loan_amount).sum();
381    println!(
382        "  Test portfolio size: ${:.2}M",
383        total_loan_volume / 1_000_000.0
384    );
385
386    // ========================================================================
387    // 2. Train Quantum Credit Risk Model
388    // ========================================================================
389
390    println!("\n🔬 Training quantum credit risk model...\n");
391
392    let qcrm = QuantumCreditRiskModel::new(n_features, 0.2);
393
394    // Get predictions
395    let cal_probs = qcrm.predict_batch(&cal_apps);
396    let cal_labels =
397        Array1::from_shape_fn(cal_apps.len(), |i| usize::from(cal_apps[i].true_default));
398
399    let test_probs = qcrm.predict_batch(&test_apps);
400    let test_labels =
401        Array1::from_shape_fn(test_apps.len(), |i| usize::from(test_apps[i].true_default));
402
403    println!("Model trained! Evaluating uncalibrated performance...");
404
405    let test_preds = test_probs.mapv(|p| usize::from(p >= 0.5));
406    let acc = accuracy(&test_preds, &test_labels);
407    let prec = precision(&test_preds, &test_labels, 2).expect("Precision failed");
408    let rec = recall(&test_preds, &test_labels, 2).expect("Recall failed");
409    let f1 = f1_score(&test_preds, &test_labels, 2).expect("F1 failed");
410    let auc = auc_roc(&test_probs, &test_labels).expect("AUC failed");
411
412    println!("  Accuracy: {:.2}%", acc * 100.0);
413    println!("  Precision (class 1): {:.2}%", prec[1] * 100.0);
414    println!("  Recall (class 1): {:.2}%", rec[1] * 100.0);
415    println!("  F1 Score (class 1): {:.3}", f1[1]);
416    println!("  AUC-ROC: {auc:.3}");
417
418    // ========================================================================
419    // 3. Analyze Uncalibrated Model
420    // ========================================================================
421
422    println!("\n📉 Analyzing uncalibrated model calibration...\n");
423
424    let uncalib_ece =
425        expected_calibration_error(&test_probs, &test_labels, 10).expect("ECE failed");
426    let uncalib_mce = maximum_calibration_error(&test_probs, &test_labels, 10).expect("MCE failed");
427    let uncalib_logloss = log_loss(&test_probs, &test_labels);
428
429    println!("Uncalibrated calibration metrics:");
430    println!("  Expected Calibration Error (ECE): {uncalib_ece:.4}");
431    println!("  Maximum Calibration Error (MCE): {uncalib_mce:.4}");
432    println!("  Log Loss: {uncalib_logloss:.4}");
433
434    if uncalib_ece > 0.10 {
435        println!("  ⚠️  High ECE - probabilities are poorly calibrated!");
436        println!("     This violates regulatory requirements for risk models.");
437    }
438
439    // ========================================================================
440    // 4. Apply Multiple Calibration Methods
441    // ========================================================================
442
443    println!("\n🔧 Applying advanced calibration methods...\n");
444
445    // Apply calibration methods
446    println!("🔧 Applying calibration methods...\n");
447
448    // Try different calibration methods
449    let mut platt = PlattScaler::new();
450    platt
451        .fit(&cal_probs, &cal_labels)
452        .expect("Platt fit failed");
453    let platt_probs = platt
454        .transform(&test_probs)
455        .expect("Platt transform failed");
456    let platt_ece = expected_calibration_error(&platt_probs, &test_labels, 10).expect("ECE failed");
457
458    let mut isotonic = IsotonicRegression::new();
459    isotonic
460        .fit(&cal_probs, &cal_labels)
461        .expect("Isotonic fit failed");
462    let isotonic_probs = isotonic
463        .transform(&test_probs)
464        .expect("Isotonic transform failed");
465    let isotonic_ece =
466        expected_calibration_error(&isotonic_probs, &test_labels, 10).expect("ECE failed");
467
468    let mut bbq = BayesianBinningQuantiles::new(10);
469    bbq.fit(&cal_probs, &cal_labels).expect("BBQ fit failed");
470    let bbq_probs = bbq.transform(&test_probs).expect("BBQ transform failed");
471    let bbq_ece = expected_calibration_error(&bbq_probs, &test_labels, 10).expect("ECE failed");
472
473    println!("Calibration Results:");
474    println!("  Platt Scaling: ECE = {platt_ece:.4}");
475    println!("  Isotonic Regression: ECE = {isotonic_ece:.4}");
476    println!("  BBQ-10: ECE = {bbq_ece:.4}");
477
478    // Choose best method
479    let (best_method_name, best_test_probs) = if bbq_ece < isotonic_ece && bbq_ece < platt_ece {
480        ("BBQ-10", bbq_probs)
481    } else if isotonic_ece < platt_ece {
482        ("Isotonic", isotonic_probs)
483    } else {
484        ("Platt", platt_probs)
485    };
486
487    println!("\n🏆 Best method: {best_method_name}\n");
488
489    let best_ece =
490        expected_calibration_error(&best_test_probs, &test_labels, 10).expect("ECE failed");
491
492    println!("Calibrated model performance:");
493    println!(
494        "  ECE: {:.4} ({:.1}% improvement)",
495        best_ece,
496        (uncalib_ece - best_ece) / uncalib_ece * 100.0
497    );
498
499    // ========================================================================
500    // 5. Economic Impact Analysis
501    // ========================================================================
502
503    println!("\n\n╔═══════════════════════════════════════════════════════╗");
504    println!("║  Economic Impact of Calibration                      ║");
505    println!("╚═══════════════════════════════════════════════════════╝\n");
506
507    let default_loss_rate = 0.60; // Lose 60% of principal on default
508    let profit_margin = 0.08; // 8% profit on successful loans
509
510    for threshold in &[0.3, 0.5, 0.7] {
511        println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
512        println!(
513            "Decision Threshold: {:.0}% default probability",
514            threshold * 100.0
515        );
516        println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
517
518        let (uncalib_value, uncalib_approved, uncalib_tp, uncalib_fp, uncalib_fn) =
519            calculate_lending_value(
520                &test_apps,
521                &test_probs,
522                *threshold,
523                default_loss_rate,
524                profit_margin,
525            );
526
527        let (calib_value, calib_approved, calib_tp, calib_fp, calib_fn) = calculate_lending_value(
528            &test_apps,
529            &best_test_probs,
530            *threshold,
531            default_loss_rate,
532            profit_margin,
533        );
534
535        println!("Uncalibrated Model:");
536        println!("  Loans approved: {}/{}", uncalib_approved, test_apps.len());
537        println!("  Correctly rejected defaults: {uncalib_tp}");
538        println!("  Missed profit opportunities: {uncalib_fp}");
539        println!("  Approved defaults (losses): {uncalib_fn}");
540        println!(
541            "  Net portfolio value: ${:.2}M",
542            uncalib_value / 1_000_000.0
543        );
544
545        println!("\nCalibrated Model:");
546        println!("  Loans approved: {}/{}", calib_approved, test_apps.len());
547        println!("  Correctly rejected defaults: {calib_tp}");
548        println!("  Missed profit opportunities: {calib_fp}");
549        println!("  Approved defaults (losses): {calib_fn}");
550        println!("  Net portfolio value: ${:.2}M", calib_value / 1_000_000.0);
551
552        let value_improvement = calib_value - uncalib_value;
553        println!("\n💰 Economic Impact:");
554        if value_improvement > 0.0 {
555            println!(
556                "  Additional profit: ${:.2}M ({:.1}% improvement)",
557                value_improvement / 1_000_000.0,
558                value_improvement / uncalib_value.abs() * 100.0
559            );
560        } else {
561            println!("  Value change: ${:.2}M", value_improvement / 1_000_000.0);
562        }
563
564        let default_reduction = uncalib_fn as i32 - calib_fn as i32;
565        if default_reduction > 0 {
566            println!(
567                "  Defaults avoided: {} ({:.1}% reduction)",
568                default_reduction,
569                default_reduction as f64 / uncalib_fn as f64 * 100.0
570            );
571        }
572    }
573
574    // ========================================================================
575    // 6. Basel III Capital Requirements
576    // ========================================================================
577
578    demonstrate_capital_impact(&test_apps, &test_probs, &best_test_probs);
579
580    // ========================================================================
581    // 7. Stress Testing
582    // ========================================================================
583
584    println!("\n\n╔═══════════════════════════════════════════════════════╗");
585    println!("║  Regulatory Stress Testing (CCAR/DFAST)              ║");
586    println!("╚═══════════════════════════════════════════════════════╝\n");
587
588    println!("Stress scenarios:");
589    println!("  📉 Severe economic downturn (unemployment +5%)");
590    println!("  📊 Market volatility increase (+200%)");
591    println!("  🏦 Credit spread widening (+300 bps)\n");
592
593    // Simulate stress by increasing default probabilities
594    let stress_factor = 2.5;
595    let stressed_probs = test_probs.mapv(|p| (p * stress_factor).min(0.95));
596    let stressed_calib_probs = best_test_probs.mapv(|p| (p * stress_factor).min(0.95));
597
598    let (stress_uncalib_value, _, _, _, _) = calculate_lending_value(
599        &test_apps,
600        &stressed_probs,
601        0.5,
602        default_loss_rate,
603        profit_margin,
604    );
605
606    let (stress_calib_value, _, _, _, _) = calculate_lending_value(
607        &test_apps,
608        &stressed_calib_probs,
609        0.5,
610        default_loss_rate,
611        profit_margin,
612    );
613
614    println!("Portfolio value under stress:");
615    println!(
616        "  Uncalibrated Model: ${:.2}M",
617        stress_uncalib_value / 1_000_000.0
618    );
619    println!(
620        "  Calibrated Model: ${:.2}M",
621        stress_calib_value / 1_000_000.0
622    );
623
624    let stress_resilience = stress_calib_value - stress_uncalib_value;
625    if stress_resilience > 0.0 {
626        println!(
627            "  ✅ Better stress resilience: +${:.2}M",
628            stress_resilience / 1_000_000.0
629        );
630    }
631
632    // ========================================================================
633    // 8. Recommendations
634    // ========================================================================
635
636    println!("\n\n╔═══════════════════════════════════════════════════════╗");
637    println!("║  Production Deployment Recommendations                ║");
638    println!("╚═══════════════════════════════════════════════════════╝\n");
639
640    println!("Based on the analysis:\n");
641    println!("1. 🎯 Deploy {best_method_name} calibration method");
642    println!("2. 📊 Implement monthly recalibration schedule");
643    println!("3. 🔍 Monitor ECE and backtest predictions quarterly");
644    println!("4. 💰 Optimize decision threshold for portfolio objectives");
645    println!("5. 📈 Track calibration drift using hold-out validation set");
646    println!("6. 🏛️  Document calibration methodology for regulators");
647    println!("7. ⚖️  Conduct annual model validation review");
648    println!("8. 🚨 Set up alerts for ECE > 0.10 (regulatory threshold)");
649    println!("9. 📉 Perform stress testing with calibrated probabilities");
650    println!("10. 💼 Integrate with capital allocation framework");
651
652    println!("\n\n╔═══════════════════════════════════════════════════════╗");
653    println!("║  Regulatory Compliance Checklist                      ║");
654    println!("╚═══════════════════════════════════════════════════════╝\n");
655
656    println!("✅ Model Validation:");
657    println!("   ✓ Calibration metrics documented (ECE, NLL, Brier)");
658    println!("   ✓ Backtesting performed on hold-out set");
659    println!("   ✓ Stress testing under adverse scenarios");
660    println!("   ✓ Uncertainty quantification available\n");
661
662    println!("✅ Basel III Compliance:");
663    println!("   ✓ Expected Loss calculated with calibrated probabilities");
664    println!("   ✓ Risk-weighted assets computed correctly");
665    println!("   ✓ Capital requirements meet regulatory minimums");
666    println!("   ✓ Model approved for internal ratings-based approach\n");
667
668    println!("✅ Ongoing Monitoring:");
669    println!("   ✓ Quarterly performance reviews scheduled");
670    println!("   ✓ Calibration drift detection in place");
671    println!("   ✓ Model governance framework established");
672    println!("   ✓ Audit trail for all predictions maintained");
673
674    println!("\n✨ Financial risk calibration demonstration complete! ✨\n");
675}
Source

pub fn transform(&self, scores: &Array1<f64>) -> Result<Array1<f64>>

Transform decision scores to calibrated probabilities

Examples found in repository?
examples/calibration_demo.rs (line 124)
103fn demo_isotonic_regression() -> Result<(), Box<dyn std::error::Error>> {
104    // Generate non-linearly separable scores
105    let scores = array![
106        0.1, 0.25, 0.2, // Low scores
107        0.4, 0.35, 0.55, // Mid-low scores
108        0.6, 0.75, 0.7, // Mid-high scores
109        0.85, 0.95, 0.9 // High scores
110    ];
111    // Non-linear relationship with labels
112    let labels = array![0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1];
113
114    println!("   Input scores: {scores:?}");
115    println!("   True labels:  {labels:?}\n");
116
117    // Fit isotonic regression
118    let mut iso = IsotonicRegression::new();
119    iso.fit(&scores, &labels)?;
120
121    println!("   Fitted isotonic regression (maintains monotonicity)");
122
123    // Transform scores
124    let calibrated_probs = iso.transform(&scores)?;
125    println!("\n   Calibrated probabilities:");
126    for (i, (&score, &prob)) in scores.iter().zip(calibrated_probs.iter()).enumerate() {
127        println!("   Sample {i}: score={score:.2} → P(class=1)={prob:.4}");
128    }
129
130    // Verify monotonicity
131    let mut is_monotonic = true;
132    for i in 0..calibrated_probs.len() - 1 {
133        if calibrated_probs[i] > calibrated_probs[i + 1] + 1e-6 {
134            is_monotonic = false;
135            break;
136        }
137    }
138    println!(
139        "\n   Monotonicity preserved: {}",
140        if is_monotonic { "✓" } else { "✗" }
141    );
142
143    Ok(())
144}
More examples
Hide additional examples
examples/calibration_drug_discovery.rs (line 336)
221fn main() {
222    println!("\n╔══════════════════════════════════════════════════════════╗");
223    println!("║  Quantum ML Calibration for Drug Discovery              ║");
224    println!("║  Molecular Binding Affinity Prediction                  ║");
225    println!("╚══════════════════════════════════════════════════════════╝\n");
226
227    // ========================================================================
228    // 1. Generate Drug Discovery Dataset
229    // ========================================================================
230
231    println!("📊 Generating drug discovery dataset...\n");
232
233    let n_train = 1000;
234    let n_cal = 300; // Calibration set
235    let n_test = 500; // Test set
236    let n_features = 20;
237
238    let mut all_molecules = generate_drug_dataset(n_train + n_cal + n_test, n_features);
239
240    // Split into train, calibration, and test sets
241    let test_molecules: Vec<_> = all_molecules.split_off(n_train + n_cal);
242    let cal_molecules: Vec<_> = all_molecules.split_off(n_train);
243    let train_molecules = all_molecules;
244
245    println!("Dataset statistics:");
246    println!("  Training set: {} molecules", train_molecules.len());
247    println!("  Calibration set: {} molecules", cal_molecules.len());
248    println!("  Test set: {} molecules", test_molecules.len());
249    println!("  Features per molecule: {n_features}");
250
251    let train_positive = train_molecules.iter().filter(|m| m.true_binding).count();
252    println!(
253        "  Training set binding ratio: {:.1}%",
254        train_positive as f64 / train_molecules.len() as f64 * 100.0
255    );
256
257    // ========================================================================
258    // 2. Train Quantum Neural Network (Simplified)
259    // ========================================================================
260
261    println!("\n🔬 Training quantum molecular predictor...\n");
262
263    let qnn = QuantumMolecularPredictor::new(n_features, 0.3);
264
265    // Get predictions on calibration set
266    let cal_probs = qnn.predict_batch(&cal_molecules);
267    let cal_labels = Array1::from_shape_fn(cal_molecules.len(), |i| {
268        usize::from(cal_molecules[i].true_binding)
269    });
270
271    // Get predictions on test set
272    let test_probs = qnn.predict_batch(&test_molecules);
273    let test_labels = Array1::from_shape_fn(test_molecules.len(), |i| {
274        usize::from(test_molecules[i].true_binding)
275    });
276
277    println!("Model trained! Evaluating uncalibrated performance...");
278
279    let test_preds = test_probs.mapv(|p| usize::from(p >= 0.5));
280    let acc = accuracy(&test_preds, &test_labels);
281    let prec = precision(&test_preds, &test_labels, 2).expect("Precision failed");
282    let rec = recall(&test_preds, &test_labels, 2).expect("Recall failed");
283    let f1 = f1_score(&test_preds, &test_labels, 2).expect("F1 failed");
284
285    println!("  Accuracy: {:.2}%", acc * 100.0);
286    println!("  Precision (class 1): {:.2}%", prec[1] * 100.0);
287    println!("  Recall (class 1): {:.2}%", rec[1] * 100.0);
288    println!("  F1 Score (class 1): {:.3}", f1[1]);
289
290    // ========================================================================
291    // 3. Analyze Uncalibrated Model
292    // ========================================================================
293
294    println!("\n📉 Analyzing uncalibrated model calibration...\n");
295
296    let uncalib_ece =
297        expected_calibration_error(&test_probs, &test_labels, 10).expect("ECE failed");
298
299    println!("Uncalibrated metrics:");
300    println!("  Expected Calibration Error (ECE): {uncalib_ece:.4}");
301
302    if uncalib_ece > 0.1 {
303        println!("  ⚠️  High ECE indicates poor calibration!");
304    }
305
306    // ========================================================================
307    // 4. Apply Calibration Methods
308    // ========================================================================
309
310    println!("\n🔧 Applying calibration methods...\n");
311
312    // Method 1: Platt Scaling
313    println!("1️⃣  Platt Scaling (parametric, fast)");
314    let mut platt = PlattScaler::new();
315    platt
316        .fit(&cal_probs, &cal_labels)
317        .expect("Platt fitting failed");
318    let platt_test_probs = platt
319        .transform(&test_probs)
320        .expect("Platt transform failed");
321    let platt_ece =
322        expected_calibration_error(&platt_test_probs, &test_labels, 10).expect("ECE failed");
323    println!(
324        "   ECE after Platt: {:.4} ({:.1}% improvement)",
325        platt_ece,
326        (uncalib_ece - platt_ece) / uncalib_ece * 100.0
327    );
328
329    // Method 2: Isotonic Regression
330    println!("\n2️⃣  Isotonic Regression (non-parametric, flexible)");
331    let mut isotonic = IsotonicRegression::new();
332    isotonic
333        .fit(&cal_probs, &cal_labels)
334        .expect("Isotonic fitting failed");
335    let isotonic_test_probs = isotonic
336        .transform(&test_probs)
337        .expect("Isotonic transform failed");
338    let isotonic_ece =
339        expected_calibration_error(&isotonic_test_probs, &test_labels, 10).expect("ECE failed");
340    println!(
341        "   ECE after Isotonic: {:.4} ({:.1}% improvement)",
342        isotonic_ece,
343        (uncalib_ece - isotonic_ece) / uncalib_ece * 100.0
344    );
345
346    // Method 3: Bayesian Binning into Quantiles (BBQ)
347    println!("\n3️⃣  Bayesian Binning into Quantiles (BBQ-10)");
348    let mut bbq = BayesianBinningQuantiles::new(10);
349    bbq.fit(&cal_probs, &cal_labels)
350        .expect("BBQ fitting failed");
351    let bbq_test_probs = bbq.transform(&test_probs).expect("BBQ transform failed");
352    let bbq_ece =
353        expected_calibration_error(&bbq_test_probs, &test_labels, 10).expect("ECE failed");
354    println!(
355        "   ECE after BBQ: {:.4} ({:.1}% improvement)",
356        bbq_ece,
357        (uncalib_ece - bbq_ece) / uncalib_ece * 100.0
358    );
359
360    // ========================================================================
361    // 5. Compare All Methods
362    // ========================================================================
363
364    println!("\n📊 Comprehensive method comparison...\n");
365
366    println!("Method Comparison (ECE on test set):");
367    println!("  Uncalibrated:      {uncalib_ece:.4}");
368    println!("  Platt Scaling:     {platt_ece:.4}");
369    println!("  Isotonic Regr.:    {isotonic_ece:.4}");
370    println!("  BBQ-10:            {bbq_ece:.4}");
371
372    // ========================================================================
373    // 6. Decision Impact Analysis
374    // ========================================================================
375
376    // Choose best method based on ECE
377    let (best_method, best_probs, best_ece) = if bbq_ece < isotonic_ece && bbq_ece < platt_ece {
378        ("BBQ-10", bbq_test_probs, bbq_ece)
379    } else if isotonic_ece < platt_ece {
380        ("Isotonic Regression", isotonic_test_probs, isotonic_ece)
381    } else {
382        ("Platt Scaling", platt_test_probs, platt_ece)
383    };
384
385    println!("\n🏆 Best calibration method: {best_method}");
386
387    // Demonstrate impact on different decision thresholds
388    for threshold in &[0.3, 0.5, 0.7, 0.9] {
389        demonstrate_decision_impact(&test_molecules, &test_probs, &best_probs, *threshold);
390    }
391
392    // ========================================================================
393    // 7. Regulatory Compliance Analysis
394    // ========================================================================
395
396    println!("\n\n╔═══════════════════════════════════════════════════════╗");
397    println!("║  Regulatory Compliance Analysis (FDA Guidelines)     ║");
398    println!("╚═══════════════════════════════════════════════════════╝\n");
399
400    println!("FDA requires ML/AI models to provide:\n");
401    println!("✓ Well-calibrated probability estimates");
402    println!("✓ Uncertainty quantification");
403    println!("✓ Transparency in decision thresholds");
404    println!("✓ Performance on diverse molecular scaffolds\n");
405
406    println!("Calibration status:");
407    if best_ece < 0.05 {
408        println!("  ✅ Excellent calibration (ECE < 0.05)");
409    } else if best_ece < 0.10 {
410        println!("  ✅ Good calibration (ECE < 0.10)");
411    } else if best_ece < 0.15 {
412        println!("  ⚠️  Acceptable calibration (ECE < 0.15) - consider improvement");
413    } else {
414        println!("  ❌ Poor calibration (ECE >= 0.15) - recalibration required");
415    }
416
417    println!("\nUncertainty quantification:");
418    println!("  📊 Calibration curve available: Yes");
419    println!("  📊 Confidence intervals: Yes (via BBQ method)");
420
421    // ========================================================================
422    // 8. Recommendations
423    // ========================================================================
424
425    println!("\n\n╔═══════════════════════════════════════════════════════╗");
426    println!("║  Recommendations for Production Deployment           ║");
427    println!("╚═══════════════════════════════════════════════════════╝\n");
428
429    println!("Based on the analysis:\n");
430    println!("1. 🎯 Use {best_method} for best calibration");
431    println!("2. 📊 Monitor ECE and NLL in production");
432    println!("3. 🔄 Recalibrate when data distribution shifts");
433    println!("4. 💰 Optimize decision threshold based on cost/benefit analysis");
434    println!("5. 🔬 Consider ensemble methods for critical decisions");
435    println!("6. 📈 Track calibration degradation over time");
436    println!("7. ⚗️  Validate on diverse molecular scaffolds");
437    println!("8. 🚨 Set up alerts for calibration drift (ECE > 0.15)");
438
439    println!("\n✨ Drug discovery calibration demonstration complete! ✨\n");
440}
examples/calibration_finance.rs (line 463)
343fn main() {
344    println!("\n╔══════════════════════════════════════════════════════════╗");
345    println!("║  Quantum ML Calibration for Financial Risk Prediction   ║");
346    println!("║  Credit Default & Portfolio Risk Assessment             ║");
347    println!("╚══════════════════════════════════════════════════════════╝\n");
348
349    // ========================================================================
350    // 1. Generate Credit Application Dataset
351    // ========================================================================
352
353    println!("📊 Generating credit application dataset...\n");
354
355    let n_train = 5000;
356    let n_cal = 1000;
357    let n_test = 2000;
358    let n_features = 15;
359
360    let mut all_applications = generate_credit_dataset(n_train + n_cal + n_test, n_features);
361
362    // Split into train, calibration, and test sets
363    let test_apps: Vec<_> = all_applications.split_off(n_train + n_cal);
364    let cal_apps: Vec<_> = all_applications.split_off(n_train);
365    let train_apps = all_applications;
366
367    println!("Dataset statistics:");
368    println!("  Training set: {} applications", train_apps.len());
369    println!("  Calibration set: {} applications", cal_apps.len());
370    println!("  Test set: {} applications", test_apps.len());
371    println!("  Features per application: {n_features}");
372
373    let train_default_rate =
374        train_apps.iter().filter(|a| a.true_default).count() as f64 / train_apps.len() as f64;
375    println!(
376        "  Historical default rate: {:.2}%",
377        train_default_rate * 100.0
378    );
379
380    let total_loan_volume: f64 = test_apps.iter().map(|a| a.loan_amount).sum();
381    println!(
382        "  Test portfolio size: ${:.2}M",
383        total_loan_volume / 1_000_000.0
384    );
385
386    // ========================================================================
387    // 2. Train Quantum Credit Risk Model
388    // ========================================================================
389
390    println!("\n🔬 Training quantum credit risk model...\n");
391
392    let qcrm = QuantumCreditRiskModel::new(n_features, 0.2);
393
394    // Get predictions
395    let cal_probs = qcrm.predict_batch(&cal_apps);
396    let cal_labels =
397        Array1::from_shape_fn(cal_apps.len(), |i| usize::from(cal_apps[i].true_default));
398
399    let test_probs = qcrm.predict_batch(&test_apps);
400    let test_labels =
401        Array1::from_shape_fn(test_apps.len(), |i| usize::from(test_apps[i].true_default));
402
403    println!("Model trained! Evaluating uncalibrated performance...");
404
405    let test_preds = test_probs.mapv(|p| usize::from(p >= 0.5));
406    let acc = accuracy(&test_preds, &test_labels);
407    let prec = precision(&test_preds, &test_labels, 2).expect("Precision failed");
408    let rec = recall(&test_preds, &test_labels, 2).expect("Recall failed");
409    let f1 = f1_score(&test_preds, &test_labels, 2).expect("F1 failed");
410    let auc = auc_roc(&test_probs, &test_labels).expect("AUC failed");
411
412    println!("  Accuracy: {:.2}%", acc * 100.0);
413    println!("  Precision (class 1): {:.2}%", prec[1] * 100.0);
414    println!("  Recall (class 1): {:.2}%", rec[1] * 100.0);
415    println!("  F1 Score (class 1): {:.3}", f1[1]);
416    println!("  AUC-ROC: {auc:.3}");
417
418    // ========================================================================
419    // 3. Analyze Uncalibrated Model
420    // ========================================================================
421
422    println!("\n📉 Analyzing uncalibrated model calibration...\n");
423
424    let uncalib_ece =
425        expected_calibration_error(&test_probs, &test_labels, 10).expect("ECE failed");
426    let uncalib_mce = maximum_calibration_error(&test_probs, &test_labels, 10).expect("MCE failed");
427    let uncalib_logloss = log_loss(&test_probs, &test_labels);
428
429    println!("Uncalibrated calibration metrics:");
430    println!("  Expected Calibration Error (ECE): {uncalib_ece:.4}");
431    println!("  Maximum Calibration Error (MCE): {uncalib_mce:.4}");
432    println!("  Log Loss: {uncalib_logloss:.4}");
433
434    if uncalib_ece > 0.10 {
435        println!("  ⚠️  High ECE - probabilities are poorly calibrated!");
436        println!("     This violates regulatory requirements for risk models.");
437    }
438
439    // ========================================================================
440    // 4. Apply Multiple Calibration Methods
441    // ========================================================================
442
443    println!("\n🔧 Applying advanced calibration methods...\n");
444
445    // Apply calibration methods
446    println!("🔧 Applying calibration methods...\n");
447
448    // Try different calibration methods
449    let mut platt = PlattScaler::new();
450    platt
451        .fit(&cal_probs, &cal_labels)
452        .expect("Platt fit failed");
453    let platt_probs = platt
454        .transform(&test_probs)
455        .expect("Platt transform failed");
456    let platt_ece = expected_calibration_error(&platt_probs, &test_labels, 10).expect("ECE failed");
457
458    let mut isotonic = IsotonicRegression::new();
459    isotonic
460        .fit(&cal_probs, &cal_labels)
461        .expect("Isotonic fit failed");
462    let isotonic_probs = isotonic
463        .transform(&test_probs)
464        .expect("Isotonic transform failed");
465    let isotonic_ece =
466        expected_calibration_error(&isotonic_probs, &test_labels, 10).expect("ECE failed");
467
468    let mut bbq = BayesianBinningQuantiles::new(10);
469    bbq.fit(&cal_probs, &cal_labels).expect("BBQ fit failed");
470    let bbq_probs = bbq.transform(&test_probs).expect("BBQ transform failed");
471    let bbq_ece = expected_calibration_error(&bbq_probs, &test_labels, 10).expect("ECE failed");
472
473    println!("Calibration Results:");
474    println!("  Platt Scaling: ECE = {platt_ece:.4}");
475    println!("  Isotonic Regression: ECE = {isotonic_ece:.4}");
476    println!("  BBQ-10: ECE = {bbq_ece:.4}");
477
478    // Choose best method
479    let (best_method_name, best_test_probs) = if bbq_ece < isotonic_ece && bbq_ece < platt_ece {
480        ("BBQ-10", bbq_probs)
481    } else if isotonic_ece < platt_ece {
482        ("Isotonic", isotonic_probs)
483    } else {
484        ("Platt", platt_probs)
485    };
486
487    println!("\n🏆 Best method: {best_method_name}\n");
488
489    let best_ece =
490        expected_calibration_error(&best_test_probs, &test_labels, 10).expect("ECE failed");
491
492    println!("Calibrated model performance:");
493    println!(
494        "  ECE: {:.4} ({:.1}% improvement)",
495        best_ece,
496        (uncalib_ece - best_ece) / uncalib_ece * 100.0
497    );
498
499    // ========================================================================
500    // 5. Economic Impact Analysis
501    // ========================================================================
502
503    println!("\n\n╔═══════════════════════════════════════════════════════╗");
504    println!("║  Economic Impact of Calibration                      ║");
505    println!("╚═══════════════════════════════════════════════════════╝\n");
506
507    let default_loss_rate = 0.60; // Lose 60% of principal on default
508    let profit_margin = 0.08; // 8% profit on successful loans
509
510    for threshold in &[0.3, 0.5, 0.7] {
511        println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
512        println!(
513            "Decision Threshold: {:.0}% default probability",
514            threshold * 100.0
515        );
516        println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
517
518        let (uncalib_value, uncalib_approved, uncalib_tp, uncalib_fp, uncalib_fn) =
519            calculate_lending_value(
520                &test_apps,
521                &test_probs,
522                *threshold,
523                default_loss_rate,
524                profit_margin,
525            );
526
527        let (calib_value, calib_approved, calib_tp, calib_fp, calib_fn) = calculate_lending_value(
528            &test_apps,
529            &best_test_probs,
530            *threshold,
531            default_loss_rate,
532            profit_margin,
533        );
534
535        println!("Uncalibrated Model:");
536        println!("  Loans approved: {}/{}", uncalib_approved, test_apps.len());
537        println!("  Correctly rejected defaults: {uncalib_tp}");
538        println!("  Missed profit opportunities: {uncalib_fp}");
539        println!("  Approved defaults (losses): {uncalib_fn}");
540        println!(
541            "  Net portfolio value: ${:.2}M",
542            uncalib_value / 1_000_000.0
543        );
544
545        println!("\nCalibrated Model:");
546        println!("  Loans approved: {}/{}", calib_approved, test_apps.len());
547        println!("  Correctly rejected defaults: {calib_tp}");
548        println!("  Missed profit opportunities: {calib_fp}");
549        println!("  Approved defaults (losses): {calib_fn}");
550        println!("  Net portfolio value: ${:.2}M", calib_value / 1_000_000.0);
551
552        let value_improvement = calib_value - uncalib_value;
553        println!("\n💰 Economic Impact:");
554        if value_improvement > 0.0 {
555            println!(
556                "  Additional profit: ${:.2}M ({:.1}% improvement)",
557                value_improvement / 1_000_000.0,
558                value_improvement / uncalib_value.abs() * 100.0
559            );
560        } else {
561            println!("  Value change: ${:.2}M", value_improvement / 1_000_000.0);
562        }
563
564        let default_reduction = uncalib_fn as i32 - calib_fn as i32;
565        if default_reduction > 0 {
566            println!(
567                "  Defaults avoided: {} ({:.1}% reduction)",
568                default_reduction,
569                default_reduction as f64 / uncalib_fn as f64 * 100.0
570            );
571        }
572    }
573
574    // ========================================================================
575    // 6. Basel III Capital Requirements
576    // ========================================================================
577
578    demonstrate_capital_impact(&test_apps, &test_probs, &best_test_probs);
579
580    // ========================================================================
581    // 7. Stress Testing
582    // ========================================================================
583
584    println!("\n\n╔═══════════════════════════════════════════════════════╗");
585    println!("║  Regulatory Stress Testing (CCAR/DFAST)              ║");
586    println!("╚═══════════════════════════════════════════════════════╝\n");
587
588    println!("Stress scenarios:");
589    println!("  📉 Severe economic downturn (unemployment +5%)");
590    println!("  📊 Market volatility increase (+200%)");
591    println!("  🏦 Credit spread widening (+300 bps)\n");
592
593    // Simulate stress by increasing default probabilities
594    let stress_factor = 2.5;
595    let stressed_probs = test_probs.mapv(|p| (p * stress_factor).min(0.95));
596    let stressed_calib_probs = best_test_probs.mapv(|p| (p * stress_factor).min(0.95));
597
598    let (stress_uncalib_value, _, _, _, _) = calculate_lending_value(
599        &test_apps,
600        &stressed_probs,
601        0.5,
602        default_loss_rate,
603        profit_margin,
604    );
605
606    let (stress_calib_value, _, _, _, _) = calculate_lending_value(
607        &test_apps,
608        &stressed_calib_probs,
609        0.5,
610        default_loss_rate,
611        profit_margin,
612    );
613
614    println!("Portfolio value under stress:");
615    println!(
616        "  Uncalibrated Model: ${:.2}M",
617        stress_uncalib_value / 1_000_000.0
618    );
619    println!(
620        "  Calibrated Model: ${:.2}M",
621        stress_calib_value / 1_000_000.0
622    );
623
624    let stress_resilience = stress_calib_value - stress_uncalib_value;
625    if stress_resilience > 0.0 {
626        println!(
627            "  ✅ Better stress resilience: +${:.2}M",
628            stress_resilience / 1_000_000.0
629        );
630    }
631
632    // ========================================================================
633    // 8. Recommendations
634    // ========================================================================
635
636    println!("\n\n╔═══════════════════════════════════════════════════════╗");
637    println!("║  Production Deployment Recommendations                ║");
638    println!("╚═══════════════════════════════════════════════════════╝\n");
639
640    println!("Based on the analysis:\n");
641    println!("1. 🎯 Deploy {best_method_name} calibration method");
642    println!("2. 📊 Implement monthly recalibration schedule");
643    println!("3. 🔍 Monitor ECE and backtest predictions quarterly");
644    println!("4. 💰 Optimize decision threshold for portfolio objectives");
645    println!("5. 📈 Track calibration drift using hold-out validation set");
646    println!("6. 🏛️  Document calibration methodology for regulators");
647    println!("7. ⚖️  Conduct annual model validation review");
648    println!("8. 🚨 Set up alerts for ECE > 0.10 (regulatory threshold)");
649    println!("9. 📉 Perform stress testing with calibrated probabilities");
650    println!("10. 💼 Integrate with capital allocation framework");
651
652    println!("\n\n╔═══════════════════════════════════════════════════════╗");
653    println!("║  Regulatory Compliance Checklist                      ║");
654    println!("╚═══════════════════════════════════════════════════════╝\n");
655
656    println!("✅ Model Validation:");
657    println!("   ✓ Calibration metrics documented (ECE, NLL, Brier)");
658    println!("   ✓ Backtesting performed on hold-out set");
659    println!("   ✓ Stress testing under adverse scenarios");
660    println!("   ✓ Uncertainty quantification available\n");
661
662    println!("✅ Basel III Compliance:");
663    println!("   ✓ Expected Loss calculated with calibrated probabilities");
664    println!("   ✓ Risk-weighted assets computed correctly");
665    println!("   ✓ Capital requirements meet regulatory minimums");
666    println!("   ✓ Model approved for internal ratings-based approach\n");
667
668    println!("✅ Ongoing Monitoring:");
669    println!("   ✓ Quarterly performance reviews scheduled");
670    println!("   ✓ Calibration drift detection in place");
671    println!("   ✓ Model governance framework established");
672    println!("   ✓ Audit trail for all predictions maintained");
673
674    println!("\n✨ Financial risk calibration demonstration complete! ✨\n");
675}
Source

pub fn fit_transform( &mut self, scores: &Array1<f64>, labels: &Array1<usize>, ) -> Result<Array1<f64>>

Fit and transform in one step

Trait Implementations§

Source§

impl Clone for IsotonicRegression

Source§

fn clone(&self) -> IsotonicRegression

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IsotonicRegression

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for IsotonicRegression

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V