bunobee 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. bunobee-0.0.4/LICENSE +21 -0
  2. bunobee-0.0.4/PKG-INFO +45 -0
  3. bunobee-0.0.4/README.md +13 -0
  4. bunobee-0.0.4/pyproject.toml +57 -0
  5. bunobee-0.0.4/setup.cfg +4 -0
  6. bunobee-0.0.4/src/bunobee/__init__.py +4 -0
  7. bunobee-0.0.4/src/bunobee/hyper_tune.py +180 -0
  8. bunobee-0.0.4/src/bunobee/models/__init__.py +1 -0
  9. bunobee-0.0.4/src/bunobee/models/dlt.py +541 -0
  10. bunobee-0.0.4/src/bunobee/models/m5/__init__.py +0 -0
  11. bunobee-0.0.4/src/bunobee/models/m5/m5_ssp_mcmc.py +135 -0
  12. bunobee-0.0.4/src/bunobee/models/m5/m5_ssp_optim.py +423 -0
  13. bunobee-0.0.4/src/bunobee/models/ssp/__init__.py +19 -0
  14. bunobee-0.0.4/src/bunobee/models/ssp/kalman_1d.py +537 -0
  15. bunobee-0.0.4/src/bunobee/models/ssp/kalman_1d_ekf.py +455 -0
  16. bunobee-0.0.4/src/bunobee/models/ssp/kalman_1d_st.py +194 -0
  17. bunobee-0.0.4/src/bunobee/models/ssp/kalman_1d_st_ekf.py +209 -0
  18. bunobee-0.0.4/src/bunobee/models/ssp/plotting.py +249 -0
  19. bunobee-0.0.4/src/bunobee/models/ssp/posterior.py +157 -0
  20. bunobee-0.0.4/src/bunobee/models/ssp/prior.py +239 -0
  21. bunobee-0.0.4/src/bunobee/models/ssp/transforms.py +608 -0
  22. bunobee-0.0.4/src/bunobee/models/ssp/vfactory.py +73 -0
  23. bunobee-0.0.4/src/bunobee/regression.py +232 -0
  24. bunobee-0.0.4/src/bunobee/saturation.py +131 -0
  25. bunobee-0.0.4/src/bunobee/simulation/__init__.py +0 -0
  26. bunobee-0.0.4/src/bunobee/simulation/ssp.py +97 -0
  27. bunobee-0.0.4/src/bunobee/utils.py +33 -0
  28. bunobee-0.0.4/src/bunobee.egg-info/PKG-INFO +45 -0
  29. bunobee-0.0.4/src/bunobee.egg-info/SOURCES.txt +37 -0
  30. bunobee-0.0.4/src/bunobee.egg-info/dependency_links.txt +1 -0
  31. bunobee-0.0.4/src/bunobee.egg-info/requires.txt +12 -0
  32. bunobee-0.0.4/src/bunobee.egg-info/top_level.txt +1 -0
  33. bunobee-0.0.4/tests/test_dlt.py +176 -0
  34. bunobee-0.0.4/tests/test_extend_states_prior.py +217 -0
  35. bunobee-0.0.4/tests/test_kalman_filters.py +418 -0
  36. bunobee-0.0.4/tests/test_kalman_smoothers.py +246 -0
  37. bunobee-0.0.4/tests/test_saturation.py +63 -0
  38. bunobee-0.0.4/tests/test_ssp_prior.py +86 -0
  39. bunobee-0.0.4/tests/test_transforms.py +744 -0
bunobee-0.0.4/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edwin Ng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
bunobee-0.0.4/PKG-INFO ADDED
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: bunobee
3
+ Version: 0.0.4
4
+ Summary: The time-series engine for forecasting with JAX and NumPyro
5
+ Author-email: Edwin Ng <edwinnglabs@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/edwinnglabs/bunobee
8
+ Project-URL: Repository, https://github.com/edwinnglabs/bunobee
9
+ Project-URL: Issues, https://github.com/edwinnglabs/bunobee/issues
10
+ Project-URL: Documentation, https://github.com/edwinnglabs/bunobee#readme
11
+ Keywords: time-series,forecasting,bayesian,jax,numpyro,kalman-filter
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpyro>=0.20.0
22
+ Requires-Dist: jax<0.10.0,>=0.8.0
23
+ Requires-Dist: pandas>=2.0
24
+ Requires-Dist: arviz
25
+ Requires-Dist: xarray
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=8; extra == "test"
28
+ Requires-Dist: black==26.1.0; extra == "test"
29
+ Provides-Extra: notebook
30
+ Requires-Dist: ipywidgets; extra == "notebook"
31
+ Dynamic: license-file
32
+
33
+ <div align="center">
34
+ <img src="./bunobee.png" alt="Bunobee Logo" width="320" />
35
+ </div>
36
+
37
+ # Bunobee
38
+
39
+ Bunobee is an experimental Bayesian time-series library built on [JAX](https://github.com/google/jax) and [NumPyro](https://github.com/pyro-ppl/numpyro). It currently supports the following model families:
40
+
41
+ - **Damped Local Trend (DLT)** — an exponential smoothing model with a damped slope component
42
+ - **State Space Models (SSP)** — structured state space models with Kalman filtering and smoothing
43
+
44
+ **Fun fact:** "bunobee" is a word invented by my kids during their childhood to describe the action of the role they were playing.
45
+
@@ -0,0 +1,13 @@
1
+ <div align="center">
2
+ <img src="./bunobee.png" alt="Bunobee Logo" width="320" />
3
+ </div>
4
+
5
+ # Bunobee
6
+
7
+ Bunobee is an experimental Bayesian time-series library built on [JAX](https://github.com/google/jax) and [NumPyro](https://github.com/pyro-ppl/numpyro). It currently supports the following model families:
8
+
9
+ - **Damped Local Trend (DLT)** — an exponential smoothing model with a damped slope component
10
+ - **State Space Models (SSP)** — structured state space models with Kalman filtering and smoothing
11
+
12
+ **Fun fact:** "bunobee" is a word invented by my kids during their childhood to describe the action of the role they were playing.
13
+
@@ -0,0 +1,57 @@
1
+ [project]
2
+ name = "bunobee"
3
+ version = "0.0.4"
4
+ description = "The time-series engine for forecasting with JAX and NumPyro"
5
+ authors = [{name = "Edwin Ng", email = "edwinnglabs@gmail.com"}]
6
+ readme = "README.md"
7
+ requires-python = ">=3.11"
8
+ license = "MIT"
9
+ license-files = ["LICENSE"]
10
+ keywords = ["time-series", "forecasting", "bayesian", "jax", "numpyro", "kalman-filter"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Science/Research",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Topic :: Scientific/Engineering",
18
+ ]
19
+ dependencies = [
20
+ "numpyro>=0.20.0",
21
+ "jax>=0.8.0,<0.10.0",
22
+ "pandas>=2.0",
23
+ "arviz",
24
+ "xarray",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ test = [
29
+ "pytest>=8",
30
+ "black==26.1.0",
31
+ ]
32
+ notebook = [
33
+ "ipywidgets",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/edwinnglabs/bunobee"
38
+ Repository = "https://github.com/edwinnglabs/bunobee"
39
+ Issues = "https://github.com/edwinnglabs/bunobee/issues"
40
+ Documentation = "https://github.com/edwinnglabs/bunobee#readme"
41
+
42
+ [build-system]
43
+ requires = ["setuptools"]
44
+ build-backend = "setuptools.build_meta"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.black]
50
+ line-length = 120
51
+ target-version = ['py311']
52
+ include = '\.pyi?$'
53
+ extend-exclude = ""
54
+
55
+ [tool.pytest.ini_options]
56
+ log_cli = true
57
+ log_cli_level = "INFO"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from importlib.metadata import version
2
+
3
+ name = "bunobee"
4
+ __version__ = version("bunobee")
@@ -0,0 +1,180 @@
1
+ import jax
2
+ import jax.numpy as jnp
3
+ import numpy as np
4
+ from jax import vmap
5
+ import logging
6
+ import xarray as xr
7
+
8
+ from typing import Dict, Tuple
9
+
10
+ from .utils import flatten_front_dim
11
+ from .models.dlt import run_dlt_model
12
+
13
+ logger = logging.getLogger("wunku")
14
+
15
+
16
+ def slice_trend_single(trend_comp: jnp.ndarray, h: int) -> jnp.ndarray:
17
+ """Slices a single trend into overlapping windows of size h.
18
+
19
+ Args
20
+ ----
21
+ trend_comp: array representing the trend component with shape (n_steps, )
22
+ h: Size of the window to slice the trend into.
23
+
24
+ Returns
25
+ -------
26
+ A 2D array of shape (n_windows, h) where each row is a window of size h where
27
+ num_windows = n_steps - h + 1.
28
+ """
29
+ n_steps = trend_comp.shape[0]
30
+ num_windows = n_steps - h + 1
31
+
32
+ def get_window(t_idx):
33
+ return jax.lax.dynamic_slice(trend_comp, start_indices=(t_idx,), slice_sizes=(h,))
34
+
35
+ t_indices = jnp.arange(num_windows)
36
+ # (n_windows, h)
37
+ return vmap(get_window)(t_indices)
38
+
39
+
40
+ def slice_trend(trend_comp, h):
41
+ """Slices multiple trends into overlapping windows of size h.
42
+ Args
43
+ ----
44
+ trend_comp: array representing multiple trend components with shape (N_samples, n_steps)
45
+ h: Size of the window to slice the trends into.
46
+
47
+ Returns
48
+ -------
49
+ A 3D array of shape (N_samples, n_windows, h) where each row is a window of size h
50
+ and num_windows = n_steps - h + 1 for each trend.
51
+ """
52
+ # trends: shape (N_samples, T)
53
+ return vmap(lambda trend_comp: slice_trend_single(trend_comp, h))(trend_comp)
54
+
55
+
56
+ def generate_forecast_span_samples(idata: xr.Dataset, h: int) -> jnp.ndarray:
57
+ # how to do a sliding window prediction?
58
+ dlt_comp = flatten_front_dim(idata["dlt_comp"].to_numpy(), n=2)
59
+ reg_comp = flatten_front_dim(idata["reg_comp"].to_numpy(), n=2)
60
+
61
+ logger.debug("dlt_comp shape:", dlt_comp.shape)
62
+ logger.debug("reg_comp shape:", reg_comp.shape)
63
+
64
+ dlt_comp_slice = dlt_comp[:, : -(h - 1), None]
65
+ reg_comp_slice = slice_trend(reg_comp, h=h)
66
+
67
+ # (n_samples, n_windows, h)
68
+ yhat_span = dlt_comp_slice + reg_comp_slice
69
+ return yhat_span
70
+
71
+
72
+ def compute_log_likelihood(yhat_span, sigma, y):
73
+ """Computes the log likelihood of the forecasted span given the observations.
74
+
75
+ Args
76
+ ----
77
+ yhat_span: Forecasted span of shape (n_samples, n_windows, h).
78
+ sigma: Standard deviation of the noise, shape (n_samples,)
79
+ y_slice: Observations for the forecasted span, shape (n_windows, h).
80
+
81
+ Returns
82
+ -------
83
+ A 3D array of log probabilities of shape (n_samples, n_windows, h).
84
+ """
85
+ h = yhat_span.shape[-1]
86
+ # reshape for broadcasting
87
+ # (n_samples, 1, 1)
88
+ sigma_broadcast = sigma[:, None, None]
89
+ # (n_windos, h)
90
+ y_slice = slice_trend_single(y, h=h)
91
+ # (1, n_windows, h)
92
+ y_slice_broadcast = y_slice[None, :, :]
93
+
94
+ # compute squared error
95
+ sq_error = (y_slice_broadcast - yhat_span) ** 2
96
+
97
+ # compute log likelihood per (s, t, h)
98
+ log_prob = -0.5 * jnp.log(2 * jnp.pi) - jnp.log(sigma_broadcast) - 0.5 * sq_error / (sigma_broadcast**2)
99
+
100
+ return log_prob
101
+
102
+
103
+ def compute_wbic(loglk: jnp.ndarray) -> float:
104
+ """Compute Weighted Bayesian Information Criterion (WBIC) from log likelihood.
105
+ Args
106
+ ----
107
+ loglk: array-like, log likelihood per samples per obs; should be with values of shape (n_samples, n_steps, h)
108
+ """
109
+ # in original paper, they use sum but it leads to unstable scale due to different size of datasets
110
+ # we use mean instead
111
+ loglk_per_sample = jnp.nanmean(loglk, axis=(-1, -2))
112
+ # (n_steps * h, )
113
+ nobs = loglk.shape[-1] * loglk.shape[-2]
114
+ beta = 1.0 / jnp.log(nobs)
115
+ wbic = -(1.0 / beta) * jnp.nanmean(loglk_per_sample)
116
+ wbic = float(wbic)
117
+ return wbic
118
+
119
+
120
+ def run_dlt_model_and_compute_wbic(params: Tuple, data: Dict[str, np.ndarray], h: int = 12) -> float:
121
+ lev_sm, slp_sm, theta = params
122
+ y = data["y"]
123
+ x_seas = data["x_seas"]
124
+ x_glb_trend = data["x_glb_trend"]
125
+ logger.info(f"Trying lev_sm={lev_sm:.4f}, slp_sm={slp_sm:.4f}, theta={theta:.4f}")
126
+
127
+ idata = run_dlt_model(
128
+ lev_sm=lev_sm,
129
+ slp_sm=slp_sm,
130
+ theta=theta,
131
+ x_seas=x_seas,
132
+ x_glb_trend=x_glb_trend,
133
+ y=y,
134
+ )
135
+
136
+ yhat_span = generate_forecast_span_samples(idata=idata, h=h)
137
+ loglk = compute_log_likelihood(
138
+ yhat_span=yhat_span,
139
+ sigma=flatten_front_dim(idata["sigma"].to_numpy(), n=2),
140
+ y=y,
141
+ )
142
+ wbic = compute_wbic(loglk)
143
+
144
+ print(f"WBIC: {wbic:.4f}")
145
+ return wbic
146
+
147
+
148
+ def hyper_tuning_dlt_with_wbic(
149
+ data: Dict[str, np.ndarray],
150
+ # forecast horizon
151
+ h: int = 12,
152
+ n_calls: int = 15,
153
+ random_state: int = 42,
154
+ ):
155
+ print("Starting hyperparameter tuning for DLT model using WBIC...")
156
+ # print args
157
+ print(f"h: {h}, n_calls: {n_calls}, random_state: {random_state}")
158
+ # try import skopt
159
+ try:
160
+ from skopt import gp_minimize
161
+ from skopt.space import Real
162
+ except ImportError:
163
+ raise ImportError("Please install scikit-optimize to run hyperparameter tuning.")
164
+
165
+ # Define the hyperparam space
166
+ search_space = [
167
+ Real(0.0001, 0.1, prior="log-uniform", name="lev_sm"),
168
+ Real(0.001, 0.1, prior="log-uniform", name="slp_sm"),
169
+ Real(0, 1.0, prior="uniform", name="theta"),
170
+ ]
171
+
172
+ # Run Bayesian Optimization
173
+ result = gp_minimize(
174
+ func=lambda params: run_dlt_model_and_compute_wbic(params=params, data=data, h=h),
175
+ dimensions=search_space,
176
+ n_calls=n_calls,
177
+ random_state=random_state,
178
+ )
179
+
180
+ return result