fn assert_linear(xy: &[(f64, f64)]) {
    let (mut xs, mut ys): (Vec<_>, Vec<_>) = xy.iter().copied().unzip();
    normalize(&mut xs);
    normalize(&mut ys);
    let xy = xs.iter().copied().zip(ys.iter().copied());

    // Linear regression: finding a and b to fit y = a + b*x.

    let mean_x = mean(&xs);
    let mean_y = mean(&ys);

    let b = {
        let mut num = 0.0;
        let mut denom = 0.0;
        for (x, y) in xy.clone() {
            num += (x - mean_x) * (y - mean_y);
            denom += (x - mean_x).powi(2);
        }
        num / denom
    };

    let a = mean_y - b * mean_x;

    let mut plot = format!("y_pred = {:.3} + {:.3} * x\n\nx     y     y_pred\n", a, b);

    let mut se = 0.0;
    let mut max_error = 0.0f64;
    for (x, y) in xy {
        let y_pred = a + b * x;
        se += (y - y_pred).powi(2);
        max_error = max_error.max((y_pred - y).abs());

        format_to!(plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred);
    }

    let rmse = (se / xs.len() as f64).sqrt();
    format_to!(plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error);

    assert!(rmse < 0.05 && max_error < 0.1 && a > -0.1, "\nLooks quadratic\n{}", plot);

    fn normalize(xs: &mut Vec<f64>) {
        let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
        xs.iter_mut().for_each(|it| *it /= max);
    }

    fn mean(xs: &[f64]) -> f64 {
        xs.iter().copied().sum::<f64>() / (xs.len() as f64)
    }
}
/// Checks that a set of measurements looks like a liner function rather than
/// like a quadratic function. Algorithm:
///
/// 1. Linearly scale input to be in [0; 1)
/// 2. Using linear regression, compute the best linear function approximating
///    the input.
/// 3. Compute RMSE and  maximal absolute error.
/// 4. Check that errors are within tolerances and that the constant term is not
///    too negative.
///
/// Ideally, we should use a proper "model selection" to directly compare
/// quadratic and linear models, but that sounds rather complicated:
///
///     https://stats.stackexchange.com/questions/21844/selecting-best-model-based-on-linear-quadratic-and-cubic-fit-of-data
Checks that a set of measurements looks like a liner function rather than
like a quadratic function. Algorithm.

1. Linearly scale input to be in [0; 1)
2. Using linear regression, compute the best linear function approximating
the input.
3. Compute RMSE and  maximal absolute error.
4. Check that errors are within tolerances and that the constant term is not
too negative.

Ideally, we should use a proper "model selection" to directly compare
quadratic and linear models, but that sounds rather complicated.


Checks that a set of measurements looks like a liner function rather than like a quadratic function . Algorithm . 1 . Linearly scale input to be in [ 0 ; 1 ) 2 . Using linear regression compute the best linear function approximating the input . 3 . Compute RMSE and maximal absolute error . 4 . Check that errors are within tolerances and that the constant term is not too negative . Ideally we should use a proper " model selection " to directly compare quadratic and linear models but that sounds rather complicated .
fn assert_linear(xy: &[(f64, f64)]) {
    let (mut xs, mut ys): (Vec<_>, Vec<_>) = xy.iter().copied().unzip();
    normalize(&mut xs);
    normalize(&mut ys);
    let xy = xs.iter().copied().zip(ys.iter().copied());
    // Linear regression: finding a and b to fit y = a + b*x.
    let mean_x = mean(&xs);
    let mean_y = mean(&ys);
    let b = {
        let mut num = 0.0;
        let mut denom = 0.0;
        for (x, y) in xy.clone() {
            num += (x - mean_x) * (y - mean_y);
            denom += (x - mean_x).powi(2);
        }
        num / denom
    };
    let a = mean_y - b * mean_x;
    let mut plot = format!("y_pred = {:.3} + {:.3} * x\n\nx     y     y_pred\n", a, b);
    let mut se = 0.0;
    let mut max_error = 0.0f64;
    for (x, y) in xy {
        let y_pred = a + b * x;
        se += (y - y_pred).powi(2);
        max_error = max_error.max((y_pred - y).abs());
        format_to!(plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred);
    }
    let rmse = (se / xs.len() as f64).sqrt();
    format_to!(plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error);
    assert!(rmse < 0.05 && max_error < 0.1 && a > -0.1, "\nLooks quadratic\n{}", plot);
    fn normalize(xs: &mut Vec<f64>) {
        let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
        xs.iter_mut().for_each(|it| *it /= max);
    }
    fn mean(xs: &[f64]) -> f64 {
        xs.iter().copied().sum::<f64>() / (xs.len() as f64)
    }
}
fn assert_linear ( xy : & [ ( f64 , f64 ) ] ) { let ( mut xs , mut ys ) : ( Vec < _ > , Vec < _ > ) = xy . iter ( ) . copied ( ) . unzip ( ) ; normalize ( & mut xs ) ; normalize ( & mut ys ) ; let xy = xs . iter ( ) . copied ( ) . zip ( ys . iter ( ) . copied ( ) ) ; // Linear regression: finding a and b to fit y = a + b*x. let mean_x = mean ( & xs ) ; let mean_y = mean ( & ys ) ; let b = { let mut num = 0.0 ; let mut denom = 0.0 ; for ( x , y ) in xy . clone ( ) { num += ( x - mean_x ) * ( y - mean_y ) ; denom += ( x - mean_x ) . powi ( 2 ) ; } num / denom } ; let a = mean_y - b * mean_x ; let mut plot = format ! ( " \n \n \n " a b ) ; let mut se = 0.0 ; let mut max_error = 0.0f64 ; for ( x , y ) in xy { let y_pred = a + b * x ; se += ( y - y_pred ) . powi ( 2 ) ; max_error = max_error . max ( ( y_pred - y ) . abs ( ) ) ; format_to ! ( plot " \n " x y y_pred ) ; } let rmse = ( se / xs . len ( ) as f64 ) . sqrt ( ) ; format_to ! ( plot " \n " rmse max_error ) ; assert ! ( rmse 0.05 max_error 0.1 a 0.1 " \n \n " plot ) ; fn normalize ( xs : & mut Vec < f64 > ) { let max = xs . iter ( ) . copied ( ) . max_by ( | a , b | a . partial_cmp ( b ) . unwrap ( ) ) . unwrap ( ) ; xs . iter_mut ( ) . for_each ( | it | * it /= max ) ; } fn mean ( xs : & [ f64 ] ) -> f64 { xs . iter ( ) . copied ( ) . sum :: < f64 > ( ) / ( xs . len ( ) as f64 ) } }
Checks that a set of measurements looks like a liner function rather than
like a quadratic function.
Checks that a set of measurements looks like a liner function rather than like a quadratic function .
xy
&[(f64, f64)]
xy
&[(f64, f64)]