1 use std::process::Child;
2 
3 use crate::stats::bivariate::regression::Slope;
4 use criterion_plot::prelude::*;
5 
6 use super::*;
7 use crate::report::{BenchmarkId, ComparisonData, MeasurementData, ReportContext};
8 use crate::stats::bivariate::Data;
9 
10 use crate::estimate::{ConfidenceInterval, Estimate};
11 
12 use crate::measurement::ValueFormatter;
13 
regression_figure( formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, size: Option<Size>, ) -> Figure14 fn regression_figure(
15     formatter: &dyn ValueFormatter,
16     measurements: &MeasurementData<'_>,
17     size: Option<Size>,
18 ) -> Figure {
19     let slope_estimate = measurements.absolute_estimates.slope.as_ref().unwrap();
20     let slope_dist = measurements.distributions.slope.as_ref().unwrap();
21     let (lb, ub) =
22         slope_dist.confidence_interval(slope_estimate.confidence_interval.confidence_level);
23 
24     let data = &measurements.data;
25     let (max_iters, typical) = (data.x().max(), data.y().max());
26     let mut scaled_y: Vec<f64> = data.y().iter().cloned().collect();
27     let unit = formatter.scale_values(typical, &mut scaled_y);
28     let scaled_y = Sample::new(&scaled_y);
29 
30     let point_estimate = Slope::fit(&measurements.data).0;
31     let mut scaled_points = [point_estimate * max_iters, lb * max_iters, ub * max_iters];
32     let _ = formatter.scale_values(typical, &mut scaled_points);
33     let [point, lb, ub] = scaled_points;
34 
35     let exponent = (max_iters.log10() / 3.).floor() as i32 * 3;
36     let x_scale = 10f64.powi(-exponent);
37 
38     let x_label = if exponent == 0 {
39         "Iterations".to_owned()
40     } else {
41         format!("Iterations (x 10^{})", exponent)
42     };
43 
44     let mut figure = Figure::new();
45     figure
46         .set(Font(DEFAULT_FONT))
47         .set(size.unwrap_or(SIZE))
48         .configure(Axis::BottomX, |a| {
49             a.configure(Grid::Major, |g| g.show())
50                 .set(Label(x_label))
51                 .set(ScaleFactor(x_scale))
52         })
53         .configure(Axis::LeftY, |a| {
54             a.configure(Grid::Major, |g| g.show())
55                 .set(Label(format!("Total sample time ({})", unit)))
56         })
57         .plot(
58             Points {
59                 x: data.x().as_ref(),
60                 y: scaled_y.as_ref(),
61             },
62             |c| {
63                 c.set(DARK_BLUE)
64                     .set(Label("Sample"))
65                     .set(PointSize(0.5))
66                     .set(PointType::FilledCircle)
67             },
68         )
69         .plot(
70             Lines {
71                 x: &[0., max_iters],
72                 y: &[0., point],
73             },
74             |c| {
75                 c.set(DARK_BLUE)
76                     .set(LINEWIDTH)
77                     .set(Label("Linear regression"))
78                     .set(LineType::Solid)
79             },
80         )
81         .plot(
82             FilledCurve {
83                 x: &[0., max_iters],
84                 y1: &[0., lb],
85                 y2: &[0., ub],
86             },
87             |c| {
88                 c.set(DARK_BLUE)
89                     .set(Label("Confidence interval"))
90                     .set(Opacity(0.25))
91             },
92         );
93     figure
94 }
95 
regression( id: &BenchmarkId, context: &ReportContext, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, size: Option<Size>, ) -> Child96 pub(crate) fn regression(
97     id: &BenchmarkId,
98     context: &ReportContext,
99     formatter: &dyn ValueFormatter,
100     measurements: &MeasurementData<'_>,
101     size: Option<Size>,
102 ) -> Child {
103     let mut figure = regression_figure(formatter, measurements, size);
104     figure.set(Title(gnuplot_escape(id.as_title())));
105     figure.configure(Key, |k| {
106         k.set(Justification::Left)
107             .set(Order::SampleText)
108             .set(Position::Inside(Vertical::Top, Horizontal::Left))
109     });
110 
111     let path = context.report_path(id, "regression.svg");
112     debug_script(&path, &figure);
113     figure.set(Output(path)).draw().unwrap()
114 }
115 
regression_small( id: &BenchmarkId, context: &ReportContext, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, size: Option<Size>, ) -> Child116 pub(crate) fn regression_small(
117     id: &BenchmarkId,
118     context: &ReportContext,
119     formatter: &dyn ValueFormatter,
120     measurements: &MeasurementData<'_>,
121     size: Option<Size>,
122 ) -> Child {
123     let mut figure = regression_figure(formatter, measurements, size);
124     figure.configure(Key, |k| k.hide());
125 
126     let path = context.report_path(id, "regression_small.svg");
127     debug_script(&path, &figure);
128     figure.set(Output(path)).draw().unwrap()
129 }
130 
regression_comparison_figure( formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, comparison: &ComparisonData, base_data: &Data<'_, f64, f64>, size: Option<Size>, ) -> Figure131 fn regression_comparison_figure(
132     formatter: &dyn ValueFormatter,
133     measurements: &MeasurementData<'_>,
134     comparison: &ComparisonData,
135     base_data: &Data<'_, f64, f64>,
136     size: Option<Size>,
137 ) -> Figure {
138     let data = &measurements.data;
139     let max_iters = base_data.x().max().max(data.x().max());
140     let typical = base_data.y().max().max(data.y().max());
141 
142     let exponent = (max_iters.log10() / 3.).floor() as i32 * 3;
143     let x_scale = 10f64.powi(-exponent);
144 
145     let x_label = if exponent == 0 {
146         "Iterations".to_owned()
147     } else {
148         format!("Iterations (x 10^{})", exponent)
149     };
150 
151     let Estimate {
152         confidence_interval:
153             ConfidenceInterval {
154                 lower_bound: base_lb,
155                 upper_bound: base_ub,
156                 ..
157             },
158         point_estimate: base_point,
159         ..
160     } = comparison.base_estimates.slope.as_ref().unwrap();
161 
162     let Estimate {
163         confidence_interval:
164             ConfidenceInterval {
165                 lower_bound: lb,
166                 upper_bound: ub,
167                 ..
168             },
169         point_estimate: point,
170         ..
171     } = measurements.absolute_estimates.slope.as_ref().unwrap();
172 
173     let mut points = [
174         base_lb * max_iters,
175         base_point * max_iters,
176         base_ub * max_iters,
177         lb * max_iters,
178         point * max_iters,
179         ub * max_iters,
180     ];
181     let unit = formatter.scale_values(typical, &mut points);
182     let [base_lb, base_point, base_ub, lb, point, ub] = points;
183 
184     let mut figure = Figure::new();
185     figure
186         .set(Font(DEFAULT_FONT))
187         .set(size.unwrap_or(SIZE))
188         .configure(Axis::BottomX, |a| {
189             a.configure(Grid::Major, |g| g.show())
190                 .set(Label(x_label))
191                 .set(ScaleFactor(x_scale))
192         })
193         .configure(Axis::LeftY, |a| {
194             a.configure(Grid::Major, |g| g.show())
195                 .set(Label(format!("Total sample time ({})", unit)))
196         })
197         .configure(Key, |k| {
198             k.set(Justification::Left)
199                 .set(Order::SampleText)
200                 .set(Position::Inside(Vertical::Top, Horizontal::Left))
201         })
202         .plot(
203             FilledCurve {
204                 x: &[0., max_iters],
205                 y1: &[0., base_lb],
206                 y2: &[0., base_ub],
207             },
208             |c| c.set(DARK_RED).set(Opacity(0.25)),
209         )
210         .plot(
211             FilledCurve {
212                 x: &[0., max_iters],
213                 y1: &[0., lb],
214                 y2: &[0., ub],
215             },
216             |c| c.set(DARK_BLUE).set(Opacity(0.25)),
217         )
218         .plot(
219             Lines {
220                 x: &[0., max_iters],
221                 y: &[0., base_point],
222             },
223             |c| {
224                 c.set(DARK_RED)
225                     .set(LINEWIDTH)
226                     .set(Label("Base sample"))
227                     .set(LineType::Solid)
228             },
229         )
230         .plot(
231             Lines {
232                 x: &[0., max_iters],
233                 y: &[0., point],
234             },
235             |c| {
236                 c.set(DARK_BLUE)
237                     .set(LINEWIDTH)
238                     .set(Label("New sample"))
239                     .set(LineType::Solid)
240             },
241         );
242     figure
243 }
244 
regression_comparison( id: &BenchmarkId, context: &ReportContext, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, comparison: &ComparisonData, base_data: &Data<'_, f64, f64>, size: Option<Size>, ) -> Child245 pub(crate) fn regression_comparison(
246     id: &BenchmarkId,
247     context: &ReportContext,
248     formatter: &dyn ValueFormatter,
249     measurements: &MeasurementData<'_>,
250     comparison: &ComparisonData,
251     base_data: &Data<'_, f64, f64>,
252     size: Option<Size>,
253 ) -> Child {
254     let mut figure =
255         regression_comparison_figure(formatter, measurements, comparison, base_data, size);
256     figure.set(Title(gnuplot_escape(id.as_title())));
257 
258     let path = context.report_path(id, "both/regression.svg");
259     debug_script(&path, &figure);
260     figure.set(Output(path)).draw().unwrap()
261 }
262 
regression_comparison_small( id: &BenchmarkId, context: &ReportContext, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, comparison: &ComparisonData, base_data: &Data<'_, f64, f64>, size: Option<Size>, ) -> Child263 pub(crate) fn regression_comparison_small(
264     id: &BenchmarkId,
265     context: &ReportContext,
266     formatter: &dyn ValueFormatter,
267     measurements: &MeasurementData<'_>,
268     comparison: &ComparisonData,
269     base_data: &Data<'_, f64, f64>,
270     size: Option<Size>,
271 ) -> Child {
272     let mut figure =
273         regression_comparison_figure(formatter, measurements, comparison, base_data, size);
274     figure.configure(Key, |k| k.hide());
275 
276     let path = context.report_path(id, "relative_regression_small.svg");
277     debug_script(&path, &figure);
278     figure.set(Output(path)).draw().unwrap()
279 }
280