5 releases

Uses new Rust 2024

new 0.1.4 Jan 29, 2026
0.1.3 Jan 21, 2026
0.1.2 Jan 16, 2026
0.1.1 Jan 10, 2026
0.1.0 Jan 5, 2026

#147 in Finance


Used in 11 crates

MIT/Apache

2.5MB
53K SLoC

legalis-sim

Simulation engine for Legalis-RS.

Overview

This crate provides an ECS-like (Entity Component System) simulation engine for testing legal statutes against populations of agents. It uses Tokio for async parallel execution, enabling high-throughput law application simulations.

Features

  • Async parallel simulation using Tokio
  • Configurable population generation
  • Comprehensive metrics collection
  • Per-statute and aggregate statistics

Usage

Basic Simulation

use legalis_sim::{SimEngine, PopulationBuilder};
use legalis_core::{Statute, Condition, Effect, EffectType, ComparisonOp};

// Define statutes
let statute = Statute::new(
    "voting-rights",
    "Voting Rights Act",
    Effect::new(EffectType::Grant, "Right to vote"),
)
.with_precondition(Condition::Age {
    operator: ComparisonOp::GreaterOrEqual,
    value: 18,
});

// Create population
let population = PopulationBuilder::new()
    .generate_random(10_000)
    .build();

// Run simulation
let engine = SimEngine::new(vec![statute], population);
let metrics = engine.run_simulation().await;

println!("{}", metrics.summary());

Custom Entities

use legalis_core::BasicEntity;

let mut citizen = BasicEntity::new();
citizen.set_attribute("age", "35".to_string());
citizen.set_attribute("income", "5000000".to_string());
citizen.set_attribute("citizenship", "JP".to_string());

let population = PopulationBuilder::new()
    .add_entity(citizen)
    .generate_random(999)
    .build();

Simulation Engine

pub struct SimEngine {
    statutes: Vec<Statute>,
    population: Vec<Arc<dyn LegalEntity>>,
}

impl SimEngine {
    pub fn new(statutes: Vec<Statute>, population: Vec<Box<dyn LegalEntity>>) -> Self;
    pub fn population_size(&self) -> usize;
    pub fn statute_count(&self) -> usize;
    pub async fn run_simulation(&self) -> SimulationMetrics;
    pub fn apply_law(agent: &dyn LegalEntity, law: &Statute) -> LegalResult<Effect>;
}

Metrics

SimulationMetrics

pub struct SimulationMetrics {
    pub total_applications: usize,
    pub deterministic_count: usize,
    pub discretion_count: usize,
    pub void_count: usize,
    pub statute_metrics: HashMap<String, StatuteMetrics>,
    pub discretion_agents: Vec<Uuid>,
}

impl SimulationMetrics {
    pub fn deterministic_ratio(&self) -> f64;
    pub fn discretion_ratio(&self) -> f64;
    pub fn summary(&self) -> String;
}

StatuteMetrics

pub struct StatuteMetrics {
    pub total: usize,
    pub deterministic: usize,
    pub discretion: usize,
    pub void: usize,
}

impl StatuteMetrics {
    pub fn effectiveness(&self) -> f64;  // deterministic / total
    pub fn ambiguity(&self) -> f64;      // discretion / total
}

Example Output

=== Simulation Summary ===
Total applications: 10000
Deterministic: 7523 (75.2%)
Discretionary: 1834 (18.3%)
Void: 643

=== Per-Statute Breakdown ===
voting-rights: D=7523 / J=0 / V=2477
housing-subsidy: D=0 / J=1834 / V=8166

Condition Evaluation

The engine evaluates conditions with three possible outcomes:

  • True: Condition is satisfied
  • False: Condition is not satisfied
  • Indeterminate: Cannot be evaluated (missing data, custom conditions)

Indeterminate results trigger JudicialDiscretion in the LegalResult.

License

MIT OR Apache-2.0

Dependencies

~9–13MB
~223K SLoC