Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e53a96e
wip refactor
jwdink Jul 26, 2025
392b9be
continued wip
jwdink Jul 28, 2025
02739e5
fix training tests
jwdink Jul 28, 2025
1aef1c8
allow batched group/time indexing
jwdink Jul 28, 2025
438db50
misc fixes
jwdink Jul 30, 2025
306ef7a
default to separate decay per lm predictor
jwdink Aug 1, 2025
07eb9f0
get tests working
jwdink Aug 1, 2025
e495e9d
bugfixes
jwdink Aug 1, 2025
de2168f
no py3.8 support; py3.11 support
jwdink Aug 1, 2025
0a6d656
fix typo; scale init var by mcov
jwdink Aug 1, 2025
edd1695
wip components df
jwdink Aug 7, 2025
0602ba1
get binomial filter working
jwdink Aug 13, 2025
43d5e35
Update air_quality.ipynb
jwdink Aug 13, 2025
8239483
fix predictions.to_dataframe handling
jwdink Aug 14, 2025
3eb7965
use cls instead of true closure
jwdink Aug 14, 2025
44b5dd3
speedups and bugfixes
jwdink Aug 14, 2025
b0ede4c
update trainer classes and dataloader...
jwdink Sep 9, 2025
6d2da39
misc fixes to get non-linear processes working
jwdink Sep 9, 2025
e41d63f
Update utils.py
jwdink Sep 9, 2025
be89fb9
wip
jwdink Sep 9, 2025
5b29006
use __getitems__
jwdink Sep 10, 2025
5c41e6d
Create electricity.py
jwdink Sep 12, 2025
4674dc2
subgroups
jwdink Sep 16, 2025
1ed1b52
try removing masking
jwdink Sep 16, 2025
67d8665
Update state_space.py
jwdink Sep 16, 2025
0e19e10
fix conf=None in to_dataframe
jwdink Sep 17, 2025
edb1d2c
add adaptive-measure-var functionality
jwdink Sep 17, 2025
03862c4
fix inits
jwdink Sep 17, 2025
5740ee2
fix bug in ewm
jwdink Sep 17, 2025
4def218
go back to precomputing measure-scaling
jwdink Sep 18, 2025
28c1775
Update kalman_filter.py
jwdink Sep 19, 2025
82bf6ba
use cheaper scaling method
jwdink Sep 21, 2025
bd58afd
fix nans for multivariate; add clamp for overflow
jwdink Sep 21, 2025
7d7d67a
use decay schedule for alpha
jwdink Sep 21, 2025
c85acf7
better inits for tau
jwdink Sep 21, 2025
640f7f4
Update state_space.py
jwdink Sep 22, 2025
e758825
update example notebooks
jwdink Nov 23, 2025
b96085d
fix measure_scaling when measure-cov has empties
jwdink Dec 3, 2025
1224712
honor model_mat_kwarg_name
jwdink Dec 3, 2025
8814f04
fixes to BinomialFilter._mask_mats
jwdink Dec 3, 2025
4022327
fix cov-empty-idx in adaptive_scaling
jwdink Dec 3, 2025
7651095
move to cpu
jwdink Dec 3, 2025
696b903
move process2slice to util
jwdink Dec 9, 2025
53b0f87
Add mc_sampling and FixedWhiteNoise to simplify deterministic MC
jwdink Jan 4, 2026
0a67c87
switch to pytest
jwdink Jan 9, 2026
2d35faf
install cpu version of torch for tests/docs
jwdink Jan 9, 2026
aafae31
pin pandas
jwdink Jan 22, 2026
e7e8bc3
no adaptive scaling if no measure var
jwdink Jan 25, 2026
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
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
py-version: [ '3.8', '3.9', '3.10' ]
py-version: [ '3.9', '3.10', '3.11' ]

steps:
- uses: actions/checkout@v4
Expand All @@ -22,10 +22,10 @@ jobs:
python-version: ${{ matrix.py-version }}

- name: Install torchcast
run: pip install .[tests]
run: pip install .[tests] --extra-index-url https://download.pytorch.org/whl/cpu

- name: Run tests
run: python3 -m unittest
run: pytest

release:
needs: test
Expand Down Expand Up @@ -74,7 +74,7 @@ jobs:
- name: Install dependencies
run: |
pip install awscli
pip install ".[docs]"
pip install ".[docs]" --extra-index-url https://download.pytorch.org/whl/cpu

- name: Install pandoc
uses: pandoc/actions/setup@main
Expand Down
216 changes: 139 additions & 77 deletions docs/examples/air_quality.ipynb

Large diffs are not rendered by default.

693 changes: 366 additions & 327 deletions docs/examples/electricity.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/quick_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@

# %%
pred.plot(
pred.to_dataframe(dataset_all, type='components').query("group=='Changping'"), split_dt=SPLIT_DT,
pred.to_dataframe(dataset_all, type='observed_states').query("group=='Changping'"), split_dt=SPLIT_DT,
time_colname='time', group_colname='group'
)

Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
name = "torchcast"
description = "Forecasting in PyTorch"
readme = "README.md"
requires-python = ">= 3.8"
requires-python = ">= 3.9"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
license = { file = "LICENSE" }
authors = [
Expand All @@ -20,7 +20,8 @@ dependencies = [
"numpy>=1.4",
"scipy>=1.10",
"tqdm>=4.59",
"platformdirs>=4.3"
"platformdirs>=4.3",
"pandas<3"
]
dynamic = ["version"]

Expand All @@ -35,9 +36,8 @@ dev = [
"black",
]
tests = [
"parameterized>=0.7",
"pytest>=7.0",
"filterpy>=1.4",
"pandas>=1.0"
]
docs = [
"jupytext>=1.11",
Expand Down
37 changes: 18 additions & 19 deletions tests/test_covariance.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import torch
from torchcast.covariance import Covariance
import unittest


class TestCovariance(unittest.TestCase):
torch.no_grad()
@torch.no_grad()
def test_from_log_cholesky():
module = Covariance(id='test', rank=3)

def test_from_log_cholesky(self):
module = Covariance(id='test', rank=3)
module.state_dict()['cholesky_log_diag'][:] = torch.arange(1., 3.1)
module.state_dict()['cholesky_off_diag'][:] = torch.arange(1., 3.1)

module.state_dict()['cholesky_log_diag'][:] = torch.arange(1., 3.1)
module.state_dict()['cholesky_off_diag'][:] = torch.arange(1., 3.1)
expected = torch.tensor([[7.3891, 2.7183, 5.4366],
[2.7183, 55.5982, 24.1672],
[5.4366, 24.1672, 416.4288]])
diff = (expected - module({}, num_groups=1, num_times=1)).abs()
assert (diff < .0001).all()

expected = torch.tensor([[7.3891, 2.7183, 5.4366],
[2.7183, 55.5982, 24.1672],
[5.4366, 24.1672, 416.4288]])
diff = (expected - module({}, num_groups=1, num_times=1)).abs()
self.assertTrue((diff < .0001).all())

def test_empty_idx(self):
module = torch.jit.script(Covariance(id='test', rank=3, empty_idx=[0]))
cov = module({}, num_groups=1, num_times=1)
cov = cov.squeeze()
self.assertTrue((cov[0, :] == 0).all())
self.assertTrue((cov[:, 0] == 0).all())
self.assertTrue((cov == cov.t()).all())
@torch.no_grad()
def test_empty_idx():
module = Covariance(id='test', rank=3, empty_idx=[0])
cov = module({}, num_groups=1, num_times=1)
cov = cov.squeeze()
assert (cov[0, :] == 0).all()
assert (cov[:, 0] == 0).all()
assert (cov == cov.t()).all()
101 changes: 50 additions & 51 deletions tests/test_data_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import unittest
from warnings import warn

import numpy as np
Expand All @@ -7,53 +6,53 @@
from torchcast.utils.data import TimeSeriesDataset


class TestDataUtils(unittest.TestCase):
def test_time_series_dataset(self):
values = torch.randn((3, 39, 2))

batch = TimeSeriesDataset(
values,
group_names=['one', 'two', 'three'],
start_times=[0, 0, 0],
measures=[['y1', 'y2']],
dt_unit=None
)
try:
import pandas as pd
except ImportError:
warn("Not testing TimeSeriesDataset.to_dataframe, pandas not installed.")
return
df1 = batch.to_dataframe()

df2 = pd.concat([
pd.DataFrame(values[i].numpy(), columns=batch.all_measures).assign(group=group, time=batch.times()[0])
for i, group in enumerate(batch.group_names)
])
self.assertTrue((df1 == df2).all().all())

def test_pad_x(self, num_times: int = 10):
from pandas import DataFrame
df = DataFrame({'x1': np.random.randn(num_times), 'x2': np.random.randn(num_times)})
df['y'] = 1.5 * df['x1'] + -.5 * df['x2'] + .1 * np.random.randn(num_times)
df['time'] = df.index.values
df['group'] = '1'
dataset1 = TimeSeriesDataset.from_dataframe(
dataframe=df,
group_colname='group',
time_colname='time',
dt_unit=None,
X_colnames=['x1', 'x2'],
y_colnames=['y']
)
dataset2 = TimeSeriesDataset.from_dataframe(
dataframe=df,
group_colname='group',
time_colname='time',
dt_unit=None,
X_colnames=['x1', 'x2'],
y_colnames=['y'],
pad_X=None
)
self.assertFalse(torch.isnan(dataset1.tensors[1]).any())
self.assertFalse(torch.isnan(dataset2.tensors[1]).any())
self.assertTrue((dataset1.tensors[1] == dataset2.tensors[1]).all())
def test_time_series_dataset():
values = torch.randn((3, 39, 2))

batch = TimeSeriesDataset(
values,
group_names=['one', 'two', 'three'],
start_times=[0, 0, 0],
measures=[['y1', 'y2']],
dt_unit=None
)
try:
import pandas as pd
except ImportError:
warn("Not testing TimeSeriesDataset.to_dataframe, pandas not installed.")
return
df1 = batch.to_dataframe()

df2 = pd.concat([
pd.DataFrame(values[i].numpy(), columns=batch.all_measures).assign(group=group, time=batch.times()[0])
for i, group in enumerate(batch.group_names)
])
assert (df1 == df2).all().all()


def test_pad_x(num_times: int = 10):
from pandas import DataFrame
df = DataFrame({'x1': np.random.randn(num_times), 'x2': np.random.randn(num_times)})
df['y'] = 1.5 * df['x1'] + -.5 * df['x2'] + .1 * np.random.randn(num_times)
df['time'] = df.index.values
df['group'] = '1'
dataset1 = TimeSeriesDataset.from_dataframe(
dataframe=df,
group_colname='group',
time_colname='time',
dt_unit=None,
X_colnames=['x1', 'x2'],
y_colnames=['y']
)
dataset2 = TimeSeriesDataset.from_dataframe(
dataframe=df,
group_colname='group',
time_colname='time',
dt_unit=None,
X_colnames=['x1', 'x2'],
y_colnames=['y'],
pad_X=None
)
assert not torch.isnan(dataset1.tensors[1]).any()
assert not torch.isnan(dataset2.tensors[1]).any()
assert (dataset1.tensors[1] == dataset2.tensors[1]).all()
Loading