Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add benchmark for Prophet #140

Merged
merged 4 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/augurs-clustering/benches/dbscan.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use criterion::{criterion_group, criterion_main, Criterion};

use augurs_clustering::DbscanClusterer;
Expand Down
2 changes: 2 additions & 0 deletions crates/augurs-dtw/benches/dtw.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use itertools::Itertools;

Expand Down
2 changes: 2 additions & 0 deletions crates/augurs-ets/benches/air_passengers.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use criterion::{criterion_group, criterion_main, Criterion};
use pprof::criterion::{Output, PProfProfiler};

Expand Down
2 changes: 2 additions & 0 deletions crates/augurs-ets/benches/air_passengers_iai.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use iai::{black_box, main};

use augurs_core::Fit;
Expand Down
2 changes: 2 additions & 0 deletions crates/augurs-mstl/benches/vic_elec.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use pprof::criterion::{Output, PProfProfiler};

Expand Down
2 changes: 2 additions & 0 deletions crates/augurs-mstl/benches/vic_elec_iai.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use iai::{black_box, main};

use augurs_core::Fit;
Expand Down
10 changes: 10 additions & 0 deletions crates/augurs-prophet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ zip = { version = "2.2.0", optional = true }
augurs.workspace = true
augurs-testing.workspace = true
chrono.workspace = true
criterion.workspace = true
pretty_assertions.workspace = true
tracing-subscriber = { workspace = true, default-features = true }

[build-dependencies]
tempfile = { version = "3.13.0", optional = true }
Expand All @@ -45,6 +47,14 @@ download = ["dep:ureq", "dep:zip"]
internal-ignore-cmdstan-failure = []
serde = ["dep:serde"]

[lib]
bench = false

[[bench]]
name = "real-life"
harness = false
required-features = ["cmdstan", "compile-cmdstan"]
sd2k marked this conversation as resolved.
Show resolved Hide resolved

[[bin]]
name = "download-stan-model"
path = "src/bin/main.rs"
Expand Down
2,111 changes: 2,111 additions & 0 deletions crates/augurs-prophet/benches/real-life.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/augurs-prophet/src/prophet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
};

/// The Prophet time series forecasting model.
#[derive(Debug)]
#[derive(Debug, Clone)]
sd2k marked this conversation as resolved.
Show resolved Hide resolved
pub struct Prophet<O> {
/// Options to be used for fitting.
opts: ProphetOptions,
Expand Down
73 changes: 40 additions & 33 deletions crates/augurs-prophet/src/prophet/predict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,34 +167,27 @@ impl<O> Prophet<O> {
let cp_zipped = deltas.iter().zip(changepoints_t);
let deltas_t = cp_zipped
.cartesian_product(t)
.map(|((delta, cp_t), t)| if cp_t <= t { *delta } else { 0.0 })
.collect_vec();
// `k_t` is a contiguous array with the rate to apply at each time point.
let k_t = deltas_t
.map(|((delta, cp_t), t)| if cp_t <= t { *delta } else { 0.0 });

// Repeat each changepoint effect `n` times so we can zip it up.
let changepoints_repeated = changepoints_t
.iter()
.enumerate()
.fold(vec![k; t.len()], |mut acc, (i, delta)| {
.flat_map(|x| std::iter::repeat(*x).take(t.len()));
let indexes = (0..t.len()).cycle();
// `k_m_t` is a contiguous array where each element contains the rate and offset to
// apply at each time point.
let k_m_t = izip!(deltas_t, changepoints_repeated, indexes).fold(
vec![(k, m); t.len()],
|mut acc, (delta, cp_t, idx)| {
// Add the changepoint rate to the initial rate.
acc[i % t.len()] += *delta;
acc
});
// `m_t` is a contiguous array with the offset to apply at each time point.
let m_t = deltas_t
.iter()
.zip(
// Repeat each changepoint effect `n` times so we can zip it up.
changepoints_t
.iter()
.flat_map(|x| std::iter::repeat(*x).take(t.len())),
)
.enumerate()
.fold(vec![m; t.len()], |mut acc, (i, (delta, cp_t))| {
acc[idx].0 += delta;
// Add the changepoint offset to the initial offset where applicable.
acc[i % t.len()] += -cp_t * delta;
acc[idx].1 += -cp_t * delta;
acc
});
},
);

izip!(t, k_t, m_t).map(|(t, k, m)| t * k + m)
izip!(t, k_m_t).map(|(t, (k, m))| t * k + m)
sd2k marked this conversation as resolved.
Show resolved Hide resolved
}

fn piecewise_logistic<'a>(
Expand Down Expand Up @@ -416,9 +409,15 @@ impl<O> Prophet<O> {
.take(n_timestamps)
.collect_vec(),
};
// Use temporary buffers to avoid allocating a new Vec for each
// call to `sample_model`.
let (mut yhat, mut trend) = (
Vec::with_capacity(n_timestamps),
Vec::with_capacity(n_timestamps),
);
for i in 0..n_iterations {
for _ in 0..samples_per_iter {
let (yhat, trend) = self.sample_model(
self.sample_model(
df,
features,
params,
Expand All @@ -427,11 +426,13 @@ impl<O> Prophet<O> {
&component_columns.multiplicative,
y_scale,
i,
&mut yhat,
&mut trend,
)?;
// We have to transpose things, unfortunately.
for ((i, yhat), trend) in yhat.into_iter().enumerate().zip(trend) {
sim_values.yhat[i].push(yhat);
sim_values.trend[i].push(trend);
for ((i, yhat), trend) in yhat.iter().enumerate().zip(&trend) {
sim_values.yhat[i].push(*yhat);
sim_values.trend[i].push(*trend);
}
}
}
Expand All @@ -452,9 +453,14 @@ impl<O> Prophet<O> {
multiplicative: &[i32],
y_scale: f64,
iteration: usize,
) -> Result<(Vec<f64>, Vec<f64>), Error> {
yhat_tmp: &mut Vec<f64>,
trend_tmp: &mut Vec<f64>,
) -> Result<(), Error> {
yhat_tmp.clear();
trend_tmp.clear();
let n = df.ds.len();
let trend = self.sample_predictive_trend(df, params, changepoints_t, y_scale, iteration)?;
*trend_tmp =
self.sample_predictive_trend(df, params, changepoints_t, y_scale, iteration)?;
let beta = &params.beta;
let mut xb_a = vec![0.0; n];
for (feature, b, a) in izip!(&features.data, beta, additive) {
Expand All @@ -475,11 +481,12 @@ impl<O> Prophet<O> {
let mut rng = thread_rng();
let noise = (&mut rng).sample_iter(dist).take(n).map(|x| x * y_scale);

let yhat = izip!(&trend, &xb_a, &xb_m, noise)
.map(|(t, a, m, n)| t * (1.0 + m) + a + n)
.collect();
for yhat in izip!(trend_tmp, &xb_a, &xb_m, noise).map(|(t, a, m, n)| *t * (1.0 + m) + a + n)
{
yhat_tmp.push(yhat);
}

Ok((yhat, trend))
Ok(())
}

fn sample_predictive_trend(
Expand Down
2 changes: 1 addition & 1 deletion crates/augurs-prophet/src/prophet/prep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub(super) struct Scales {
pub(super) regressors: HashMap<String, RegressorScale>,
}

#[derive(Debug, Default, PartialEq, Eq)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(super) struct Modes {
pub(super) additive: HashSet<ComponentName>,
pub(super) multiplicative: HashSet<ComponentName>,
Expand Down
2 changes: 2 additions & 0 deletions crates/augurs-seasons/benches/periodogram.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use criterion::{criterion_group, criterion_main, Criterion};
use pprof::criterion::{Output, PProfProfiler};

Expand Down
2 changes: 2 additions & 0 deletions crates/augurs/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Integration tests for the augurs wrapper crate.

#[cfg(feature = "changepoint")]
#[test]
fn test_changepoint() {
Expand Down
Loading