Skip to main content

lzcomplexity

LZ76-based complexity analysis for symbolic sequences and time-series.

MIT Python Rust

Documentation

lzcomplexity computes information-theoretic measures of symbolic sequences using Lempel–Ziv 76 (LZ76) factorization [1]. The LZ76 complexity c(S) is the minimum number of factors needed to represent a sequence, where each factor is either a new symbol or the longest previously-seen substring. From c(S) you get a non-parametric entropy-rate estimator — h ≈ c(S)·log_k(n)/n — that converges to the true entropy rate of ergodic sources [2].

The library ships two things:

  1. A Python library (import lzcomplexity) built on a Rust core via PyO3.
  2. Two standalone command-line tools — lzcomplexity and lzdistance.

The core is implemented in Rust; the two front-ends share it. Common applications: neuroscience time-series, DNA analysis, anomaly detection, structural pattern analysis.

This is the Rust implementation, the current production backend. The original C++/nanobind implementation lives on the main branch. Numerical outputs are equivalent up to deterministic shuffle seeding (see Differences from the C++ version).

Spectral analysis (psd, spectral entropy, semc) has been removed from this library and now lives in a separate package.


Python library

Install

You need Python ≥ 3.9. From PyPI:

pip install lzcomplexity

Or from a clone of this repository (requires a Rust toolchain):

pip install .

pip invokes maturin, which compiles the Rust workspace, produces a wheel, and installs it. No CMake, no submodules, no C++ toolchain.

Development install

python3 -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop --release

Re-run maturin develop --release after Rust changes.

Quick start

import lzcomplexity as lz

# Complexity + factor boundaries — factor i spans [factors[i], factors[i+1])
complexity, factors = lz.factorization("banana")
# → (3, [0, 1, 2, 3, 7])

# Normalised entropy density / entropy-rate estimator — `h`
lz.h("01010101")
# → 0.75

# Effective measure complexity: the value and the terms that sum to it
emc_value, summands = lz.emc("01001010101101010101110101010101010000100101011")

# Everything in one call (returns a dict)
full = lz.lz76("ABRACADABRA")
full["complexity"], full["h"], full["factors"]
full["emc"]     # {"value", "summands", "max_block_size", "multi_information"}
full["extras"]  # {"rajski_distance", "redundancy", "fh_uncertainty", ...}

# Normalized information distance (NID) between two sequences
lz.nid("ABRACADABRA", "ABRACADABRZ")     # → small, similar
lz.nid("ABRACADABRA", "ZYXWVUTSRQP")     # → large, dissimilar

Public API

Symbol Signature Returns
lz.factorization(seq, ...) complexity + factor boundaries (int, list[int])
lz.h(seq, ...) normalised entropy density (entropy rate) float
lz.emc(seq, ...) effective measure complexity (float, list[float]) — (value, summands)
lz.nid(seq1, seq2, ...) normalised information distance float
lz.lz76(seq, ...) the full analysis dict (see below)

lz.lz76(...) returns a dict with keys: complexity, h, factors, emc (a nested dict), epsilon, factors_stddev, normal_error, poison_error, extras (a nested dict).

Common keyword arguments:

  • partitions (int, default 1) — suffix-array partition count; performance knob, no effect on results.
  • alphabet (int | None, default None) — auto-detect (distinct symbols, min 2) when None.
  • log_base (int | None, default None) — matches alphabet when None (normalised entropy in units of symbols); pass 2 for bits.
  • max_block_size (int, default −1) — shuffle block-size cap for emc/lz76; −1 auto-selects.
  • jobs (int, default 0) — reserved; currently ignored (rayon manages its pool).

Run help(lzcomplexity.<name>) for full per-function docs. Type stubs (__init__.pyi) ship with the package.

Accepted input types

Every sequence-accepting function accepts any of:

  • str — symbols taken from the string bytes directly.
  • bytes — raw byte sequence.
  • list[str] — concatenated as-is (e.g. ["A","C","G","T"]).
  • list[int] — each element becomes its decimal string, concatenated, so [0, 1, 10] → "0110".
  • Any iterable of ints — covers NumPy arrays; same conversion as list[int].

Standalone binaries

Two command-line tools are built from the same Rust core.

Build them from a clone:

cargo build --release
# → target/release/lzcomplexity   and   target/release/lzdistance

Prebuilt binaries for Linux/macOS/Windows are attached to each GitHub release.

lzcomplexity

Reads a sequence file and writes a JSON report with the LZ76 complexity, entropy density, and random-shuffle effective measure complexity.

lzcomplexity input.txt                 # → input.lz76.json
lzcomplexity input.txt -m -d           # multi-line + distance between consecutive lines
lzcomplexity input.txt -n              # entropy density only
lzcomplexity input.txt -a 4 -l 2       # explicit alphabet / log base

Key flags: -a/--alphabet, -l/--log-base, -p/--partitions, -m/--multi-line, -d/--dlz, -n/--entropy-density, -f/--factors <file>, -F/--format <fmt>, -o/--output, -v/--verbose, -V/--version.

lzdistance

Computes pairwise information-distance and shuffle-distance matrices between one or two data sources (files or directories).

lzdistance sequences.txt               # self-distance matrix (first_dim × first_dim)
lzdistance A.txt B.txt                  # cross matrix
lzdistance genomes/ -a                  # a directory of files, DNA complement-aware
lzdistance A.txt B.txt -g 5             # also emit the directed graph

Key flags: -a/--adn (DNA), -b/--binary, -r/--reverse, -y/--trajectory, -g/--get-direction [threshold], -I/--first-format, -S/--second-format, -i/--first #:#, -s/--second #:#, -p/--partitions, -o/--output, -L/--logs, -v/--version.

Input formats

Both tools auto-detect the format (or accept -F/-I/-S): raw text and binary, CSV/TSV, PBM/PGM images (P1/P2/P4/P5), and FASTA/DNA/RNA (including .gz). Use -m (or a directory) to treat each line/file as a separate sequence.


Contributing & releases

This repo uses Conventional Commits together with release-please so that versioning and PyPI publishing happen automatically — you never bump a version or push a tag by hand.

How to commit

Prefix every commit title with a type:

Prefix Use for Version effect (the project is 1.x)
feat: a new feature bumps the minor (1.0.0 → 1.1.0)
fix: a bug fix bumps the patch (1.0.0 → 1.0.1)
perf: a performance improvement patch
refactor:, docs:, build:, ci:, test:, chore: everything else no release on its own

Add ! after the type (e.g. feat!:) or a BREAKING CHANGE: footer to force a major bump (1.x → 2.0.0). Before 1.0 a breaking change only bumped the minor; that is no longer the case.

Keep commit messages ASCII-only. release-please failed to parse a message containing Unicode maths symbols and silently dropped it from the changelog.

feat: add spectral-free NID batch mode
fix: correct off-by-one in PGM reader
docs: document the lzdistance directed graph

What happens automatically

The Python package is released from the rust-backend branch; the C++ main branch is kept only as a verification reference and is not published.

  1. You merge conventional commits into rust-backend.
  2. release-please opens (and keeps updating) a "release PR" that bumps the version in Cargo.toml and pyproject.toml, and updates CHANGELOG.md.
  3. When you merge that release PR, release-please tags the release (vX.Y.Z) and creates a GitHub release.
  4. The Release workflow then builds the wheels with maturin, publishes them to PyPI via OIDC trusted publishing (no token needed), and attaches the standalone binaries to the GitHub release.

So the entire flow is: write conventional commits → merge the release PR → PyPI updates itself.

One-time setup on PyPI: the trusted publisher for the lzcomplexity project (Settings → Publishing) is keyed on the repository and the workflow filename (plus an optional environment) — not on a branch. Point its Workflow name at release.yml (the previous C++ setup used wheels.yml). No API token or secret is stored in the repo.

CI

Every push and PR runs CI: cargo fmt --check, cargo clippy -D warnings, cargo test, a smoke test of both standalone binaries, and a wheel build + Python API smoke test on Linux/macOS/Windows.


Repository layout

crates/
├── lzcomplexity-core/   Rust crate: algorithms (LZ76, suffix array, LPF,
│                        shuffle, metrics). No Python or CLI types.
├── lzcomplexity-py/     PyO3 bindings → the `lzcomplexity` Python extension.
└── lzcomplexity-cli/    The `lzcomplexity` and `lzdistance` binaries.
python/lzcomplexity/     Python package skin: __init__.py, __init__.pyi, py.typed.
pyproject.toml           Maturin build config.

cargo test --workspace exercises the algorithmic core.


Differences from the C++ version

Aspect C++ (main branch) Rust (this branch)
Build system CMake + nanobind Cargo + maturin
Suffix array CaPS (custom parallel) comparison sort below 2048 bytes, cdivsufsort above, + Kasai LCP
Parallelism OpenMP / TBB / Cilk rayon
Shuffle RNG std::mt19937 (time-seeded) ChaCha8 (seeded from input → deterministic)
Spectral analysis included (psd, entropy, semc) removed (moved to a separate package)
Python surface many names, nid/rid, class exports factorization, h, emc, nid, lz76

Same input ⇒ same outputs across Rust and C++ within float tolerance for the deterministic measures (factorization, factors, entropy density, information distance) — verified by differential testing. Shuffle-based metrics (emc) differ only in that Rust is reproducible run-to-run.


References

  1. Lempel, A., & Ziv, J. (1976). On the complexity of finite sequences. IEEE Transactions on Information Theory, 22(1), 75–81.
  2. Kontoyiannis, I., Algoet, P. H., Suhov, Y. M., & Wyner, A. J. (1998). Nonparametric entropy estimation for stationary processes and random fields. IEEE Transactions on Information Theory, 44(3), 1319–1327.

Authors

  • Efrén Aragón Pérez — principal author and creator of lzcomplexity. The library exists because of him; he wrote the original implementation this work descends from.
  • Daniel Estévez Moya — the Rust rewrite that is the current production backend.
  • Ernesto Estévez Rams — from the research lineage of the original C++ implementation.

Citation

@software{lzcomplexity_2026,
  title   = {lzcomplexity: LZ76 complexity, entropy rate and information distance
             for symbolic sequences},
  author  = {Arag{\'o}n P{\'e}rez, Efr{\'e}n and
             Est{\'e}vez Moya, Daniel and
             Est{\'e}vez Rams, Ernesto},
  url     = {https://github.com/pleros-ai/lzcomplexity},
  version = {1.0.0},
  year    = {2026}
}

License

MIT — see LICENSE.

Metadata

Release files for lzcomplexity 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lzcomplexity 2.0.0
File Size Uploaded
lzcomplexity-2.0.0.tar.gz 38.1 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for lzcomplexity 2.0.0
File
lzcomplexity-2.0.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
lzcomplexity-2.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
lzcomplexity-2.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
lzcomplexity-2.0.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
lzcomplexity-2.0.0-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details

Total release size: 1.4 MB

Release files / lzcomplexity-2.0.0.tar.gz

Download URL lzcomplexity-2.0.0.tar.gz
Size 38.1 kB
Tags Source
SHA-256 checksum
How to use checksums
0da1a3d27df0f43d8cbd4a8e36b55f2332f32cbb8a0a97ac5a4c6f68db515b5d
BLAKE2b-256 checksum
How to use checksums
24be11e795a51d2003220738f4ef79ec051dcfdaee20568d66f3f4f3601e37bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / lzcomplexity-2.0.0-cp39-abi3-win_amd64.whl

Download URL lzcomplexity-2.0.0-cp39-abi3-win_amd64.whl
Size 206.4 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
b37311aeca334cb2bd44fbb89395a2a61fd23f6094da93e414c82dbcd475946e
BLAKE2b-256 checksum
How to use checksums
68f2b324f558fe16ec652509302236114f720fee3ff7ec97ab483f36747808f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / lzcomplexity-2.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL lzcomplexity-2.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 304.5 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
a5a7bb6a403bc72c5cb200a303e334bfc96bf219d7a7e8f495bb6c945b030b77
BLAKE2b-256 checksum
How to use checksums
2f8879ccc9dabc1a5fc3863a8f8ec00d139d1fb21e0d904072ee3d8e3dc37bbf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / lzcomplexity-2.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL lzcomplexity-2.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 290.5 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
f3b7b6346d6461f615c104c1dab0765b1520412d91c357d8700a44d52aee4509
BLAKE2b-256 checksum
How to use checksums
e341a626c2cb2a9b8fa2c3c3e20d1dbdf06eb2a6b26f1679661ed1530edf7b81
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / lzcomplexity-2.0.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL lzcomplexity-2.0.0-cp39-abi3-macosx_11_0_arm64.whl
Size 267.1 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
750f4450a148b22fb125563bec677d0c1bc1e4520b1755ef7cde4932d49a6081
BLAKE2b-256 checksum
How to use checksums
8931874b33742e373f21dbd36a84752a1879003edf3cdc7e59482b93af7875c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / lzcomplexity-2.0.0-cp39-abi3-macosx_10_12_x86_64.whl

Download URL lzcomplexity-2.0.0-cp39-abi3-macosx_10_12_x86_64.whl
Size 278.6 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
58bb8f34a40585d07732623b21d2cfc93ee8f4c8a40482167ff949db3f9b47d5
BLAKE2b-256 checksum
How to use checksums
035487603c4a3325eaf1b7aae2b14c0ed410a65e12d4a17f4be7d0f77553a7df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.0 This release

6 release files

1.0.2

6 release files

1.0.1

6 release files

1.0.0

6 release files

0.13.0

6 release files

0.12.0

6 release files

0.11.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page