Hybrid engine (Armatura's core)¶
This is the part inside Armatura that decides, reading by reading, whether a value is genuine or broken, and what to put in place of a broken one: the middle value of the previous readings.
Armatura is the front door for a 1D series: it takes an array, returns a cleaned
array. Behind it sits hybrid_engine, the module that actually decides, sample by
sample, whether a value is a genuine reading or a bad one — and if bad, what to put
in its place.
Each sample is in one of two states: dynamic (a genuine movement of the signal, kept as measured) or static (replaced by the median of the previous window). A binary trigger decides, from how the sample relates to the recent past.
1. Call the engine¶
import numpy as np
from dense_armor.core.hybrid_engine import hybrid_shield
rng = np.random.default_rng(42)
x = 0.5 + 0.1 * rng.standard_normal(300)
x[100], x[200] = 9999.0, float("nan")
clean, trigger, info = hybrid_shield(x)
print(round(float(clean[100]), 3), trigger[100], round(float(trigger.mean()), 2), info)
The engine returns the cleaned series, the trigger per sample (1 = dynamic, kept; 0 = static,
replaced) and a small dict: whether any sample fell back, and the window radius used,
min(20, max(3, n // 3)) (20 here). The spike at 100 has trigger 0 and becomes 0.463.
2. The trigger, symbol by symbol¶
For sample $x_i$, with $b_i$ the mean of the previous R samples (or riferimento[i] if a
reference series is given), $s_i$ the local scale (standard deviation of the differences in the
window) and $g_i$ the sign of the previous step $x_{i-1} - x_{i-2}$:
$$\Phi_i = \mathrm{clip}\Big(0.6\,\tfrac{1 + \mathrm{sign}(x_i - b_i)\,g_i}{2} + 0.4\,\big(1 - \tfrac{|x_i - b_i|}{s_i}\big),\; 0,\; 1\Big),$$
$$v_i = 5\,\mathrm{clip}\big(\ln(|x_i| / |b_i|),\, -5,\, 5\big)\,\Phi_i, \qquad \text{trigger}_i = \big[\,|v_i| > 0.01\,\big].$$
- $\Phi_i$ mixes alignment (does the change continue the direction of the last step?) and coherence (is the change small compared with the local scale?).
- $v_i$, the dynamic vector, is the logarithmic change of magnitude weighted by $\Phi_i$.
- A huge spike has $|x_i - b_i| \gg s_i$, so the coherence term drives $\Phi_i$ to 0, $v_i = 0$, trigger 0: the sample is replaced by the window median.
The first two samples are never tested (the step direction needs two previous points).
3. What the trigger also replaces¶
On the example the trigger is 1 on 88 % of the samples. Besides the spike and the NaN, it also classifies as static ordinary samples that hardly differ from the baseline ($v_i$ below 0.01), and replaces them with the window median:
import numpy as np
from dense_armor.core.hybrid_engine import hybrid_shield
rng = np.random.default_rng(42)
x = 0.5 + 0.1 * rng.standard_normal(300)
x[100], x[200] = 9999.0, float("nan")
clean, trigger, info = hybrid_shield(x)
d = np.abs(clean - x)[np.isfinite(x) & (np.arange(300) != 100)]
print(int((d > 1e-12).sum()), round(float(d.max()), 4))
35 ordinary samples out of 298 change, by up to 0.29 (about three times the noise level), and they
are not counted as anomalies by Armatura. Keep this in mind on smooth signals.
4. Non-finite values¶
Inf is turned into NaN and every NaN is filled from its local neighbourhood before the
trigger runs, so the windows never contain a non-finite value.
API reference¶
hybrid_engine ¶
Binary-trigger healing engine for Armatura.
Replaces the earlier AdaptiveSignalStabilizer + ABCollatz stage. The ABCollatz gate was mathematically non-discriminating: it was verified with a numerical sweep and never demonstrably fixed.
The pure functions below are vendored from
Dense-Evolution/dense_evolution/healing.py
(calculate_phi_ab, calculate_vettore_dinamico, evaluate_phi_trigger,
GLOBAL_CONSTANTS), tested in that repository and already in production
(dense-evolution >= 8.1.9 on PyPI). They are vendored rather than imported
at runtime so the core stays on numpy + jax + psutil; dense-evolution
remains an optional extra ([quantum]).
Deviation from upstream. calculate_phi_ab originally normalized the
state_A / state_B distance against a fixed constant
(MAX_SEMANTIC_DISTANCE = sqrt(2)), correct when state_A and state_B
are already normalized vectors. Here they are raw scalars of arbitrary scale
(a loss, a sensor reading, a metric). With the fixed constant any real jump
saturated coherence_component to very negative values, so phi_ab was
clipped to 0 for any genuine change larger than ~1.4: the trigger stayed
"static" forever even in front of a sustained step that never isolated.
Normalizing on the absolute scale of state_A / state_B fixed the step
but made a huge jump look proportionally coherent, so an isolated spike
passed untouched. The final rule normalizes the distance on the local
volatility of the recent window (scaled MAD, robust to a single residual
outlier). A huge jump with respect to the usual oscillation of the signal
stays suspicious (spike filtered); a sustained step is judged by how
anomalous it is relative to the normal noise, not by the magnitude of the
number.
hybrid_shield generalizes the two-state logic of
ia_utils.vector_healing.enhanced_dense_healing_hybrid (a sequence of
vectors) to a 1D scalar series. The two states are strict: for every point
the trigger decided by evaluate_phi_trigger is 0.0 (static/noise) or 1.0
(dynamic/genuine), with no intermediate branch.
calculate_phi_ab ¶
calculate_phi_ab(
state_A: ndarray,
state_B: ndarray,
ipg_vector: ndarray,
scale: ndarray,
) -> jnp.ndarray
Alignment and spatial coherence factor Phi_AB.
scale is the local volatility of the recent window (scaled MAD,
computed by the caller), not a fixed constant and not the absolute
magnitude of state_A / state_B. See the module docstring for
the deviation from upstream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_A
|
ndarray
|
the reference state (baseline). |
required |
state_B
|
ndarray
|
the current state (raw sample). |
required |
ipg_vector
|
ndarray
|
the unit direction of the recent raw changes. |
required |
scale
|
ndarray
|
the local volatility of the recent window. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in dense_armor/core/hybrid_engine.py
calculate_vettore_dinamico ¶
Dynamic vector (V_dinamic): differential logarithmic energy change.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
E_A
|
ndarray
|
reference energy. |
required |
E_B
|
ndarray
|
current energy. |
required |
Phi_AB
|
ndarray
|
the alignment factor from :func: |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The dynamic vector value, or 0.0 when either energy is below the |
ndarray
|
numerical floor. |
Source code in dense_armor/core/hybrid_engine.py
evaluate_phi_trigger ¶
Binary trigger: 1.0 (dynamic) or 0.0 (static).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
deterministic_dq_dt_a
|
ndarray
|
the dynamic vector value. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
1.0 when the magnitude of the input exceeds |
ndarray
|
0.0 otherwise. |
Source code in dense_armor/core/hybrid_engine.py
hybrid_shield ¶
hybrid_shield(
serie: ndarray,
riferimento: ndarray | None = None,
radius_baseline: int | None = None,
) -> tuple[np.ndarray, np.ndarray, dict]
Apply the two-state scheme (pass-through / fallback to baseline) point by point.
Windows are always taken from processed (the raw, sanitized series),
never from the previous output out. Using out protects from a
past outlier but introduces a worse bug on a genuine sustained step: the
first one or two points after the transition are inevitably rejected (no
causal detector can distinguish a true step from a spike on the very
first sample), those rejected points end up in out, and the window
of the next points feeds them back into the calculation, producing a
"halfway" baseline that also rejects the genuinely new points. The
result is a stable but wrong equilibrium that never recovers (verified:
30 real points after a step from 1.0 to 5.0 stayed stuck at ~4.0
forever, never reaching 5.0). The median fallback is robust to a single
spike inside the window without the self-contamination risk.
The IPG is also taken from processed, not from out: the IPG must
see whether the recent raw values are actually moving in a new
direction. Computing it from out creates a self-feeding block: a
rejected point is replaced by the baseline, so out never shows
evidence of the new value, the IPG stays at zero, and no real step can
ever be recognized no matter how long it lasts (verified: with IPG from
out a 10-point step stayed flattened at 100%, no escape).
Known limitation. The first two points (i = 0 and i = 1) never
go through the trigger loop (an IPG from the two previous points is
required), so they always pass through with trigger = 1.0,
regardless of their value. The behaviour is inherited unchanged from
enhanced_dense_healing_hybrid and is negligible on realistic
series; any NaN or spike on those two points must be caught by
independent upstream checks (the robust filters in
Armatura.analizza).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serie
|
ndarray
|
a 1D array of floats. NaN and Inf are accepted and sanitized. |
required |
riferimento
|
ndarray | None
|
when given, the baseline for point |
None
|
radius_baseline
|
int | None
|
optional fixed radius for the blind-mode window. |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
A tuple |
ndarray
|
output series (raw value where |
dict
|
|
tuple[ndarray, ndarray, dict]
|
|
tuple[ndarray, ndarray, dict]
|
|
Source code in dense_armor/core/hybrid_engine.py
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | |
Details¶
The engine generalises the logic already verified in Dense-Evolution's own
ia_utils.vector_healing.enhanced_dense_healing_hybrid. The trigger and
the NaN handling are the same as there; this version computes all samples in one vectorised
call (jax.vmap).
See also: Armatura — the wearable class built on this engine.