Runnable examples and toy applications
Install the package first. Each program below generates its own data, uses a fixed seed where randomness is involved, runs without network access and prints numerical results. Run the programs from the repository root:
python example/toy_models.py
python example/export_codebook.py
python example/tolerance_sweep.py
python example/shared_codebook.py
The CI executes these programs against an installed wheel outside the source tree. This also checks that examples use packaged public APIs.
Five toy signals
The constant and linear signals exercise zero-variance grouping. The sine wave illustrates repeated patterns; the step exercises abrupt changes; the noisy sine illustrates the tradeoff between detail and compactness. Each row reports sample count, symbol count, alphabet size and reconstruction RMSE. These counts are representation sizes, not measured byte compression ratios.
"""Five reproducible signals: run `python example/toy_models.py` after installation."""
import numpy as np
from fABBA import fABBA
def main():
t = np.linspace(0, 4 * np.pi, 240)
rng = np.random.default_rng(42)
signals = {
"constant": np.full(t.size, 3.0),
"linear trend": 2 + 0.2 * t,
"periodic": np.sin(t),
"step": np.where(t < 2 * np.pi, 0.0, 1.0),
"noisy periodic": np.sin(t) + 0.05 * rng.normal(size=t.size),
}
print(f"{'signal':18s} {'samples':>7s} {'symbols':>7s} {'alphabet':>8s} {'RMSE':>10s}")
for name, x in signals.items():
model = fABBA(tol=0.001, alpha=0.05, verbose=0)
symbols = model.fit_transform(x)
y = np.asarray(model.inverse_transform(symbols, start=x[0]))
assert y.shape == x.shape
rmse = np.sqrt(np.mean((x - y) ** 2))
print(f"{name:18s} {len(x):7d} {len(symbols):7d} {len(model.parameters.centers):8d} {rmse:10.5f}")
print(" symbols:", symbols[:80])
# Rows of centers are [segment length, increment], in original units.
assert np.isfinite(model.parameters.centers).all()
if __name__ == "__main__":
main()
Parameter sweep
This program reports polygonal and symbolic errors separately. Its final row checks the zero-tolerance limit. Do not infer a universal monotonic relationship from this single signal; see Parameters, units and error interpretation and Testing, builds and numerical contracts.
"""Measure compression-only and full-pipeline errors independently."""
import numpy as np
from fABBA import fABBA, inverse_compress
def main():
x = np.sin(np.linspace(0, 12, 300)) + 0.1 * np.cos(np.linspace(0, 37, 300))
print("tol alpha pieces alphabet polygon RMSE symbolic RMSE")
for tol, alpha in [(0.1, 0.5), (0.01, 0.1), (0.001, 0.01), (0.0, 0.0)]:
model = fABBA(tol=tol, alpha=alpha, verbose=0)
symbols = model.fit_transform(x)
polygon = np.asarray(inverse_compress(model.pieces_, start=x[0]))
decoded = np.asarray(model.inverse_transform(symbols, start=x[0]))
polygon_rmse = np.sqrt(np.mean((polygon - x) ** 2))
symbolic_rmse = np.sqrt(np.mean((decoded - x) ** 2))
print(f"{tol:<9g} {alpha:<9g} {len(symbols):6d} {len(model.parameters.centers):9d} "
f"{polygon_rmse:13.6g} {symbolic_rmse:14.6g}")
if tol == 0:
np.testing.assert_allclose(decoded, x, atol=1e-12)
# Greedy segmentation and clustering can change memberships abruptly.
# Do not assume monotonically decreasing end-to-end RMSE at every step.
if __name__ == "__main__":
main()