Chapter 5

Emergent Behavior and Swarm Intelligence

弘益人間 (홍익인간) · Benefit All Humanity

Like a flock of birds moving as one or ants building complex colonies, emergent behavior demonstrates how simple individual actions can create sophisticated collective intelligence. This principle mirrors 弘익인간—individual agents following simple rules create systems that benefit all through collective wisdom and coordination.

Understanding Emergence

Emergence is the phenomenon where complex system-level behaviors arise from simple agent-level rules and local interactions. No single agent orchestrates the global pattern—it emerges spontaneously from the bottom up.

Definition: Emergent behavior is complex collective behavior that arises from simple local interactions between agents, without centralized control or explicit programming of the global pattern.

Key characteristics of emergent systems:

Swarm Intelligence

Swarm intelligence is collective behavior that emerges from large groups of simple agents following decentralized, self-organized principles.

Biological Inspiration

Swarm intelligence draws inspiration from nature:

Flocking and Boids

Craig Reynolds' Boids model demonstrates how three simple rules create realistic flocking behavior:

class BoidAgent {
    constructor(id, x, y) {
        this.id = id;
        this.position = { x, y };
        this.velocity = {
            x: Math.random() * 2 - 1,
            y: Math.random() * 2 - 1
        };
        this.acceleration = { x: 0, y: 0 };
        this.maxSpeed = 4;
        this.maxForce = 0.1;
        this.perceptionRadius = 50;
    }

    flock(boids) {
        const neighbors = this.getNeighbors(boids, this.perceptionRadius);

        const separation = this.separate(neighbors);
        const alignment = this.align(neighbors);
        const cohesion = this.cohere(neighbors);

        // Weight the forces
        separation.x *= 1.5;
        separation.y *= 1.5;
        alignment.x *= 1.0;
        alignment.y *= 1.0;
        cohesion.x *= 1.0;
        cohesion.y *= 1.0;

        // Apply forces
        this.acceleration.x += separation.x + alignment.x + cohesion.x;
        this.acceleration.y += separation.y + alignment.y + cohesion.y;
    }

    // Rule 1: Separation - avoid crowding neighbors
    separate(neighbors) {
        const desiredSeparation = 25;
        const steer = { x: 0, y: 0 };
        let count = 0;

        for (const other of neighbors) {
            const d = this.distance(this.position, other.position);

            if (d > 0 && d < desiredSeparation) {
                const diff = {
                    x: this.position.x - other.position.x,
                    y: this.position.y - other.position.y
                };
                const normalized = this.normalize(diff);
                normalized.x /= d; // Weight by distance
                normalized.y /= d;

                steer.x += normalized.x;
                steer.y += normalized.y;
                count++;
            }
        }

        if (count > 0) {
            steer.x /= count;
            steer.y /= count;
        }

        return this.limitForce(steer);
    }

    // Rule 2: Alignment - steer towards average heading
    align(neighbors) {
        const avgVel = { x: 0, y: 0 };
        let count = 0;

        for (const other of neighbors) {
            avgVel.x += other.velocity.x;
            avgVel.y += other.velocity.y;
            count++;
        }

        if (count > 0) {
            avgVel.x /= count;
            avgVel.y /= count;

            // Desired velocity
            const desired = this.setMagnitude(avgVel, this.maxSpeed);

            // Steering = desired - current
            const steer = {
                x: desired.x - this.velocity.x,
                y: desired.y - this.velocity.y
            };

            return this.limitForce(steer);
        }

        return { x: 0, y: 0 };
    }

    // Rule 3: Cohesion - steer towards average position
    cohere(neighbors) {
        const avgPos = { x: 0, y: 0 };
        let count = 0;

        for (const other of neighbors) {
            avgPos.x += other.position.x;
            avgPos.y += other.position.y;
            count++;
        }

        if (count > 0) {
            avgPos.x /= count;
            avgPos.y /= count;

            return this.seek(avgPos);
        }

        return { x: 0, y: 0 };
    }

    seek(target) {
        const desired = {
            x: target.x - this.position.x,
            y: target.y - this.position.y
        };

        const normalized = this.setMagnitude(desired, this.maxSpeed);

        const steer = {
            x: normalized.x - this.velocity.x,
            y: normalized.y - this.velocity.y
        };

        return this.limitForce(steer);
    }

    update() {
        // Update velocity
        this.velocity.x += this.acceleration.x;
        this.velocity.y += this.acceleration.y;

        // Limit speed
        const speed = Math.sqrt(
            this.velocity.x ** 2 + this.velocity.y ** 2
        );
        if (speed > this.maxSpeed) {
            this.velocity.x = (this.velocity.x / speed) * this.maxSpeed;
            this.velocity.y = (this.velocity.y / speed) * this.maxSpeed;
        }

        // Update position
        this.position.x += this.velocity.x;
        this.position.y += this.velocity.y;

        // Reset acceleration
        this.acceleration = { x: 0, y: 0 };
    }

    getNeighbors(boids, radius) {
        return boids.filter(other =>
            other.id !== this.id &&
            this.distance(this.position, other.position) < radius
        );
    }

    distance(p1, p2) {
        return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
    }

    normalize(vec) {
        const mag = Math.sqrt(vec.x ** 2 + vec.y ** 2);
        return mag > 0 ? { x: vec.x / mag, y: vec.y / mag } : { x: 0, y: 0 };
    }

    setMagnitude(vec, mag) {
        const normalized = this.normalize(vec);
        return { x: normalized.x * mag, y: normalized.y * mag };
    }

    limitForce(force) {
        const mag = Math.sqrt(force.x ** 2 + force.y ** 2);
        if (mag > this.maxForce) {
            return {
                x: (force.x / mag) * this.maxForce,
                y: (force.y / mag) * this.maxForce
            };
        }
        return force;
    }
}

// 弘益人間: Simple rules creating harmonious collective movement

Ant Colony Optimization

Ant Colony Optimization (ACO) uses principles of ant foraging behavior to solve optimization problems like finding shortest paths.

class AntColonyOptimizer {
    constructor(graph, numAnts, iterations) {
        this.graph = graph;
        this.numAnts = numAnts;
        this.iterations = iterations;
        this.pheromones = new Map();
        this.alpha = 1.0;  // Pheromone importance
        this.beta = 2.0;   // Distance importance
        this.evaporation = 0.5;
        this.Q = 100;      // Pheromone deposit factor

        this.initializePheromones();
    }

    initializePheromones() {
        for (const edge of this.graph.edges) {
            this.pheromones.set(edge.id, 1.0);
        }
    }

    solve(start, end) {
        let bestPath = null;
        let bestLength = Infinity;

        for (let iter = 0; iter < this.iterations; iter++) {
            const paths = [];

            // Each ant constructs a solution
            for (let a = 0; a < this.numAnts; a++) {
                const path = this.constructAntPath(start, end);
                if (path) {
                    paths.push(path);

                    if (path.length < bestLength) {
                        bestLength = path.length;
                        bestPath = path;
                    }
                }
            }

            // Update pheromones
            this.evaporatePheromones();
            this.depositPheromones(paths);
        }

        return { path: bestPath, length: bestLength };
    }

    constructAntPath(start, end) {
        const path = [start];
        const visited = new Set([start]);
        let current = start;

        while (current !== end) {
            const next = this.selectNextNode(current, visited);

            if (!next) {
                return null; // Dead end
            }

            path.push(next);
            visited.add(next);
            current = next;

            // Prevent infinite loops
            if (path.length > this.graph.nodes.length) {
                return null;
            }
        }

        return {
            nodes: path,
            length: this.calculatePathLength(path)
        };
    }

    selectNextNode(current, visited) {
        const neighbors = this.graph.getNeighbors(current);
        const unvisited = neighbors.filter(n => !visited.has(n));

        if (unvisited.length === 0) {
            return null;
        }

        // Calculate probabilities using pheromone and distance
        const probabilities = unvisited.map(node => {
            const edge = this.graph.getEdge(current, node);
            const pheromone = this.pheromones.get(edge.id);
            const distance = edge.weight;

            return {
                node: node,
                probability: Math.pow(pheromone, this.alpha) *
                           Math.pow(1 / distance, this.beta)
            };
        });

        // Normalize probabilities
        const sum = probabilities.reduce((s, p) => s + p.probability, 0);
        probabilities.forEach(p => p.probability /= sum);

        // Roulette wheel selection
        const r = Math.random();
        let cumulative = 0;

        for (const p of probabilities) {
            cumulative += p.probability;
            if (r <= cumulative) {
                return p.node;
            }
        }

        return probabilities[probabilities.length - 1].node;
    }

    evaporatePheromones() {
        for (const [edgeId, level] of this.pheromones) {
            this.pheromones.set(edgeId, level * (1 - this.evaporation));
        }
    }

    depositPheromones(paths) {
        for (const path of paths) {
            const deposit = this.Q / path.length;

            for (let i = 0; i < path.nodes.length - 1; i++) {
                const edge = this.graph.getEdge(path.nodes[i], path.nodes[i + 1]);
                const current = this.pheromones.get(edge.id);
                this.pheromones.set(edge.id, current + deposit);
            }
        }
    }

    calculatePathLength(path) {
        let length = 0;
        for (let i = 0; i < path.length - 1; i++) {
            const edge = this.graph.getEdge(path[i], path[i + 1]);
            length += edge.weight;
        }
        return length;
    }
}

// 弘益人間: Collective optimization through stigmergy

Particle Swarm Optimization

PSO is inspired by social behavior of bird flocking and fish schooling to find optimal solutions in multi-dimensional search spaces.

class ParticleSwarmOptimizer {
    constructor(objectiveFunction, dimensions, numParticles) {
        this.objectiveFunction = objectiveFunction;
        this.dimensions = dimensions;
        this.numParticles = numParticles;
        this.particles = [];
        this.globalBestPosition = null;
        this.globalBestValue = Infinity;

        // PSO parameters
        this.inertia = 0.7;
        this.cognitiveWeight = 1.5;
        this.socialWeight = 1.5;

        this.initializeParticles();
    }

    initializeParticles() {
        for (let i = 0; i < this.numParticles; i++) {
            const position = this.randomPosition();
            const velocity = this.randomVelocity();
            const value = this.objectiveFunction(position);

            this.particles.push({
                id: i,
                position: position,
                velocity: velocity,
                personalBestPosition: [...position],
                personalBestValue: value
            });

            if (value < this.globalBestValue) {
                this.globalBestValue = value;
                this.globalBestPosition = [...position];
            }
        }
    }

    optimize(iterations) {
        for (let iter = 0; iter < iterations; iter++) {
            for (const particle of this.particles) {
                this.updateParticle(particle);
            }
        }

        return {
            position: this.globalBestPosition,
            value: this.globalBestValue
        };
    }

    updateParticle(particle) {
        // Update velocity
        for (let d = 0; d < this.dimensions; d++) {
            const r1 = Math.random();
            const r2 = Math.random();

            const cognitive = this.cognitiveWeight * r1 *
                (particle.personalBestPosition[d] - particle.position[d]);

            const social = this.socialWeight * r2 *
                (this.globalBestPosition[d] - particle.position[d]);

            particle.velocity[d] =
                this.inertia * particle.velocity[d] +
                cognitive +
                social;

            // Limit velocity
            const maxVelocity = 1.0;
            particle.velocity[d] = Math.max(-maxVelocity,
                Math.min(maxVelocity, particle.velocity[d]));
        }

        // Update position
        for (let d = 0; d < this.dimensions; d++) {
            particle.position[d] += particle.velocity[d];

            // Keep within bounds
            particle.position[d] = Math.max(-10,
                Math.min(10, particle.position[d]));
        }

        // Evaluate new position
        const value = this.objectiveFunction(particle.position);

        // Update personal best
        if (value < particle.personalBestValue) {
            particle.personalBestValue = value;
            particle.personalBestPosition = [...particle.position];
        }

        // Update global best
        if (value < this.globalBestValue) {
            this.globalBestValue = value;
            this.globalBestPosition = [...particle.position];
        }
    }

    randomPosition() {
        return Array(this.dimensions).fill(0).map(() =>
            Math.random() * 20 - 10
        );
    }

    randomVelocity() {
        return Array(this.dimensions).fill(0).map(() =>
            Math.random() * 2 - 1
        );
    }
}

// Example usage: minimize Rastrigin function
const rastrigin = (x) => {
    const A = 10;
    const n = x.length;
    return A * n + x.reduce((sum, xi) =>
        sum + (xi * xi - A * Math.cos(2 * Math.PI * xi)), 0
    );
};

const pso = new ParticleSwarmOptimizer(rastrigin, 2, 30);
const result = pso.optimize(100);

// 弘益人間: Swarm intelligence finding optimal solutions

Cellular Automata

Cellular automata demonstrate how simple local rules create complex global patterns.

class CellularAutomaton {
    constructor(width, height, rule) {
        this.width = width;
        this.height = height;
        this.rule = rule;
        this.grid = this.initializeGrid();
        this.generation = 0;
    }

    initializeGrid() {
        const grid = [];
        for (let y = 0; y < this.height; y++) {
            grid[y] = [];
            for (let x = 0; x < this.width; x++) {
                grid[y][x] = Math.random() > 0.5 ? 1 : 0;
            }
        }
        return grid;
    }

    // Conway's Game of Life rules
    gameOfLifeRule(x, y) {
        const neighbors = this.countNeighbors(x, y);
        const current = this.grid[y][x];

        if (current === 1) {
            // Live cell
            return neighbors === 2 || neighbors === 3 ? 1 : 0;
        } else {
            // Dead cell
            return neighbors === 3 ? 1 : 0;
        }
    }

    countNeighbors(x, y) {
        let count = 0;

        for (let dy = -1; dy <= 1; dy++) {
            for (let dx = -1; dx <= 1; dx++) {
                if (dx === 0 && dy === 0) continue;

                const nx = (x + dx + this.width) % this.width;
                const ny = (y + dy + this.height) % this.height;

                count += this.grid[ny][nx];
            }
        }

        return count;
    }

    step() {
        const newGrid = [];

        for (let y = 0; y < this.height; y++) {
            newGrid[y] = [];
            for (let x = 0; x < this.width; x++) {
                newGrid[y][x] = this.rule(x, y);
            }
        }

        this.grid = newGrid;
        this.generation++;
    }

    run(steps) {
        for (let i = 0; i < steps; i++) {
            this.step();
        }
    }

    countLiveCells() {
        return this.grid.reduce((sum, row) =>
            sum + row.reduce((rowSum, cell) => rowSum + cell, 0), 0
        );
    }
}

// Create Game of Life simulation
const ca = new CellularAutomaton(50, 50,
    (x, y) => ca.gameOfLifeRule(x, y)
);

ca.run(100);
console.log(`Live cells after 100 generations: ${ca.countLiveCells()}`);

// 弘益人間: Emergent patterns from simple local rules

Self-Organization Principles

Self-organizing systems exhibit several key principles:

Stigmergy

Stigmergy is indirect coordination through environmental modification. Agents leave traces in the environment that influence future agent behaviors.

class StigmergySystem {
    constructor() {
        this.environment = new Map();
        this.agents = [];
        this.evaporationRate = 0.01;
    }

    depositPheromone(location, amount, type = 'trail') {
        const key = `${location.x},${location.y}`;
        const current = this.environment.get(key) || { trail: 0, marker: 0 };
        current[type] += amount;
        this.environment.set(key, current);
    }

    getPheromone(location, type = 'trail') {
        const key = `${location.x},${location.y}`;
        const cell = this.environment.get(key);
        return cell ? cell[type] : 0;
    }

    evaporate() {
        for (const [key, cell] of this.environment) {
            cell.trail *= (1 - this.evaporationRate);
            cell.marker *= (1 - this.evaporationRate);

            if (cell.trail < 0.01 && cell.marker < 0.01) {
                this.environment.delete(key);
            } else {
                this.environment.set(key, cell);
            }
        }
    }

    step() {
        // Agents move and deposit pheromones
        for (const agent of this.agents) {
            agent.move(this);
            this.depositPheromone(agent.position, 1.0);
        }

        // Evaporate pheromones
        this.evaporate();
    }
}

// 弘益人間: Coordination through environmental traces

Chapter Summary

Review Questions

  1. What is emergent behavior and how does it differ from programmed behavior?
  2. Describe the three rules of Reynolds' Boids algorithm.
  3. How does Ant Colony Optimization use pheromones to find solutions?
  4. Explain the role of inertia, cognitive, and social components in PSO.
  5. What are the rules of Conway's Game of Life?
  6. How do positive and negative feedback contribute to self-organization?
  7. What is stigmergy and how does it enable indirect coordination?
  8. Compare centralized planning with emergent swarm behavior.
  9. What makes swarm systems robust to individual agent failures?
  10. How do emergent systems embody the 弘益인간 principle?