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