tcn-ae
A temporal convolutional autoencoder for anomaly detection in multivariate time series.
This is a reference implementation of:
Markus Thill, Wolfgang Konen, Hao Wang, Thomas Bäck. Temporal convolutional autoencoder for unsupervised anomaly detection in time series. Applied Soft Computing 112 (2021), 107751.
[!WARNING] Not ready for use. The package currently contains no model implementation — it is packaging and infrastructure only, and installing it gives you nothing usable yet. The released versions exist to validate the release pipeline, not to be depended on. The TCN-AE implementation and a stable public API are still to come.
Installation
pip install tcn-ae
This installs Keras with the TensorFlow backend, matching the paper's original implementation.
Keras 3 is multi-backend, so PyTorch can be used instead:
pip install "tcn-ae[torch]"
KERAS_BACKEND=torch python your_script.py
TensorFlow is a required dependency rather than an extra, so tcn-ae[torch] installs both.
To make the backend choice permanent, set "backend": "torch" in ~/.keras/keras.json.
There is one implementation, written against the Keras 3 API — the backend is chosen at install and run time, not by a separate code path.
Documentation
https://markusthill.github.io/tcn-ae/
Citing
If you use this software, please cite the paper above. GitHub reads CITATION.cff, so the "Cite this repository" button produces BibTeX for both the software and the paper.
Development
The project uses uv for environments and packaging. In a Codespace or devcontainer everything below is already provisioned by .devcontainer/post-create.sh.
uv sync # create .venv from uv.lock, incl. the dev group
uv run prek install --hook-type pre-commit --hook-type commit-msg
The dev interpreter is pinned to Python 3.13 (.python-version). The package supports
Python 3.11+, which is what CI's matrix exercises — keep those two numbers distinct.
Everyday commands
| Task | Command |
|---|---|
| Run tests | uv run pytest |
| Tests with coverage gate | uv run pytest --cov |
| Lint | uv run ruff check . |
| Format | uv run ruff format . |
| Type check | uv run pyrefly check |
| Workflow security lint | uv run zizmor .github/workflows |
| Docs preview | uv run --group docs zensical serve |
| Docs build | uv run --group docs zensical build |
Type checking
pyrefly is the authority and gates CI and commits.
ty is also installed and can be run with
uv run ty check, but it is experimental and advisory only — it never blocks a commit or
a build, and where the two disagree, pyrefly wins.
Git hooks (prek)
Hooks are managed by prek, a drop-in replacement for
pre-commit that reads the same .pre-commit-config.yaml.
uv run prek run --all-files # run every hook against the whole repo
uv run prek auto-update # bump pinned hook revisions
On commit this runs file hygiene checks, ruff check --fix, ruff format, pyrefly,
nbstripout (strips notebook outputs), and zizmor. On commit-msg it validates the
message with commitizen.
Commit messages (commitizen)
Commit messages follow Conventional Commits and are
enforced by the commit-msg hook.
uv run cz commit # interactive, guided commit message
The commit type determines the next version: fix: → patch, feat: → minor. With
major_version_zero = true the project stays on 0.x until you deliberately go to 1.0.
cz bump writes the version to [project] version in pyproject.toml and to uv.lock
(version_provider = "uv"); the package reads it back at runtime via importlib.metadata.
Do not switch to a dynamic/hatch version, it would break cz bump.
Trusted Publishing configuration
Already configured on both registries — nothing to do for a normal release. This is the trust relationship that lets the workflows publish without any API token stored in the repo.
| Field | PyPI | TestPyPI |
|---|---|---|
| PyPI project name | tcn-ae |
tcn-ae |
| Owner | MarkusThill |
MarkusThill |
| Repository | tcn-ae |
tcn-ae |
| Workflow | release.yml |
release-testpypi.yml |
| Environment | pypi |
testpypi |
Renaming the repository, the workflow file, or the GitHub environment breaks the match and publishing fails with an OIDC error — update the publisher on the registry to match. The same table applies when setting up a fork: before a project exists, it is added under Publishing → Add a pending publisher; afterwards it lives in the project's own Publishing settings.
Cutting a release to PyPI
From a clean main that is up to date with origin:
# 1. Preview the version derived from the commits since the last tag
uv run cz bump --dry-run
# 2. Bump: updates pyproject.toml, uv.lock and CHANGELOG.md, commits, creates the v* tag
uv run cz bump
# 3. Push the commit *and* the tag
git push --follow-tags
# 4. Confirm the tag actually arrived — this is where releases usually fail silently
git ls-remote --tags origin
# 5. Watch the release run
gh run watch
Step 3 is the one that catches people: a plain git push does not push tags. The bump
commit lands, main looks correct, and nothing is ever released. If step 4 prints no tag,
push it explicitly:
git push origin "v$(uv run cz version --project)"
The tag push triggers release.yml: build → twine check →
publish to PyPI via Trusted Publishing (OIDC, no API token stored) → GitHub release with the
artifacts attached.
[!WARNING] Do not remove
annotated_tag = truefrom[tool.commitizen]. Git has two kinds of tags: annotated ones are real objects carrying a tagger, date and message, while lightweight ones are just a name pointing at a commit.git push --follow-tagspushes annotated tags only, and commitizen creates lightweight ones unless told otherwise — so without that settingcz bumpproduces a tag the push silently ignores, and no release ever runs.
If a release fails
A version number is consumed permanently on PyPI — a deleted release cannot be re-uploaded under the same version. If the run failed before anything was uploaded (a bad publisher config, a failing build), drop the tag, fix, and retag:
git push --delete origin v0.1.0
git tag -d v0.1.0
If the upload already succeeded, do not try to reuse the version — bump to a new one.
Rehearsing a release on TestPyPI
TestPyPI exists purely to prove the procedure works before it matters. Run
release-testpypi.yml manually from the Actions tab;
it exercises the same build → twine check → OIDC publish path as a real release.
It does not cover the tag trigger, cz bump, or GitHub release creation — those only run
in release.yml.
Optional, if verifying the upload is installable: an extra index is required, because
TestPyPI's own tensorflow stops at 1.0.0 and keras at 2.0.0.
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ tcn-ae
Notebooks
Notebooks in notebooks/ are executed in CI with
nbmake so the examples cannot silently rot:
uv run pytest --nbmake notebooks/
Outputs are stripped on commit by nbstripout, so keep notebooks free of large embedded
results in git.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tcn_ae-0.0.3.tar.gz.
File metadata
- Download URL: tcn_ae-0.0.3.tar.gz
- Upload date:
- Size: 151.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07870f0419f600bc63746f11594068e4e360504adbc00d2728d03a88925f2097
|
|
| MD5 |
d2b8cdcf868953d851b20162241815d0
|
|
| BLAKE2b-256 |
c11e75be5ec50c83587d1dec51de5d0b8af8c89933b9b87fcae849aa4f0786d6
|
Provenance
The following attestation bundles were made for tcn_ae-0.0.3.tar.gz:
Publisher:
release.yml on MarkusThill/tcn-ae
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tcn_ae-0.0.3.tar.gz -
Subject digest:
07870f0419f600bc63746f11594068e4e360504adbc00d2728d03a88925f2097 - Sigstore transparency entry: 2328899777
- Sigstore integration time:
-
Permalink:
MarkusThill/tcn-ae@9abcf3e8633874544d5e155bf31d4840a52f6d74 -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/MarkusThill
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9abcf3e8633874544d5e155bf31d4840a52f6d74 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tcn_ae-0.0.3-py3-none-any.whl.
File metadata
- Download URL: tcn_ae-0.0.3-py3-none-any.whl
- Upload date:
- Size: 6.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89e7864920c993d7642b07aca4dda4d3fdb0e92e77bc1a6a5c1fe25e616edb8f
|
|
| MD5 |
464e142ad5e7048ac7e6e42c072f5c63
|
|
| BLAKE2b-256 |
35dc307ae807aed78cad933e670b8510f7ba3d118c3a00a4c63d0153430b796d
|
Provenance
The following attestation bundles were made for tcn_ae-0.0.3-py3-none-any.whl:
Publisher:
release.yml on MarkusThill/tcn-ae
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tcn_ae-0.0.3-py3-none-any.whl -
Subject digest:
89e7864920c993d7642b07aca4dda4d3fdb0e92e77bc1a6a5c1fe25e616edb8f - Sigstore transparency entry: 2328900109
- Sigstore integration time:
-
Permalink:
MarkusThill/tcn-ae@9abcf3e8633874544d5e155bf31d4840a52f6d74 -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/MarkusThill
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9abcf3e8633874544d5e155bf31d4840a52f6d74 -
Trigger Event:
push
-
Statement type: