Metadata-Version: 2.4
Name: spiral-time
Version: 0.1.0
Summary: Geometric temporal embeddings for machine learning — Archimedean spiral positional encodings for LSTMs, Transformers, and any time series model
Author: Spiral Time Authors
License: MIT
Project-URL: Homepage, https://github.com/frankajieh-ship-it/spiral-time
Project-URL: Repository, https://github.com/frankajieh-ship-it/spiral-time
Project-URL: Issues, https://github.com/frankajieh-ship-it/spiral-time/issues
Keywords: time series,forecasting,positional encoding,transformer,temporal embedding,seasonality,machine learning
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: numpy>=1.24
Provides-Extra: torch
Requires-Dist: torch>=2.0; extra == "torch"
Provides-Extra: full
Requires-Dist: torch>=2.0; extra == "full"
Requires-Dist: pandas>=2.0; extra == "full"
Requires-Dist: scikit-learn>=1.3; extra == "full"
Requires-Dist: matplotlib>=3.7; extra == "full"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: torch>=2.0; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: scikit-learn>=1.3; extra == "dev"

# spiral-time

**Geometric temporal embeddings for machine learning.**

Spiral time replaces scalar time features with a geometrically structured embedding derived from an Archimedean spiral. Every timestep is represented as a point in 2D spiral space, decomposing naturally into:

- **Angular component** `[cos(θ), sin(θ)]` — phase within the recurring cycle (seasonality)
- **Radial component** `r = z-score(θ)` — cumulative progression along the trend

In a 10-experiment ablation on US monthly retail sales, multi-period spiral time embeddings achieved **1.69% MAPE** — outperforming scalar time (9.76%) by 83% and hand-engineered sinusoidal features (4.62%) by 63%.

---

## Installation

```bash
# Core (NumPy only)
pip install spiral-time

# With PyTorch support
pip install spiral-time[torch]

# Full (torch + pandas + sklearn + matplotlib)
pip install spiral-time[full]
```

---

## Quick Start

### NumPy — works with any model

```python
import numpy as np
from spiral_time import spiral_embedding

t   = np.arange(168)                          # 168 monthly timesteps
emb = spiral_embedding(t, periods=[3, 6, 12]) # quarterly + semi-annual + annual
# emb.shape → (168, 9)

# Drop into any sklearn / XGBoost / LightGBM pipeline
from sklearn.ensemble import GradientBoostingRegressor
X = np.hstack([values.reshape(-1, 1), emb])
model = GradientBoostingRegressor().fit(X[:-12], y[:-12])
```

### LSTM / RNN

```python
from spiral_time import spiral_embedding

# Replace scalar time feature with spiral embedding
t_features = spiral_embedding(t, periods=[3, 6, 12])   # (n, 9)
# Concatenate with value sequence as LSTM input
x = np.concatenate([values[:, None], t_features], axis=1)
```

### Transformer — drop-in positional encoding

```python
import torch
from spiral_time import SpiralTimeEncoding

# Replace this:
# self.pos_enc = PositionalEncoding(d_model=128)

# With this:
self.pos_enc = SpiralTimeEncoding(
    d_model=128,
    periods=[24, 168, 720],   # daily, weekly, monthly (hourly data)
)

# Forward call unchanged:
x = self.pos_enc(x)   # (batch, seq_len, 128)
```

### Auto-detect periods from data

```python
from spiral_time.embedding import detect_periods, spiral_embedding

periods = detect_periods(y, n_periods=3)
print(periods)   # e.g. [365.2, 30.4, 7.0]

emb = spiral_embedding(t, periods=periods)
```

### Stateful embedding (no data leakage)

```python
from spiral_time import SpiralEmbedding

emb = SpiralEmbedding(periods=[3, 6, 12])
X_train = emb.fit_transform(t_train)   # fits normalisation on train set
X_test  = emb.transform(t_test)        # applies train stats to test set
```

---

## The Embedding

For a set of periods `{T₁, T₂, ..., T_K}`:

```
MTE(t) = [cos(θ₁), sin(θ₁), r₁,   cos(θ₂), sin(θ₂), r₂,   ...,   cos(θ_K), sin(θ_K), r_K]

where:
  θ_k = 2π·t / T_k
  r_k = z-score(θ_k)   ← independently normalised per period
```

Output dimension: `3K` (with radial) or `2K` (phase only).

The angular components `[cos(θ), sin(θ)]` are identical to the sinusoidal positional encodings used in Transformer architectures. The radial component `r_k` adds the trend axis — making explicit how many cycles have elapsed at each timescale.

---

## Transformer Classes

```python
from spiral_time import (
    SpiralTimeEncoding,          # fixed multi-period spiral PE
    MultiPeriodSpiralAttention,  # learned period weighting
    SpiralTransformerForecaster, # full reference implementation
)
```

### `SpiralTimeEncoding`
Drop-in for `PositionalEncoding`. Pre-computes spiral embeddings and projects to `d_model` via a learned linear layer.

### `MultiPeriodSpiralAttention`
Learns a softmax-normalised weight over a set of candidate periods. After training, inspect `period_weights` to discover which timescales the model relied on:

```python
mpa = MultiPeriodSpiralAttention(
    d_model=128,
    candidate_periods=[3, 7, 14, 30, 60, 90, 180, 365],
)
# ... train model ...
weights = torch.softmax(mpa.period_weights, dim=0)
for T, w in sorted(zip(candidate_periods, weights.tolist()), key=lambda x: -x[1]):
    print(f"T={T:>4}  weight={w:.3f}")
```

### `SpiralTransformerForecaster`
Minimal end-to-end reference implementation. Use this as a template for integrating spiral PE into PatchTST, iTransformer, Autoformer, or any existing architecture.

---

## Benchmark Results

### LSTM ablation — US Monthly Retail Sales (168 months)

| Encoding | MAPE |
|---|---|
| Scalar time | 9.76% |
| Engineered [cos, sin] | 4.62% |
| Spiral Archimedean (z-score r) | 3.19% |
| **Multi-period Spiral ×3** | **1.69%** |

### Transformer benchmark — ETTh1 / ETTm1 (OT target)

*(Run `python benchmarks/ett_benchmark.py` to reproduce)*

| Dataset | PE | pred=24 MAE | pred=48 MAE | pred=96 MAE |
|---|---|---|---|---|
| ETTh1 | No PE | — | — | — |
| ETTh1 | Sinusoidal | — | — | — |
| ETTh1 | **Spiral Time** | — | — | — |
| ETTm1 | No PE | — | — | — |
| ETTm1 | Sinusoidal | — | — | — |
| ETTm1 | **Spiral Time** | — | — | — |

*Fill in after running the benchmark — results are saved to `benchmarks/ett_results.json`.*

---

## When to Use Spiral Time

Spiral time is most effective when your data has:
- **Known or estimable dominant periods** (daily, weekly, annual, etc.)
- **A long-run trend** layered on those cycles (growth, decline, drift)
- **Multiple interacting periodicities** (the multi-period extension gives the largest gain)

It is a direct drop-in for:
- Scalar time index features (`t = 0, 1, 2, ...`)
- Hand-engineered date features (`month`, `day_of_week`, `hour`)
- Standard sinusoidal positional encodings in Transformers

---

## Applications

| Domain | Periods to use | Expected benefit |
|---|---|---|
| Energy demand (hourly) | [24, 168, 8760] | Daily + weekly + annual cycles |
| Retail sales (monthly) | [3, 6, 12] | Quarterly + semi-annual + annual |
| Financial returns (daily) | [5, 21, 63, 252] | Weekly + monthly + quarterly + annual |
| Drug dosing (hourly) | [24, 168] | Circadian + weekly schedule |
| EEG / neural signals | [band-specific] | Neural oscillation phase encoding |
| Weather (daily) | [365, 1461] | Annual + 4-year leap cycle |

---

## Theoretical Background

Spiral time is grounded in differential geometry. Each timestep maps to a point on an Archimedean spiral in 2D space:

```
tₓ(θ) = aθ · cos(θ)
tᵧ(θ) = aθ · sin(θ)
```

This framework:
- Generalises Transformer sinusoidal PE by adding an explicit trend coordinate
- Connects to Hawking–Hartle imaginary time and Kaluza–Klein extra dimensions
- Provides a geometric basis for phase resonance in attention mechanisms

See the full paper: `blog_post.md`

---

## Citation

```bibtex
@article{spiraltime2026,
  title   = {Spiral Time: A Geometric Reframing of Temporal Structure and Its Applications in Machine Learning},
  author  = {[Your Name]},
  year    = {2026},
  url     = {https://github.com/your-username/spiral-time}
}
```

---

## License

MIT
