Neural time series forecasting in pure C++ — no Python, no runtime dependencies beyond libc.
Runs Amazon Chronos and Google TimesFM locally via ggml, with GGUF quantization for minimal memory footprint.
echo "112 118 132 129 121 135 148 148 136 119 104 118" | tr ' ' '\n' \
| ts-forecast -m chronos-t5-small.gguf -p 12
step,mean,q10,q20,q30,q40,q50,q60,q70,q80,q90,median
1,121.4,102.1,109.3,114.2,117.8,121.1,124.5,128.3,133.7,141.2,121.1
...
Every major neural time series foundation model — Chronos, TimesFM, Moirai, Lag-Llama — is Python-only. Deploying them in production means shipping a Python runtime, PyTorch, and several GB of dependencies. timeseries.cpp runs these models with:
- Zero Python at inference time
- GGUF quantized weights (Q8: ~50MB for Chronos-Small vs 185MB float)
- CPU or CUDA inference via ggml backends
- A clean C API embeddable in any application
| Model | Type | Params | Context | Status |
|---|---|---|---|---|
chronos-t5-tiny |
Encoder-decoder T5 | 8M | 512 | ✅ |
chronos-t5-mini |
Encoder-decoder T5 | 20M | 512 | ✅ |
chronos-t5-small |
Encoder-decoder T5 | 46M | 512 | ✅ |
chronos-t5-base |
Encoder-decoder T5 | 200M | 512 | ✅ |
chronos-t5-large |
Encoder-decoder T5 | 710M | 512 | ✅ |
timesfm-1.0-200m |
Decoder-only patched transformer | 200M | 512 | ✅ |
timesfm-2.0-500m |
Decoder-only patched transformer | 500M | 512 | 🚧 |
| Lag-Llama | Decoder-only LLaMA | 370M | 1024 | 🚧 |
| Moirai-1.0 | Encoder-decoder | 311M | 2048 | 🚧 |
git clone https://github.com/liodon-ai/timeseries.cpp
cd timeseries.cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)With CUDA:
cmake -B build -DTS_CUDA=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)pip install transformers torch scipy
python convert/convert_chronos.py amazon/chronos-t5-small -o chronos-t5-small.gguf
python convert/convert_timesfm.py google/timesfm-1.0-200m-pytorch -o timesfm-200m.gguf# Forecast from CSV (one value per line)
ts-forecast -m chronos-t5-small.gguf -i history.csv -p 24 -o forecast.csv
# Benchmark
ts-bench -m chronos-t5-small.gguf -p 64 -j 8
# Model info
ts-forecast -m chronos-t5-small.gguf --info#include "timeseries.h"
// Load model
struct ts_params params = ts_default_params();
params.n_threads = 8;
struct ts_context * ctx = ts_init("chronos-t5-small.gguf", params);
// Forecast
float history[] = {112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118};
struct ts_forecast_params fp = ts_default_forecast_params();
fp.prediction_length = 12;
fp.num_samples = 20;
struct ts_forecast * fc = ts_forecast_run(ctx, history, 12, fp);
printf("Next 12 steps (mean): ");
for (int t = 0; t < 12; t++) printf("%.1f ", fc->mean[t]);
// Prediction interval
printf("\n90%% interval at t=1: [%.1f, %.1f]\n",
fc->quantiles[0], // q10
fc->quantiles[8 * 12]); // q90
ts_forecast_free(fc);
ts_free(ctx);Measured on NVIDIA GB10 (Grace Blackwell, 128 GB unified memory), chronos-t5-small (46M params).
| Backend | History | Pred | Samples | Latency |
|---|---|---|---|---|
| CPU (20 cores) | 512 | 64 | 20 | 310 ms |
| CPU (20 cores) | 512 | 64 | 1 | 18 ms |
| CUDA (SM 12.1) | 512 | 64 | 20 | 42 ms |
| CUDA (SM 12.1) | 512 | 64 | 1 | 4 ms |
TimesFM (200M, single forward pass, no sampling):
| Backend | History | Pred | Latency |
|---|---|---|---|
| CPU (20 cores) | 512 | 128 | 180 ms |
| CUDA (SM 12.1) | 512 | 128 | 22 ms |
Chronos tokenizes time series as sequences of bin indices and uses a T5 encoder-decoder to forecast autoregressively. The probabilistic output comes from Monte Carlo sampling over the output distribution (default: 20 samples).
history → mean-scale → quantile bins → T5 encoder → T5 decoder → sample bins → rescale → forecast
TimesFM splits the context into non-overlapping patches and processes them with a causal decoder-only transformer. The final token produces all quantile forecasts in a single forward pass — no sampling needed.
history → normalize → patches → linear projection → transformer → output head → [q10..q90]
Models use GGUF with ts.* metadata keys:
| Key | Description |
|---|---|
ts.model_type |
"chronos" or "timesfm" |
ts.n_bins |
Chronos vocabulary size (default 4096) |
ts.d_model |
Hidden dimension |
ts.n_encoder_layers / ts.n_decoder_layers |
Layer counts |
ts.patch_size |
TimesFM input patch size |
PRs welcome. Priority areas:
- Lag-Llama support (decoder-only LLaMA variant)
- Moirai support (masked encoder for arbitrary frequencies)
- KV-cache for Chronos decoder (speeds up long predictions)
- Metal backend testing (Apple Silicon)
- Python bindings (
ctypeswrapper)
MIT