multi-agent-engine

Crates.iomulti-agent-engine
lib.rsmulti-agent-engine
version0.1.0
created_at2025-12-23 12:16:52.593155+00
updated_at2025-12-23 12:16:52.593155+00
descriptionA concurrent library for building multi-agent simulators with lock-free CPU/GPU hybrid execution.
homepagehttps://github.com/NickSpyker/multi-agent-engine/blob/main/README.md
repositoryhttps://github.com/NickSpyker/multi-agent-engine
max_upload_size
id2001398
size15,914
Nicolas Spijkerman (NickSpyker)

documentation

https://github.com/NickSpyker/multi-agent-engine/blob/main/docs/documentation.md

README

Multi-Agent Engine

A concurrent Rust library for building multi-agent simulators with lock-free CPU/GPU hybrid execution.

Overview

multi-agent-engine is a library designed for building simulators and applications based on multi-agent systems (MAS), agent-based models (ABM), and agent-oriented programming (AOP) paradigms. It provides a ready-to-use pipeline and tooling for generic use cases, with native support for multithreaded CPU execution and GPU acceleration.

Core Architecture

The engine follows a dual-thread architecture that separates simulation logic from user interaction, enabling independent operation at different frequencies while maintaining efficient data synchronization.

┌──────────────────────────────────────────────────────────────┐
│                      Multi-Agent Engine                      │
├──────────────────────────────────────────────────────────────┤
│ ┌───────────────────┐                   ┌──────────────────┐ │
│ │ Controller Thread │                   │ Simulator Thread │ │
│ │  (e.g., 60 Hz)    │                   │  (e.g., 30 Hz)   │ │
│ │                   │                   │                  │ │
│ │  - User Input     │                   │  - Agent Logic   │ │
│ │  - Rendering      │                   │  - Physics       │ │
│ │  - UI Logic       │                   │  - Simulation    │ │
│ └─────────┬─────────┘                   └────────┬─────────┘ │
│           │      ┌────────────────────────┐      │           │
│           ├─────→│     Config (ArcSwap)   ├→─────┤           │
│           │      │ Controller → Simulator │      │           │
│           │      └────────────────────────┘      │           │
│           │      ┌────────────────────────┐      │           │
│           └─────←┤     State (ArcSwap)    │←─────┘           │
│                  │ Controller ← Simulator │                  │
│                  └────────────────────────┘                  │
│                ┌────────────────────────────┐                │
│                │ Message Queue (Controller) │                │
│                │  Controller → Simulator    │                │
│                └────────────────────────────┘                │
│                ┌────────────────────────────┐                │
│                │ Message Queue (Simulator)  │                │
│                │  Controller ← Simulator    │                │
│                └────────────────────────────┘                │
└──────────────────────────────────────────────────────────────┘

User Interface

Required Trait Implementations

You must implement two core traits to connect with the engine:

1. Controller Trait

Handles user interaction, input processing, and visualization.

trait Controller {
    fn initialize(&mut self, initial_state: &State);
    fn update(&mut self, state: &State) -> Option<Config>;
    fn handle_messages(&mut self, messages: Vec<SimulatorMessage>);
}

2. Simulator Trait

Contains the core simulation logic, agent behaviors, and physics computation.

trait Simulator {
    fn initialize(&mut self, config: &Config);
    fn tick(&mut self, config: &Config) -> State;
    fn handle_messages(&mut self, messages: Vec<ControllerMessage>);
}

Required Data Structures

Config

Configuration and input parameters (Controller → Simulator).

struct Config {
    // User-defined fields
    // e.g., simulation_speed, agent_count, environment_params
}

State

Current simulation state (Simulator → Controller).

struct State {
    // User-defined fields
    // e.g., agent_positions, environment_state, statistics
}

Concurrency Model

Thread Independence

The Controller and Simulator operate in separate threads that never block each other. This design enables:

  • Asynchronous execution: Each thread runs at its own frequency
  • Responsive UI: The controller remains responsive even during heavy simulation
  • Scalable performance: Simulation can utilize multiple cores independently
Timeline Example:
─────────────────────────────────────────────────────────────────>

Controller (60 Hz): |─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─
                        |       |       |       |       |       |
Simulator  (30 Hz): |───●───────●───────●───────●───────●───────●─
                        |       |       |       |       |       |
                       tick    tick    tick    tick    tick    tick

● = frame/tick execution

Data Sharing with ArcSwap

Both threads share Config and State through lock-free atomic swap (ArcSwap):

┌────────────────────────────────────────────────┐
│                Data Flow Diagram               │
└────────────────────────────────────────────────┘

 Controller Thread               Simulator Thread
 ─────────────────               ────────────────
       WRITE                          WRITE
         ↓                              ↓
    ┌────────┐                     ┌────────┐
    │ Config │ ←──── ArcSwap ────→ │ State  │
    └────────┘                     └────────┘
       READ                           READ
         ↓                              ↓
    ┌────────┐                     ┌────────┐
    │ State  │ ←──── ArcSwap ────→ │ Config │
    └────────┘                     └────────┘

Access Patterns:

Thread Config State
Simulator Read-only Write-only
Controller Write-only Read-only

This design ensures:

  • No locks or mutexes needed for state sharing
  • Zero blocking between threads
  • Data race freedom through clear ownership

Message Passing

In addition to shared data, threads communicate through typed message queues using distinct enum types:

┌─────────────────────────────────────────────────────────────┐
│                    Message Communication                    │
└─────────────────────────────────────────────────────────────┘

 Controller                                  Simulator
 ──────────                                  ─────────
     ├→ [Controller Message Queue] ────────→  handle_messages()
     │   - Pause                                 ↓
     │   - Resume                             Process msg
     │   - Reset                                 ↓
     │   - SpawnAgent                         Execute
     │   - CustomCmd                             │
     │                                           │
  handle_messages() ← [Simulator Message Queue] ←┤
     ↓                 - SimComplete             │
  Process msg          - AgentDied               │
     ↓                 - Statistics              │
  Execute              - CustomEvent             │

Example Message Definitions:

// Controller → Simulator
enum ControllerMessage {
    Pause,
    Resume,
    Reset,
    SpawnAgent { position: Vec3, agent_type: AgentType },
    CustomCommand(String),
}

// Simulator → Controller
enum SimulatorMessage {
    SimulationComplete,
    AgentDied { id: AgentId, reason: String },
    Statistics { tick: u64, data: SimStats },
    CustomEvent(String),
}

GPU Acceleration & Multi-Layer Pipeline

Hybrid CPU/GPU Execution

The simulator supports flexible compute allocation between CPU and GPU, enabling efficient execution of agent behaviors and physics on appropriate hardware.

┌──────────────────────────────────────────────┐
│       Multi-Layer Processing Pipeline        │
└──────────────────────────────────────────────┘

 Input State
      │
      ↓
 ┌────┴────┐
 │ Layer 1 │ ← CPU: Agent Decision Making
 │  (CPU)  │    - Perception processing
 └────┬────┘    - Behavior selection
      ↓ output1
 ┌────┴────┐
 │ Layer 2 │ ← GPU: Spatial Queries
 │  (GPU)  │    - Neighbor detection
 └────┬────┘    - Collision detection
      ↓ output2
 ┌────┴────┐
 │ Layer 3 │ ← GPU: Physics Simulation
 │  (GPU)  │    - Force calculations
 └────┬────┘    - Position updates
      ↓ output3
 ┌────┴────┐
 │ Layer 4 │ ← CPU: Environment Update
 │  (CPU)  │    - Resource distribution
 └────┬────┘    - Event handling
      ↓ output4
      │
 Final State

Agent-Level GPU Execution

Each agent's behavior can be individually executed on GPU, enabling:

  • Massive parallelization for homogeneous agent populations
  • Efficient SIMD operations for agent computations
  • Scalability to thousands or millions of agents
Agents: A1, A2, A3, A4, ..., An

 CPU Execution          GPU Execution
 ─────────────          ─────────────

 A1 → compute           ┌──────────────────┐
    ↓                   │ A1 A2 A3 A4 ...  │
 A2 → compute     VS    │ ║  ║  ║  ║       │
    ↓                   │ Parallel Compute │
 A3 → compute           │ ↓  ↓  ↓  ↓       │
    ↓                   └──────────────────┘
   ...
 (Sequential)              (Parallel)

Getting Started

// 1. Define your data structures
struct MyConfig {
    /* ... */
}
struct MyState {
    /* ... */
}

// 2. Define your message enums
enum ControllerMessage {
    /* ... */
}
enum SimulatorMessage {
    /* ... */
}

// 3. Implement the required traits
struct MyController {
    /* ... */
}
impl Controller for MyController { /* ... */ }

struct MySimulator {
    /* ... */
}
impl Simulator for MySimulator { /* ... */ }

fn main() {
    // 3. Run the engine
    let engine = MultiAgentEngine::new(
        MyController::new(),
        MySimulator::new(),
        config,
    );

    engine.run();
}

License

See LICENSE-APACHE and LICENSE-MIT for details.

Commit count: 0

cargo fmt