sofia / examples /basic_inference.py
rootcastleengineering's picture
release: publish Sofia Engine 3.0.0a1 artifacts, manifests, and documentation
876458a
Raw
History Blame Contribute Delete
3.71 kB
"""Basic Inference Example for Sofia Engine.
Demonstrates deterministic telemetry feature extraction, statistical anomaly
detection, and evidence-based diagnostic health scoring.
Run:
python examples/basic_inference.py
"""
from __future__ import annotations
import numpy as np
from sofia_ai.core.contracts import DataQuality, FeatureVector, SignalWindow
from sofia_ai.diagnostics.engine import (
DiagnosticEngine,
HealthEvent,
HealthScore,
compute_health_score,
)
from sofia_ai.diagnostics.rules import RuleContext
from sofia_ai.features import extract_from_array
from sofia_ai.inference.detectors import ThresholdDetector, build_manifest_for
def run_basic_inference() -> None:
print("=== Sofia Engine: Basic Inference Example ===")
# 1. Synthesize 1 second of vibration telemetry (fs = 1000 Hz, 25 Hz shaft rotation)
fs = 1000.0
t = np.arange(1000) / fs
rng = np.random.default_rng(42)
# 25 Hz fundamental + 50 Hz harmonic + Gaussian background noise
raw_signal = (
0.45 * np.sin(2 * np.pi * 25.0 * t)
+ 0.15 * np.sin(2 * np.pi * 50.0 * t)
+ 0.05 * rng.normal(size=len(t))
)
print(f"Input Signal: {len(raw_signal)} samples @ {fs} Hz ({len(raw_signal)/fs:.2f} s)")
# 2. Package into a validated SignalWindow
window = SignalWindow(
values=raw_signal,
sample_rate=fs,
device_id="pump-motor-01",
channel="vibration_de",
unit="m/s^2",
quality=DataQuality.GOOD,
)
# 3. Extract versioned feature vector (14 statistical + spectral metrics)
features: FeatureVector = extract_from_array(
window.values,
window.sample_rate,
channel=window.channel,
quality=window.quality,
)
# 4. Verify feature schema version (v3.0)
features.validate_schema("3.0")
print(f"Extracted FeatureVector (schema {features.schema_version}): {len(features.names)} features")
for name, val in list(zip(features.names, features.values, strict=True))[:5]:
print(f" - {name}: {val:.4f}")
# 5. Anomaly detection via ThresholdDetector (monitoring vibration RMS)
detector = ThresholdDetector(feature="rms", high=0.45)
manifest = build_manifest_for(detector, model_id="rms_threshold_v1", version="1.0.0")
detector.load(manifest)
inference_result = detector.infer(features)
print("\n--- Inference Result (Threshold Detector) ---")
print(f"Monitored Feature: 'rms' = {inference_result.score:.4f} m/s^2 (high limit = 0.45)")
print(f"Outcome: {inference_result.outcome.name}")
print(f"Confidence: {inference_result.confidence:.2%}")
# 6. Diagnostic Engine: Convert inference into evidence-backed health event
diag_engine = DiagnosticEngine()
context = RuleContext(
device_id=window.device_id,
channel=window.channel,
score=inference_result.score,
confidence=inference_result.confidence,
unit="z-score",
)
event: HealthEvent | None = diag_engine.evaluate(context)
events = [event] if event else []
health_score: HealthScore = compute_health_score(events)
print("\n--- Health Assessment ---")
print(f"Health Score: {health_score.score:.1f} / 100.0")
print(f"Credible Interval: [{health_score.lower:.1f}, {health_score.upper:.1f}]")
print(f"Uncertainty: ±{health_score.uncertainty:.2%}")
if event:
print(f"Health Event: {event.event_type} (Severity: {event.severity.name})")
print(f"Recommendation: {event.recommendation}")
else:
print("Status: Nominal operating condition.")
if __name__ == "__main__":
run_basic_inference()