| Crates.io | multi-agent-engine |
| lib.rs | multi-agent-engine |
| version | 0.1.0 |
| created_at | 2025-12-23 12:16:52.593155+00 |
| updated_at | 2025-12-23 12:16:52.593155+00 |
| description | A concurrent library for building multi-agent simulators with lock-free CPU/GPU hybrid execution. |
| homepage | https://github.com/NickSpyker/multi-agent-engine/blob/main/README.md |
| repository | https://github.com/NickSpyker/multi-agent-engine |
| max_upload_size | |
| id | 2001398 |
| size | 15,914 |
A concurrent Rust library for building multi-agent simulators with lock-free CPU/GPU hybrid execution.
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.
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 │ │
│ └────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
You must implement two core traits to connect with the engine:
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>);
}
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>);
}
Configuration and input parameters (Controller → Simulator).
struct Config {
// User-defined fields
// e.g., simulation_speed, agent_count, environment_params
}
Current simulation state (Simulator → Controller).
struct State {
// User-defined fields
// e.g., agent_positions, environment_state, statistics
}
The Controller and Simulator operate in separate threads that never block each other. This design enables:
Timeline Example:
─────────────────────────────────────────────────────────────────>
Controller (60 Hz): |─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─
| | | | | |
Simulator (30 Hz): |───●───────●───────●───────●───────●───────●─
| | | | | |
tick tick tick tick tick tick
● = frame/tick execution
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:
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 │
// 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),
}
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
Each agent's behavior can be individually executed on GPU, enabling:
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)
// 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();
}
See LICENSE-APACHE and LICENSE-MIT for details.