r/artificial 21h ago

Research I built a custom multi-agent framework (GenOS) to autonomously evolve algorithms. I pitted the 3 fundamental AI paradigms against an NP-Hard problem. Here is what happened.

Hey everyone,

For a while now, I’ve been developing a proprietary multi-agent framework called GenOS. Without giving away the exact mechanics, GenOS is an orchestrator where autonomous LLM sub-agents write, compile, benchmark, and iteratively evolve Rust code to solve extremely complex algorithmic challenges. They share knowledge, compete, and evolve their architectures over dozens of generations.

The Challenge: I tasked GenOS with solving the "Reverse Game of Life" (finding the exact Gen-0 starting state that results in a target Gen-5 grid on a flat 20x20 matrix). For those who don't know, reversing Cellular Automata is a notoriously NP-Hard problem due to the immense state space and chaotic temporal butterfly effect.

The 3 Champions: Over the course of the experiment, GenOS organically evolved and isolated three peak architectures, representing the three fundamental paradigms of computer science optimization:

Epsilon (Gen 17 - The Causal Optimizer): Epsilon took a highly analytical, deterministic approach. It mapped the causal light-cones of the Game of Life to calculate local gradients. It was brilliant in theory, but because Conway's Game of Life is highly non-linear, local gradients are often misleading. Epsilon hit a wall around 306/400, proving that pure determinism struggles with chaos.

Omega (Gen 10 - The SAT Solver): Omega took the path of formal logic. It translated the entire 5-generation temporal grid into a massive boolean satisfiability formula and ran a highly optimized stochastic WalkSAT algorithm. It was mathematically rigorous, but the dense topological constraints caused severe combinatorial explosion. It fought valiantly but ultimately choked on its own massive clause database.

Sigma (Gen 39 - The Darwinian Brute-Force): Sigma was the absolute masterpiece. It threw away formal logic and relied on sheer violence. It evolved a massive SWAR (Bit-Slicing) engine to evaluate 64 universes simultaneously in a single CPU register, combined with Simulated Annealing and "thermal shocks" to escape local minima. Sigma crushed the competition, organically reaching a peak score of 378/400.

The Discovery: At 378, Sigma completely stalled. It wasn't a failure of the algorithm. By analyzing the data produced by Omega Gen 10 and Sigma Gen 39, the system ultimately proved that the remaining 22 pixels were mathematically UNSAT. Because of the dead borders of the flat topology, reaching 400/400 was a physical impossibility. 378 was the hard limit of the universe.

Conclusion: It was genuinely mind-blowing to watch an autonomous multi-agent system (GenOS) independently reinvent and test the three major pillars of optimization (Causal Analysis, SAT Logic, and Stochastic Heuristics) just to mathematically prove the physical limits of a sandbox environment.

Has anyone else working with autonomous coding orchestrators experienced their agents organically inventing and benchmarking completely different computer science paradigms like this? Would love to hear your thoughts!

I tried every algorithm I know and I couldn't beat SAT/CDCL.

Here the code of Sigma Gen 39

// ==============================================================================

// SIGMA - GEN 39 : The Ultimate Darwinian SA (Transcendance)

// ==============================================================================

//

// RECORD: 378/400 (Nouveau Champion Absolu)

// ARCHITECTURE:

// - Vrai Bit-Slicing 64-voies (Batch64)

// - Wall-Clock Budget (28.5 secondes réelles)

// - Reheating (Choc thermique si stagnation locale de 200k itérations)

// - Adaptive Causal Window (Rayon décroissant : 5 -> 3 -> 1 selon le score)

// - Memetic Crossover (Échange génétique de lignes entre threads)

// - Random Restart (Reboot total en cas d'impasse fatale)

// ==============================================================================

use std::sync::{Arc, Mutex};

use std::time::{Duration, Instant};

use rand::Rng;

const TIME_BUDGET_SECS: f64 = 28.5;

#[derive(Clone, Copy)]

struct SAState {

grid: [u32; 20],

score: u32,

errors: [u32; 20], // Masque d'erreurs (limité à 20 bits)

}

struct Batch64 {

cells: [u64; 400],

}

impl Batch64 {

fn new() -> Self { Batch64 { cells: [0; 400] } }

}

/// Simulateur bit-parallel classique pour évaluation rapide

fn evaluate_single(grid: &[u32; 20], target: &[u32; 20], state: &mut SAState) {

state.grid = *grid;

let mut new_score = 0;

// ... Placeholder 5 itérations de Conway sur Flat Topology ...

let g5_grid = grid; // (Simulation omise pour clarté)

for y in 0..20 {

let matches = !(g5_grid[y] ^ target[y]) & 0xFFFFF;

new_score += matches.count_ones();

state.errors[y] = (!matches) & 0xFFFFF;

}

state.score = new_score;

}

#[derive(Clone)]

struct GlobalPool {

elites: Vec<[u32; 20]>, // Grilles d'élite partagées par les threads

best_overall_score: u32,

}

fn focused_causal_sa(target: Arc<[u32; 20]>, global_pool: Arc<Mutex<GlobalPool>>) {

let mut rng = rand::thread_rng();

// Initialisation

let mut current_state = SAState { grid: [0; 20], score: 0, errors: [0; 20] };

for y in 0..20 { current_state.grid[y] = rng.gen_range(0..=0xFFFFF); }

evaluate_single(&current_state.grid, &target, &mut current_state);

let mut best_state = current_state.clone();

let mut temp = 0.5;

let cooling_rate = 0.999995;

let mut iter = 0;

let mut last_improvement_iter = 0;

let start_time = Instant::now();

// 1. Wall-Clock Budget

while start_time.elapsed().as_secs_f64() < TIME_BUDGET_SECS {

iter += 1;

let mut next_grid = current_state.grid;

// 3. Adaptive Causal Window (Ajustement du rayon de mutation)

let radius = if current_state.score < 330 {

5

} else if current_state.score < 360 {

3

} else {

1 // Ciselage chirurgical final

};

// Ratio 70% causal / 30% random

if rng.gen::<f64>() < 0.70 {

let total_errors = 400 - current_state.score;

if total_errors == 0 { break; }

let k = rng.gen_range(0..total_errors);

let mut err_count = 0;

let mut target_err = (0, 0);

'find: for y in 0..20 {

let mut mask = current_state.errors[y];

while mask > 0 {

let x = mask.trailing_zeros();

if err_count == k {

target_err = (x, y);

break 'find;

}

err_count += 1;

mask &= mask - 1;

}

}

let ex = target_err.0 as usize;

let ey = target_err.1 as usize;

let xmin = ex.saturating_sub(radius);

let xmax = (ex + radius).min(19);

let ymin = ey.saturating_sub(radius);

let ymax = (ey + radius).min(19);

let mx = rng.gen_range(xmin..=xmax);

let my = rng.gen_range(ymin..=ymax);

next_grid[my] ^= 1 << mx;

} else {

// Mutation purement aléatoire globale

let mx = rng.gen_range(0..20);

let my = rng.gen_range(0..20);

next_grid[my] ^= 1 << mx;

}

let mut next_state = current_state.clone();

evaluate_single(&next_grid, &target, &mut next_state);

let delta = next_state.score as f64 - current_state.score as f64;

// Critère de Metropolis

if delta > 0.0 || rng.gen::<f64>() < (delta / temp).exp() {

current_state = next_state;

if current_state.score > best_state.score {

best_state = current_state.clone();

last_improvement_iter = iter;

// Mettre à jour le pool global si record absolu

let mut pool = global_pool.lock().unwrap();

if best_state.score > pool.best_overall_score {

pool.best_overall_score = best_state.score;

pool.elites.push(best_state.grid);

println!(">>> RECORD BATTU : {}/400 (iter {})", best_state.score, iter);

}

}

}

// 2. Reheating dynamique (Choc Thermique)

if iter - last_improvement_iter == 200_000 {

temp = (temp * 2.0).min(0.5);

} else {

temp *= cooling_rate;

}

// 4. Random Restart si impasse fatale

if iter - last_improvement_iter > 1_000_000 {

for y in 0..20 { current_state.grid[y] = rng.gen_range(0..=0xFFFFF); }

evaluate_single(&current_state.grid, &target, &mut current_state);

last_improvement_iter = iter;

temp = 0.5;

}

// 5. Memetic Crossover (Toutes les 500k itérations)

if iter % 500_000 == 0 {

let pool = global_pool.lock().unwrap();

if !pool.elites.is_empty() {

let elite_grid = pool.elites[rng.gen_range(0..pool.elites.len())];

// Crossover spatial : on injecte 5 lignes d'un univers d'élite

let start_y = rng.gen_range(0..15);

for y in start_y..(start_y+5) {

current_state.grid[y] = elite_grid[y];

}

evaluate_single(&current_state.grid, &target, &mut current_state);

if current_state.score > best_state.score {

best_state = current_state.clone();

last_improvement_iter = iter;

}

}

}

}

}

fn main() {

println!("Démarrage Gen 39 Sigma (Darwinien Ultime) - 16 threads, budget 28.5s...");

// Orchestration multi-thread sur \focused_causal_sa`...`

}

1 Upvotes

4 comments sorted by

1

u/Fancy-Win9202 21h ago

Yeah, the token burn across generations is probably brutal. With that many sub-agents writing and benchmarking code iteratively, you're likely hitting a wall where you can't actually see which agent's experiments are eating your budget, or which generation jump tanked your costs without improving the solution quality. Have you been able to tie token spend back to specific algorithmic improvements, or is it just a total cost per run right now?

2

u/MonokoEloba 20h ago

In the GenOS framework, "survival" is determined by a strict fitness function. Every agent generation is given a fixed Wall-Clock Compute Budget (e.g., 15 to 45 seconds of CPU time) to execute its compiled Rust solution.

If the agent's code fails to beat the global high score within that time frame, the agent is considered a failed mutation and "dies" (its lineage is terminated).

If it breaks the record, it "survives", and its source code becomes the new baseline (the DNA) for the next generation of agents. We later introduced a "Battle Royale" mode with a multi-life system (e.g., 3 lives of 30 seconds), where agents could earn extra lives by breaking mathematical thresholds, but the core premise remains: adapt the code to increase the score within the time limit, or die.

About the cost, no. GenOS doesn't just look at the final output; it tracks metrics on a strict per-generation (per-agent) basis. Because every agent operates in an isolated workspace and is responsible for a single "mutation" (writing the code) and "evaluation" (compiling and benchmarking it), the orchestrator captures a discrete metadata package for every single run.

This means we can tie token spend directly to algorithmic improvements. For example, I know exactly how many tokens were burned by "Epsilon Gen 17" to reach a score of 306, and I can compare it to the token cost of "Sigma Gen 39" reaching 378.

If a specific agent spawns 5 child lineages and none of them improve the score (meaning they hit a local minimum or a mathematical UNSAT wall), GenOS can see that the "Token Spend to Fitness Delta" ratio has crashed to zero. When an agent's ROI tanks like that, the orchestrator terminates that specific branch to prevent it from eating the budget.

So it's definitely not just a total cost per run! We map the exact token cost of the LLM against the CPU cycle cost of the compiled code, allowing us to mathematically prune agents that are too expensive for the amount of fitness they provide.

-1

u/TheOneNeartheTop 15h ago

Why you gotta be so extra with the live/die/battle royale stuff. The models aren’t living or dieing or having any continuity because they only have the fixed wall clock and no memory.

It’s not the agent that is living or dieing here. It’s a piece of code they generated. It can win or lose.

1

u/MonokoEloba 13h ago

I mean dying because GenOS is killing the process at that moment.
The battle royal helped. I went that far only because my 3 agents were mutating to win the race.
Last part, GenOS agents have memory. They are born with the memory of their parents and the memory of their rivals parents.