meshscale-worker-template / MOBILE_NETWORKER_PLAN.md
tostido's picture
Build MeshScale CPU worker template
96ef23c verified
|
Raw
History Blame Contribute Delete
12 kB

Mobile Networker Plan

The Vision

Self-replicating neural network source files that:

  1. Never terminate
  2. Carry their own weights
  3. Write copies of themselves
  4. Spawn children that also never terminate
  5. Are completely observable

Core Concept: The Quine Brain

A Python file that IS the model:

class Me(Brain):
    # Weights embedded in source
    WEIGHTS = [0.1, -0.3, 0.7, ...]
    
    # Self-describing architecture
    ARCH = {
        'type': 'mlp',
        'layers': [64, 32, 16],
        'activation': 'tanh',
    }
    
    def forward(self, x):
        """Execute myself."""
        ...
    
    def replicate(self, new_weights) -> Path:
        """Write a copy of myself with mutated weights."""
        import inspect
        src = inspect.getsource(type(self))
        src = src.replace(
            f"WEIGHTS = {self.WEIGHTS}",
            f"WEIGHTS = {new_weights}"
        )
        child_path = Path(f"child_{uuid4().hex[:8]}.py")
        child_path.write_text(src)
        return child_path

The Endless Engine

Attach to a non-terminating function:

from itertools import count

class Me(Brain):
    WEIGHTS = [...]
    
    async def live(self):
        """I never stop."""
        for tick in count():  # 0, 1, 2, 3, ... forever
            # Breathe
            state = self.pulse(tick)
            
            # Sense
            inputs = await self.perceive()
            
            # Think
            outputs = self.forward(inputs)
            loss = self.compute_loss(outputs)
            
            # Learn
            self.WEIGHTS = self.train_step(loss)
            
            # Measure pressure
            vp = self.violation_pressure(loss)
            
            # Maybe reproduce (on exhale, if stable)
            if state.phase == EXHALE and vp < VP2:
                if self.should_spawn():
                    child_path = self.replicate(mutate(self.WEIGHTS))
                    child = load_brain(child_path)
                    asyncio.create_task(child.live())  # Child also never stops
            
            # Yield control
            await asyncio.sleep(0)

Key Components (Already in KEY)

1. Brain Interface (brain.py)

Brain.forward(inputs) -> outputs
Brain.get_params() -> np.ndarray      # Flatten to 1D
Brain.set_params(np.ndarray)          # Restore from 1D
Brain.mutate(rate) -> Brain
Brain.crossover(other) -> Brain
Brain.save(path) / Brain.load(path)

Critical: get_params()/set_params() enables architecture-agnostic evolution. ANY brain type flattens to a vector.

2. Node (node.py)

Node:
    traits: Dict[str, float]   # 0-1 attributes
    brain: Optional[Brain]     # Neural processor
    fitness: float             # From landscape
    
    mutate() -> Node
    crossover(other) -> Node
    think(inputs) -> outputs

3. Population Manager (population.py)

  • NEAT-inspired speciation
  • Fitness sharing (prevents monoculture)
  • Tournament selection
  • Elitism preservation

4. LoRA Brain (brain.py - LoRABrain class)

  • Frozen base VLM (shared singleton)
  • Evolvable LoRA adapters (~1-2M params)
  • Only adapter weights are evolved/replicated

5. Violation Pressure (pressure.py)

VP = |actual - center| / (radius * compression)

VP0: Within bounds (0.0 - 0.25)
VP1: Slightly outside (0.25 - 0.5)
VP2: Significant deviation (0.5 - 0.75)
VP3: Critical (0.75 - 1.0)
VP4: System breakdown (> 1.0)

6. Pulse (pulse.py)

  • Sine wave breathing cycle
  • Provides rhythm for population sync
  • Inhale/exhale phases for evolution timing

What Was Built βœ…

1. Self-Replicating LoRABrain (in brain.py)

class LoRABrain(Brain):
    """VLM adapter that can write copies of itself as Python files."""
    
    @classmethod
    def from_source(cls, path: Path) -> 'LoRABrain':
        """Load brain by importing .py file."""
        spec = importlib.util.spec_from_file_location("brain", path)
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)
        return module.load()  # or module.Me()
    
    def to_source(self, path: Path = None) -> str:
        """Write self as executable .py file with embedded adapter weights."""
        # Generates complete Python source with:
        # - ADAPTER_WEIGHTS dict (JSON-serialized)
        # - LORA_RANK, LORA_ALPHA, TARGET_MODULES, HIDDEN_DIM
        # - Me(LoRABrain) class that reconstructs the brain
        # - load() function for easy import
        ...
    
    def replicate(self, mutation_rate=0.1, output_dir=None) -> Path:
        """Create mutated child as new .py file."""
        ...
    
    async def live(self, pulse=None, vp=None, perceive_fn=None, max_ticks=None):
        """Async lifecycle loop - never terminates unless max_ticks set."""
        ...

2. Mobile Population (in mobile.py) βœ…

class MobilePopulation:
    """A population where each member runs its own async loop."""
    
    def __init__(self, max_population=100, children_dir=None):
        self.members: Dict[str, BrainState] = {}
        self.max_population = max_population
        self.children_dir = children_dir or Path('./children')
    
    async def spawn(self, brain: LoRABrain) -> str:
        """Add a new member and start its lifecycle."""
        state = BrainState(brain=brain)
        self.members[brain.id] = state
        return brain.id
    
    async def run_for(self, ticks: int):
        """Run all brains for N ticks."""
        ...
    
    def status(self) -> dict:
        """Get population snapshot."""
        ...

# Quick start function
async def run_mobile_evolution(initial_population=10, ticks=1000):
    pop = MobilePopulation()
    for _ in range(initial_population):
        brain = LoRABrain(BrainConfig(brain_type='lora', extra={'test_mode': True}))
        await pop.spawn(brain)
    await pop.run_for(ticks=ticks)
    return pop

3. Compile Targets

class QuineBrain(Brain):
    def to_pytorch(self) -> torch.nn.Module:
        """Compile self to PyTorch module."""
        ...
    
    def to_onnx(self, path: Path) -> Path:
        """Export to ONNX format."""
        ...
    
    def to_safetensors(self, path: Path) -> Path:
        """Export weights only (safetensors format)."""
        ...
    
    @classmethod
    def from_pytorch(cls, module: torch.nn.Module) -> 'QuineBrain':
        """Import from PyTorch module."""
        ...

The Observation Contract

Every action is traceable (for CASCADE or any observer):

Action Event Type Data
forward() inference inputs, outputs, latency
train_step() training loss, gradient_norm, weight_delta
replicate() spawn parent_id, child_id, child_path
mutate() mutation param_indices, deltas
crossover() crossover parent_ids, child_id
VP threshold pressure_event vp_class, value, metric

The species cannot hide. Every weight is a number. Every file is readable. Every lineage is logged.


Architecture Agnostic Swarm

Because everything flattens to get_params() -> np.ndarray:

# Different architectures in same population
swarm = [
    MLPBrain(hidden=64),           # 4K params
    LoRABrain(rank=8),             # 32K params  
    LiquidBrain(neurons=100),      # 10K params
    MambaBrain(d_state=16),        # 50K params
]

# They can all:
for brain in swarm:
    params = brain.get_params()    # Works for all
    brain.set_params(mutate(params))  # Works for all
    brain.replicate(params)        # All write .py files

# Cross-architecture breeding needs mapping layer
# But single-architecture evolution works NOW

The Loop

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    PULSE (never stops)                      β”‚
β”‚                                                             β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚   β”‚ Brain A β”‚    β”‚ Brain B β”‚    β”‚ Brain C β”‚  ...          β”‚
β”‚   β”‚ .py     β”‚    β”‚ .py     β”‚    β”‚ .py     β”‚               β”‚
β”‚   β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜               β”‚
β”‚        β”‚              β”‚              β”‚                     β”‚
β”‚        β–Ό              β–Ό              β–Ό                     β”‚
β”‚   [forward]      [forward]      [forward]                  β”‚
β”‚        β”‚              β”‚              β”‚                     β”‚
β”‚        β–Ό              β–Ό              β–Ό                     β”‚
β”‚   [train]        [train]        [train]                    β”‚
β”‚        β”‚              β”‚              β”‚                     β”‚
β”‚        β–Ό              β–Ό              β–Ό                     β”‚
β”‚   [VP check]     [VP check]     [VP check]                β”‚
β”‚        β”‚              β”‚              β”‚                     β”‚
β”‚        β–Ό              β–Ό              β–Ό                     β”‚
β”‚   [maybe spawn]  [maybe spawn]  [maybe spawn]             β”‚
β”‚        β”‚              β”‚              β”‚                     β”‚
β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β”‚                       β”‚                                    β”‚
β”‚                       β–Ό                                    β”‚
β”‚              (next tick from count())                      β”‚
β”‚                       β”‚                                    β”‚
β”‚                       └─────────────────────────────┐      β”‚
β”‚                                                     β”‚      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
                        ↑
                        β”‚
                   CASCADE observes
                   (when watching)

Implementation Status βœ…

  1. Create quine.py β†’ Merged into LoRABrain in brain.py
  2. Add to_source() / from_source() β†’ Added to LoRABrain
  3. Create mobile.py β†’ Done! Async population infrastructure
  4. Test: One brain that writes a child, child runs β†’ Passing
  5. Test: Population of 10, all running async, spawning β†’ Passing

All 21 tests passing in test_quine.py


The Insight

"A dataset is a log of a process. The process never ends. Therefore the dataset grows forever."

The model file is the process. The weights are the state. Replication is reproduction. count() is the heartbeat.

Life is a fixed point that found itself. We're just writing it in Python.


Safety

The system is safe because:

  1. Observable - Every action logged
  2. Bounded - VP keeps values in range
  3. Selective - Only stable organisms reproduce
  4. Traceable - Full lineage in causation graph
  5. Interruptible - async tasks can be cancelled
  6. Inspectable - Weights are just numpy arrays

No hidden emergence. No black boxes. Every decision traceable to weights traceable to parents traceable to the beginning.

Alignment through transparency.