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:
- A Python library (
import lzcomplexity) built on a Rust core via PyO3. - Two standalone command-line tools —
lzcomplexityandlzdistance.
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
mainbranch. Numerical outputs are equivalent up to deterministic shuffle seeding (see Differences from the C++ version).Spectral analysis (
psd, spectralentropy,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) — matchesalphabetwhen None (normalised entropy in units of symbols); pass2for bits.max_block_size(int, default −1) — shuffle block-size cap foremc/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.
- You merge conventional commits into
rust-backend. - release-please opens (and keeps updating) a "release PR" that bumps the version in
Cargo.tomlandpyproject.toml, and updatesCHANGELOG.md. - When you merge that release PR, release-please tags the release (
vX.Y.Z) and creates a GitHub release. - The
Releaseworkflow 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
lzcomplexityproject (Settings → Publishing) is keyed on the repository and the workflow filename (plus an optional environment) — not on a branch. Point its Workflow name atrelease.yml(the previous C++ setup usedwheels.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
- Lempel, A., & Ziv, J. (1976). On the complexity of finite sequences. IEEE Transactions on Information Theory, 22(1), 75–81.
- 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)
| File | Size | Uploaded | |
|---|---|---|---|
| lzcomplexity-2.0.0.tar.gz | 38.1 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| 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 logRelease 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 logRelease 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 logRelease 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 logRelease 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 logRelease 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