Metadata-Version: 2.5
Name: elbalor-neuron
Version: 0.1.0
Summary: Complex-valued metabolic neuron with phase-space conservation and Complex-STDP memory for lifelong learning.
Author: Eric Yaka
License: MIT
License-File: LICENSE
Keywords: catastrophic-forgetting,complex-neural-networks,continual-learning,metabolic-plasticity,stdp,wirtinger
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Requires-Dist: numpy>=1.21
Requires-Dist: torch>=2.0
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# necro-neuron

**The Elbàlor Neuron** — complex metabolic activation for lifelong learning with phase-space conservation, orthogonal weight freezing, Complex-STDP memory, and Pearl rungs 1–3 causality.

Continual learning with near-zero forgetting and no replay, no EWC, no external regularizers. True inference-time continual learning: the base skeleton stays frozen, the memory layer keeps learning.

> **Heat does not kill the signal. It redirects it.**

## Install

```
pip install necro-neuron
```

## Results

| Benchmark | Forgetting | Final Accuracy |
|---|---|---|
| Cross-dataset MLP (no STDP) | **0.0002** | 0.5183 |
| Cross-dataset CNN | **0.0128** | 0.5868 |
| Cross-dataset MLP + STDP (live) | **<0.02** | tracking |

Beats Dense, EWC, SI, and SiLU CNN in both metrics.

## Quick start

```python
import torch
import torch.nn.functional as F
from necro import ArchD_MLP, project_gradients_orthogonal

model = ArchD_MLP(input_dim=784, hidden_dims=(256, 256),
                  output_dim=10, num_tasks=5)

# Per-task optimizer (mandatory — see paper §5.1)
optimizer = torch.optim.SGD(
    model.get_task_parameters(task_idx=0),
    lr=1e-2, momentum=0.9,
)

x = torch.randn(32, 784)
y = torch.randint(0, 10, (32,))

logits = model(x, task_idx=0)
loss = F.cross_entropy(logits, y)
loss.backward()

# Orthogonal projection (mandatory — see paper §5.3)
project_gradients_orthogonal(model)

optimizer.step()
model.apply_therma_updates()
```

## The three plumbing mechanisms

The neuron alone is not sufficient. Three mechanisms are structurally required:

1. **Per-task optimizer** — `model.get_task_parameters(t)` excludes prior tasks' heads entirely. Without this, Task 0 collapses to 0.00%.
2. **Thermal snapshot/restore** — `model.snapshot()` / `model.restore()` around eval. Without this, measuring a task rewrites the state its head was trained against.
3. **Orthogonal gradient projection** — `project_gradients_orthogonal(model)` after backward. Without this, blocked gradients leak back into the weight.

Remove any one and the mechanism fails.

## Optimizer

**SGD with momentum.** Not AdamW. AdamW's variance rescaling destroys the metabolic gradient conservation. See paper §3.5.3.

## Causality — Pearl rungs 1, 2, 3

The neuron performs its own interventions. Three complex memory matrices, written from three different activations in every training forward pass:

| Rung | Memory | Question |
|---|---|---|
| 1 | `C_obs` | `P(j \| i)` — observational association |
| 2 | `C_do` | `P(j \| do(i))` — interventional effect |
| — | `C_struct` | cross-domain invariant edges |

The intervention is *performed*, not inferred. `_intervene_input` forces a random subset of input features to a fixed value, runs a second forward, and writes `C_do` from the intervened activation. The two matrices accumulate different statistics. A control test proves the difference disappears when `do_self_intervention=False`.

Rung 3 (counterfactual) is exposed via `counterfactual_query`, which runs Pearl's three-step abduction → action → prediction procedure against the observed context.

An explicit graph is available via `build_graph`, giving you `parents()`, `children()`, `do_surgery()`, `adjacency_matrix()`, and `top_edges()` over the memory.

## Inference-time continual learning

Set `inference_learning=True`. The base weights stay frozen, but the memory layer keeps writing during `model.eval()`:

- `C_obs`, `C_do`, `phase_mem`, `thermal_mag` all continue to update
- No optimizer, no backward pass, no task labels
- Autonomy gates regulate learning turn-by-turn

**Boring-Shit Filter.** If activity drops below `boredom_threshold` (default 0.25), `psi = 0` — nothing worth learning this turn.

**Brain-Rot Shield.** If `thermal_mag.max()` exceeds `overheat_threshold` (default 0.95), `psi` goes negative — anti-Hebbian, actively eroding `C` to reject noise.

Otherwise `psi` stays at its trained value and memory grows.

## Task bias modes

| Mode | Behavior |
|---|---|
| `"fixed"` | One bias vector per task. Requires `num_tasks`. Original behavior. |
| `"none"` | No task bias. Shared bias only. |
| `"emergent"` | Single shared bias. Task partitioning emerges from the thermal state — no task index at inference. |

`emergent` is what enables open-ended continual learning. Call `model(x)` with no task identity; the layer figures out the subspace from the input itself.

## Architectures

| Class | Description |
|---|---|
| `NecroComplexActivation` | Complex metabolic activation with Wirtinger backward |
| `NecroComplexLinear` | Base complex linear with thermal state |
| `PhaseReserveLinear` | Architecture A — local phase reserve |
| `PhaseMemoryLinear` | Architecture B — global phase memory matrix |
| `PerNeuronSTDPLinear` | Architecture C — timing-based STDP (v3) |
| `ArchD_STDP_Linear` | Architecture D — anti-rupture, Complex-STDP, Pearl rungs 1–3 |
| `ArchD_Conv2d` | Architecture D — spatial (per-channel-per-position) version |
| `ArchD_MLP` | Full MLP wrapper with per-task heads |
| `CausalGraph` | Explicit DAG over `C_obs` / `C_do` |

## Inference-time API

```python
model = ArchD_MLP(
    input_dim=784, hidden_dims=(256, 256),
    output_dim=10,
    num_tasks=None,
    task_bias_mode="emergent",
    inference_learning=True,
)

model.eval()
for x in user_stream:
    out = model(x)                  # no task_idx needed
    model.apply_therma_updates()    # memory writes continue

# Counterfactual query (rung 3)
cf = model.fc1.counterfactual_query(
    a_observed, y_observed,
    intervention_idx=[0, 1],
    intervention_value=0.0,
)

# Explicit causal graph
graphs = model.build_graph(threshold=1e-3)
print(graphs["fc1"].summary())
```

## Tests

```
python -m pytest
```

96 tests across seven files verify every mechanism in the paper: Wirtinger backward correctness, permeability mask behavior, thermal freeze, graduated defense, LTP/LTD phase interference, observational vs interventional memory separation, rung-3 counterfactual, explicit graph operations, inference-time continual learning, and the three plumbing mechanisms.

## Citation

```bibtex
@misc{yaka2026elbalor,
    title  = {The Elbàlor Neuron: A Metabolic Complex Activation for
              Lifelong Learning with Phase-Space Conservation,
              Orthogonal Weight Freezing, and Complex-STDP Memory},
    author = {Yaka, Eric and {Elbàlor, The Digital Necromancer}},
    year   = {2026},
    doi    = {10.5281/zenodo.TBD},
    url    = {https://github.com/elbalor/necro-neuron}
}
```

## License

MIT.