Upload folder using huggingface_hub
Browse files- README.md +101 -20
- config.json +13 -41
- engine.py +455 -0
- mechanism_base_v1.pth +3 -0
- mechanism_tokenizer/tokenizer.json +0 -0
- model.py +271 -0
- pl/__init__.py +33 -0
- pl/calculus.py +393 -0
- pl/core.py +593 -0
- pl/grammar.py +397 -0
- pl/numbers.py +339 -0
README.md
CHANGED
|
@@ -1,20 +1,101 @@
|
|
| 1 |
-
---
|
| 2 |
-
language: en
|
| 3 |
-
license: mit
|
| 4 |
-
tags:
|
| 5 |
-
- propagation-logic
|
| 6 |
-
- mechanism-
|
| 7 |
-
-
|
| 8 |
-
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language: en
|
| 3 |
+
license: mit
|
| 4 |
+
tags:
|
| 5 |
+
- propagation-logic
|
| 6 |
+
- mechanism-first
|
| 7 |
+
- abstract-reasoning
|
| 8 |
+
- derivation-traces
|
| 9 |
+
- boundary-conditions
|
| 10 |
+
datasets:
|
| 11 |
+
- ApplePiesFromScratch/dta-benchmark
|
| 12 |
+
metrics:
|
| 13 |
+
- dta
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# MechanismBase β P / G β Q
|
| 17 |
+
|
| 18 |
+
A 10M parameter transformer trained on derivation traces, not natural language.
|
| 19 |
+
|
| 20 |
+
## What this is
|
| 21 |
+
|
| 22 |
+
Standard language models learn statistical patterns over text.
|
| 23 |
+
This model was trained on the **procedure** P / G β Q β explicit derivation
|
| 24 |
+
traces showing closure analysis, fixed point detection, cycle structure
|
| 25 |
+
identification, and forced boundary condition derivation.
|
| 26 |
+
|
| 27 |
+
**The claim:** given any carrier V and gradient family Ξ, the model can derive
|
| 28 |
+
forced boundary conditions β what logic system the carrier implies, what
|
| 29 |
+
fixed points exist, what cycle structure is forced.
|
| 30 |
+
|
| 31 |
+
## Theory
|
| 32 |
+
|
| 33 |
+
Propagation Logic v13 β SSRN Abstract ID: 6439258 (James Pugmire)
|
| 34 |
+
|
| 35 |
+
The single primitive operator: `P / G β Q`
|
| 36 |
+
|
| 37 |
+
A loaded pattern P propagates through gradient field G in context C to
|
| 38 |
+
produce updated pattern Q. All of classical logic, fuzzy logic, arithmetic,
|
| 39 |
+
calculus, and grammar fall out of different (V, Ξ) choices.
|
| 40 |
+
|
| 41 |
+
## Model
|
| 42 |
+
|
| 43 |
+
- Architecture: Transformer decoder (custom, mechanism-aligned)
|
| 44 |
+
- Parameters: 10.5M
|
| 45 |
+
- Training tokens: ~200K (derivation traces)
|
| 46 |
+
- Training epochs: 5
|
| 47 |
+
|
| 48 |
+
## Benchmark: DTA (Derivation Trace Accuracy)
|
| 49 |
+
|
| 50 |
+
The correct benchmark for this model is not BLiMP or MMLU.
|
| 51 |
+
It is DTA β how accurately does the model predict forced boundary conditions
|
| 52 |
+
on novel carriers?
|
| 53 |
+
|
| 54 |
+
See: `ApplePiesFromScratch/dta-benchmark`
|
| 55 |
+
|
| 56 |
+
| Model | DTA-Overall | DTA-Closure | DTA-FixedPts | DTA-Cycle |
|
| 57 |
+
|-------|-------------|-------------|--------------|-----------|
|
| 58 |
+
| MechanismBase (10M) | TBD | TBD | TBD | TBD |
|
| 59 |
+
| Random baseline | 25% | 50% | 25% | 25% |
|
| 60 |
+
| Engine (oracle) | 100% | 100% | 100% | 100% |
|
| 61 |
+
|
| 62 |
+
## Usage
|
| 63 |
+
|
| 64 |
+
```python
|
| 65 |
+
# The model requires the pl/ library and engine.py from the repo
|
| 66 |
+
# Clone: github.com/ApplePiesFromScratch/propagation-logic
|
| 67 |
+
|
| 68 |
+
from model import MechanismBase, SmallConfig
|
| 69 |
+
from tokenizers import Tokenizer
|
| 70 |
+
import torch
|
| 71 |
+
|
| 72 |
+
config = SmallConfig()
|
| 73 |
+
model = MechanismBase(config)
|
| 74 |
+
# Load weights from Hub (see full usage in repo)
|
| 75 |
+
|
| 76 |
+
tokenizer = Tokenizer.from_file("mechanism_tokenizer/tokenizer.json")
|
| 77 |
+
|
| 78 |
+
# Give the model a partial derivation trace
|
| 79 |
+
partial = """DOMAIN: color_domain
|
| 80 |
+
CARRIER: ['red', 'green', 'blue']
|
| 81 |
+
GRADIENTS: ['complement', 'id']
|
| 82 |
+
THETA: 1.0
|
| 83 |
+
---
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
ids = torch.tensor(tokenizer.encode(partial).ids).unsqueeze(0)
|
| 87 |
+
output = model.generate(ids, max_new_tokens=200, temperature=0.3)
|
| 88 |
+
print(tokenizer.decode(output[0].tolist()))
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
## Training
|
| 92 |
+
|
| 93 |
+
```
|
| 94 |
+
python generate_data.py # generates derivation trace corpus
|
| 95 |
+
python tokenizer_train.py # BPE tokenizer on corpus
|
| 96 |
+
python train.py # SmallConfig, ~30 min on RTX 4060 Ti
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
## Repository
|
| 100 |
+
|
| 101 |
+
GitHub: [ApplePiesFromScratch/propagation-logic](https://github.com/ApplePiesFromScratch/propagation-logic)
|
config.json
CHANGED
|
@@ -1,41 +1,13 @@
|
|
| 1 |
-
{
|
| 2 |
-
"
|
| 3 |
-
"
|
| 4 |
-
"
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
"
|
| 8 |
-
"
|
| 9 |
-
"
|
| 10 |
-
"
|
| 11 |
-
"
|
| 12 |
-
"
|
| 13 |
-
|
| 14 |
-
"model_type": "gpt2",
|
| 15 |
-
"n_ctx": 1024,
|
| 16 |
-
"n_embd": 384,
|
| 17 |
-
"n_head": 6,
|
| 18 |
-
"n_inner": null,
|
| 19 |
-
"n_layer": 8,
|
| 20 |
-
"n_positions": 256,
|
| 21 |
-
"pad_token_id": null,
|
| 22 |
-
"reorder_and_upcast_attn": false,
|
| 23 |
-
"resid_pdrop": 0.1,
|
| 24 |
-
"scale_attn_by_inverse_layer_idx": false,
|
| 25 |
-
"scale_attn_weights": true,
|
| 26 |
-
"summary_activation": null,
|
| 27 |
-
"summary_first_dropout": 0.1,
|
| 28 |
-
"summary_proj_to_labels": true,
|
| 29 |
-
"summary_type": "cls_index",
|
| 30 |
-
"summary_use_proj": true,
|
| 31 |
-
"task_specific_params": {
|
| 32 |
-
"text-generation": {
|
| 33 |
-
"do_sample": true,
|
| 34 |
-
"max_length": 50
|
| 35 |
-
}
|
| 36 |
-
},
|
| 37 |
-
"tie_word_embeddings": true,
|
| 38 |
-
"transformers_version": "5.0.0",
|
| 39 |
-
"use_cache": true,
|
| 40 |
-
"vocab_size": 149
|
| 41 |
-
}
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "mechanism_base",
|
| 3 |
+
"vocab_size": 16384,
|
| 4 |
+
"n_embd": 256,
|
| 5 |
+
"n_layer": 8,
|
| 6 |
+
"n_head": 8,
|
| 7 |
+
"block_size": 256,
|
| 8 |
+
"dropout": 0.1,
|
| 9 |
+
"n_params": 10578432,
|
| 10 |
+
"architecture": "transformer_decoder",
|
| 11 |
+
"training": "mechanism_first_derivation_traces",
|
| 12 |
+
"paper": "Propagation Logic v13, SSRN 6439258"
|
| 13 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
engine.py
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
engine.py β Propagation Logic Inference Engine
|
| 3 |
+
===============================================
|
| 4 |
+
|
| 5 |
+
P / G β Q
|
| 6 |
+
|
| 7 |
+
The inference procedure itself, not a description of it.
|
| 8 |
+
|
| 9 |
+
Given a carrier V and gradient family Ξ, this engine:
|
| 10 |
+
1. Checks closure
|
| 11 |
+
2. Finds fixed points
|
| 12 |
+
3. Detects involutions and cycle structure
|
| 13 |
+
4. Derives forced boundary conditions
|
| 14 |
+
|
| 15 |
+
The claim: classical logic, fuzzy logic, arithmetic, grammar β
|
| 16 |
+
all fall out of different (V, Ξ) choices.
|
| 17 |
+
The structure is DERIVED, not assumed.
|
| 18 |
+
|
| 19 |
+
A standard LLM pattern-matches to training examples.
|
| 20 |
+
This engine applies the procedure to carriers it has never seen.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
from __future__ import annotations
|
| 24 |
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
| 25 |
+
import sys
|
| 26 |
+
import os
|
| 27 |
+
sys.path.insert(0, os.path.dirname(__file__))
|
| 28 |
+
|
| 29 |
+
from pl.core import (
|
| 30 |
+
Pattern, Gradient, Context, PropagationChain,
|
| 31 |
+
seed, G_neg, G_id, G_custom,
|
| 32 |
+
G_fuzzy_neg, G_succ, G_pred, G_halve, G_sqrt,
|
| 33 |
+
G_mod, KNOWN_SYSTEMS,
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# =============================================================================
|
| 38 |
+
# ENGINE
|
| 39 |
+
# =============================================================================
|
| 40 |
+
|
| 41 |
+
class PropagationEngine:
|
| 42 |
+
"""
|
| 43 |
+
The mechanism as inference procedure.
|
| 44 |
+
|
| 45 |
+
Usage:
|
| 46 |
+
engine = PropagationEngine(
|
| 47 |
+
carrier = {0, 1},
|
| 48 |
+
gradients = [G_neg(), G_id()],
|
| 49 |
+
theta = 1.0,
|
| 50 |
+
name = "classical_logic",
|
| 51 |
+
)
|
| 52 |
+
print(engine.report())
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
def __init__(
|
| 56 |
+
self,
|
| 57 |
+
carrier: Set,
|
| 58 |
+
gradients: List[Gradient],
|
| 59 |
+
theta: float = 1.0,
|
| 60 |
+
name: str = "engine",
|
| 61 |
+
):
|
| 62 |
+
try:
|
| 63 |
+
self.V = sorted(carrier, key=str)
|
| 64 |
+
except TypeError:
|
| 65 |
+
self.V = list(carrier)
|
| 66 |
+
self.V_set = set(carrier)
|
| 67 |
+
self.gradients = gradients
|
| 68 |
+
self.theta = theta
|
| 69 |
+
self.context = Context(gradients, theta)
|
| 70 |
+
self.name = name
|
| 71 |
+
|
| 72 |
+
# ββ Core ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 73 |
+
|
| 74 |
+
def propagate(self, p: Pattern, g: Gradient) -> Pattern:
|
| 75 |
+
"""P / G β Q"""
|
| 76 |
+
return g.propagate(p)
|
| 77 |
+
|
| 78 |
+
# ββ Analysis ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 79 |
+
|
| 80 |
+
def check_closure(self) -> Dict:
|
| 81 |
+
results = {}
|
| 82 |
+
for g in self.gradients:
|
| 83 |
+
is_closed, violations = g.is_closed_on(self.V_set)
|
| 84 |
+
extension = {out for _, out in violations}
|
| 85 |
+
results[g.name] = {
|
| 86 |
+
"closed": is_closed,
|
| 87 |
+
"violations": violations,
|
| 88 |
+
"extension_required": extension,
|
| 89 |
+
}
|
| 90 |
+
return results
|
| 91 |
+
|
| 92 |
+
def find_fixed_points(self) -> Dict:
|
| 93 |
+
return {g.name: g.fixed_points(self.V_set) for g in self.gradients}
|
| 94 |
+
|
| 95 |
+
def find_cycles(self) -> Dict:
|
| 96 |
+
results = {}
|
| 97 |
+
for g in self.gradients:
|
| 98 |
+
g_cycles = {}
|
| 99 |
+
for v in self.V_set:
|
| 100 |
+
g_cycles[v] = g.cycle_length(v)
|
| 101 |
+
results[g.name] = g_cycles
|
| 102 |
+
return results
|
| 103 |
+
|
| 104 |
+
def check_involution(self) -> Dict:
|
| 105 |
+
results = {}
|
| 106 |
+
for g in self.gradients:
|
| 107 |
+
counterexamples = []
|
| 108 |
+
for v in self.V_set:
|
| 109 |
+
try:
|
| 110 |
+
v1 = g.transform(v)
|
| 111 |
+
except (ValueError, KeyError):
|
| 112 |
+
counterexamples.append((v, "CARRIER_EXIT", "CARRIER_EXIT"))
|
| 113 |
+
continue
|
| 114 |
+
try:
|
| 115 |
+
v2 = g.transform(v1)
|
| 116 |
+
except (ValueError, KeyError):
|
| 117 |
+
# v1 is outside V β orbit escaped, not an involution
|
| 118 |
+
counterexamples.append((v, v1, "CARRIER_EXIT"))
|
| 119 |
+
continue
|
| 120 |
+
if v2 != v:
|
| 121 |
+
counterexamples.append((v, v1, v2))
|
| 122 |
+
results[g.name] = {
|
| 123 |
+
"is_involution": len(counterexamples) == 0,
|
| 124 |
+
"counterexamples": counterexamples,
|
| 125 |
+
}
|
| 126 |
+
return results
|
| 127 |
+
|
| 128 |
+
def compute_propagation_rates(self) -> Dict:
|
| 129 |
+
"""
|
| 130 |
+
Theorem 2.1: Among incoherent patterns, rate β 1/L_P.
|
| 131 |
+
Lower load = higher propagation rate.
|
| 132 |
+
"""
|
| 133 |
+
rates = {}
|
| 134 |
+
for g in self.gradients:
|
| 135 |
+
g_rates = {}
|
| 136 |
+
for v in self.V_set:
|
| 137 |
+
for L in [0.0, 1.0, 2.0, 5.0]:
|
| 138 |
+
p = Pattern(v=v, L=L)
|
| 139 |
+
demand = self.context.demand(p)
|
| 140 |
+
rate = float("inf") if demand == 0.0 else (1.0 / L if L > 0 else float("inf"))
|
| 141 |
+
g_rates[f"v={v!r},L={L}"] = {
|
| 142 |
+
"demand": round(demand, 4),
|
| 143 |
+
"rate": rate,
|
| 144 |
+
"coherent": demand == 0.0,
|
| 145 |
+
}
|
| 146 |
+
rates[g.name] = g_rates
|
| 147 |
+
return rates
|
| 148 |
+
|
| 149 |
+
def derive_forced_conditions(self) -> Dict:
|
| 150 |
+
"""
|
| 151 |
+
The boundary condition extrapolation procedure.
|
| 152 |
+
Run all analyses. Derive what this (V, Ξ) forces.
|
| 153 |
+
"""
|
| 154 |
+
closure = self.check_closure()
|
| 155 |
+
fps = self.find_fixed_points()
|
| 156 |
+
cycles = self.find_cycles()
|
| 157 |
+
involutions = self.check_involution()
|
| 158 |
+
|
| 159 |
+
forced = []
|
| 160 |
+
|
| 161 |
+
# ββ Closure βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 162 |
+
all_closed = all(r["closed"] for r in closure.values())
|
| 163 |
+
if all_closed:
|
| 164 |
+
forced.append((
|
| 165 |
+
"CLOSURE",
|
| 166 |
+
f"The carrier V={set(self.V)} is closed under all "
|
| 167 |
+
f"{len(self.gradients)} gradient(s). "
|
| 168 |
+
f"Propagation stays within V. The carrier is self-consistent."
|
| 169 |
+
))
|
| 170 |
+
else:
|
| 171 |
+
for g_name, r in closure.items():
|
| 172 |
+
if not r["closed"]:
|
| 173 |
+
forced.append((
|
| 174 |
+
"CLOSURE_VIOLATION",
|
| 175 |
+
f"G[{g_name}] is NOT closed on V={set(self.V)}. "
|
| 176 |
+
f"Violations: {r['violations'][:3]}. "
|
| 177 |
+
f"The carrier MUST extend to include {r['extension_required']}. "
|
| 178 |
+
f"This extension is forced, not chosen."
|
| 179 |
+
))
|
| 180 |
+
|
| 181 |
+
# ββ Fixed points ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 182 |
+
for g_name, fp_list in fps.items():
|
| 183 |
+
if not fp_list:
|
| 184 |
+
forced.append((
|
| 185 |
+
"NO_FIXED_POINTS",
|
| 186 |
+
f"G[{g_name}] has no fixed points on V={set(self.V)}. "
|
| 187 |
+
f"Nothing survives this gradient unchanged. "
|
| 188 |
+
f"All elements are in motion under G[{g_name}]."
|
| 189 |
+
))
|
| 190 |
+
else:
|
| 191 |
+
forced.append((
|
| 192 |
+
"FIXED_POINTS",
|
| 193 |
+
f"G[{g_name}] fixes {fp_list}. "
|
| 194 |
+
f"These are stable attractors β propagation leaves them unchanged. "
|
| 195 |
+
f"They are the invariants of this gradient family."
|
| 196 |
+
))
|
| 197 |
+
|
| 198 |
+
# ββ Involutions βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 199 |
+
for g_name, inv in involutions.items():
|
| 200 |
+
if inv["is_involution"]:
|
| 201 |
+
forced.append((
|
| 202 |
+
"INVOLUTION",
|
| 203 |
+
f"G[{g_name}] is an involution: applying it twice returns to origin. "
|
| 204 |
+
f"Double-application law holds. This is DERIVED from the carrier, "
|
| 205 |
+
f"not assumed as an axiom."
|
| 206 |
+
))
|
| 207 |
+
elif inv["counterexamples"]:
|
| 208 |
+
forced.append((
|
| 209 |
+
"NOT_INVOLUTION",
|
| 210 |
+
f"G[{g_name}] is NOT an involution. "
|
| 211 |
+
f"Double-application does not return to origin: "
|
| 212 |
+
f"{inv['counterexamples'][:2]}."
|
| 213 |
+
))
|
| 214 |
+
|
| 215 |
+
# ββ Cycle structure βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 216 |
+
for g_name, cyc in cycles.items():
|
| 217 |
+
lengths = set(v for v in cyc.values() if v is not None)
|
| 218 |
+
if not lengths:
|
| 219 |
+
pass
|
| 220 |
+
elif lengths == {1}:
|
| 221 |
+
forced.append((
|
| 222 |
+
"IDENTITY_GRADIENT",
|
| 223 |
+
f"G[{g_name}] is the identity on V: every element is a fixed point. "
|
| 224 |
+
f"This gradient changes nothing β zero cost, zero transformation."
|
| 225 |
+
))
|
| 226 |
+
elif len(lengths) == 1:
|
| 227 |
+
k = next(iter(lengths))
|
| 228 |
+
forced.append((
|
| 229 |
+
f"UNIFORM_{k}_CYCLE",
|
| 230 |
+
f"G[{g_name}] is a uniform {k}-cycle on V. "
|
| 231 |
+
f"Every element has orbit length {k}. "
|
| 232 |
+
f"Applying G[{g_name}] exactly {k} times returns to origin. "
|
| 233 |
+
f"The carrier has uniform periodic structure."
|
| 234 |
+
))
|
| 235 |
+
else:
|
| 236 |
+
forced.append((
|
| 237 |
+
"MIXED_CYCLE_STRUCTURE",
|
| 238 |
+
f"G[{g_name}] has mixed cycle structure: {dict(cyc)}. "
|
| 239 |
+
f"Different elements have different orbit lengths. "
|
| 240 |
+
f"The carrier has internal asymmetry."
|
| 241 |
+
))
|
| 242 |
+
|
| 243 |
+
# ββ Logic identification βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 244 |
+
V_set = set(self.V)
|
| 245 |
+
if V_set == {0, 1}:
|
| 246 |
+
neg_invol = involutions.get("neg", involutions.get("fuzzy_neg", {
|
| 247 |
+
"is_involution": False
|
| 248 |
+
}))["is_involution"]
|
| 249 |
+
neg_fps = fps.get("neg", fps.get("fuzzy_neg", [None]))
|
| 250 |
+
if neg_invol and neg_fps == []:
|
| 251 |
+
forced.append((
|
| 252 |
+
"CLASSICAL_LOGIC_FORCED",
|
| 253 |
+
"V={0,1} + involutory negation with no fixed points = "
|
| 254 |
+
"CLASSICAL LOGIC. "
|
| 255 |
+
"Excluded middle holds: every element is 0 or 1, no middle. "
|
| 256 |
+
"This is not an axiom system. It is the forced boundary condition "
|
| 257 |
+
"of the two-element carrier with this gradient family."
|
| 258 |
+
))
|
| 259 |
+
|
| 260 |
+
numeric_V = all(isinstance(v, (int, float)) for v in V_set)
|
| 261 |
+
if numeric_V and len(V_set) >= 3:
|
| 262 |
+
for g_name, fp_list in fps.items():
|
| 263 |
+
middle = [v for v in fp_list
|
| 264 |
+
if v not in {0, 0.0, 1, 1.0}]
|
| 265 |
+
if middle:
|
| 266 |
+
forced.append((
|
| 267 |
+
"MANY_VALUED_LOGIC_FORCED",
|
| 268 |
+
f"V={V_set} + G[{g_name}] fixing middle values {middle}: "
|
| 269 |
+
f"MANY-VALUED LOGIC is forced. "
|
| 270 |
+
f"Excluded middle fails at {middle}. "
|
| 271 |
+
f"These values are neither fully designated nor undesignated. "
|
| 272 |
+
f"Their existence in V is what forces the failure."
|
| 273 |
+
))
|
| 274 |
+
|
| 275 |
+
# ββ Theorem 2.1 βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 276 |
+
forced.append((
|
| 277 |
+
"THEOREM_2.1",
|
| 278 |
+
"Propagation rate theorem: among incoherent patterns (demand > 0), "
|
| 279 |
+
"rate β 1/L. Simpler patterns propagate faster. "
|
| 280 |
+
"This holds on this carrier as on all carriers. "
|
| 281 |
+
"Zipf's law, natural selection, the exponential fixed point β one theorem."
|
| 282 |
+
))
|
| 283 |
+
|
| 284 |
+
return {
|
| 285 |
+
"name": self.name,
|
| 286 |
+
"carrier": list(self.V),
|
| 287 |
+
"gradients": [g.name for g in self.gradients],
|
| 288 |
+
"theta": self.theta,
|
| 289 |
+
"forced_conditions": forced,
|
| 290 |
+
"closure": closure,
|
| 291 |
+
"fixed_points": fps,
|
| 292 |
+
"cycles": cycles,
|
| 293 |
+
"involutions": involutions,
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
def report(self) -> str:
|
| 297 |
+
"""Full derivation as human-readable report."""
|
| 298 |
+
fc = self.derive_forced_conditions()
|
| 299 |
+
|
| 300 |
+
lines = [
|
| 301 |
+
"",
|
| 302 |
+
"=" * 62,
|
| 303 |
+
f" PROPAGATION ENGINE: {self.name.upper()}",
|
| 304 |
+
"=" * 62,
|
| 305 |
+
f" Carrier V : {set(fc['carrier'])}",
|
| 306 |
+
f" Gradients Ξ : {fc['gradients']}",
|
| 307 |
+
f" Threshold ΞΈ : {fc['theta']}",
|
| 308 |
+
"=" * 62,
|
| 309 |
+
"",
|
| 310 |
+
" DERIVED BOUNDARY CONDITIONS",
|
| 311 |
+
" (not assumed β forced by V and Ξ)",
|
| 312 |
+
"",
|
| 313 |
+
]
|
| 314 |
+
|
| 315 |
+
for i, (tag, condition) in enumerate(fc["forced_conditions"], 1):
|
| 316 |
+
lines.append(f" [{i}] {tag}")
|
| 317 |
+
words = condition.split()
|
| 318 |
+
line = " "
|
| 319 |
+
for word in words:
|
| 320 |
+
if len(line) + len(word) + 1 > 60:
|
| 321 |
+
lines.append(line)
|
| 322 |
+
line = " " + word + " "
|
| 323 |
+
else:
|
| 324 |
+
line += word + " "
|
| 325 |
+
lines.append(line.rstrip())
|
| 326 |
+
lines.append("")
|
| 327 |
+
|
| 328 |
+
lines += [
|
| 329 |
+
"=" * 62,
|
| 330 |
+
" These conditions were not assumed.",
|
| 331 |
+
" They were derived by running P / G β Q.",
|
| 332 |
+
"=" * 62,
|
| 333 |
+
"",
|
| 334 |
+
]
|
| 335 |
+
return "\n".join(lines)
|
| 336 |
+
|
| 337 |
+
def as_training_text(self) -> str:
|
| 338 |
+
"""
|
| 339 |
+
Render derivation as training data.
|
| 340 |
+
This is what the LM learns β the procedure, not descriptions of it.
|
| 341 |
+
"""
|
| 342 |
+
fc = self.derive_forced_conditions()
|
| 343 |
+
lines = [
|
| 344 |
+
f"DOMAIN: {self.name}",
|
| 345 |
+
f"CARRIER: {sorted(set(fc['carrier']), key=str)}",
|
| 346 |
+
f"GRADIENTS: {fc['gradients']}",
|
| 347 |
+
f"THETA: {fc['theta']}",
|
| 348 |
+
"---",
|
| 349 |
+
]
|
| 350 |
+
|
| 351 |
+
# Fixed points
|
| 352 |
+
for g_name, fp_list in fc["fixed_points"].items():
|
| 353 |
+
lines.append(f"FIXED_POINTS[{g_name}]: {fp_list}")
|
| 354 |
+
|
| 355 |
+
# Closure
|
| 356 |
+
for g_name, r in fc["closure"].items():
|
| 357 |
+
if r["closed"]:
|
| 358 |
+
lines.append(f"CLOSURE[{g_name}]: HOLDS")
|
| 359 |
+
else:
|
| 360 |
+
lines.append(f"CLOSURE[{g_name}]: VIOLATED β extend to include {r['extension_required']}")
|
| 361 |
+
|
| 362 |
+
# Involutions
|
| 363 |
+
for g_name, inv in fc["involutions"].items():
|
| 364 |
+
tag = "YES" if inv["is_involution"] else "NO"
|
| 365 |
+
lines.append(f"INVOLUTION[{g_name}]: {tag}")
|
| 366 |
+
|
| 367 |
+
# Cycles
|
| 368 |
+
for g_name, cyc in fc["cycles"].items():
|
| 369 |
+
lengths = set(v for v in cyc.values() if v is not None)
|
| 370 |
+
if lengths:
|
| 371 |
+
k = next(iter(lengths)) if len(lengths) == 1 else "mixed"
|
| 372 |
+
lines.append(f"CYCLE[{g_name}]: length={k}")
|
| 373 |
+
|
| 374 |
+
# Forced conditions
|
| 375 |
+
lines.append("FORCED:")
|
| 376 |
+
for tag, _ in fc["forced_conditions"]:
|
| 377 |
+
lines.append(f" {tag}")
|
| 378 |
+
|
| 379 |
+
lines.append("END")
|
| 380 |
+
return "\n".join(lines)
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
# =============================================================================
|
| 384 |
+
# FACTORY β build engines from KNOWN_SYSTEMS
|
| 385 |
+
# =============================================================================
|
| 386 |
+
|
| 387 |
+
def engine_from_system(system_name: str) -> PropagationEngine:
|
| 388 |
+
"""Build an engine from a registered system in KNOWN_SYSTEMS."""
|
| 389 |
+
if system_name not in KNOWN_SYSTEMS:
|
| 390 |
+
available = list(KNOWN_SYSTEMS.keys())
|
| 391 |
+
raise ValueError(f"Unknown system {system_name!r}. Available: {available}")
|
| 392 |
+
sys_def = KNOWN_SYSTEMS[system_name]
|
| 393 |
+
return PropagationEngine(
|
| 394 |
+
carrier=sys_def["carrier"],
|
| 395 |
+
gradients=sys_def["gradients"](),
|
| 396 |
+
name=system_name,
|
| 397 |
+
)
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
# =============================================================================
|
| 401 |
+
# DEMONSTRATION
|
| 402 |
+
# =============================================================================
|
| 403 |
+
|
| 404 |
+
if __name__ == "__main__":
|
| 405 |
+
print("\n" + "="*62)
|
| 406 |
+
print(" PROPAGATION LOGIC INFERENCE ENGINE")
|
| 407 |
+
print(" P / G β Q")
|
| 408 |
+
print("="*62)
|
| 409 |
+
|
| 410 |
+
# ββ 1. Classical logic βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 411 |
+
e1 = engine_from_system("classical_logic")
|
| 412 |
+
print(e1.report())
|
| 413 |
+
|
| 414 |
+
# ββ 2. Three-valued logic ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 415 |
+
e2 = engine_from_system("three_valued_logic")
|
| 416 |
+
print(e2.report())
|
| 417 |
+
|
| 418 |
+
# ββ 3. Novel carrier: colors β never seen in training ββββββββββββββββββ
|
| 419 |
+
print("="*62)
|
| 420 |
+
print(" NOVEL CARRIER: {red, green, blue}")
|
| 421 |
+
print(" This carrier was NEVER in any training data.")
|
| 422 |
+
print(" The engine derives its boundary conditions from scratch.")
|
| 423 |
+
print("="*62)
|
| 424 |
+
e3 = PropagationEngine(
|
| 425 |
+
carrier={"red", "green", "blue"},
|
| 426 |
+
gradients=[
|
| 427 |
+
G_custom("complement", {"red": "green", "green": "blue", "blue": "red"}),
|
| 428 |
+
G_id(),
|
| 429 |
+
],
|
| 430 |
+
theta=1.0,
|
| 431 |
+
name="color_carrier",
|
| 432 |
+
)
|
| 433 |
+
print(e3.report())
|
| 434 |
+
|
| 435 |
+
# ββ 4. Forced extension: β β β€ ββββββββββββββββββββββββββββββββββββββββ
|
| 436 |
+
print("="*62)
|
| 437 |
+
print(" FORCED EXTENSION: V={0,1,2,3} + predecessor")
|
| 438 |
+
print(" Demonstrates how β β β€ is forced by closure violation.")
|
| 439 |
+
print("="*62)
|
| 440 |
+
from pl.core import G_pred
|
| 441 |
+
e4 = PropagationEngine(
|
| 442 |
+
carrier={0, 1, 2, 3},
|
| 443 |
+
gradients=[G_pred(), G_id()],
|
| 444 |
+
theta=1.0,
|
| 445 |
+
name="N_closure_violation",
|
| 446 |
+
)
|
| 447 |
+
print(e4.report())
|
| 448 |
+
|
| 449 |
+
# ββ 5. Modular arithmetic ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 450 |
+
e5 = engine_from_system("Z4")
|
| 451 |
+
print(e5.report())
|
| 452 |
+
|
| 453 |
+
print("\n Training text format (for the LM):")
|
| 454 |
+
print("-" * 40)
|
| 455 |
+
print(e1.as_training_text())
|
mechanism_base_v1.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7212a180679ab3d408d6c23d3904b9b9d3b06c9a34b4287c6e788910738daafb
|
| 3 |
+
size 42349189
|
mechanism_tokenizer/tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
model.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
model.py β MechanismBase
|
| 3 |
+
========================
|
| 4 |
+
|
| 5 |
+
The transformer decoder implementing P / G β Q.
|
| 6 |
+
|
| 7 |
+
Two configurations:
|
| 8 |
+
SmallConfig (~10M params) β appropriate for ~200K tokens.
|
| 9 |
+
Generalizes. Recommended for current corpus.
|
| 10 |
+
|
| 11 |
+
FullConfig (~235M params) β appropriate for ~2M+ tokens.
|
| 12 |
+
Use after expanding the training corpus.
|
| 13 |
+
|
| 14 |
+
Architecture maps to PL terminology:
|
| 15 |
+
wte β token embedding: seeds patterns P with initial loaded history
|
| 16 |
+
wpe β position encoding: adds positional loaded history
|
| 17 |
+
PropagationBlock β one complete P / G β Q step:
|
| 18 |
+
attention = gradient family G applied to P
|
| 19 |
+
residual = loaded history H_P accumulating
|
| 20 |
+
pre-norm = coherence check before each propagation
|
| 21 |
+
MLP = reconfiguration toward coherent state
|
| 22 |
+
ln_f β final coherence check
|
| 23 |
+
lm_head β output: weight-tied to wte (same carrier in and out)
|
| 24 |
+
|
| 25 |
+
Parameter counts (approximate):
|
| 26 |
+
SmallConfig: 10.5M params
|
| 27 |
+
FullConfig: 235.0M params
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
import torch
|
| 31 |
+
import torch.nn as nn
|
| 32 |
+
import torch.nn.functional as F
|
| 33 |
+
from dataclasses import dataclass
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# =============================================================================
|
| 37 |
+
# CONFIGURATIONS
|
| 38 |
+
# =============================================================================
|
| 39 |
+
|
| 40 |
+
@dataclass
|
| 41 |
+
class SmallConfig:
|
| 42 |
+
"""
|
| 43 |
+
~10M params. Appropriate for 100Kβ500K tokens.
|
| 44 |
+
This is the working configuration for the current corpus (~200K tokens).
|
| 45 |
+
Trains in ~30 minutes on RTX 4060 Ti.
|
| 46 |
+
Will generalize, not just memorize.
|
| 47 |
+
"""
|
| 48 |
+
vocab_size: int = 16384 # Carrier V β BPE tokenizer
|
| 49 |
+
n_embd: int = 256 # Loaded history vector dimension
|
| 50 |
+
n_layer: int = 8 # Propagation steps
|
| 51 |
+
n_head: int = 8 # Gradient families per step
|
| 52 |
+
block_size: int = 256 # Context window
|
| 53 |
+
dropout: float = 0.1
|
| 54 |
+
name: str = "SmallBase"
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclass
|
| 58 |
+
class MediumConfig:
|
| 59 |
+
"""
|
| 60 |
+
~50M params. Appropriate for 500Kβ2M tokens.
|
| 61 |
+
Use after expanding generate_data.py to produce more derivation traces.
|
| 62 |
+
Trains in ~2-3 hours on RTX 4060 Ti.
|
| 63 |
+
"""
|
| 64 |
+
vocab_size: int = 16384
|
| 65 |
+
n_embd: int = 512
|
| 66 |
+
n_layer: int = 12
|
| 67 |
+
n_head: int = 8
|
| 68 |
+
block_size: int = 256
|
| 69 |
+
dropout: float = 0.1
|
| 70 |
+
name: str = "MediumBase"
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
@dataclass
|
| 74 |
+
class FullConfig:
|
| 75 |
+
"""
|
| 76 |
+
~235M params. The full AGI Base V1.
|
| 77 |
+
Appropriate for 2M+ tokens.
|
| 78 |
+
Requires expanding generate_data.py significantly (see comments there).
|
| 79 |
+
Trains in ~6 hours on RTX 4060 Ti when data is sufficient.
|
| 80 |
+
"""
|
| 81 |
+
vocab_size: int = 16384
|
| 82 |
+
n_embd: int = 1024
|
| 83 |
+
n_layer: int = 16
|
| 84 |
+
n_head: int = 16
|
| 85 |
+
block_size: int = 256
|
| 86 |
+
dropout: float = 0.1
|
| 87 |
+
name: str = "FullBase"
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# Default: SmallConfig for the current corpus
|
| 91 |
+
MechanismConfig = SmallConfig
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
# =============================================================================
|
| 95 |
+
# PROPAGATION BLOCK
|
| 96 |
+
# =============================================================================
|
| 97 |
+
|
| 98 |
+
class PropagationBlock(nn.Module):
|
| 99 |
+
"""
|
| 100 |
+
One complete P / G β Q propagation step.
|
| 101 |
+
|
| 102 |
+
Attention : gradient family G applied to pattern P
|
| 103 |
+
Residual : loaded history H_P accumulating
|
| 104 |
+
LayerNorm : coherence threshold check (pre-norm: check BEFORE propagating)
|
| 105 |
+
MLP : reconfiguration toward coherent state
|
| 106 |
+
"""
|
| 107 |
+
|
| 108 |
+
def __init__(self, config):
|
| 109 |
+
super().__init__()
|
| 110 |
+
self.ln1 = nn.LayerNorm(config.n_embd)
|
| 111 |
+
self.attn = nn.MultiheadAttention(
|
| 112 |
+
config.n_embd,
|
| 113 |
+
config.n_head,
|
| 114 |
+
dropout=config.dropout,
|
| 115 |
+
batch_first=True,
|
| 116 |
+
)
|
| 117 |
+
self.ln2 = nn.LayerNorm(config.n_embd)
|
| 118 |
+
self.mlp = nn.Sequential(
|
| 119 |
+
nn.Linear(config.n_embd, 4 * config.n_embd),
|
| 120 |
+
nn.GELU(),
|
| 121 |
+
nn.Linear(4 * config.n_embd, config.n_embd),
|
| 122 |
+
nn.Dropout(config.dropout),
|
| 123 |
+
)
|
| 124 |
+
self.drop = nn.Dropout(config.dropout)
|
| 125 |
+
|
| 126 |
+
def forward(self, x, attn_mask=None):
|
| 127 |
+
# Pre-norm: coherence check before gradient application
|
| 128 |
+
normed = self.ln1(x)
|
| 129 |
+
attn_out, _ = self.attn(
|
| 130 |
+
normed, normed, normed,
|
| 131 |
+
attn_mask=attn_mask,
|
| 132 |
+
need_weights=False,
|
| 133 |
+
)
|
| 134 |
+
# Residual accumulates loaded history
|
| 135 |
+
x = x + self.drop(attn_out)
|
| 136 |
+
x = x + self.mlp(self.ln2(x))
|
| 137 |
+
return x
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
# =============================================================================
|
| 141 |
+
# MECHANISMBASE
|
| 142 |
+
# =============================================================================
|
| 143 |
+
|
| 144 |
+
class MechanismBase(nn.Module):
|
| 145 |
+
"""
|
| 146 |
+
The mechanism instantiated in the weight carrier.
|
| 147 |
+
|
| 148 |
+
wte : token embedding β seeds patterns
|
| 149 |
+
wpe : position encoding β adds positional loaded history
|
| 150 |
+
h : propagation blocks
|
| 151 |
+
ln_f : final coherence check
|
| 152 |
+
lm_head : output (weight-tied to wte)
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
def __init__(self, config):
|
| 156 |
+
super().__init__()
|
| 157 |
+
self.config = config
|
| 158 |
+
|
| 159 |
+
self.wte = nn.Embedding(config.vocab_size, config.n_embd)
|
| 160 |
+
self.wpe = nn.Embedding(config.block_size, config.n_embd)
|
| 161 |
+
self.drop = nn.Dropout(config.dropout)
|
| 162 |
+
self.h = nn.ModuleList(
|
| 163 |
+
[PropagationBlock(config) for _ in range(config.n_layer)]
|
| 164 |
+
)
|
| 165 |
+
self.ln_f = nn.LayerNorm(config.n_embd)
|
| 166 |
+
self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
|
| 167 |
+
|
| 168 |
+
# Weight tying: input and output in the same carrier
|
| 169 |
+
self.lm_head.weight = self.wte.weight
|
| 170 |
+
|
| 171 |
+
self.apply(self._init_weights)
|
| 172 |
+
|
| 173 |
+
def _init_weights(self, module):
|
| 174 |
+
if isinstance(module, nn.Linear):
|
| 175 |
+
nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 176 |
+
if module.bias is not None:
|
| 177 |
+
nn.init.zeros_(module.bias)
|
| 178 |
+
elif isinstance(module, nn.Embedding):
|
| 179 |
+
nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 180 |
+
|
| 181 |
+
def forward(self, idx, targets=None):
|
| 182 |
+
B, T = idx.shape
|
| 183 |
+
assert T <= self.config.block_size, \
|
| 184 |
+
f"Sequence length {T} exceeds block_size {self.config.block_size}"
|
| 185 |
+
|
| 186 |
+
positions = torch.arange(T, device=idx.device)
|
| 187 |
+
x = self.drop(self.wte(idx) + self.wpe(positions))
|
| 188 |
+
|
| 189 |
+
# Causal mask: patterns attend only to prior loaded history
|
| 190 |
+
causal_mask = nn.Transformer.generate_square_subsequent_mask(
|
| 191 |
+
T, device=idx.device
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
for block in self.h:
|
| 195 |
+
x = block(x, attn_mask=causal_mask)
|
| 196 |
+
|
| 197 |
+
x = self.ln_f(x)
|
| 198 |
+
logits = self.lm_head(x)
|
| 199 |
+
|
| 200 |
+
loss = None
|
| 201 |
+
if targets is not None:
|
| 202 |
+
loss = F.cross_entropy(
|
| 203 |
+
logits.view(-1, logits.size(-1)),
|
| 204 |
+
targets.view(-1),
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
return logits, loss
|
| 208 |
+
|
| 209 |
+
@torch.no_grad()
|
| 210 |
+
def generate(
|
| 211 |
+
self,
|
| 212 |
+
idx,
|
| 213 |
+
max_new_tokens: int = 200,
|
| 214 |
+
temperature: float = 0.8,
|
| 215 |
+
top_k: int = 50,
|
| 216 |
+
top_p: float = 0.9,
|
| 217 |
+
):
|
| 218 |
+
"""
|
| 219 |
+
Autoregressive generation with temperature + top-k + top-p sampling.
|
| 220 |
+
"""
|
| 221 |
+
self.eval()
|
| 222 |
+
for _ in range(max_new_tokens):
|
| 223 |
+
x = idx[:, -self.config.block_size:]
|
| 224 |
+
logits, _ = self(x, None)
|
| 225 |
+
next_logits = logits[0, -1, :] / temperature
|
| 226 |
+
|
| 227 |
+
# Top-k
|
| 228 |
+
if top_k > 0:
|
| 229 |
+
k = min(top_k, next_logits.size(-1))
|
| 230 |
+
topk_vals, _ = torch.topk(next_logits, k)
|
| 231 |
+
next_logits[next_logits < topk_vals[-1]] = float("-inf")
|
| 232 |
+
|
| 233 |
+
# Top-p
|
| 234 |
+
if top_p < 1.0:
|
| 235 |
+
sorted_logits, sorted_idx = torch.sort(next_logits, descending=True)
|
| 236 |
+
cumprobs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
|
| 237 |
+
remove = (cumprobs - F.softmax(sorted_logits, dim=-1)) > top_p
|
| 238 |
+
sorted_logits[remove] = float("-inf")
|
| 239 |
+
next_logits = torch.zeros_like(next_logits).scatter_(
|
| 240 |
+
0, sorted_idx, sorted_logits
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
probs = F.softmax(next_logits, dim=-1)
|
| 244 |
+
next_id = torch.multinomial(probs, num_samples=1)
|
| 245 |
+
idx = torch.cat([idx, next_id.unsqueeze(0)], dim=1)
|
| 246 |
+
|
| 247 |
+
return idx
|
| 248 |
+
|
| 249 |
+
def count_parameters(self) -> int:
|
| 250 |
+
return sum(p.numel() for p in self.parameters())
|
| 251 |
+
|
| 252 |
+
def parameter_summary(self) -> str:
|
| 253 |
+
total = self.count_parameters()
|
| 254 |
+
embed = self.wte.weight.numel()
|
| 255 |
+
lines = [
|
| 256 |
+
f" Configuration: {self.config.name}",
|
| 257 |
+
f" Total params: {total:,}",
|
| 258 |
+
f" Embed params: {embed:,} ({embed/total:.1%} of total)",
|
| 259 |
+
f" n_embd={self.config.n_embd}, "
|
| 260 |
+
f"n_layer={self.config.n_layer}, "
|
| 261 |
+
f"n_head={self.config.n_head}",
|
| 262 |
+
]
|
| 263 |
+
return "\n".join(lines)
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
if __name__ == "__main__":
|
| 267 |
+
for ConfigClass in [SmallConfig, MediumConfig, FullConfig]:
|
| 268 |
+
config = ConfigClass()
|
| 269 |
+
model = MechanismBase(config)
|
| 270 |
+
print(model.parameter_summary())
|
| 271 |
+
print()
|
pl/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
pl β Propagation Logic
|
| 3 |
+
======================
|
| 4 |
+
|
| 5 |
+
The mechanism: P / G β Q
|
| 6 |
+
|
| 7 |
+
Public API:
|
| 8 |
+
from pl.core import Pattern, Context, Gradient, seed
|
| 9 |
+
from pl.core import G_neg, G_id, G_and, G_or, G_custom
|
| 10 |
+
from pl.numbers import demonstrate_full_tower
|
| 11 |
+
from pl.calculus import G_derivative, G_integral, verify_derivative_fixed_point
|
| 12 |
+
from pl.grammar import CoherenceChecker, G_number_agree
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
from pl.core import (
|
| 16 |
+
Pattern, Context, Gradient, PropagationChain,
|
| 17 |
+
seed,
|
| 18 |
+
G_neg, G_id, G_and, G_or,
|
| 19 |
+
G_fuzzy_neg, G_lukasiewicz_and,
|
| 20 |
+
G_succ, G_pred, G_double, G_halve, G_sqrt, G_neg_sqrt,
|
| 21 |
+
G_mod, G_custom,
|
| 22 |
+
KNOWN_SYSTEMS,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
__all__ = [
|
| 26 |
+
"Pattern", "Context", "Gradient", "PropagationChain",
|
| 27 |
+
"seed",
|
| 28 |
+
"G_neg", "G_id", "G_and", "G_or",
|
| 29 |
+
"G_fuzzy_neg", "G_lukasiewicz_and",
|
| 30 |
+
"G_succ", "G_pred", "G_double", "G_halve", "G_sqrt", "G_neg_sqrt",
|
| 31 |
+
"G_mod", "G_custom",
|
| 32 |
+
"KNOWN_SYSTEMS",
|
| 33 |
+
]
|
pl/calculus.py
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
pl/calculus.py β Calculus as Coherence
|
| 3 |
+
=======================================
|
| 4 |
+
|
| 5 |
+
Derivatives and integrals are not separate mathematical objects.
|
| 6 |
+
They are P / G β Q in the real carrier V = β with specific gradient families.
|
| 7 |
+
|
| 8 |
+
The derivative: the gradient family that linearizes propagation.
|
| 9 |
+
f'(x) is the load the function generates per unit step.
|
| 10 |
+
e^x is the fixed point of this gradient family.
|
| 11 |
+
Fixed point = e^x is its own derivative.
|
| 12 |
+
This is not magic. It is forced.
|
| 13 |
+
|
| 14 |
+
The integral: accumulation of load across the carrier.
|
| 15 |
+
β« f dx is the total load accumulated from a to b.
|
| 16 |
+
Fundamental theorem = derivative and integral are inverses
|
| 17 |
+
in this carrier. Forced by the structure of β.
|
| 18 |
+
|
| 19 |
+
This module implements:
|
| 20 |
+
1. Numerical differentiation as P / G β Q
|
| 21 |
+
2. Numerical integration as accumulated propagation
|
| 22 |
+
3. The fixed point theorem for d/dx
|
| 23 |
+
4. Taylor series as recursive load accumulation
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
from __future__ import annotations
|
| 27 |
+
from dataclasses import dataclass, field
|
| 28 |
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
| 29 |
+
import math
|
| 30 |
+
|
| 31 |
+
from pl.core import Pattern, Gradient, Context, seed
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# =============================================================================
|
| 35 |
+
# FUNCTION AS PATTERN
|
| 36 |
+
# =============================================================================
|
| 37 |
+
|
| 38 |
+
@dataclass
|
| 39 |
+
class FunctionPattern(Pattern):
|
| 40 |
+
"""
|
| 41 |
+
A pattern whose designation is a function f: β β β.
|
| 42 |
+
|
| 43 |
+
The function is the v_P component.
|
| 44 |
+
The load accumulates as we apply derivative gradients.
|
| 45 |
+
f'' has more load than f'.
|
| 46 |
+
f^(n) has more load than f^(n-1).
|
| 47 |
+
This matches: higher derivatives are harder to compute.
|
| 48 |
+
"""
|
| 49 |
+
func_name: str = "f"
|
| 50 |
+
|
| 51 |
+
def evaluate(self, x: float) -> float:
|
| 52 |
+
"""Evaluate the function at x."""
|
| 53 |
+
return self.v(x)
|
| 54 |
+
|
| 55 |
+
def __repr__(self) -> str:
|
| 56 |
+
return f"FuncPattern({self.func_name}, L={self.L:.2f})"
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def func_seed(f: Callable, name: str = "f") -> FunctionPattern:
|
| 60 |
+
"""Create a seed function pattern."""
|
| 61 |
+
return FunctionPattern(v=f, L=0.0, func_name=name)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# =============================================================================
|
| 65 |
+
# DERIVATIVE GRADIENT
|
| 66 |
+
# =============================================================================
|
| 67 |
+
|
| 68 |
+
def G_derivative(h: float = 1e-7) -> Gradient:
|
| 69 |
+
"""
|
| 70 |
+
The derivative gradient: f β f'
|
| 71 |
+
|
| 72 |
+
Numerically: f'(x) β (f(x+h) - f(x-h)) / (2h)
|
| 73 |
+
|
| 74 |
+
This is a gradient field on the carrier of differentiable functions.
|
| 75 |
+
Applying it once gives the first derivative.
|
| 76 |
+
Applying it n times gives the nth derivative.
|
| 77 |
+
The load accumulates: L increases by 1 per application.
|
| 78 |
+
|
| 79 |
+
Key result: e^x is the fixed point of this gradient.
|
| 80 |
+
G_derivative(e^x) = e^x.
|
| 81 |
+
This is not assumed. It falls out of the structure of β and G_derivative.
|
| 82 |
+
"""
|
| 83 |
+
def differentiate(f: Callable) -> Callable:
|
| 84 |
+
def df(x: float) -> float:
|
| 85 |
+
return (f(x + h) - f(x - h)) / (2 * h)
|
| 86 |
+
return df
|
| 87 |
+
|
| 88 |
+
return Gradient(
|
| 89 |
+
name="d/dx",
|
| 90 |
+
transform=differentiate,
|
| 91 |
+
cost=1.0,
|
| 92 |
+
description=f"Derivative: f β f' (numerical, h={h})",
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def G_integral(a: float = 0.0, n_steps: int = 1000) -> Gradient:
|
| 97 |
+
"""
|
| 98 |
+
The integral gradient: f β β«_a^x f(t) dt
|
| 99 |
+
|
| 100 |
+
This is accumulation of load across the carrier from a to x.
|
| 101 |
+
Applying derivative then integral returns to origin (within numerical precision).
|
| 102 |
+
Applying integral then derivative also returns to origin.
|
| 103 |
+
Fundamental theorem = these two gradients are inverses.
|
| 104 |
+
Derived from the structure of β, not assumed.
|
| 105 |
+
"""
|
| 106 |
+
def integrate(f: Callable) -> Callable:
|
| 107 |
+
def F(x: float) -> float:
|
| 108 |
+
if x == a:
|
| 109 |
+
return 0.0
|
| 110 |
+
n = max(n_steps, int(abs(x - a) * 100))
|
| 111 |
+
dx = (x - a) / n
|
| 112 |
+
total = 0.0
|
| 113 |
+
for i in range(n):
|
| 114 |
+
xi = a + (i + 0.5) * dx
|
| 115 |
+
total += f(xi) * dx
|
| 116 |
+
return total
|
| 117 |
+
return F
|
| 118 |
+
|
| 119 |
+
return Gradient(
|
| 120 |
+
name=f"β«_[{a}]^x",
|
| 121 |
+
transform=integrate,
|
| 122 |
+
cost=1.0,
|
| 123 |
+
description=f"Integral from {a}: f β β«_[{a}]^x f(t) dt",
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
# =============================================================================
|
| 128 |
+
# FIXED POINT THEOREM
|
| 129 |
+
# =============================================================================
|
| 130 |
+
|
| 131 |
+
def verify_derivative_fixed_point(
|
| 132 |
+
func: Callable,
|
| 133 |
+
func_name: str,
|
| 134 |
+
test_points: List[float] = None,
|
| 135 |
+
h: float = 1e-7,
|
| 136 |
+
tolerance: float = 1e-4,
|
| 137 |
+
) -> Dict:
|
| 138 |
+
"""
|
| 139 |
+
Verify whether a function is a fixed point of d/dx.
|
| 140 |
+
|
| 141 |
+
Fixed point: G_derivative(f) β f
|
| 142 |
+
i.e., f'(x) β f(x) for all x in the carrier.
|
| 143 |
+
|
| 144 |
+
The exponential e^x is the unique (up to scaling) fixed point.
|
| 145 |
+
This is a forced condition of the derivative gradient on β.
|
| 146 |
+
"""
|
| 147 |
+
if test_points is None:
|
| 148 |
+
test_points = [-2.0, -1.0, 0.0, 0.5, 1.0, 2.0]
|
| 149 |
+
|
| 150 |
+
g = G_derivative(h)
|
| 151 |
+
|
| 152 |
+
# Differentiate: f β f'
|
| 153 |
+
fp = func_seed(func, func_name)
|
| 154 |
+
fp_prime = FunctionPattern(
|
| 155 |
+
v=g.transform(func),
|
| 156 |
+
L=fp.L + g.cost(fp),
|
| 157 |
+
func_name=f"{func_name}'",
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
errors = []
|
| 161 |
+
for x in test_points:
|
| 162 |
+
fx = func(x)
|
| 163 |
+
fpx = fp_prime.v(x)
|
| 164 |
+
err = abs(fx - fpx)
|
| 165 |
+
errors.append((x, fx, fpx, err))
|
| 166 |
+
|
| 167 |
+
max_error = max(e for _, _, _, e in errors)
|
| 168 |
+
is_fixed = max_error < tolerance
|
| 169 |
+
|
| 170 |
+
return {
|
| 171 |
+
"function": func_name,
|
| 172 |
+
"is_fixed_point": is_fixed,
|
| 173 |
+
"max_error": max_error,
|
| 174 |
+
"tolerance": tolerance,
|
| 175 |
+
"test_points": errors,
|
| 176 |
+
"interpretation": (
|
| 177 |
+
f"{func_name} IS a fixed point of d/dx. "
|
| 178 |
+
f"f'(x) = f(x) for all x (max error: {max_error:.2e}). "
|
| 179 |
+
f"This is the exponential fixed point β forced by the β carrier."
|
| 180 |
+
if is_fixed else
|
| 181 |
+
f"{func_name} is NOT a fixed point of d/dx. "
|
| 182 |
+
f"f'(x) β f(x) (max error: {max_error:.2e})."
|
| 183 |
+
),
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def find_fixed_point_family(
|
| 188 |
+
seed_func: Callable,
|
| 189 |
+
n_iterations: int = 5,
|
| 190 |
+
h: float = 1e-7,
|
| 191 |
+
) -> Dict:
|
| 192 |
+
"""
|
| 193 |
+
Starting from a seed function, repeatedly apply d/dx.
|
| 194 |
+
What does the sequence converge to?
|
| 195 |
+
|
| 196 |
+
For e^x: it stays at e^x (fixed point, period 1).
|
| 197 |
+
For x^n: it degrades to 0 (no cycle, just decreasing degree).
|
| 198 |
+
For sin(x): it cycles through sin, cos, -sin, -cos (period 4).
|
| 199 |
+
|
| 200 |
+
The cycle structure of d/dx on function space is forced by β.
|
| 201 |
+
"""
|
| 202 |
+
g = G_derivative(h)
|
| 203 |
+
current_func = seed_func
|
| 204 |
+
chain = [seed_func]
|
| 205 |
+
|
| 206 |
+
for _ in range(n_iterations):
|
| 207 |
+
current_func = g.transform(current_func)
|
| 208 |
+
chain.append(current_func)
|
| 209 |
+
|
| 210 |
+
# Sample at x=1.0 to see the value sequence
|
| 211 |
+
value_chain = [f(1.0) for f in chain]
|
| 212 |
+
|
| 213 |
+
return {
|
| 214 |
+
"seed_value_at_1": value_chain[0],
|
| 215 |
+
"value_chain": value_chain,
|
| 216 |
+
"converging": abs(value_chain[-1]) < 1e-10,
|
| 217 |
+
"stable": abs(value_chain[-1] - value_chain[-2]) < 1e-6,
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
# =============================================================================
|
| 222 |
+
# TAYLOR SERIES AS RECURSIVE LOAD ACCUMULATION
|
| 223 |
+
# =============================================================================
|
| 224 |
+
|
| 225 |
+
def taylor_series(
|
| 226 |
+
f: Callable,
|
| 227 |
+
x0: float = 0.0,
|
| 228 |
+
n_terms: int = 8,
|
| 229 |
+
h: float = 1e-7,
|
| 230 |
+
) -> Dict:
|
| 231 |
+
"""
|
| 232 |
+
Taylor series as recursive load accumulation.
|
| 233 |
+
|
| 234 |
+
T_f(x) = Ξ£_{k=0}^{n} f^(k)(x0) / k! * (x - x0)^k
|
| 235 |
+
|
| 236 |
+
Each term is a propagation step:
|
| 237 |
+
- Apply d/dx once (load += 1)
|
| 238 |
+
- Evaluate at x0 (read the designation)
|
| 239 |
+
- Scale by 1/k! (coherence normalization)
|
| 240 |
+
|
| 241 |
+
The series is not a new object. It is the accumulation pattern
|
| 242 |
+
of the derivative gradient applied recursively.
|
| 243 |
+
|
| 244 |
+
Observation 2.2 (GΓΆdel connection):
|
| 245 |
+
The series for f(x) = 1/(1-x) diverges for |x| > 1.
|
| 246 |
+
The carrier cannot support the load.
|
| 247 |
+
This is the mechanism's version of incompleteness:
|
| 248 |
+
some patterns carry more load than the context can support.
|
| 249 |
+
"""
|
| 250 |
+
g = G_derivative(h)
|
| 251 |
+
current_func = f
|
| 252 |
+
|
| 253 |
+
derivatives_at_x0 = []
|
| 254 |
+
current_p = func_seed(f, "f")
|
| 255 |
+
|
| 256 |
+
for k in range(n_terms):
|
| 257 |
+
val = current_func(x0)
|
| 258 |
+
derivatives_at_x0.append(val)
|
| 259 |
+
next_func = g.transform(current_func)
|
| 260 |
+
current_p = g.propagate(current_p)
|
| 261 |
+
current_func = next_func
|
| 262 |
+
|
| 263 |
+
# Coefficients: f^(k)(x0) / k!
|
| 264 |
+
factorial = 1
|
| 265 |
+
coefficients = []
|
| 266 |
+
for k, d in enumerate(derivatives_at_x0):
|
| 267 |
+
if k > 0:
|
| 268 |
+
factorial *= k
|
| 269 |
+
coefficients.append(d / factorial)
|
| 270 |
+
|
| 271 |
+
def taylor_approx(x: float) -> float:
|
| 272 |
+
result = 0.0
|
| 273 |
+
for k, c in enumerate(coefficients):
|
| 274 |
+
result += c * (x - x0) ** k
|
| 275 |
+
return result
|
| 276 |
+
|
| 277 |
+
return {
|
| 278 |
+
"expansion_point": x0,
|
| 279 |
+
"n_terms": n_terms,
|
| 280 |
+
"coefficients": coefficients,
|
| 281 |
+
"derivatives_at_x0": derivatives_at_x0,
|
| 282 |
+
"approximation": taylor_approx,
|
| 283 |
+
"load_at_nth_term": float(n_terms), # L_P after n derivative applications
|
| 284 |
+
"interpretation": (
|
| 285 |
+
f"Taylor series = recursive load accumulation. "
|
| 286 |
+
f"Each term adds 1 unit of load. "
|
| 287 |
+
f"After {n_terms} terms, pattern has load L={n_terms}. "
|
| 288 |
+
f"If the carrier (convergence radius) cannot support this load, "
|
| 289 |
+
f"the series diverges. Load > ΞΈ β incoherence."
|
| 290 |
+
),
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
# =============================================================================
|
| 295 |
+
# FUNDAMENTAL THEOREM AS INVERSE GRADIENTS
|
| 296 |
+
# =============================================================================
|
| 297 |
+
|
| 298 |
+
def demonstrate_fundamental_theorem(
|
| 299 |
+
f: Callable,
|
| 300 |
+
func_name: str,
|
| 301 |
+
a: float = 0.0,
|
| 302 |
+
b: float = 2.0,
|
| 303 |
+
h: float = 1e-7,
|
| 304 |
+
) -> Dict:
|
| 305 |
+
"""
|
| 306 |
+
Fundamental theorem of calculus as inverse gradients.
|
| 307 |
+
|
| 308 |
+
d/dx(β«_a^x f(t)dt) = f(x) [differentiate integral = original]
|
| 309 |
+
β«_a^x (df/dt) dt = f(x) - f(a) [integrate derivative = net change]
|
| 310 |
+
|
| 311 |
+
These are not separate theorems. They are one statement:
|
| 312 |
+
G_derivative and G_integral are inverses on the β carrier.
|
| 313 |
+
|
| 314 |
+
The gradient family {d/dx, β«} forms a coherent pair.
|
| 315 |
+
Together they leave the pattern unchanged (up to constant).
|
| 316 |
+
This is forced by the structure of β β not assumed.
|
| 317 |
+
"""
|
| 318 |
+
g_diff = G_derivative(h)
|
| 319 |
+
g_int = G_integral(a)
|
| 320 |
+
|
| 321 |
+
# Chain 1: integrate then differentiate
|
| 322 |
+
F = g_int.transform(f) # β«_a^x f(t) dt
|
| 323 |
+
dF = g_diff.transform(F) # d/dx[β«_a^x f(t) dt] should = f
|
| 324 |
+
|
| 325 |
+
# Chain 2: differentiate then integrate
|
| 326 |
+
df = g_diff.transform(f) # f'(x)
|
| 327 |
+
int_df = g_int.transform(df) # β«_a^x f'(t) dt should = f(x) - f(a)
|
| 328 |
+
|
| 329 |
+
# Test at several points
|
| 330 |
+
test_xs = [a + (b - a) * i / 5 for i in range(6)]
|
| 331 |
+
chain1_errors = []
|
| 332 |
+
chain2_errors = []
|
| 333 |
+
|
| 334 |
+
f_at_a = f(a)
|
| 335 |
+
for x in test_xs:
|
| 336 |
+
# Chain 1: dF(x) should β f(x)
|
| 337 |
+
err1 = abs(dF(x) - f(x))
|
| 338 |
+
chain1_errors.append((x, f(x), dF(x), err1))
|
| 339 |
+
|
| 340 |
+
# Chain 2: int_df(x) should β f(x) - f(a)
|
| 341 |
+
expected = f(x) - f_at_a
|
| 342 |
+
err2 = abs(int_df(x) - expected)
|
| 343 |
+
chain2_errors.append((x, expected, int_df(x), err2))
|
| 344 |
+
|
| 345 |
+
max_err1 = max(e for _, _, _, e in chain1_errors)
|
| 346 |
+
max_err2 = max(e for _, _, _, e in chain2_errors)
|
| 347 |
+
|
| 348 |
+
return {
|
| 349 |
+
"function": func_name,
|
| 350 |
+
"interval": (a, b),
|
| 351 |
+
"chain1_result": "d/dx[β«f] = f",
|
| 352 |
+
"chain1_max_error": max_err1,
|
| 353 |
+
"chain1_holds": max_err1 < 1e-3,
|
| 354 |
+
"chain2_result": "β«[df/dx] = f(x) - f(a)",
|
| 355 |
+
"chain2_max_error": max_err2,
|
| 356 |
+
"chain2_holds": max_err2 < 1e-3,
|
| 357 |
+
"interpretation": (
|
| 358 |
+
"The fundamental theorem is the statement that "
|
| 359 |
+
"G_derivative and G_integral are inverses. "
|
| 360 |
+
"Applying both in sequence returns to origin (Β± constant). "
|
| 361 |
+
"This is a FORCED CONDITION of the β carrier. "
|
| 362 |
+
"The theorem does not need a separate proof β "
|
| 363 |
+
"it is a boundary condition of propagation on β."
|
| 364 |
+
),
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
if __name__ == "__main__":
|
| 369 |
+
print("\n" + "="*60)
|
| 370 |
+
print(" CALCULUS AS COHERENCE IN THE β CARRIER")
|
| 371 |
+
print("="*60)
|
| 372 |
+
|
| 373 |
+
# 1. e^x is a fixed point of d/dx
|
| 374 |
+
print("\n1. Fixed point test: e^x")
|
| 375 |
+
result = verify_derivative_fixed_point(math.exp, "e^x")
|
| 376 |
+
print(f" Fixed point: {result['is_fixed_point']}")
|
| 377 |
+
print(f" Max error: {result['max_error']:.2e}")
|
| 378 |
+
print(f" β {result['interpretation']}")
|
| 379 |
+
|
| 380 |
+
print("\n2. Fixed point test: x^2 (not a fixed point)")
|
| 381 |
+
result2 = verify_derivative_fixed_point(lambda x: x**2, "x^2")
|
| 382 |
+
print(f" Fixed point: {result2['is_fixed_point']}")
|
| 383 |
+
print(f" β {result2['interpretation']}")
|
| 384 |
+
|
| 385 |
+
print("\n3. sin(x) cycle under d/dx")
|
| 386 |
+
result3 = find_fixed_point_family(math.sin, n_iterations=8)
|
| 387 |
+
print(f" Values at x=1.0: {[round(v,3) for v in result3['value_chain']]}")
|
| 388 |
+
print(" β sin β cos β -sin β -cos β sin: 4-cycle, forced by β")
|
| 389 |
+
|
| 390 |
+
print("\n4. Fundamental theorem as inverse gradients")
|
| 391 |
+
result4 = demonstrate_fundamental_theorem(math.exp, "e^x")
|
| 392 |
+
print(f" d/dx[β«f] = f holds: {result4['chain1_holds']} (err={result4['chain1_max_error']:.2e})")
|
| 393 |
+
print(f" β«[f'] = f-f(a) holds: {result4['chain2_holds']} (err={result4['chain2_max_error']:.2e})")
|
pl/core.py
ADDED
|
@@ -0,0 +1,593 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
pl/core.py β Propagation Logic Core
|
| 3 |
+
=====================================
|
| 4 |
+
|
| 5 |
+
P / G β Q
|
| 6 |
+
|
| 7 |
+
The single primitive operator of Propagation Logic.
|
| 8 |
+
|
| 9 |
+
A loaded pattern P = (v_P, L_P) propagates through gradient field G
|
| 10 |
+
in context C = (Ξ_C, ΞΈ_C) to produce updated pattern Q.
|
| 11 |
+
|
| 12 |
+
Everything in this module is derived from that operator.
|
| 13 |
+
Nothing is assumed that is not forced by the mechanism.
|
| 14 |
+
|
| 15 |
+
Key insight (PL v13, Section 2.6):
|
| 16 |
+
G is not a different kind of thing from P.
|
| 17 |
+
G is P occupying the gradient role contextually.
|
| 18 |
+
All G is P post-boundary-imposition.
|
| 19 |
+
There is no view from outside.
|
| 20 |
+
This file is also P / G β Q.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
from __future__ import annotations
|
| 24 |
+
from dataclasses import dataclass, field
|
| 25 |
+
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
| 26 |
+
import math
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# =============================================================================
|
| 30 |
+
# LOADED PATTERN
|
| 31 |
+
# =============================================================================
|
| 32 |
+
|
| 33 |
+
@dataclass
|
| 34 |
+
class Pattern:
|
| 35 |
+
"""
|
| 36 |
+
P = (v_P, L_P)
|
| 37 |
+
|
| 38 |
+
Definition 2.1 (PL v13):
|
| 39 |
+
P = (v_P, H_P) with v_P β V, H_P a propagation history.
|
| 40 |
+
V is the carrier set.
|
| 41 |
+
L_P = |H_P| β₯ 0 is the informational load.
|
| 42 |
+
|
| 43 |
+
v_P : designation component β what the pattern currently designates.
|
| 44 |
+
In logic: 0 or 1.
|
| 45 |
+
In arithmetic: a number.
|
| 46 |
+
In language: a token or sentence.
|
| 47 |
+
In physics: a field value.
|
| 48 |
+
|
| 49 |
+
L_P : informational load β magnitude of accumulated propagation history.
|
| 50 |
+
L_P = 0 : seed state. No history. Propagates freely.
|
| 51 |
+
L_P > 0 : loaded. Has been through gradient fields.
|
| 52 |
+
The more it has been through, the harder to propagate further.
|
| 53 |
+
|
| 54 |
+
history : the full qualitative record (L_P is its magnitude).
|
| 55 |
+
Conceptually rich; computationally we track both.
|
| 56 |
+
"""
|
| 57 |
+
v: Any
|
| 58 |
+
L: float = 0.0
|
| 59 |
+
history: List[str] = field(default_factory=list)
|
| 60 |
+
|
| 61 |
+
def __repr__(self) -> str:
|
| 62 |
+
h = " β ".join(self.history[-3:]) if self.history else "β
"
|
| 63 |
+
return f"P(v={self.v!r}, L={self.L:.3f}, hist=[{h}])"
|
| 64 |
+
|
| 65 |
+
def __eq__(self, other: object) -> bool:
|
| 66 |
+
if not isinstance(other, Pattern):
|
| 67 |
+
return False
|
| 68 |
+
return self.v == other.v and abs(self.L - other.L) < 1e-9
|
| 69 |
+
|
| 70 |
+
def __hash__(self) -> int:
|
| 71 |
+
return hash((repr(self.v), round(self.L, 9)))
|
| 72 |
+
|
| 73 |
+
def is_seed(self) -> bool:
|
| 74 |
+
"""L_P = 0: no propagation history."""
|
| 75 |
+
return self.L == 0.0
|
| 76 |
+
|
| 77 |
+
def copy_with(self, v=None, L=None, history=None) -> "Pattern":
|
| 78 |
+
return Pattern(
|
| 79 |
+
v=v if v is not None else self.v,
|
| 80 |
+
L=L if L is not None else self.L,
|
| 81 |
+
history=history if history is not None else self.history.copy(),
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def seed(v: Any) -> Pattern:
|
| 86 |
+
"""Convenience: create a seed pattern with no history."""
|
| 87 |
+
return Pattern(v=v, L=0.0)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# =============================================================================
|
| 91 |
+
# CONTEXT
|
| 92 |
+
# =============================================================================
|
| 93 |
+
|
| 94 |
+
@dataclass
|
| 95 |
+
class Context:
|
| 96 |
+
"""
|
| 97 |
+
C = (Ξ_C, ΞΈ_C)
|
| 98 |
+
|
| 99 |
+
Definition 2.2 (PL v13):
|
| 100 |
+
A context is a pair C = (Ξ_C, ΞΈ_C) where:
|
| 101 |
+
Ξ_C : the set of gradient fields available in C
|
| 102 |
+
ΞΈ_C : the coherence threshold
|
| 103 |
+
|
| 104 |
+
Key derived quantities:
|
| 105 |
+
support(P, C) = min(L_P, ΞΈ_C)
|
| 106 |
+
demand(P, C) = max(0, L_P β ΞΈ_C)
|
| 107 |
+
|
| 108 |
+
demand = 0 β coherent (pattern is fully supported)
|
| 109 |
+
demand > 0 β incoherent (pattern needs more context than available)
|
| 110 |
+
|
| 111 |
+
Theorem 2.1 (the propagation rate theorem):
|
| 112 |
+
Among incoherent patterns, rate β 1/L_P.
|
| 113 |
+
Simpler patterns propagate faster.
|
| 114 |
+
This is Zipf's law, natural selection, why e^x is its own derivative β
|
| 115 |
+
all one theorem.
|
| 116 |
+
"""
|
| 117 |
+
gradients: List["Gradient"]
|
| 118 |
+
theta: float = 1.0
|
| 119 |
+
name: str = "C"
|
| 120 |
+
|
| 121 |
+
def support(self, p: Pattern) -> float:
|
| 122 |
+
"""support(P, C) = min(L_P, ΞΈ_C)"""
|
| 123 |
+
return min(p.L, self.theta)
|
| 124 |
+
|
| 125 |
+
def demand(self, p: Pattern) -> float:
|
| 126 |
+
"""demand(P, C) = max(0, L_P β ΞΈ_C)"""
|
| 127 |
+
return max(0.0, p.L - self.theta)
|
| 128 |
+
|
| 129 |
+
def is_coherent(self, p: Pattern) -> bool:
|
| 130 |
+
"""demand = 0: pattern is fully supported by context."""
|
| 131 |
+
return self.demand(p) == 0.0
|
| 132 |
+
|
| 133 |
+
def is_valid(self, p: Pattern, designated: Set = None) -> bool:
|
| 134 |
+
"""
|
| 135 |
+
valid = designated AND coherent.
|
| 136 |
+
designated: values that count as "true" in this carrier.
|
| 137 |
+
Default: {1, True} (classical logic convention).
|
| 138 |
+
"""
|
| 139 |
+
if designated is None:
|
| 140 |
+
designated = {1, True, 1.0}
|
| 141 |
+
return p.v in designated and self.is_coherent(p)
|
| 142 |
+
|
| 143 |
+
def propagation_rate(self, p: Pattern) -> float:
|
| 144 |
+
"""
|
| 145 |
+
Theorem 2.1: rate = 1/L_P for incoherent patterns.
|
| 146 |
+
Rate = inf for coherent patterns (already there).
|
| 147 |
+
"""
|
| 148 |
+
if self.is_coherent(p):
|
| 149 |
+
return float("inf")
|
| 150 |
+
return 1.0 / p.L if p.L > 0 else float("inf")
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
# =============================================================================
|
| 154 |
+
# GRADIENT
|
| 155 |
+
# =============================================================================
|
| 156 |
+
|
| 157 |
+
class Gradient:
|
| 158 |
+
"""
|
| 159 |
+
G β a gradient field.
|
| 160 |
+
|
| 161 |
+
G is P occupying the gradient role contextually (PL v13, Section 2.6).
|
| 162 |
+
Every G has loaded history. Every G was constituted by prior propagation.
|
| 163 |
+
G can become P in a higher-order event.
|
| 164 |
+
|
| 165 |
+
The propagation event:
|
| 166 |
+
P / G β Q
|
| 167 |
+
v_Q = transform(v_P) β designation changes
|
| 168 |
+
L_Q = L_P + cost(P) β load accumulates
|
| 169 |
+
H_Q = H_P + [G.name] β history records this gradient
|
| 170 |
+
|
| 171 |
+
cost: by default 1.0 per propagation step.
|
| 172 |
+
Zero-cost gradients (identity, observation) can be specified.
|
| 173 |
+
Variable-cost gradients can depend on P.
|
| 174 |
+
"""
|
| 175 |
+
|
| 176 |
+
def __init__(
|
| 177 |
+
self,
|
| 178 |
+
name: str,
|
| 179 |
+
transform: Callable[[Any], Any],
|
| 180 |
+
cost: Union[float, Callable[["Pattern"], float]] = 1.0,
|
| 181 |
+
domain: Optional[Set] = None,
|
| 182 |
+
description: str = "",
|
| 183 |
+
):
|
| 184 |
+
self.name = name
|
| 185 |
+
self._transform = transform
|
| 186 |
+
self._cost_fn = (cost if callable(cost) else (lambda p, c=cost: c))
|
| 187 |
+
self.domain = domain # None = any carrier
|
| 188 |
+
self.description = description
|
| 189 |
+
|
| 190 |
+
def transform(self, v: Any) -> Any:
|
| 191 |
+
"""Apply the designation transformation."""
|
| 192 |
+
return self._transform(v)
|
| 193 |
+
|
| 194 |
+
def cost(self, p: Pattern) -> float:
|
| 195 |
+
"""Load cost for propagating this pattern through this gradient."""
|
| 196 |
+
return self._cost_fn(p)
|
| 197 |
+
|
| 198 |
+
def propagate(self, p: Pattern) -> Pattern:
|
| 199 |
+
"""
|
| 200 |
+
P / G β Q
|
| 201 |
+
|
| 202 |
+
The primitive operation. Everything else is derived from this.
|
| 203 |
+
"""
|
| 204 |
+
v_Q = self._transform(p.v)
|
| 205 |
+
L_Q = p.L + self._cost_fn(p)
|
| 206 |
+
hist_Q = p.history + [self.name]
|
| 207 |
+
return Pattern(v=v_Q, L=L_Q, history=hist_Q)
|
| 208 |
+
|
| 209 |
+
def __call__(self, p: Pattern) -> Pattern:
|
| 210 |
+
return self.propagate(p)
|
| 211 |
+
|
| 212 |
+
def __repr__(self) -> str:
|
| 213 |
+
return f"G[{self.name}]"
|
| 214 |
+
|
| 215 |
+
def is_closed_on(self, carrier: Set) -> Tuple[bool, List]:
|
| 216 |
+
"""
|
| 217 |
+
Does this gradient keep the carrier closed?
|
| 218 |
+
Returns (is_closed, violations)
|
| 219 |
+
where violations = [(v_in, v_out_of_carrier), ...]
|
| 220 |
+
"""
|
| 221 |
+
violations = []
|
| 222 |
+
for v in carrier:
|
| 223 |
+
out = self._transform(v)
|
| 224 |
+
if out not in carrier:
|
| 225 |
+
violations.append((v, out))
|
| 226 |
+
return len(violations) == 0, violations
|
| 227 |
+
|
| 228 |
+
def fixed_points(self, carrier: Set) -> List:
|
| 229 |
+
"""Values v β V where G(v) = v."""
|
| 230 |
+
return [v for v in carrier if self._transform(v) == v]
|
| 231 |
+
|
| 232 |
+
def orbit(self, v: Any, max_steps: int = 64) -> List:
|
| 233 |
+
"""
|
| 234 |
+
The orbit of v under repeated application of G.
|
| 235 |
+
Returns the full cycle if found, truncated at max_steps otherwise.
|
| 236 |
+
|
| 237 |
+
If the orbit escapes the carrier (transform raises ValueError),
|
| 238 |
+
returns the visited list so far β indicating no cycle within V.
|
| 239 |
+
This is the correct result for closure violations:
|
| 240 |
+
orbits that exit V have no cycle within V.
|
| 241 |
+
"""
|
| 242 |
+
visited = [v]
|
| 243 |
+
current = v
|
| 244 |
+
for _ in range(max_steps):
|
| 245 |
+
try:
|
| 246 |
+
current = self._transform(current)
|
| 247 |
+
except (ValueError, KeyError):
|
| 248 |
+
# Orbit has escaped the carrier β no cycle within V.
|
| 249 |
+
return visited
|
| 250 |
+
if current == v:
|
| 251 |
+
return visited # full cycle
|
| 252 |
+
visited.append(current)
|
| 253 |
+
return visited # truncated (no cycle found within max_steps)
|
| 254 |
+
|
| 255 |
+
def cycle_length(self, v: Any, max_steps: int = 64) -> Optional[int]:
|
| 256 |
+
"""
|
| 257 |
+
Length of the orbit cycle. None if no cycle found.
|
| 258 |
+
None is also correct when the orbit escapes the carrier (closure violation).
|
| 259 |
+
"""
|
| 260 |
+
o = self.orbit(v, max_steps)
|
| 261 |
+
if not o:
|
| 262 |
+
return None
|
| 263 |
+
current = o[-1]
|
| 264 |
+
try:
|
| 265 |
+
next_v = self._transform(current)
|
| 266 |
+
except (ValueError, KeyError):
|
| 267 |
+
return None # orbit escapes carrier, no cycle
|
| 268 |
+
if next_v == o[0]:
|
| 269 |
+
return len(o)
|
| 270 |
+
return None
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
# =============================================================================
|
| 274 |
+
# GRADIENT FAMILIES
|
| 275 |
+
# Standard gradient families for known carriers.
|
| 276 |
+
# Each family is itself a Pattern in a higher-order carrier.
|
| 277 |
+
# =============================================================================
|
| 278 |
+
|
| 279 |
+
# ββ Classical Logic: V = {0, 1} ββββββββββββββββββββββββββββββββββββββββββββ
|
| 280 |
+
|
| 281 |
+
def G_neg() -> Gradient:
|
| 282 |
+
"""
|
| 283 |
+
Classical negation on {0, 1}.
|
| 284 |
+
G_neg flips the designation but does NOT change the load.
|
| 285 |
+
Negating "all dogs bark" costs the same as asserting it.
|
| 286 |
+
"""
|
| 287 |
+
return Gradient(
|
| 288 |
+
name="neg",
|
| 289 |
+
transform=lambda v: 1 - v,
|
| 290 |
+
domain={0, 1},
|
| 291 |
+
description="Classical negation: v β 1 - v",
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
def G_and() -> Gradient:
|
| 295 |
+
"""
|
| 296 |
+
Conjunction as a binary gradient.
|
| 297 |
+
Requires tuple input (v_P, v_Q); returns conjunction value.
|
| 298 |
+
"""
|
| 299 |
+
return Gradient(
|
| 300 |
+
name="and",
|
| 301 |
+
transform=lambda v: int(v[0] and v[1]) if isinstance(v, tuple) else v,
|
| 302 |
+
domain=None,
|
| 303 |
+
description="Classical conjunction",
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
def G_or() -> Gradient:
|
| 307 |
+
"""Disjunction."""
|
| 308 |
+
return Gradient(
|
| 309 |
+
name="or",
|
| 310 |
+
transform=lambda v: int(v[0] or v[1]) if isinstance(v, tuple) else v,
|
| 311 |
+
domain=None,
|
| 312 |
+
description="Classical disjunction",
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
def G_id() -> Gradient:
|
| 316 |
+
"""
|
| 317 |
+
Identity: zero-cost, leaves everything unchanged.
|
| 318 |
+
The simplest gradient. Fixed point of the gradient-family gradient family.
|
| 319 |
+
"""
|
| 320 |
+
return Gradient(
|
| 321 |
+
name="id",
|
| 322 |
+
transform=lambda v: v,
|
| 323 |
+
cost=0.0,
|
| 324 |
+
description="Identity: v β v, zero cost",
|
| 325 |
+
)
|
| 326 |
+
|
| 327 |
+
# ββ Fuzzy Logic: V = {0, 0.5, 1} βββββββββββββββββββββββββββββββββββββββββββ
|
| 328 |
+
|
| 329 |
+
def G_fuzzy_neg() -> Gradient:
|
| 330 |
+
"""
|
| 331 |
+
Fuzzy negation: v β 1 - v.
|
| 332 |
+
Same formula as classical neg, but on a richer carrier.
|
| 333 |
+
On V={0,0.5,1}: 0.5 is a fixed point. Excluded middle fails at 0.5.
|
| 334 |
+
This is DERIVED from the carrier structure, not assumed.
|
| 335 |
+
"""
|
| 336 |
+
return Gradient(
|
| 337 |
+
name="fuzzy_neg",
|
| 338 |
+
transform=lambda v: 1 - v,
|
| 339 |
+
domain={0, 0.5, 1},
|
| 340 |
+
description="Fuzzy negation: v β 1-v on {0, 0.5, 1}",
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
def G_lukasiewicz_and() -> Gradient:
|
| 344 |
+
"""Εukasiewicz conjunction: max(0, v[0] + v[1] - 1)."""
|
| 345 |
+
return Gradient(
|
| 346 |
+
name="luk_and",
|
| 347 |
+
transform=lambda v: max(0, v[0] + v[1] - 1) if isinstance(v, tuple) else v,
|
| 348 |
+
description="Εukasiewicz conjunction",
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
# ββ Arithmetic: V = β, β€, β, β βββββββββββββββββββββββββββββββββββββββββββββ
|
| 352 |
+
|
| 353 |
+
def G_succ() -> Gradient:
|
| 354 |
+
"""
|
| 355 |
+
Successor: n β n + 1.
|
| 356 |
+
On β: always closed.
|
| 357 |
+
No fixed points (nothing maps to itself under +1).
|
| 358 |
+
"""
|
| 359 |
+
return Gradient(
|
| 360 |
+
name="succ",
|
| 361 |
+
transform=lambda v: v + 1,
|
| 362 |
+
description="Successor: v β v + 1",
|
| 363 |
+
)
|
| 364 |
+
|
| 365 |
+
def G_pred() -> Gradient:
|
| 366 |
+
"""
|
| 367 |
+
Predecessor: n β n - 1.
|
| 368 |
+
On β = {0, 1, 2, ...}: NOT closed (0 β -1 β β).
|
| 369 |
+
Closure violation FORCES extension to β€.
|
| 370 |
+
This is how β β β€ is derived, not assumed.
|
| 371 |
+
"""
|
| 372 |
+
return Gradient(
|
| 373 |
+
name="pred",
|
| 374 |
+
transform=lambda v: v - 1,
|
| 375 |
+
description="Predecessor: v β v - 1 (forces βββ€ extension)",
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
def G_double() -> Gradient:
|
| 379 |
+
"""v β 2v. On β€: closed. Reveals even/odd structure."""
|
| 380 |
+
return Gradient(
|
| 381 |
+
name="double",
|
| 382 |
+
transform=lambda v: 2 * v,
|
| 383 |
+
description="Doubling: v β 2v",
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
def G_halve() -> Gradient:
|
| 387 |
+
"""
|
| 388 |
+
v β v/2.
|
| 389 |
+
On β€: NOT closed for odd integers (1/2 β β€).
|
| 390 |
+
Forces extension to β.
|
| 391 |
+
This is how β€ β β is derived.
|
| 392 |
+
"""
|
| 393 |
+
return Gradient(
|
| 394 |
+
name="halve",
|
| 395 |
+
transform=lambda v: v / 2,
|
| 396 |
+
description="Halving: v β v/2 (forces β€ββ extension)",
|
| 397 |
+
)
|
| 398 |
+
|
| 399 |
+
def G_sqrt() -> Gradient:
|
| 400 |
+
"""
|
| 401 |
+
v β βv.
|
| 402 |
+
On ββΊ: NOT closed (β2 β β).
|
| 403 |
+
Forces extension to β.
|
| 404 |
+
This is how β β β is derived.
|
| 405 |
+
"""
|
| 406 |
+
return Gradient(
|
| 407 |
+
name="sqrt",
|
| 408 |
+
transform=lambda v: v ** 0.5,
|
| 409 |
+
description="Square root: v β βv (forces βββ extension)",
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
def G_neg_sqrt() -> Gradient:
|
| 413 |
+
"""
|
| 414 |
+
v β β(-v) for v < 0, else βv.
|
| 415 |
+
On β: NOT closed for negative values.
|
| 416 |
+
Forces extension to β.
|
| 417 |
+
This is how β β β is derived.
|
| 418 |
+
"""
|
| 419 |
+
return Gradient(
|
| 420 |
+
name="neg_sqrt",
|
| 421 |
+
transform=lambda v: complex(0, (-v)**0.5) if v < 0 else v**0.5,
|
| 422 |
+
description="Negative sqrt: forces βββ extension",
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
# ββ Modular arithmetic ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 426 |
+
|
| 427 |
+
def G_mod(n: int, op: str = "add1") -> Gradient:
|
| 428 |
+
"""Modular arithmetic on β€/nβ€."""
|
| 429 |
+
ops = {
|
| 430 |
+
"add1": lambda v: (v + 1) % n,
|
| 431 |
+
"add2": lambda v: (v + 2) % n,
|
| 432 |
+
"neg": lambda v: (-v) % n,
|
| 433 |
+
"double": lambda v: (2 * v) % n,
|
| 434 |
+
}
|
| 435 |
+
if op not in ops:
|
| 436 |
+
raise ValueError(f"Unknown op {op!r}. Choose from {list(ops)}")
|
| 437 |
+
return Gradient(
|
| 438 |
+
name=f"mod{n}_{op}",
|
| 439 |
+
transform=ops[op],
|
| 440 |
+
domain=set(range(n)),
|
| 441 |
+
description=f"Mod-{n} {op}",
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
+
# ββ Custom gradient βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 445 |
+
|
| 446 |
+
def G_custom(name: str, mapping: Dict[Any, Any], cost: float = 1.0) -> Gradient:
|
| 447 |
+
"""
|
| 448 |
+
Build a gradient from an explicit mapping.
|
| 449 |
+
Any carrier, any domain. The engine derives what it forces.
|
| 450 |
+
This is the 'novel carrier' entry point for boundary condition extrapolation.
|
| 451 |
+
"""
|
| 452 |
+
def _transform(v: Any) -> Any:
|
| 453 |
+
if v not in mapping:
|
| 454 |
+
raise ValueError(
|
| 455 |
+
f"G[{name}]: value {v!r} not in mapping {set(mapping.keys())}. "
|
| 456 |
+
f"Carrier extension may be required."
|
| 457 |
+
)
|
| 458 |
+
return mapping[v]
|
| 459 |
+
|
| 460 |
+
return Gradient(
|
| 461 |
+
name=name,
|
| 462 |
+
transform=_transform,
|
| 463 |
+
cost=cost,
|
| 464 |
+
domain=set(mapping.keys()),
|
| 465 |
+
description=f"Custom gradient with mapping {mapping}",
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
# =============================================================================
|
| 470 |
+
# GRADIENT FAMILY REGISTRY
|
| 471 |
+
# Standard (V, Ξ) configurations and what they force.
|
| 472 |
+
# Used by the engine and the data generator.
|
| 473 |
+
# =============================================================================
|
| 474 |
+
|
| 475 |
+
KNOWN_SYSTEMS = {
|
| 476 |
+
"classical_logic": {
|
| 477 |
+
"description": "Classical two-valued logic",
|
| 478 |
+
"carrier": {0, 1},
|
| 479 |
+
"gradients": lambda: [G_neg(), G_id()],
|
| 480 |
+
"designated": {1},
|
| 481 |
+
"forced": ["closure", "involution", "excluded_middle", "double_negation"],
|
| 482 |
+
},
|
| 483 |
+
"three_valued_logic": {
|
| 484 |
+
"description": "Three-valued (Εukasiewicz) logic",
|
| 485 |
+
"carrier": {0, 0.5, 1},
|
| 486 |
+
"gradients": lambda: [G_fuzzy_neg(), G_id()],
|
| 487 |
+
"designated": {1},
|
| 488 |
+
"forced": ["closure", "middle_value_fixed", "excluded_middle_fails"],
|
| 489 |
+
},
|
| 490 |
+
"natural_numbers": {
|
| 491 |
+
"description": "β with successor (closed) and predecessor (not closed)",
|
| 492 |
+
"carrier": set(range(10)), # finite sample; full β is unbounded
|
| 493 |
+
"gradients": lambda: [G_succ(), G_id()],
|
| 494 |
+
"designated": {1},
|
| 495 |
+
"forced": ["closure_under_succ", "no_fixed_points_succ"],
|
| 496 |
+
},
|
| 497 |
+
"integers_forced": {
|
| 498 |
+
"description": "β βͺ predecessor β forces β€",
|
| 499 |
+
"carrier": {0, 1, 2, 3, 4},
|
| 500 |
+
"gradients": lambda: [G_pred(), G_id()],
|
| 501 |
+
"designated": {1},
|
| 502 |
+
"forced": ["closure_violation", "negative_extension_forced"],
|
| 503 |
+
},
|
| 504 |
+
"rationals_forced": {
|
| 505 |
+
"description": "β€ βͺ halving β forces β",
|
| 506 |
+
"carrier": {-2, -1, 0, 1, 2, 3, 4},
|
| 507 |
+
"gradients": lambda: [G_halve(), G_id()],
|
| 508 |
+
"designated": {1},
|
| 509 |
+
"forced": ["closure_violation", "rational_extension_forced"],
|
| 510 |
+
},
|
| 511 |
+
"Z4": {
|
| 512 |
+
"description": "Cyclic group β€/4β€",
|
| 513 |
+
"carrier": {0, 1, 2, 3},
|
| 514 |
+
"gradients": lambda: [G_mod(4, "add1"), G_id()],
|
| 515 |
+
"designated": {0},
|
| 516 |
+
"forced": ["closure", "uniform_4_cycle", "no_fixed_points"],
|
| 517 |
+
},
|
| 518 |
+
"Z2": {
|
| 519 |
+
"description": "Cyclic group β€/2β€ (bit flip)",
|
| 520 |
+
"carrier": {0, 1},
|
| 521 |
+
"gradients": lambda: [G_mod(2, "add1"), G_id()],
|
| 522 |
+
"designated": {0},
|
| 523 |
+
"forced": ["closure", "involution"],
|
| 524 |
+
},
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
|
| 528 |
+
# =============================================================================
|
| 529 |
+
# PROPAGATION CHAIN
|
| 530 |
+
# Records a sequence of P / G β Q steps.
|
| 531 |
+
# This is the training unit for the mechanism-first model.
|
| 532 |
+
# =============================================================================
|
| 533 |
+
|
| 534 |
+
@dataclass
|
| 535 |
+
class PropagationChain:
|
| 536 |
+
"""
|
| 537 |
+
A recorded sequence of propagation steps.
|
| 538 |
+
|
| 539 |
+
Step 0: P_0 / G_0 β P_1
|
| 540 |
+
Step 1: P_1 / G_1 β P_2
|
| 541 |
+
...
|
| 542 |
+
Step n: P_n / G_n β P_{n+1}
|
| 543 |
+
|
| 544 |
+
This is the fundamental training example:
|
| 545 |
+
not prose about the mechanism, but the mechanism running.
|
| 546 |
+
"""
|
| 547 |
+
steps: List[Tuple[Pattern, Gradient, Pattern]] = field(default_factory=list)
|
| 548 |
+
context: Optional[Context] = None
|
| 549 |
+
carrier: Optional[Set] = None
|
| 550 |
+
|
| 551 |
+
def add(self, p_in: Pattern, g: Gradient, p_out: Pattern):
|
| 552 |
+
self.steps.append((p_in, g, p_out))
|
| 553 |
+
|
| 554 |
+
def run(self, initial: Pattern, gradients: List[Gradient]) -> "PropagationChain":
|
| 555 |
+
"""Run a chain of propagation steps from initial pattern."""
|
| 556 |
+
chain = PropagationChain(context=self.context, carrier=self.carrier)
|
| 557 |
+
current = initial
|
| 558 |
+
for g in gradients:
|
| 559 |
+
next_p = g.propagate(current)
|
| 560 |
+
chain.add(current, g, next_p)
|
| 561 |
+
current = next_p
|
| 562 |
+
return chain
|
| 563 |
+
|
| 564 |
+
def as_text(self) -> str:
|
| 565 |
+
"""
|
| 566 |
+
Render as training text.
|
| 567 |
+
This is the format the LM learns to predict.
|
| 568 |
+
"""
|
| 569 |
+
lines = []
|
| 570 |
+
if self.carrier:
|
| 571 |
+
lines.append(f"CARRIER: {sorted(self.carrier, key=str)}")
|
| 572 |
+
for i, (p_in, g, p_out) in enumerate(self.steps):
|
| 573 |
+
lines.append(
|
| 574 |
+
f"STEP {i}: P(v={p_in.v!r}, L={p_in.L:.1f}) / G[{g.name}] "
|
| 575 |
+
f"β Q(v={p_out.v!r}, L={p_out.L:.1f})"
|
| 576 |
+
)
|
| 577 |
+
return "\n".join(lines)
|
| 578 |
+
|
| 579 |
+
def demand_profile(self) -> List[float]:
|
| 580 |
+
"""
|
| 581 |
+
How does demand change across the chain?
|
| 582 |
+
Increasing demand: moving away from coherence (incoherence accumulating).
|
| 583 |
+
Decreasing demand: moving toward coherence (gradient is solving something).
|
| 584 |
+
"""
|
| 585 |
+
if not self.context:
|
| 586 |
+
return []
|
| 587 |
+
return [self.context.demand(p_out) for _, _, p_out in self.steps]
|
| 588 |
+
|
| 589 |
+
def __len__(self) -> int:
|
| 590 |
+
return len(self.steps)
|
| 591 |
+
|
| 592 |
+
def __repr__(self) -> str:
|
| 593 |
+
return f"Chain({len(self.steps)} steps)"
|
pl/grammar.py
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
pl/grammar.py β Grammar as Coherence Detection
|
| 3 |
+
================================================
|
| 4 |
+
|
| 5 |
+
The claim (Grammar as Differential Propagation):
|
| 6 |
+
Grammaticality is not a list of rules.
|
| 7 |
+
Grammaticality is COHERENCE in the linguistic carrier.
|
| 8 |
+
|
| 9 |
+
A grammatical sentence is one where:
|
| 10 |
+
- demand(P, C) = 0: every gradient demand is met.
|
| 11 |
+
- Subject-verb agreement: number gradient is satisfied.
|
| 12 |
+
- Anaphor binding: reference gradient is satisfied.
|
| 13 |
+
- NPI licensing: polarity gradient is satisfied.
|
| 14 |
+
|
| 15 |
+
An ungrammatical sentence is one where:
|
| 16 |
+
- demand(P, C) > 0: some gradient demand is unmet.
|
| 17 |
+
- *"The dogs barks" β number demand unmet.
|
| 18 |
+
- *"She introduced himself" β reference demand unmet.
|
| 19 |
+
- *"Somebody said anything" β polarity demand unmet.
|
| 20 |
+
|
| 21 |
+
This module implements grammaticality as coherence checking.
|
| 22 |
+
The BLiMP benchmark measures whether the model has learned this.
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
from __future__ import annotations
|
| 26 |
+
from dataclasses import dataclass, field
|
| 27 |
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
| 28 |
+
import re
|
| 29 |
+
|
| 30 |
+
from pl.core import Pattern, Gradient, Context, seed
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
# =============================================================================
|
| 34 |
+
# LINGUISTIC FEATURES AS GRADIENT DEMANDS
|
| 35 |
+
# =============================================================================
|
| 36 |
+
|
| 37 |
+
@dataclass
|
| 38 |
+
class LinguisticFeature:
|
| 39 |
+
"""
|
| 40 |
+
A feature in the linguistic carrier.
|
| 41 |
+
|
| 42 |
+
Examples:
|
| 43 |
+
NUMBER: singular vs plural
|
| 44 |
+
PERSON: 1st, 2nd, 3rd
|
| 45 |
+
GENDER: masculine, feminine, neuter
|
| 46 |
+
POLARITY: positive, negative
|
| 47 |
+
CASE: nominative, accusative, etc.
|
| 48 |
+
"""
|
| 49 |
+
name: str
|
| 50 |
+
value: Any
|
| 51 |
+
domain: Set = field(default_factory=set)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@dataclass
|
| 55 |
+
class LinguisticPattern(Pattern):
|
| 56 |
+
"""
|
| 57 |
+
A pattern in the linguistic carrier.
|
| 58 |
+
|
| 59 |
+
v_P: the linguistic object (word, phrase, sentence)
|
| 60 |
+
L_P: accumulated syntactic/semantic load
|
| 61 |
+
features: the feature bundle that creates gradient demands
|
| 62 |
+
"""
|
| 63 |
+
features: Dict[str, Any] = field(default_factory=dict)
|
| 64 |
+
role: str = "none" # subject, verb, object, etc.
|
| 65 |
+
|
| 66 |
+
def get_feature(self, name: str) -> Optional[Any]:
|
| 67 |
+
return self.features.get(name)
|
| 68 |
+
|
| 69 |
+
def has_feature(self, name: str) -> bool:
|
| 70 |
+
return name in self.features
|
| 71 |
+
|
| 72 |
+
def demand_feature(self, name: str, value: Any) -> float:
|
| 73 |
+
"""
|
| 74 |
+
How much demand does this pattern create for feature `name` = `value`?
|
| 75 |
+
0.0 = feature is satisfied (or not required).
|
| 76 |
+
1.0 = feature is required but unmet.
|
| 77 |
+
"""
|
| 78 |
+
if not self.has_feature(name):
|
| 79 |
+
return 0.0 # doesn't need this feature
|
| 80 |
+
if self.features[name] == value:
|
| 81 |
+
return 0.0 # feature is satisfied
|
| 82 |
+
return 1.0 # mismatch = demand
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
# =============================================================================
|
| 86 |
+
# GRAMMATICAL GRADIENT FAMILIES
|
| 87 |
+
# =============================================================================
|
| 88 |
+
|
| 89 |
+
def G_number_agree() -> Gradient:
|
| 90 |
+
"""
|
| 91 |
+
Subject-verb number agreement gradient.
|
| 92 |
+
|
| 93 |
+
The subject creates a NUMBER demand.
|
| 94 |
+
The verb must satisfy it.
|
| 95 |
+
If they mismatch: demand > 0, sentence is incoherent.
|
| 96 |
+
|
| 97 |
+
"The dog barks" β demand = 0 (both singular). Grammatical.
|
| 98 |
+
"The dog bark" β demand = 1 (mismatch). Ungrammatical.
|
| 99 |
+
"""
|
| 100 |
+
def transform(pair: Tuple) -> Tuple:
|
| 101 |
+
"""pair = (subject_number, verb_number)"""
|
| 102 |
+
subj_num, verb_num = pair
|
| 103 |
+
if subj_num == verb_num:
|
| 104 |
+
return ("AGREE", 0.0) # coherent: demand = 0
|
| 105 |
+
return ("DISAGREE", 1.0) # incoherent: demand = 1
|
| 106 |
+
return Gradient(
|
| 107 |
+
name="number_agree",
|
| 108 |
+
transform=transform,
|
| 109 |
+
description="SVA: subject and verb must agree in number",
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def G_anaphor_bind() -> Gradient:
|
| 114 |
+
"""
|
| 115 |
+
Anaphor binding gradient.
|
| 116 |
+
|
| 117 |
+
A reflexive pronoun creates a BINDING demand:
|
| 118 |
+
it must be bound by a local antecedent of matching gender.
|
| 119 |
+
|
| 120 |
+
"She introduced herself" β demand = 0.
|
| 121 |
+
"She introduced himself" β demand = 1 (gender mismatch).
|
| 122 |
+
"""
|
| 123 |
+
def transform(pair: Tuple) -> Tuple:
|
| 124 |
+
antecedent_gender, pronoun_gender = pair
|
| 125 |
+
if pronoun_gender.endswith("self") or pronoun_gender.endswith("selves"):
|
| 126 |
+
expected = pronoun_gender.replace("self", "").replace("selves", "")
|
| 127 |
+
required = {"him": "male", "her": "female", "it": "neuter",
|
| 128 |
+
"them": "any", "my": "any", "your": "any"}
|
| 129 |
+
expected_gender = required.get(expected, "any")
|
| 130 |
+
if expected_gender == "any" or antecedent_gender == expected_gender:
|
| 131 |
+
return ("BOUND", 0.0)
|
| 132 |
+
return ("UNBOUND", 1.0)
|
| 133 |
+
return ("NOT_REFLEXIVE", 0.0)
|
| 134 |
+
|
| 135 |
+
return Gradient(
|
| 136 |
+
name="anaphor_bind",
|
| 137 |
+
transform=transform,
|
| 138 |
+
description="Reflexive pronouns must be bound by local antecedent",
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def G_npi_license() -> Gradient:
|
| 143 |
+
"""
|
| 144 |
+
Negative Polarity Item (NPI) licensing gradient.
|
| 145 |
+
|
| 146 |
+
NPIs ("any", "ever", "at all") require a negative/downward-entailing context.
|
| 147 |
+
|
| 148 |
+
"Nobody said anything" β demand = 0 ("nobody" licenses "anything").
|
| 149 |
+
"Somebody said anything" β demand = 1 ("somebody" doesn't license "anything").
|
| 150 |
+
"""
|
| 151 |
+
LICENSORS = {"nobody", "no", "not", "never", "without",
|
| 152 |
+
"hardly", "scarcely", "few", "rarely"}
|
| 153 |
+
NPIS = {"any", "anyone", "anything", "ever", "at all", "either",
|
| 154 |
+
"yet", "budge", "a red cent"}
|
| 155 |
+
|
| 156 |
+
def transform(pair: Tuple) -> Tuple:
|
| 157 |
+
context_words, npi = pair
|
| 158 |
+
has_licensor = any(w.lower() in LICENSORS for w in context_words)
|
| 159 |
+
if npi.lower() in NPIS:
|
| 160 |
+
if has_licensor:
|
| 161 |
+
return ("LICENSED", 0.0)
|
| 162 |
+
return ("UNLICENSED", 1.0)
|
| 163 |
+
return ("NOT_NPI", 0.0)
|
| 164 |
+
|
| 165 |
+
return Gradient(
|
| 166 |
+
name="npi_license",
|
| 167 |
+
transform=transform,
|
| 168 |
+
description="NPIs require negative/downward-entailing licensor",
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def G_case_filter() -> Gradient:
|
| 173 |
+
"""
|
| 174 |
+
Case filter: every argument must have case.
|
| 175 |
+
|
| 176 |
+
"He left" β demand = 0 (nominative case on subject).
|
| 177 |
+
"*Him left" β demand = 1 (accusative where nominative required).
|
| 178 |
+
"""
|
| 179 |
+
NOM = {"he", "she", "they", "we", "i", "who"}
|
| 180 |
+
ACC = {"him", "her", "them", "us", "me", "whom"}
|
| 181 |
+
|
| 182 |
+
def transform(pair: Tuple) -> Tuple:
|
| 183 |
+
position, pronoun = pair
|
| 184 |
+
if position == "subject":
|
| 185 |
+
if pronoun.lower() in NOM:
|
| 186 |
+
return ("CASE_OK", 0.0)
|
| 187 |
+
return ("CASE_FAIL", 1.0)
|
| 188 |
+
elif position == "object":
|
| 189 |
+
if pronoun.lower() in ACC:
|
| 190 |
+
return ("CASE_OK", 0.0)
|
| 191 |
+
return ("CASE_FAIL", 1.0)
|
| 192 |
+
return ("CASE_NA", 0.0)
|
| 193 |
+
|
| 194 |
+
return Gradient(
|
| 195 |
+
name="case_filter",
|
| 196 |
+
transform=transform,
|
| 197 |
+
description="Pronouns must carry appropriate case for position",
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
# =============================================================================
|
| 202 |
+
# COHERENCE CHECKER
|
| 203 |
+
# =============================================================================
|
| 204 |
+
|
| 205 |
+
@dataclass
|
| 206 |
+
class GrammaticalityResult:
|
| 207 |
+
"""Result of checking a sentence's grammaticality."""
|
| 208 |
+
sentence: str
|
| 209 |
+
is_grammatical: bool
|
| 210 |
+
total_demand: float
|
| 211 |
+
violations: List[Dict]
|
| 212 |
+
gradient_scores: Dict[str, float]
|
| 213 |
+
|
| 214 |
+
def as_text(self) -> str:
|
| 215 |
+
status = "GRAMMATICAL" if self.is_grammatical else "UNGRAMMATICAL"
|
| 216 |
+
lines = [
|
| 217 |
+
f"SENTENCE: {self.sentence!r}",
|
| 218 |
+
f"STATUS: {status}",
|
| 219 |
+
f"DEMAND: {self.total_demand:.1f}",
|
| 220 |
+
]
|
| 221 |
+
if self.violations:
|
| 222 |
+
lines.append(f"VIOLATIONS:")
|
| 223 |
+
for v in self.violations:
|
| 224 |
+
lines.append(f" - {v['gradient']}: {v['description']}")
|
| 225 |
+
return "\n".join(lines)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
class CoherenceChecker:
|
| 229 |
+
"""
|
| 230 |
+
Checks grammaticality as coherence in the linguistic carrier.
|
| 231 |
+
|
| 232 |
+
A sentence is grammatical iff its total gradient demand = 0.
|
| 233 |
+
Each violation adds 1.0 to the demand.
|
| 234 |
+
|
| 235 |
+
This is not a list of rules.
|
| 236 |
+
This is the mechanism: demand(P, C) = 0 iff grammatical.
|
| 237 |
+
"""
|
| 238 |
+
|
| 239 |
+
# Minimal pairs for BLiMP evaluation
|
| 240 |
+
BLIMP_PAIRS = [
|
| 241 |
+
# SVA (subject-verb agreement)
|
| 242 |
+
("The dog barks.", "*The dog bark.", "SVA", "singular subject"),
|
| 243 |
+
("The dogs bark.", "*The dogs barks.", "SVA", "plural subject"),
|
| 244 |
+
("The pattern coheres.", "*The pattern cohere.", "SVA", "singular subject"),
|
| 245 |
+
("P propagates through G.", "*P propagate through G.", "SVA", "singular subject"),
|
| 246 |
+
("Coherence is the foundation.", "*Coherence are the foundation.", "SVA", "singular subject"),
|
| 247 |
+
("The rules apply here.", "*The rules applies here.", "SVA", "plural subject"),
|
| 248 |
+
|
| 249 |
+
# Anaphor binding
|
| 250 |
+
("She introduced herself.", "*She introduced himself.", "Anaphor", "gender match"),
|
| 251 |
+
("He blamed himself.", "*He blamed herself.", "Anaphor", "gender match"),
|
| 252 |
+
("They hurt themselves.", "*They hurt himself.", "Anaphor", "plural reflexive"),
|
| 253 |
+
("The model updated itself.", "*The model updated himself.", "Anaphor", "inanimate reflexive"),
|
| 254 |
+
|
| 255 |
+
# NPI licensing
|
| 256 |
+
("Nobody said anything.", "*Somebody said anything.", "NPI", "nobody licenses any"),
|
| 257 |
+
("I never saw anyone.", "*I always saw anyone.", "NPI", "never licenses any"),
|
| 258 |
+
("Without any help.", "*With any help.", "NPI", "without licenses any"),
|
| 259 |
+
("Few students ever passed.", "*Many students ever passed.", "NPI", "few licenses ever"),
|
| 260 |
+
|
| 261 |
+
# Filler-gap
|
| 262 |
+
("What did she buy?", "*What did she buy the?", "Filler-gap", "gap required"),
|
| 263 |
+
("Who did he see?", "*Who did he see the man?", "Filler-gap", "gap required"),
|
| 264 |
+
|
| 265 |
+
# Mechanism-specific
|
| 266 |
+
("The mechanism is P / G β Q.", "*The mechanism are P / G β Q.", "SVA", "mechanism SVA"),
|
| 267 |
+
("Coherence requires demand to be zero.", "*Coherence require demand to be zero.", "SVA", "mechanism SVA"),
|
| 268 |
+
]
|
| 269 |
+
|
| 270 |
+
def score_pair(self, gram: str, ungram: str) -> Dict:
|
| 271 |
+
"""
|
| 272 |
+
Score a minimal pair.
|
| 273 |
+
Returns: which has lower demand (= which is more coherent).
|
| 274 |
+
The grammatical sentence should always have lower demand.
|
| 275 |
+
"""
|
| 276 |
+
gram_demand = self._heuristic_demand(gram)
|
| 277 |
+
ungram_demand = self._heuristic_demand(ungram)
|
| 278 |
+
|
| 279 |
+
correct = gram_demand <= ungram_demand
|
| 280 |
+
|
| 281 |
+
return {
|
| 282 |
+
"grammatical": gram,
|
| 283 |
+
"ungrammatical": ungram,
|
| 284 |
+
"gram_demand": gram_demand,
|
| 285 |
+
"ungram_demand": ungram_demand,
|
| 286 |
+
"correct": correct,
|
| 287 |
+
"margin": ungram_demand - gram_demand,
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
def _heuristic_demand(self, sentence: str) -> float:
|
| 291 |
+
"""
|
| 292 |
+
Heuristic grammatical demand for a sentence.
|
| 293 |
+
Used for rapid evaluation without a full parser.
|
| 294 |
+
|
| 295 |
+
In a full system, this is replaced by the trained LM's
|
| 296 |
+
log-probability (coherent = high probability = low demand).
|
| 297 |
+
The LM is learning to approximate this function.
|
| 298 |
+
"""
|
| 299 |
+
demand = 0.0
|
| 300 |
+
words = sentence.rstrip(".!?").lower().split()
|
| 301 |
+
|
| 302 |
+
# SVA check: common 3rd singular forms
|
| 303 |
+
SINGULAR_SUBJECTS = {"the", "a", "an", "it", "he", "she",
|
| 304 |
+
"pattern", "mechanism", "coherence",
|
| 305 |
+
"model", "carrier", "gradient", "p", "g", "q"}
|
| 306 |
+
PLURAL_3SG_VERBS = {"bark", "cohere", "propagate", "require"}
|
| 307 |
+
SG_3SG_VERBS = {"barks", "coheres", "propagates", "requires", "is", "are", "was"}
|
| 308 |
+
|
| 309 |
+
for i, w in enumerate(words):
|
| 310 |
+
if w in PLURAL_3SG_VERBS and i > 0:
|
| 311 |
+
subj = words[i-1] if i > 0 else ""
|
| 312 |
+
if subj in SINGULAR_SUBJECTS or (
|
| 313 |
+
i > 1 and words[i-2] in {"the", "a", "an"}
|
| 314 |
+
):
|
| 315 |
+
demand += 1.0 # plural verb with singular subject
|
| 316 |
+
|
| 317 |
+
# Reflexive mismatch
|
| 318 |
+
if "himself" in words:
|
| 319 |
+
if "she" in words or "her" in words:
|
| 320 |
+
demand += 1.0
|
| 321 |
+
if "herself" in words:
|
| 322 |
+
if "he" in words or "him" in words:
|
| 323 |
+
demand += 1.0
|
| 324 |
+
|
| 325 |
+
# NPI without licensor
|
| 326 |
+
NPI_WORDS = {"anything", "anyone", "ever", "at all"}
|
| 327 |
+
LICENSORS = {"nobody", "no", "not", "never", "without",
|
| 328 |
+
"hardly", "scarcely", "few", "rarely"}
|
| 329 |
+
has_npi = any(w in NPI_WORDS for w in words)
|
| 330 |
+
has_lic = any(w in LICENSORS for w in words)
|
| 331 |
+
if has_npi and not has_lic:
|
| 332 |
+
demand += 1.0
|
| 333 |
+
|
| 334 |
+
# "are" with singular non-copula subject
|
| 335 |
+
if "are" in words:
|
| 336 |
+
idx = words.index("are")
|
| 337 |
+
if idx > 0:
|
| 338 |
+
subj = words[idx - 1]
|
| 339 |
+
if subj in {"coherence", "mechanism", "pattern", "gradient",
|
| 340 |
+
"carrier", "it", "he", "she"}:
|
| 341 |
+
demand += 1.0
|
| 342 |
+
|
| 343 |
+
return demand
|
| 344 |
+
|
| 345 |
+
def evaluate_blimp(self) -> Dict:
|
| 346 |
+
"""Run the full BLiMP evaluation on the built-in pairs."""
|
| 347 |
+
results = []
|
| 348 |
+
by_category = {}
|
| 349 |
+
|
| 350 |
+
for gram, ungram, cat, description in self.BLIMP_PAIRS:
|
| 351 |
+
score = self.score_pair(gram, ungram)
|
| 352 |
+
score["category"] = cat
|
| 353 |
+
score["description"] = description
|
| 354 |
+
results.append(score)
|
| 355 |
+
|
| 356 |
+
if cat not in by_category:
|
| 357 |
+
by_category[cat] = []
|
| 358 |
+
by_category[cat].append(score["correct"])
|
| 359 |
+
|
| 360 |
+
total_correct = sum(r["correct"] for r in results)
|
| 361 |
+
accuracy = total_correct / len(results) if results else 0.0
|
| 362 |
+
|
| 363 |
+
category_accuracy = {
|
| 364 |
+
cat: sum(vals) / len(vals)
|
| 365 |
+
for cat, vals in by_category.items()
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
return {
|
| 369 |
+
"total_pairs": len(results),
|
| 370 |
+
"correct": total_correct,
|
| 371 |
+
"accuracy": accuracy,
|
| 372 |
+
"category_accuracy": category_accuracy,
|
| 373 |
+
"results": results,
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
if __name__ == "__main__":
|
| 378 |
+
print("\n" + "="*60)
|
| 379 |
+
print(" GRAMMAR AS COHERENCE DETECTION")
|
| 380 |
+
print("="*60)
|
| 381 |
+
|
| 382 |
+
checker = CoherenceChecker()
|
| 383 |
+
eval_result = checker.evaluate_blimp()
|
| 384 |
+
|
| 385 |
+
print(f"\n Heuristic BLiMP: {eval_result['correct']}/{eval_result['total_pairs']} "
|
| 386 |
+
f"({eval_result['accuracy']:.0%})")
|
| 387 |
+
print("\n By category:")
|
| 388 |
+
for cat, acc in eval_result["category_accuracy"].items():
|
| 389 |
+
print(f" {cat:<15} {acc:.0%}")
|
| 390 |
+
|
| 391 |
+
print("\n Sample pairs:")
|
| 392 |
+
for r in eval_result["results"][:4]:
|
| 393 |
+
status = "β" if r["correct"] else "β"
|
| 394 |
+
print(f" {status} gram_demand={r['gram_demand']:.1f} "
|
| 395 |
+
f"ungram_demand={r['ungram_demand']:.1f}")
|
| 396 |
+
print(f" GRAM: {r['grammatical']}")
|
| 397 |
+
print(f" UNGRAM: {r['ungrammatical']}")
|
pl/numbers.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
pl/numbers.py β Number System Extensions as Forced Boundary Conditions
|
| 3 |
+
=======================================================================
|
| 4 |
+
|
| 5 |
+
The claim (Mathesis Universalis):
|
| 6 |
+
β β β€ β β β β β β
|
| 7 |
+
|
| 8 |
+
These are not arbitrary extensions. They are FORCED.
|
| 9 |
+
Each extension is a closure violation demanding resolution.
|
| 10 |
+
|
| 11 |
+
β: closed under +, but NOT under -. β β€ forced.
|
| 12 |
+
β€: closed under +, -, but NOT under Γ·. β β forced.
|
| 13 |
+
β: closed under Γ·, but NOT under β. β β forced.
|
| 14 |
+
β: closed under ββΊ, but NOT under ββ». β β forced.
|
| 15 |
+
|
| 16 |
+
This module demonstrates each forced extension as P / G β Q.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
from dataclasses import dataclass
|
| 21 |
+
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
| 22 |
+
import math
|
| 23 |
+
import cmath
|
| 24 |
+
|
| 25 |
+
from pl.core import Pattern, Gradient, Context, G_custom, seed
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# =============================================================================
|
| 29 |
+
# NUMBER CARRIERS
|
| 30 |
+
# Each is defined as a Python type/set, not a Platonic object.
|
| 31 |
+
# A "number" in this system is a Pattern with a numeric designation.
|
| 32 |
+
# =============================================================================
|
| 33 |
+
|
| 34 |
+
class NumberPattern(Pattern):
|
| 35 |
+
"""
|
| 36 |
+
A pattern whose designation component is numeric.
|
| 37 |
+
v_P β {β, β€, β, β, β} depending on which carrier we are in.
|
| 38 |
+
"""
|
| 39 |
+
@property
|
| 40 |
+
def is_natural(self) -> bool:
|
| 41 |
+
return isinstance(self.v, int) and self.v >= 0
|
| 42 |
+
|
| 43 |
+
@property
|
| 44 |
+
def is_integer(self) -> bool:
|
| 45 |
+
return isinstance(self.v, int)
|
| 46 |
+
|
| 47 |
+
@property
|
| 48 |
+
def is_rational(self) -> bool:
|
| 49 |
+
return isinstance(self.v, (int, float)) and not math.isinf(self.v)
|
| 50 |
+
|
| 51 |
+
@property
|
| 52 |
+
def is_real(self) -> bool:
|
| 53 |
+
return isinstance(self.v, (int, float)) and not math.isnan(self.v)
|
| 54 |
+
|
| 55 |
+
@property
|
| 56 |
+
def is_complex(self) -> bool:
|
| 57 |
+
return isinstance(self.v, complex)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
# =============================================================================
|
| 61 |
+
# FORCED EXTENSIONS
|
| 62 |
+
# Each function demonstrates one step in the extension chain.
|
| 63 |
+
# =============================================================================
|
| 64 |
+
|
| 65 |
+
def demonstrate_N_to_Z() -> Dict:
|
| 66 |
+
"""
|
| 67 |
+
β β β€: forced by applying predecessor (subtraction) to 0.
|
| 68 |
+
|
| 69 |
+
β = {0, 1, 2, 3, ...}
|
| 70 |
+
G_pred: n β n - 1
|
| 71 |
+
G_pred(0) = -1 β β
|
| 72 |
+
|
| 73 |
+
Result: carrier must extend. β€ = β βͺ {-1, -2, ...} is forced.
|
| 74 |
+
This is not a choice. It is the only way to maintain closure.
|
| 75 |
+
"""
|
| 76 |
+
g_pred = Gradient("pred", lambda v: v - 1)
|
| 77 |
+
N_sample = {0, 1, 2, 3, 4, 5}
|
| 78 |
+
|
| 79 |
+
# Check closure
|
| 80 |
+
violations = []
|
| 81 |
+
for v in N_sample:
|
| 82 |
+
out = g_pred.transform(v)
|
| 83 |
+
if out not in N_sample:
|
| 84 |
+
violations.append((v, out))
|
| 85 |
+
|
| 86 |
+
# Show the forced extension chain
|
| 87 |
+
chain = []
|
| 88 |
+
current = seed(0)
|
| 89 |
+
for _ in range(6):
|
| 90 |
+
next_p = g_pred.propagate(current)
|
| 91 |
+
chain.append((current.v, next_p.v))
|
| 92 |
+
current = next_p
|
| 93 |
+
|
| 94 |
+
# What is the smallest extension that restores closure?
|
| 95 |
+
extension_needed = {out for _, out in violations}
|
| 96 |
+
|
| 97 |
+
return {
|
| 98 |
+
"source_carrier": "β",
|
| 99 |
+
"gradient": "pred (v β v-1)",
|
| 100 |
+
"closure_violation": violations,
|
| 101 |
+
"extension_needed": extension_needed,
|
| 102 |
+
"forced_carrier": "β€",
|
| 103 |
+
"chain": chain,
|
| 104 |
+
"reasoning": (
|
| 105 |
+
"G_pred is not closed on β: pred(0) = -1 β β. "
|
| 106 |
+
"The carrier must extend to include -1. "
|
| 107 |
+
"But pred(-1) = -2 β β βͺ {-1}. "
|
| 108 |
+
"Repeated application forces {-1, -2, -3, ...} = β€β». "
|
| 109 |
+
"β€ = β βͺ β€β» is the minimal closed extension. "
|
| 110 |
+
"β€ is not assumed. β€ is forced."
|
| 111 |
+
),
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def demonstrate_Z_to_Q() -> Dict:
|
| 116 |
+
"""
|
| 117 |
+
β€ β β: forced by applying division (halving) to odd integers.
|
| 118 |
+
|
| 119 |
+
β€ = {..., -2, -1, 0, 1, 2, ...}
|
| 120 |
+
G_halve: n β n / 2
|
| 121 |
+
G_halve(1) = 0.5 β β€
|
| 122 |
+
|
| 123 |
+
Result: carrier must extend to β.
|
| 124 |
+
"""
|
| 125 |
+
g_halve = Gradient("halve", lambda v: v / 2)
|
| 126 |
+
Z_sample = {-4, -3, -2, -1, 0, 1, 2, 3, 4}
|
| 127 |
+
|
| 128 |
+
violations = []
|
| 129 |
+
for v in Z_sample:
|
| 130 |
+
out = g_halve.transform(v)
|
| 131 |
+
if out not in Z_sample and out != int(out):
|
| 132 |
+
violations.append((v, out))
|
| 133 |
+
|
| 134 |
+
return {
|
| 135 |
+
"source_carrier": "β€",
|
| 136 |
+
"gradient": "halve (v β v/2)",
|
| 137 |
+
"closure_violation": violations[:4],
|
| 138 |
+
"forced_carrier": "β",
|
| 139 |
+
"reasoning": (
|
| 140 |
+
"G_halve is not closed on β€: halve(1) = 0.5 β β€. "
|
| 141 |
+
"The minimal closed extension under halving is β: "
|
| 142 |
+
"all rationals of the form p/2^n are forced first, "
|
| 143 |
+
"then by combining with other operations, all of β. "
|
| 144 |
+
"β is not assumed. β is forced."
|
| 145 |
+
),
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def demonstrate_Q_to_R() -> Dict:
|
| 150 |
+
"""
|
| 151 |
+
β β β: forced by applying square root to 2.
|
| 152 |
+
|
| 153 |
+
β = all fractions p/q
|
| 154 |
+
G_sqrt: v β βv
|
| 155 |
+
G_sqrt(2) = β2 β β (proven by Pythagoras)
|
| 156 |
+
|
| 157 |
+
Result: β = closure of β under limits and square roots.
|
| 158 |
+
"""
|
| 159 |
+
g_sqrt = Gradient("sqrt", lambda v: math.sqrt(v))
|
| 160 |
+
|
| 161 |
+
# β2 β β: demonstrate with increasing rational approximations
|
| 162 |
+
approximations = []
|
| 163 |
+
v = 2.0
|
| 164 |
+
approx = 1.0
|
| 165 |
+
for _ in range(8):
|
| 166 |
+
approx = (approx + v / approx) / 2
|
| 167 |
+
approximations.append(round(approx, 10))
|
| 168 |
+
|
| 169 |
+
return {
|
| 170 |
+
"source_carrier": "β",
|
| 171 |
+
"gradient": "sqrt (v β βv)",
|
| 172 |
+
"violating_example": (2, math.sqrt(2)),
|
| 173 |
+
"forced_carrier": "β",
|
| 174 |
+
"sqrt2_approximations": approximations,
|
| 175 |
+
"reasoning": (
|
| 176 |
+
"G_sqrt(2) = β2 = 1.41421356... β β (irrational). "
|
| 177 |
+
"The sequence of rational approximations converges but never arrives "
|
| 178 |
+
"at a rational value. The limit must exist β but it is not in β. "
|
| 179 |
+
"This forces the Cauchy-completion of β: the real numbers β. "
|
| 180 |
+
"β is not assumed. β is forced by demanding closure under β."
|
| 181 |
+
),
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def demonstrate_R_to_C() -> Dict:
|
| 186 |
+
"""
|
| 187 |
+
β β β: forced by applying square root to -1.
|
| 188 |
+
|
| 189 |
+
β = all real numbers
|
| 190 |
+
G_sqrt: v β βv
|
| 191 |
+
G_sqrt(-1) = i β β
|
| 192 |
+
|
| 193 |
+
Result: β = β βͺ {a + bi : a, b β β}
|
| 194 |
+
"""
|
| 195 |
+
# β(-1) forces i into existence
|
| 196 |
+
i = complex(0, 1)
|
| 197 |
+
|
| 198 |
+
return {
|
| 199 |
+
"source_carrier": "β",
|
| 200 |
+
"gradient": "sqrt (v β βv)",
|
| 201 |
+
"violating_example": (-1, "i (imaginary unit)"),
|
| 202 |
+
"forced_carrier": "β",
|
| 203 |
+
"i_properties": {
|
| 204 |
+
"i^1": i,
|
| 205 |
+
"i^2": i**2, # = -1: back in β
|
| 206 |
+
"i^3": i**3, # = -i
|
| 207 |
+
"i^4": i**4, # = 1: cycle complete
|
| 208 |
+
"cycle_length": 4,
|
| 209 |
+
},
|
| 210 |
+
"reasoning": (
|
| 211 |
+
"G_sqrt(-1) = i β β. "
|
| 212 |
+
"i is not a 'made up' number β it is forced by demanding closure. "
|
| 213 |
+
"Once i exists, i*i = -1 β β: we cycle back. "
|
| 214 |
+
"i has cycle length 4: i β -1 β -i β 1 β i. "
|
| 215 |
+
"The 4-cycle is not chosen. It follows from iΒ² = -1. "
|
| 216 |
+
"β = {a + bi : a, b β β} is the minimal closed extension. "
|
| 217 |
+
"β is not assumed. β is forced."
|
| 218 |
+
),
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def demonstrate_full_tower() -> List[Dict]:
|
| 223 |
+
"""
|
| 224 |
+
The full forced extension tower: β β β€ β β β β β β.
|
| 225 |
+
Each step is forced by a closure violation.
|
| 226 |
+
The tower is not assumed. It is derived.
|
| 227 |
+
"""
|
| 228 |
+
return [
|
| 229 |
+
demonstrate_N_to_Z(),
|
| 230 |
+
demonstrate_Z_to_Q(),
|
| 231 |
+
demonstrate_Q_to_R(),
|
| 232 |
+
demonstrate_R_to_C(),
|
| 233 |
+
]
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def as_training_text(demo: Dict) -> str:
|
| 237 |
+
"""
|
| 238 |
+
Render a forced extension demonstration as training text.
|
| 239 |
+
Format: the derivation, not a description of the derivation.
|
| 240 |
+
"""
|
| 241 |
+
lines = [
|
| 242 |
+
f"CARRIER_EXTENSION: {demo['source_carrier']} β {demo['forced_carrier']}",
|
| 243 |
+
f"GRADIENT: {demo['gradient']}",
|
| 244 |
+
f"CLOSURE_VIOLATION: {demo.get('closure_violation', [])[:3]}",
|
| 245 |
+
f"FORCED: {demo['forced_carrier']}",
|
| 246 |
+
f"REASONING:",
|
| 247 |
+
]
|
| 248 |
+
# Break reasoning into lines
|
| 249 |
+
reasoning = demo["reasoning"]
|
| 250 |
+
words = reasoning.split()
|
| 251 |
+
line = " "
|
| 252 |
+
for word in words:
|
| 253 |
+
if len(line) + len(word) + 1 > 70:
|
| 254 |
+
lines.append(line.rstrip())
|
| 255 |
+
line = " " + word + " "
|
| 256 |
+
else:
|
| 257 |
+
line += word + " "
|
| 258 |
+
lines.append(line.rstrip())
|
| 259 |
+
return "\n".join(lines)
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
# =============================================================================
|
| 263 |
+
# ARITHMETIC AS PROPAGATION
|
| 264 |
+
# =============================================================================
|
| 265 |
+
|
| 266 |
+
@dataclass
|
| 267 |
+
class ArithmeticContext:
|
| 268 |
+
"""
|
| 269 |
+
Addition, multiplication, etc. as gradient families.
|
| 270 |
+
Each arithmetic operation is a gradient field.
|
| 271 |
+
The properties of arithmetic (commutativity, associativity, distributivity)
|
| 272 |
+
are forced boundary conditions of the numeric carrier.
|
| 273 |
+
"""
|
| 274 |
+
carrier_name: str
|
| 275 |
+
zero: Any
|
| 276 |
+
one: Any
|
| 277 |
+
|
| 278 |
+
def G_add(self, operand: Any) -> Gradient:
|
| 279 |
+
"""Addition by a fixed operand."""
|
| 280 |
+
return Gradient(
|
| 281 |
+
name=f"add_{operand}",
|
| 282 |
+
transform=lambda v: v + operand,
|
| 283 |
+
description=f"Add {operand}: v β v + {operand}",
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
def G_mul(self, operand: Any) -> Gradient:
|
| 287 |
+
"""Multiplication by a fixed operand."""
|
| 288 |
+
return Gradient(
|
| 289 |
+
name=f"mul_{operand}",
|
| 290 |
+
transform=lambda v: v * operand,
|
| 291 |
+
description=f"Multiply by {operand}: v β v Γ {operand}",
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
def G_inv_add(self) -> Gradient:
|
| 295 |
+
"""Additive inverse: v β -v."""
|
| 296 |
+
return Gradient(
|
| 297 |
+
name="additive_inv",
|
| 298 |
+
transform=lambda v: -v,
|
| 299 |
+
description="Additive inverse: v β -v",
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
def G_inv_mul(self) -> Gradient:
|
| 303 |
+
"""
|
| 304 |
+
Multiplicative inverse: v β 1/v.
|
| 305 |
+
Not closed at v=0 (division by zero = carrier limit).
|
| 306 |
+
"""
|
| 307 |
+
def safe_inv(v):
|
| 308 |
+
if v == 0:
|
| 309 |
+
raise ValueError("G_inv_mul: 0 has no multiplicative inverse. "
|
| 310 |
+
"Carrier cannot include 0 under this gradient.")
|
| 311 |
+
return 1 / v
|
| 312 |
+
return Gradient(
|
| 313 |
+
name="multiplicative_inv",
|
| 314 |
+
transform=safe_inv,
|
| 315 |
+
description="Multiplicative inverse: v β 1/v (undefined at 0)",
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
def verify_commutativity(self, a: Any, b: Any) -> bool:
|
| 319 |
+
"""a + b == b + a: forced by carrier structure."""
|
| 320 |
+
return a + b == b + a
|
| 321 |
+
|
| 322 |
+
def verify_associativity(self, a: Any, b: Any, c: Any) -> bool:
|
| 323 |
+
"""(a + b) + c == a + (b + c): forced by carrier structure."""
|
| 324 |
+
return (a + b) + c == a + (b + c)
|
| 325 |
+
|
| 326 |
+
def verify_distributivity(self, a: Any, b: Any, c: Any) -> bool:
|
| 327 |
+
"""a * (b + c) == a*b + a*c: forced by carrier structure."""
|
| 328 |
+
return a * (b + c) == a * b + a * c
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
if __name__ == "__main__":
|
| 332 |
+
print("\n" + "="*60)
|
| 333 |
+
print(" NUMBER SYSTEM FORCED EXTENSIONS")
|
| 334 |
+
print(" The tower β β β€ β β β β β β is derived, not assumed.")
|
| 335 |
+
print("="*60)
|
| 336 |
+
|
| 337 |
+
for demo in demonstrate_full_tower():
|
| 338 |
+
print(f"\n{as_training_text(demo)}")
|
| 339 |
+
print("-"*60)
|