diff options
Diffstat (limited to 'src/plot/gnuplot_backend/pdf.rs')
-rwxr-xr-x | src/plot/gnuplot_backend/pdf.rs | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/src/plot/gnuplot_backend/pdf.rs b/src/plot/gnuplot_backend/pdf.rs new file mode 100755 index 0000000..3ee2360 --- /dev/null +++ b/src/plot/gnuplot_backend/pdf.rs @@ -0,0 +1,392 @@ +use super::*; +use crate::kde; +use crate::measurement::ValueFormatter; +use crate::report::{BenchmarkId, ComparisonData, MeasurementData, ReportContext}; +use std::process::Child; + +pub(crate) fn pdf( + id: &BenchmarkId, + context: &ReportContext, + formatter: &dyn ValueFormatter, + measurements: &MeasurementData<'_>, + size: Option<Size>, +) -> Child { + let avg_times = &measurements.avg_times; + let typical = avg_times.max(); + let mut scaled_avg_times: Vec<f64> = (avg_times as &Sample<f64>).iter().cloned().collect(); + let unit = formatter.scale_values(typical, &mut scaled_avg_times); + let scaled_avg_times = Sample::new(&scaled_avg_times); + + let mean = scaled_avg_times.mean(); + + let iter_counts = measurements.iter_counts(); + let &max_iters = iter_counts + .iter() + .max_by_key(|&&iters| iters as u64) + .unwrap(); + let exponent = (max_iters.log10() / 3.).floor() as i32 * 3; + let y_scale = 10f64.powi(-exponent); + + let y_label = if exponent == 0 { + "Iterations".to_owned() + } else { + format!("Iterations (x 10^{})", exponent) + }; + + let (xs, ys) = kde::sweep(&scaled_avg_times, KDE_POINTS, None); + let (lost, lomt, himt, hist) = avg_times.fences(); + let mut fences = [lost, lomt, himt, hist]; + let _ = formatter.scale_values(typical, &mut fences); + let [lost, lomt, himt, hist] = fences; + + let vertical = &[0., max_iters]; + let zeros = iter::repeat(0); + + let mut figure = Figure::new(); + figure + .set(Font(DEFAULT_FONT)) + .set(size.unwrap_or(SIZE)) + .configure(Axis::BottomX, |a| { + let xs_ = Sample::new(&xs); + a.set(Label(format!("Average time ({})", unit))) + .set(Range::Limits(xs_.min(), xs_.max())) + }) + .configure(Axis::LeftY, |a| { + a.set(Label(y_label)) + .set(Range::Limits(0., max_iters * y_scale)) + .set(ScaleFactor(y_scale)) + }) + .configure(Axis::RightY, |a| a.set(Label("Density (a.u.)"))) + .configure(Key, |k| { + k.set(Justification::Left) + .set(Order::SampleText) + .set(Position::Outside(Vertical::Top, Horizontal::Right)) + }) + .plot( + FilledCurve { + x: &*xs, + y1: &*ys, + y2: zeros, + }, + |c| { + c.set(Axes::BottomXRightY) + .set(DARK_BLUE) + .set(Label("PDF")) + .set(Opacity(0.25)) + }, + ) + .plot( + Lines { + x: &[mean, mean], + y: vertical, + }, + |c| { + c.set(DARK_BLUE) + .set(LINEWIDTH) + .set(LineType::Dash) + .set(Label("Mean")) + }, + ) + .plot( + Points { + x: avg_times + .iter() + .zip(scaled_avg_times.iter()) + .filter_map( + |((_, label), t)| { + if label.is_outlier() { + None + } else { + Some(t) + } + }, + ), + y: avg_times + .iter() + .zip(iter_counts.iter()) + .filter_map( + |((_, label), i)| { + if label.is_outlier() { + None + } else { + Some(i) + } + }, + ), + }, + |c| { + c.set(DARK_BLUE) + .set(Label("\"Clean\" sample")) + .set(PointType::FilledCircle) + .set(POINT_SIZE) + }, + ) + .plot( + Points { + x: avg_times + .iter() + .zip(scaled_avg_times.iter()) + .filter_map( + |((_, label), t)| { + if label.is_mild() { + Some(t) + } else { + None + } + }, + ), + y: avg_times + .iter() + .zip(iter_counts.iter()) + .filter_map( + |((_, label), i)| { + if label.is_mild() { + Some(i) + } else { + None + } + }, + ), + }, + |c| { + c.set(DARK_ORANGE) + .set(Label("Mild outliers")) + .set(POINT_SIZE) + .set(PointType::FilledCircle) + }, + ) + .plot( + Points { + x: avg_times + .iter() + .zip(scaled_avg_times.iter()) + .filter_map( + |((_, label), t)| { + if label.is_severe() { + Some(t) + } else { + None + } + }, + ), + y: avg_times + .iter() + .zip(iter_counts.iter()) + .filter_map( + |((_, label), i)| { + if label.is_severe() { + Some(i) + } else { + None + } + }, + ), + }, + |c| { + c.set(DARK_RED) + .set(Label("Severe outliers")) + .set(POINT_SIZE) + .set(PointType::FilledCircle) + }, + ) + .plot( + Lines { + x: &[lomt, lomt], + y: vertical, + }, + |c| c.set(DARK_ORANGE).set(LINEWIDTH).set(LineType::Dash), + ) + .plot( + Lines { + x: &[himt, himt], + y: vertical, + }, + |c| c.set(DARK_ORANGE).set(LINEWIDTH).set(LineType::Dash), + ) + .plot( + Lines { + x: &[lost, lost], + y: vertical, + }, + |c| c.set(DARK_RED).set(LINEWIDTH).set(LineType::Dash), + ) + .plot( + Lines { + x: &[hist, hist], + y: vertical, + }, + |c| c.set(DARK_RED).set(LINEWIDTH).set(LineType::Dash), + ); + figure.set(Title(gnuplot_escape(id.as_title()))); + + let path = context.report_path(id, "pdf.svg"); + debug_script(&path, &figure); + figure.set(Output(path)).draw().unwrap() +} + +pub(crate) fn pdf_small( + id: &BenchmarkId, + context: &ReportContext, + formatter: &dyn ValueFormatter, + measurements: &MeasurementData<'_>, + size: Option<Size>, +) -> Child { + let avg_times = &*measurements.avg_times; + let typical = avg_times.max(); + let mut scaled_avg_times: Vec<f64> = (avg_times as &Sample<f64>).iter().cloned().collect(); + let unit = formatter.scale_values(typical, &mut scaled_avg_times); + let scaled_avg_times = Sample::new(&scaled_avg_times); + let mean = scaled_avg_times.mean(); + + let (xs, ys, mean_y) = kde::sweep_and_estimate(scaled_avg_times, KDE_POINTS, None, mean); + let xs_ = Sample::new(&xs); + let ys_ = Sample::new(&ys); + + let y_limit = ys_.max() * 1.1; + let zeros = iter::repeat(0); + + let mut figure = Figure::new(); + figure + .set(Font(DEFAULT_FONT)) + .set(size.unwrap_or(SIZE)) + .configure(Axis::BottomX, |a| { + a.set(Label(format!("Average time ({})", unit))) + .set(Range::Limits(xs_.min(), xs_.max())) + }) + .configure(Axis::LeftY, |a| { + a.set(Label("Density (a.u.)")) + .set(Range::Limits(0., y_limit)) + }) + .configure(Axis::RightY, |a| a.hide()) + .configure(Key, |k| k.hide()) + .plot( + FilledCurve { + x: &*xs, + y1: &*ys, + y2: zeros, + }, + |c| { + c.set(Axes::BottomXRightY) + .set(DARK_BLUE) + .set(Label("PDF")) + .set(Opacity(0.25)) + }, + ) + .plot( + Lines { + x: &[mean, mean], + y: &[0., mean_y], + }, + |c| c.set(DARK_BLUE).set(LINEWIDTH).set(Label("Mean")), + ); + + let path = context.report_path(id, "pdf_small.svg"); + debug_script(&path, &figure); + figure.set(Output(path)).draw().unwrap() +} + +fn pdf_comparison_figure( + formatter: &dyn ValueFormatter, + measurements: &MeasurementData<'_>, + comparison: &ComparisonData, + size: Option<Size>, +) -> Figure { + let base_avg_times = Sample::new(&comparison.base_avg_times); + let typical = base_avg_times.max().max(measurements.avg_times.max()); + let mut scaled_base_avg_times: Vec<f64> = comparison.base_avg_times.clone(); + let unit = formatter.scale_values(typical, &mut scaled_base_avg_times); + let scaled_base_avg_times = Sample::new(&scaled_base_avg_times); + + let mut scaled_new_avg_times: Vec<f64> = (&measurements.avg_times as &Sample<f64>) + .iter() + .cloned() + .collect(); + let _ = formatter.scale_values(typical, &mut scaled_new_avg_times); + let scaled_new_avg_times = Sample::new(&scaled_new_avg_times); + + let base_mean = scaled_base_avg_times.mean(); + let new_mean = scaled_new_avg_times.mean(); + + let (base_xs, base_ys, base_y_mean) = + kde::sweep_and_estimate(scaled_base_avg_times, KDE_POINTS, None, base_mean); + let (xs, ys, y_mean) = + kde::sweep_and_estimate(scaled_new_avg_times, KDE_POINTS, None, new_mean); + + let zeros = iter::repeat(0); + + let mut figure = Figure::new(); + figure + .set(Font(DEFAULT_FONT)) + .set(size.unwrap_or(SIZE)) + .configure(Axis::BottomX, |a| { + a.set(Label(format!("Average time ({})", unit))) + }) + .configure(Axis::LeftY, |a| a.set(Label("Density (a.u.)"))) + .configure(Axis::RightY, |a| a.hide()) + .configure(Key, |k| { + k.set(Justification::Left) + .set(Order::SampleText) + .set(Position::Outside(Vertical::Top, Horizontal::Right)) + }) + .plot( + FilledCurve { + x: &*base_xs, + y1: &*base_ys, + y2: zeros.clone(), + }, + |c| c.set(DARK_RED).set(Label("Base PDF")).set(Opacity(0.5)), + ) + .plot( + Lines { + x: &[base_mean, base_mean], + y: &[0., base_y_mean], + }, + |c| c.set(DARK_RED).set(Label("Base Mean")).set(LINEWIDTH), + ) + .plot( + FilledCurve { + x: &*xs, + y1: &*ys, + y2: zeros, + }, + |c| c.set(DARK_BLUE).set(Label("New PDF")).set(Opacity(0.5)), + ) + .plot( + Lines { + x: &[new_mean, new_mean], + y: &[0., y_mean], + }, + |c| c.set(DARK_BLUE).set(Label("New Mean")).set(LINEWIDTH), + ); + figure +} + +pub(crate) fn pdf_comparison( + id: &BenchmarkId, + context: &ReportContext, + formatter: &dyn ValueFormatter, + measurements: &MeasurementData<'_>, + comparison: &ComparisonData, + size: Option<Size>, +) -> Child { + let mut figure = pdf_comparison_figure(formatter, measurements, comparison, size); + figure.set(Title(gnuplot_escape(id.as_title()))); + let path = context.report_path(id, "both/pdf.svg"); + debug_script(&path, &figure); + figure.set(Output(path)).draw().unwrap() +} + +pub(crate) fn pdf_comparison_small( + id: &BenchmarkId, + context: &ReportContext, + formatter: &dyn ValueFormatter, + measurements: &MeasurementData<'_>, + comparison: &ComparisonData, + size: Option<Size>, +) -> Child { + let mut figure = pdf_comparison_figure(formatter, measurements, comparison, size); + figure.configure(Key, |k| k.hide()); + let path = context.report_path(id, "relative_pdf_small.svg"); + debug_script(&path, &figure); + figure.set(Output(path)).draw().unwrap() +} |