Skip to main content

SignalMap

Unsupervised condition monitoring for any recorded signal — with verdicts you can check without trusting us.

License Python Status

You give it healthy data. It learns what healthy looks like and flags deviations. No fault labels, no per-sensor tuning. Vibration, acoustics, current — anything you can record.

The part we care most about: every verdict it produces is a signed receipt that a stranger can verify offline with a script that imports nothing from this project. And where the method cannot honestly decide, it says REFUSED rather than guessing.

Install

python3 -m pip install 'signalmap[all]'   # quote the brackets so zsh does not glob them
signalmap plugins                         # confirm the install

[distill] alone is enough for distill / fit / monitor on .npy and .csv banks. Parquet needs pyarrow, which [all] includes.

Two commands

signalmap fit     --dataset healthy.parquet --healthy-label normal --out detector.pt
signalmap monitor --source replay --dataset live.parquet --detector detector.pt

Nothing is labelled as a fault anywhere in that flow. examples/ reproduces it end to end on public bearing data.

What it actually does, and where it stops

On CWRU bearing data, fitting on 945 healthy frames and monitoring 1183 frames catches 238/238 faults with 2/945 false alarms, fully unsupervised.

That number does not transfer, and we measured it rather than assuming it. On nine domains under one frozen recipe — MIMII, Paderborn, MAFAULDA and two constructed controls — the shipped alarm's gap between faulty and healthy is not above zero in eight of them, and in six it never fires at all.

Worse, the direction is not even stable. On MIMII valve and Paderborn phase current the detector ranks anomalies as more normal than normal (AUC 0.2786 and 0.3172, bootstrap CI clear of 0.5), while on MIMII slider it ranks them the expected way (0.8254). There is a theorem behind that, and it applies to any detector that sees only healthy data:

The score is a function of the healthy distribution alone. Hold the fitted detector fixed, vary only the never-observed anomaly distribution, and the AUC attains every value in [0, 1].

So "far from healthy means faulty" is an assumption, not a result. Read the study → — preregistered before any data was touched, nine domains, signed receipts, ten amendments including our own errors.

What changed because of it

The detector no longer assumes the direction. It starts out refusing:

det = DistilledDetector.fit(spec, healthy_windows)
det.decide(w).verdict            # 'REFUSED' — direction unknown

v = det.calibrate_direction(anchor_windows, labels, groups=recording_ids)
v.sign, v.ci_lo, v.ci_hi         # +1 or -1, only when the CI clears 0.5
det.decide(w).verdict            # now 'ALARM' or 'QUIET'

A handful of labelled anchors settles the direction. Without them, REFUSED is the correct answer, and groups makes the bootstrap resample recordings rather than windows — twenty windows cut from one signal are one observation.

Verdicts you can check without us

Every verdict-producing command writes a signed JSON receipt: the claim, the verdict (INCLUDED / EXCLUDED / PASS / REFUSED), the evidence, the sha256 of every input, and an Ed25519 signature. The signing key stays on your machine; only the public key travels.

The verifier is one file that imports nothing from signalmap — stdlib plus cryptography:

signalmap corpus --out receipts/     # the shipped verdicts

curl -sO https://raw.githubusercontent.com/MrPredic/signalmap/main/tools/verify_receipt.py
pip install cryptography
python3 verify_receipt.py receipts/cwru_rqa.receipt.json
# PASS — verdict INCLUDED, integrity only (NOT authenticity)

python3 verify_receipt.py receipts/cwru_rqa.receipt.json --pubkey <hex>
# PASS — verdict INCLUDED, authentic (pinned key)

Flip one byte in a receipt and verification fails. That does not make a verdict true — it makes it attributable and tamper-evident. Without --pubkey you get integrity only, and the tool says so rather than letting you misread it.

The verifier is hardened against 23 concrete attacks on the gap between the bytes that get signed and the fields a human reads: duplicate JSON keys, NaN/Infinity literals, integers past 2⁵³, unpaired surrogates, non-UTF-8 input, and a pinned-key check that used to compare transcriptions rather than key bytes.

distill: features chosen with a receipt

signalmap distill --bank recordings/ --label-by prefix --out artifacts/spec.json

Searches a compositional feature grammar for the few programs that separate your recordings, under a capacity gate (budget = C × n_recordings). Every run emits a gauntlet receipt: leakage-free nested LOGO accuracy, a group-permutation p-value, a label-shuffle null, and cost per window.

Premium families (--premium rqa,coherence) run as opt-in challengers against the distilled base and enter spec.json only on a CI-solid win. Of the eight preregistered verdicts shipped in this repo, six are exclusions — the mechanism refuses far more often than it admits.

More

  • study/ — the sign-identifiability study: preregistration, theory, receipts, and the bank manifests with the sha256 of all 7623 recordings.
  • examples/ — reproduce the CWRU result and the acoustic and electrical recipes.
  • signalmap plugins — sources, sinks, featurizers and models are all pluggable.
  • Model artifacts (.pt) are a trust boundary: loaded with weights_only=True, arbitrary pickle objects rejected.

Experimental work — cross-modal discovery, compositional search, capture adapters for salvaged hardware — lives behind its own flags and is labelled as experimental where it appears. Nothing on this page depends on it.

Contributing

Bug reports and reproductions are the most useful contributions: if a verdict on your data looks wrong, the receipt makes it checkable, and that is exactly the kind of issue we want. Run pytest -q before opening a PR.

License

Apache-2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

signalmap-0.5.3.tar.gz (104.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

signalmap-0.5.3-py3-none-any.whl (122.3 kB view details)

Uploaded Python 3

File details

Details for the file signalmap-0.5.3.tar.gz.

File metadata

  • Download URL: signalmap-0.5.3.tar.gz
  • Upload date:
  • Size: 104.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for signalmap-0.5.3.tar.gz
Algorithm Hash digest
SHA256 8857a0d1a46fcafb7b4524c16ecfc82dfcca4a6a2e6d1eead91948d8f5e2f0d9
MD5 12ff9494b9e12611775413eef15b86e7
BLAKE2b-256 3ea67e07692acde38f1593f8a362b24585ee03035232df8f2c3a40da5d8fb6c8

See more details on using hashes here.

Provenance

The following attestation bundles were made for signalmap-0.5.3.tar.gz:

Publisher: release.yml on MrPredic/signalmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file signalmap-0.5.3-py3-none-any.whl.

File metadata

  • Download URL: signalmap-0.5.3-py3-none-any.whl
  • Upload date:
  • Size: 122.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for signalmap-0.5.3-py3-none-any.whl
Algorithm Hash digest
SHA256 9b1dc761a409997267d4d9c467566ce0d15a1fa2a0ff98f4285bf0763df4abbe
MD5 da50a17709271cfaf75f107758441306
BLAKE2b-256 84042d3d7025b24642d5d414c3bdcdfffa89d2886c8715047278e7a505683c4b

See more details on using hashes here.

Provenance

The following attestation bundles were made for signalmap-0.5.3-py3-none-any.whl:

Publisher: release.yml on MrPredic/signalmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page