fn fit_lines()

in resctl-bench/src/bench/iocost_tune.rs [1214:1327]


    fn fit_lines(&mut self, gran: f64, dir: DataShape) -> Result<()> {
        if self.points.len() == 0 {
            return Ok(());
        }

        let start = self.points.iter().next().unwrap().x;
        let end = self.points.iter().last().unwrap().x;
        let intvs = ((end - start) / gran).ceil() as u32 + 1;
        if intvs <= 1 {
            return Ok(());
        }
        let gran = (end - start) / (intvs - 1) as f64;

        // We want to prefer line fittings with fewer components. Discount
        // error from the previous stage. Also, make sure each line segment
        // is at least 10% of the vrate range.
        const ERROR_DISCOUNT: f64 = 0.975;
        const MIN_SEG_DIST: f64 = 10.0;

        // Start with mean flat line which is acceptable for both dirs.
        let mean = statistical::mean(&self.points.iter().map(|p| p.y).collect::<Vec<f64>>());
        let range = Self::vrange(&self.points);
        let mut best_lines = DataLines {
            range,
            left: DataPoint::new(range.0, mean),
            right: DataPoint::new(range.1, mean),
        };

        let best_error =
            RefCell::new(Self::calc_error(self.points.iter(), &best_lines) * ERROR_DISCOUNT);

        let mut try_and_pick = |fit: &(dyn Fn() -> Option<DataLines>)| -> Result<bool> {
            if prog_exiting() {
                bail!("Program exiting");
            }
            if let Some(lines) = fit() {
                if lines.left.y <= 0.0 || lines.right.y <= 0.0 {
                    return Ok(false);
                }
                match dir {
                    DataShape::Any => {}
                    DataShape::Inc => {
                        if lines.left.y > lines.right.y {
                            return Ok(false);
                        }
                    }
                    DataShape::Dec => {
                        if lines.left.y < lines.right.y {
                            return Ok(false);
                        }
                    }
                }
                let error = Self::calc_error(self.points.iter(), &lines);
                if error < *best_error.borrow() {
                    trace!(
                        "fit-best: ({:.3}, {:.3}) - ({:.3}, {:.3}) \
                         start={:.3} end={:.3} MIN_SEG_DIST={:.3}",
                        lines.left.x,
                        lines.left.y,
                        lines.right.x,
                        lines.right.y,
                        start,
                        end,
                        MIN_SEG_DIST
                    );
                    best_lines = lines;
                    best_error.replace(error);
                    return Ok(true);
                }
            }
            Ok(false)
        };

        // Try simple linear regression.
        if self.points.len() > 3 && try_and_pick(&|| Some(Self::fit_line(&self.points)))? {
            let be = *best_error.borrow();
            best_error.replace(be * ERROR_DISCOUNT);
        }

        // Try one flat line and one slope.
        let mut updated = false;
        for i in 0..intvs {
            let infl = start + i as f64 * gran;
            if infl < start + MIN_SEG_DIST || infl > end - MIN_SEG_DIST {
                continue;
            }
            updated |= try_and_pick(&|| Self::fit_slope_with_vleft(&self.points, infl))?;
            updated |= try_and_pick(&|| Self::fit_slope_with_vright(&self.points, infl))?;
        }
        if updated {
            let be = *best_error.borrow();
            best_error.replace(be * ERROR_DISCOUNT);
        }

        // Try two flat lines connected with a slope.
        for i in 0..intvs - 1 {
            let vleft = start + i as f64 * gran;
            if vleft < start + MIN_SEG_DIST {
                continue;
            }
            for j in i..intvs {
                let vright = start + j as f64 * gran;
                if vright - vleft < MIN_SEG_DIST || vright > end - MIN_SEG_DIST {
                    continue;
                }
                try_and_pick(&|| {
                    Self::fit_slope_with_vleft_and_vright(&self.points, vleft, vright)
                })?;
            }
        }

        self.lines = best_lines;
        Ok(())
    }