1 use super::*;
2 use crate::measurement::ValueFormatter;
3 use crate::report::{BenchmarkId, ComparisonData, MeasurementData, ReportContext};
4 use plotters::data;
5 use plotters::style::RGBAColor;
6 use std::path::Path;
7 
pdf_comparison_figure( path: &Path, title: Option<&str>, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, comparison: &ComparisonData, size: Option<(u32, u32)>, )8 pub(crate) fn pdf_comparison_figure(
9     path: &Path,
10     title: Option<&str>,
11     formatter: &dyn ValueFormatter,
12     measurements: &MeasurementData<'_>,
13     comparison: &ComparisonData,
14     size: Option<(u32, u32)>,
15 ) {
16     let base_avg_times = Sample::new(&comparison.base_avg_times);
17     let typical = base_avg_times.max().max(measurements.avg_times.max());
18     let mut scaled_base_avg_times: Vec<f64> = comparison.base_avg_times.clone();
19     let unit = formatter.scale_values(typical, &mut scaled_base_avg_times);
20     let scaled_base_avg_times = Sample::new(&scaled_base_avg_times);
21 
22     let mut scaled_new_avg_times: Vec<f64> = (&measurements.avg_times as &Sample<f64>)
23         .iter()
24         .cloned()
25         .collect();
26     let _ = formatter.scale_values(typical, &mut scaled_new_avg_times);
27     let scaled_new_avg_times = Sample::new(&scaled_new_avg_times);
28 
29     let base_mean = scaled_base_avg_times.mean();
30     let new_mean = scaled_new_avg_times.mean();
31 
32     let (base_xs, base_ys, base_y_mean) =
33         kde::sweep_and_estimate(scaled_base_avg_times, KDE_POINTS, None, base_mean);
34     let (xs, ys, y_mean) =
35         kde::sweep_and_estimate(scaled_new_avg_times, KDE_POINTS, None, new_mean);
36 
37     let x_range = data::fitting_range(base_xs.iter().chain(xs.iter()));
38     let y_range = data::fitting_range(base_ys.iter().chain(ys.iter()));
39 
40     let size = size.unwrap_or(SIZE);
41     let root_area = SVGBackend::new(&path, (size.0 as u32, size.1 as u32)).into_drawing_area();
42 
43     let mut cb = ChartBuilder::on(&root_area);
44 
45     if let Some(title) = title {
46         cb.caption(title, (DEFAULT_FONT, 20));
47     }
48 
49     let mut chart = cb
50         .margin((5).percent())
51         .set_label_area_size(LabelAreaPosition::Left, (5).percent_width().min(60))
52         .set_label_area_size(LabelAreaPosition::Bottom, (5).percent_height().min(40))
53         .build_cartesian_2d(x_range, y_range.clone())
54         .unwrap();
55 
56     chart
57         .configure_mesh()
58         .disable_mesh()
59         .y_desc("Density (a.u.)")
60         .x_desc(format!("Average Time ({})", unit))
61         .x_label_formatter(&|&x| pretty_print_float(x, true))
62         .y_label_formatter(&|&y| pretty_print_float(y, true))
63         .x_labels(5)
64         .draw()
65         .unwrap();
66 
67     chart
68         .draw_series(AreaSeries::new(
69             base_xs.iter().zip(base_ys.iter()).map(|(x, y)| (*x, *y)),
70             y_range.start,
71             DARK_RED.mix(0.5).filled(),
72         ))
73         .unwrap()
74         .label("Base PDF")
75         .legend(|(x, y)| Rectangle::new([(x, y - 5), (x + 20, y + 5)], DARK_RED.mix(0.5).filled()));
76 
77     chart
78         .draw_series(AreaSeries::new(
79             xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)),
80             y_range.start,
81             DARK_BLUE.mix(0.5).filled(),
82         ))
83         .unwrap()
84         .label("New PDF")
85         .legend(|(x, y)| {
86             Rectangle::new([(x, y - 5), (x + 20, y + 5)], DARK_BLUE.mix(0.5).filled())
87         });
88 
89     chart
90         .draw_series(std::iter::once(PathElement::new(
91             vec![(base_mean, 0.0), (base_mean, base_y_mean)],
92             DARK_RED.filled().stroke_width(2),
93         )))
94         .unwrap()
95         .label("Base Mean")
96         .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &DARK_RED));
97 
98     chart
99         .draw_series(std::iter::once(PathElement::new(
100             vec![(new_mean, 0.0), (new_mean, y_mean)],
101             DARK_BLUE.filled().stroke_width(2),
102         )))
103         .unwrap()
104         .label("New Mean")
105         .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &DARK_BLUE));
106 
107     if title.is_some() {
108         chart.configure_series_labels().draw().unwrap();
109     }
110 }
111 
pdf_small( id: &BenchmarkId, context: &ReportContext, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, size: Option<(u32, u32)>, )112 pub(crate) fn pdf_small(
113     id: &BenchmarkId,
114     context: &ReportContext,
115     formatter: &dyn ValueFormatter,
116     measurements: &MeasurementData<'_>,
117     size: Option<(u32, u32)>,
118 ) {
119     let avg_times = &*measurements.avg_times;
120     let typical = avg_times.max();
121     let mut scaled_avg_times: Vec<f64> = (avg_times as &Sample<f64>).iter().cloned().collect();
122     let unit = formatter.scale_values(typical, &mut scaled_avg_times);
123     let scaled_avg_times = Sample::new(&scaled_avg_times);
124     let mean = scaled_avg_times.mean();
125 
126     let (xs, ys, mean_y) = kde::sweep_and_estimate(scaled_avg_times, KDE_POINTS, None, mean);
127     let xs_ = Sample::new(&xs);
128     let ys_ = Sample::new(&ys);
129 
130     let y_limit = ys_.max() * 1.1;
131 
132     let path = context.report_path(id, "pdf_small.svg");
133 
134     let size = size.unwrap_or(SIZE);
135     let root_area = SVGBackend::new(&path, (size.0 as u32, size.1 as u32)).into_drawing_area();
136 
137     let mut chart = ChartBuilder::on(&root_area)
138         .margin((5).percent())
139         .set_label_area_size(LabelAreaPosition::Left, (5).percent_width().min(60))
140         .set_label_area_size(LabelAreaPosition::Bottom, (5).percent_height().min(40))
141         .build_cartesian_2d(xs_.min()..xs_.max(), 0.0..y_limit)
142         .unwrap();
143 
144     chart
145         .configure_mesh()
146         .disable_mesh()
147         .y_desc("Density (a.u.)")
148         .x_desc(format!("Average Time ({})", unit))
149         .x_label_formatter(&|&x| pretty_print_float(x, true))
150         .y_label_formatter(&|&y| pretty_print_float(y, true))
151         .x_labels(5)
152         .draw()
153         .unwrap();
154 
155     chart
156         .draw_series(AreaSeries::new(
157             xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)),
158             0.0,
159             DARK_BLUE.mix(0.25).filled(),
160         ))
161         .unwrap();
162 
163     chart
164         .draw_series(std::iter::once(PathElement::new(
165             vec![(mean, 0.0), (mean, mean_y)],
166             DARK_BLUE.filled().stroke_width(2),
167         )))
168         .unwrap();
169 }
170 
pdf( id: &BenchmarkId, context: &ReportContext, formatter: &dyn ValueFormatter, measurements: &MeasurementData<'_>, size: Option<(u32, u32)>, )171 pub(crate) fn pdf(
172     id: &BenchmarkId,
173     context: &ReportContext,
174     formatter: &dyn ValueFormatter,
175     measurements: &MeasurementData<'_>,
176     size: Option<(u32, u32)>,
177 ) {
178     let avg_times = &measurements.avg_times;
179     let typical = avg_times.max();
180     let mut scaled_avg_times: Vec<f64> = (avg_times as &Sample<f64>).iter().cloned().collect();
181     let unit = formatter.scale_values(typical, &mut scaled_avg_times);
182     let scaled_avg_times = Sample::new(&scaled_avg_times);
183 
184     let mean = scaled_avg_times.mean();
185 
186     let iter_counts = measurements.iter_counts();
187     let &max_iters = iter_counts
188         .iter()
189         .max_by_key(|&&iters| iters as u64)
190         .unwrap();
191     let exponent = (max_iters.log10() / 3.).floor() as i32 * 3;
192     let y_scale = 10f64.powi(-exponent);
193 
194     let y_label = if exponent == 0 {
195         "Iterations".to_owned()
196     } else {
197         format!("Iterations (x 10^{})", exponent)
198     };
199 
200     let (xs, ys) = kde::sweep(scaled_avg_times, KDE_POINTS, None);
201     let (lost, lomt, himt, hist) = avg_times.fences();
202     let mut fences = [lost, lomt, himt, hist];
203     let _ = formatter.scale_values(typical, &mut fences);
204     let [lost, lomt, himt, hist] = fences;
205 
206     let path = context.report_path(id, "pdf.svg");
207 
208     let xs_ = Sample::new(&xs);
209 
210     let size = size.unwrap_or(SIZE);
211     let root_area = SVGBackend::new(&path, (size.0 as u32, size.1 as u32)).into_drawing_area();
212 
213     let range = data::fitting_range(ys.iter());
214 
215     let mut chart = ChartBuilder::on(&root_area)
216         .margin((5).percent())
217         .caption(id.as_title(), (DEFAULT_FONT, 20))
218         .set_label_area_size(LabelAreaPosition::Left, (5).percent_width().min(60))
219         .set_label_area_size(LabelAreaPosition::Right, (5).percent_width().min(60))
220         .set_label_area_size(LabelAreaPosition::Bottom, (5).percent_height().min(40))
221         .build_cartesian_2d(xs_.min()..xs_.max(), 0.0..max_iters)
222         .unwrap()
223         .set_secondary_coord(xs_.min()..xs_.max(), 0.0..range.end);
224 
225     chart
226         .configure_mesh()
227         .disable_mesh()
228         .y_desc(y_label)
229         .x_desc(format!("Average Time ({})", unit))
230         .x_label_formatter(&|&x| pretty_print_float(x, true))
231         .y_label_formatter(&|&y| pretty_print_float(y * y_scale, true))
232         .draw()
233         .unwrap();
234 
235     chart
236         .configure_secondary_axes()
237         .y_desc("Density (a.u.)")
238         .x_label_formatter(&|&x| pretty_print_float(x, true))
239         .y_label_formatter(&|&y| pretty_print_float(y, true))
240         .draw()
241         .unwrap();
242 
243     chart
244         .draw_secondary_series(AreaSeries::new(
245             xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)),
246             0.0,
247             DARK_BLUE.mix(0.5).filled(),
248         ))
249         .unwrap()
250         .label("PDF")
251         .legend(|(x, y)| {
252             Rectangle::new([(x, y - 5), (x + 20, y + 5)], DARK_BLUE.mix(0.5).filled())
253         });
254 
255     chart
256         .draw_series(std::iter::once(PathElement::new(
257             vec![(mean, 0.0), (mean, max_iters)],
258             &DARK_BLUE,
259         )))
260         .unwrap()
261         .label("Mean")
262         .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &DARK_BLUE));
263 
264     chart
265         .draw_series(vec![
266             PathElement::new(vec![(lomt, 0.0), (lomt, max_iters)], &DARK_ORANGE),
267             PathElement::new(vec![(himt, 0.0), (himt, max_iters)], &DARK_ORANGE),
268             PathElement::new(vec![(lost, 0.0), (lost, max_iters)], &DARK_RED),
269             PathElement::new(vec![(hist, 0.0), (hist, max_iters)], &DARK_RED),
270         ])
271         .unwrap();
272     use crate::stats::univariate::outliers::tukey::Label;
273 
274     let mut draw_data_point_series =
275         |filter: &dyn Fn(&Label) -> bool, color: RGBAColor, name: &str| {
276             chart
277                 .draw_series(
278                     avg_times
279                         .iter()
280                         .zip(scaled_avg_times.iter())
281                         .zip(iter_counts.iter())
282                         .filter_map(|(((_, label), t), i)| {
283                             if filter(&label) {
284                                 Some(Circle::new((*t, *i), POINT_SIZE, color.filled()))
285                             } else {
286                                 None
287                             }
288                         }),
289                 )
290                 .unwrap()
291                 .label(name)
292                 .legend(move |(x, y)| Circle::new((x + 10, y), POINT_SIZE, color.filled()));
293         };
294 
295     draw_data_point_series(
296         &|l| !l.is_outlier(),
297         DARK_BLUE.to_rgba(),
298         "\"Clean\" sample",
299     );
300     draw_data_point_series(
301         &|l| l.is_mild(),
302         RGBColor(255, 127, 0).to_rgba(),
303         "Mild outliers",
304     );
305     draw_data_point_series(&|l| l.is_severe(), DARK_RED.to_rgba(), "Severe outliers");
306     chart.configure_series_labels().draw().unwrap();
307 }
308