Skip to main content

Causal Neuro-Symbolic Diagnosis - a five-layer fault-diagnosis framework

Project description

CANSYD — Causal Neuro-Symbolic Diagnosis

License: MIT Python 3.11+ Version CI Code style: ruff

A five-layer framework that verifies bearing-fault diagnoses against physics and causal structure — instead of trusting a neural network's confidence alone.

Quickstart · How it works · Results · API · Reproduce · Citation


What is CANSYD?

CANSYD (Causal Neuro-Symbolic Diagnosis) is a Python framework for diagnosing rolling-element bearing faults from vibration signals. A neural network proposes a diagnosis; then three independent layers check that proposal against the physics of the signal and the causal structure of the machine, and a consensus layer turns the result into an actionable maintenance decision.

The problem it solves is operating-condition shift: a model trained at one load or speed silently loses accuracy at another, and its confidence (the usual signal for "can I trust this prediction?") is the first thing to break. CANSYD answers a question a plain classifier cannot: is this predicted fault physically present in the signal, and would it survive a change of operating condition?

from cansyd import CANSYD, Dataset

data   = Dataset.from_arrays(signals, labels, condition, fs=12000)
report = CANSYD().fit(data).diagnose(data)

print(report.summary())
#  HIGH_CONFIDENCE: 61%   RELIABLE: 22%   UNCERTAIN: 9%   MANUAL_REVIEW: 8%
#  physics verification rate: CONFIRMED 71% · CONFLICT 18% · INCONCLUSIVE 11%

Why it matters. On two cross-domain benchmarks the physics layer is a significantly better reliability signal than deep ensembles (p < 0.005), and under heavy noise it catches up to 100% of the ensemble's confident mistakes. Where the task is easy, it honestly reports no advantage — see Results.


Background & provenance

CANSYD is the consolidated, tested implementation of a line of prior work by the authors: an earlier CNSD research prototype and a set of exploratory notebooks developing the physics-verification and causal layers. Those materials are being prepared for public release and will be linked here. This repository is the release-grade framework built from that research.

Table of Contents


🚀 Quickstart

Install (core is dependency-light — no TensorFlow required to use the physics and causal tools):

pip install cansyd            # core
pip install "cansyd[all]"     # + perception (TensorFlow) and counterfactual (DoWhy)

Diagnose a dataset in five lines. Bring your own arrays — signals, labels, the operating condition per window, and the sampling rate:

import numpy as np
from cansyd import CANSYD, Dataset

X    = np.random.randn(200, 1024)          # 200 windows of 1024 samples
y    = np.random.randint(0, 10, 200)       # fault labels
cond = np.random.choice([0, 1, 2, 3], 200) # operating condition per window
data = Dataset.from_arrays(X, y, cond, fs=12000)

report = CANSYD().fit(data).diagnose(data)

print(report.summary())
for statement in report.root_causes()[:5]:
    print(statement)   # e.g. "outer-race comb at 107 Hz (2 harmonics) supports predicted Outer fault"

print(f"{len(report.conflicts())} units flagged for review (physics disagrees with the network).")

Runnable versions live in examples/: quickstart.py, public_api_demo.py, and unseen_data.py.


🧠 How it works

CANSYD is a propose → verify → decide pipeline. The network only proposes; the diagnosis is not trusted until physics and causal reasoning have checked it.

CANSYD five-layer architecture
Layer Role What it produces
1 · Perception A 1-D CNN (or self-supervised S-JEPA backbone) classifies the vibration window. predicted fault ŷ, confidence c
2 · Symbolic Checks the prediction against the bearing's characteristic frequencies in the envelope spectrum. Independently names the fault family the signal supports. verdict CONFIRMED / CONFLICT / INCONCLUSIVE, root cause, maintenance action
3 · Causal (Rung 2) Estimates the interventional effect of the operating condition do(Z) on a corrected structural causal model (vibration does not cause the fault), with a refutation suite. interventional warrant, CATE, invariance test
3B · Counterfactual (Rung 3) On an invertible SCM, asks how the diagnosis would move under a different condition, using a continuous, model-independent severity outcome (RMS). per-unit counterfactual stability
4 · Consensus Fuses the network confidence with the physics verdict. A physical conflict can veto a confident network. HIGH_CONFIDENCE / RELIABLE / UNCERTAIN / MANUAL_REVIEW

The core idea in one sentence: a bearing defect strikes at a characteristic frequency fixed by geometry and shaft speed — a signature that is invariant to load by mechanical law, computable at any operating condition, and never consulted by a plain classifier. CANSYD reads it directly and uses it to verify, and if necessary overrule, the network. See docs/architecture.md for the full design.


📊 Results

Across three public bearing datasets, we compare reliability estimators at matched coverage (every estimator is allowed to trust the same number of predictions; the reported gap Δ is accuracy(trusted) − accuracy(rest)). Full log: EXPERIMENTS.md.

Physics verification vs. deep ensemble across datasets
Dataset Regime Physics Δ Ensemble Δ Physics vs. Ensemble Verdict
PU (900→1500 rpm) shifted +0.538 +0.386 p = 0.0032 ✅ significant win
XJTU-SY (cross-condition) shifted +0.460 +0.381 p = 0.0028 ✅ significant win
CWRU (cross-load) saturated +0.189 +0.074 p = 0.072 ⚪ null (reported as control)

Noise robustness. As additive noise increases, the physics layer catches the ensemble's confident errors: 100% at 0 dB on XJTU-SY, ~40% on the saturated CWRU regime.

On the CWRU null. We report it at equal prominence because it is evidence, not a weakness. CANSYD's thesis is that mechanistic verification helps where the signal is degraded and the network is unsure and adds nothing where predictions are already reliable. CWRU's cross-load task is comparatively saturated, so the absence of an advantage there is exactly what the mechanism predicts.


✨ Features

Feature Detail
🔬 Physics verification Envelope-spectrum check against BPFO/BPFI/BSF/FTF with harmonic aggregation and FFT-resolution-adaptive tolerance.
⚖️ Three-valued verdict CONFIRMED / CONFLICT / INCONCLUSIVE — the engine abstains when the physics can't adjudicate, instead of guessing.
🧾 Explainable output Every diagnosis carries a named component, characteristic frequencies, a root-cause statement, and a maintenance action.
🎯 Runtime causal reasoning Pearl Rung-2 do(Z) intervention on a corrected SCM, with CATE, cross-load invariance, and a DoWhy refutation suite.
🔮 Counterfactuals Pearl Rung-3 on an invertible SCM with a model-independent severity outcome; graceful sensitivity-analysis fallback.
🛡️ Physics veto A confident-but-unsupported prediction is escalated to human review — the failure a confidence-only pipeline can't catch.
🔌 Pluggable physics bearing, gear, and a zero-knowledge spectral provider behind one registry; add a mechanism with a single class.
📦 Deployable Dependency-light core (no TensorFlow needed), one-call API, universal dataset contract, tested and packaged.

📥 Installation

Requirements: Python ≥ 3.11.

# from PyPI (core only — numpy / scipy / scikit-learn / pyyaml)
pip install cansyd

# with optional extras
pip install "cansyd[perception]"      # 1-D CNN / S-JEPA backbone (TensorFlow, Keras)
pip install "cansyd[counterfactual]"  # Rung-3 counterfactuals (DoWhy, pandas, networkx)
pip install "cansyd[all]"             # everything

From source (for development or exact reproduction):

git clone https://github.com/shodhx/cansyd.git
cd CANSYD
pip install -e ".[all]"
# or pin the exact validated environment:
pip install -r requirements.txt

The core install deliberately avoids heavy dependencies: from cansyd.causal import intervention_effect_of_condition and the physics engine work without TensorFlow. Only training the CNN perception layer needs the perception extra.


🛠️ Usage

CANSYD exposes one clean object with four verbs.

Diagnose

from cansyd import CANSYD, Dataset

model  = CANSYD(config="cansyd_config.yaml")   # or CANSYD() for defaults
report = model.fit(data).diagnose(data)

report.summary()             # decision + verification-rate breakdown
report.root_causes()         # human-readable root-cause statements
report.conflicts()           # units where physics disagrees with the network
report.verification_rate()   # CONFIRMED / CONFLICT / INCONCLUSIVE fractions
report.accuracy_by_verdict() # accuracy within each verdict bucket

Explain — the causal warrant (Rung 2)

effect = model.explain(data)   # interventional effect of the operating condition do(Z)

What-if — a counterfactual (Rung 3)

cf = model.what_if(data, intervention={"load": 0.8}, unit_index=0)
# "what severity would this unit have shown at load 0.8?"

Inspect the structural causal model

scm = model.scm_analysis(data)   # the fitted SCM behind the causal layers

⚙️ Configuration

CANSYD is driven by a single YAML file. The taxonomy, sampling rate, and bearing geometry live in one place, so a new machine is onboarded by editing config — not code.

# cansyd_config.yaml
dataset:
  name: "cwru"
  sampling_rate_hz: 12000
domain:
  type: "bearing"            # -> selects the bearing physics provider
physics:
  parameters:
    bearing_type: "6205-2RS"
    motor_load_rpm: { 0: 1797, 1: 1772, 2: 1750, 3: 1730 }
taxonomy:
  classes:
    0: ["Normal", null]
    7: ["Outer", 0.007]
    # ... (fault family, defect size in inches)

🗂️ Repository structure

CANSYD/
├── README.md                   # you are here
├── LICENSE                     # MIT
├── cansyd_config.yaml            # example configuration
├── requirements.txt            # exact validated environment
├── setup.py / pyproject.toml   # packaging
├── cansyd/                       # the framework (installable package)
│   ├── api.py                  #   public CANSYD class: diagnose / explain / what_if / scm_analysis
│   ├── builder.py              #   assembles the five-layer pipeline from config
│   ├── config.py               #   YAML config loader
│   ├── perception/             #   Layer 1 — 1-D CNN + S-JEPA backbone
│   │   └── cnn.py
│   ├── symbolic/               #   Layer 2 — physics verification engine
│   │   └── engine.py
│   ├── physics/                #   characteristic-frequency kinematics
│   │   ├── bearing.py          #     envelope analysis, harmonic prominence, adaptive tolerance
│   │   ├── gear.py
│   │   └── providers/          #     pluggable domain registry: bearing / gear / spectral
│   ├── scm/                    #   the corrected structural causal model
│   │   └── graph.py
│   ├── causal/                 #   Layer 3 — Rung-2 intervention, CATE, invariance
│   │   ├── estimators.py
│   │   └── refutation.py       #     DoWhy refutation suite (+ permutation fallback)
│   ├── counterfactual/         #   Layer 3B — Rung-3 counterfactual
│   │   ├── rung3.py
│   │   └── sensitivity.py      #     sensitivity-analysis fallback
│   ├── consensus/              #   Layer 4 — verdict + confidence -> decision
│   │   └── fusion.py
│   ├── datasets/               #   universal dataset contract + loaders
│   │   └── contract.py
│   └── diagnosis/              #   orchestration + the DiagnosisReport object
│       ├── system.py
│       └── report.py
├── validation/                 # reproducible cross-domain benchmarks
│   ├── multi_seed_benchmark.py #   20-seed physics-vs-ensemble benchmark
│   ├── validate_cwru.py
│   ├── validate_pu.py
│   └── validate_xjtusy.py
├── examples/                   # runnable usage demos
├── test/                       # test suite (runs without heavy deps)
├── docs/                       # documentation + README assets
│   ├── architecture.md         #   full design document
│   └── assets/                 #   README images (hero, architecture, results)
├── EXPERIMENTS.md              # the validation log (every run, incl. nulls)
├── CONTRIBUTING.md · CODE_OF_CONDUCT.md · SECURITY.md · MAINTAINERS.md
└── CHANGELOG.md

🔁 Reproducing the experiments

Every headline number in EXPERIMENTS.md comes from the validation/ scripts. With a dataset prepared under data/ (see per-script docstrings) and the pinned environment installed:

# 20-seed, matched-coverage physics-vs-ensemble benchmark
python -m validation.multi_seed_benchmark --dataset pu
python -m validation.multi_seed_benchmark --dataset xjtusy
python -m validation.multi_seed_benchmark --dataset cwru

Seeds are fixed (42–61); numbers should match within run-to-run noise. Data paths are overridable via the CANSYD_DATA_CWRU, CANSYD_DATA_PU, and CANSYD_DATA_XJTU-SY environment variables.


🔧 Extending CANSYD to new machinery

The physics layer is a registry of providers. bearing is validated here; gear and a zero-knowledge spectral fallback ship alongside it. A new mechanism is one class:

from cansyd.physics.providers import register_provider, BaseProvider

class MyProvider(BaseProvider):
    def characteristic_frequencies(self, rpm, geometry): ...
    def dominant_family(self, envelope_spectrum): ...

register_provider("my_machine", MyProvider)
# now: domain.type = "my_machine" in cansyd_config.yaml

Scope note. CANSYD is validated on bearings, where the characteristic-frequency physics is exact. See EXPERIMENTS.md for all logs.


📌 Project status & roadmap

CANSYD is v1.0.0 and one step from public release. The framework, physics/causal/counterfactual layers, and cross-domain benchmarks are complete and tested.

  • Five-layer pipeline with physics veto
  • Rung-2 intervention + refutation suite; Rung-3 counterfactual
  • Three-dataset cross-domain validation (PU, XJTU-SY, CWRU)
  • Pluggable provider registry; dependency-light core
  • Gear-mesh provider (mechanism-agnostic verification)

See ROADMAP.md for where CANSYD is headed and how to help, and CHANGELOG.md for version history.


🤝 Contributing

Contributions are welcome. The workflow, integrity norms (honest nulls, no fabricated results, auto-generated artifacts), and review process are in CONTRIBUTING.md. In short:

  1. Fork → branch → make your change.
  2. Run ruff check . && ruff format . and pytest test/ before submitting.
  3. Open a PR describing the change; CI must be green.

Please also read the Code of Conduct and, for vulnerabilities, the Security Policy.


📝 Citation

If you use CANSYD, please cite:

@software{cansyd2026,
  title   = {CANSYD: Causal Neuro-Symbolic Diagnosis},
  author  = {Prasad, Abhimanyu and Mahmud, Kazi Tasfin},
  year    = {2026},
  url     = {https://github.com/shodhx/cansyd},
  version = {1.0.0},
}

📄 License

Released under the MIT License. © 2026 the CANSYD authors.


Built for machines that shouldn't fail silently.

Project details


Download files

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

Source Distribution

cansyd-1.0.0.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

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

cansyd-1.0.0-py3-none-any.whl (45.1 kB view details)

Uploaded Python 3

File details

Details for the file cansyd-1.0.0.tar.gz.

File metadata

  • Download URL: cansyd-1.0.0.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for cansyd-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0d0977c8e8af1d338d135f7d6ea04a41bfb099c2d98ffbfd318d1fbc6f677f5c
MD5 d5f67d51460d3a79e813b0284d57f61b
BLAKE2b-256 504a8fd22390bd0766e77de57036477ce9c5ddf7451378885d3eae02b5f658be

See more details on using hashes here.

File details

Details for the file cansyd-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: cansyd-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 45.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for cansyd-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7a241dbc62fc555351aa68c49a8fe2127147097fdf297c10775f06ae147d59d2
MD5 1a6cc2ebec7dfb9615113d4081eccf10
BLAKE2b-256 81255f55d3f2c8a046a39a6fde66bf354b1b1901e92e30b2597f9a28e3b81ac1

See more details on using hashes here.

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