Skip to main content

MAPF+JSSP Joint Simulation Environment for Reinforcement Learning

Project description

Joint-Sim: MAPF+JSSP Reinforcement Learning Environment

A Gymnasium-compatible simulation environment for the joint optimization of Multi-Agent Path Finding (MAPF) and Job Shop Scheduling Problem (JSSP).

PyPI version Python 3.8+ License: MIT

Features

  • Gymnasium-compatible interface: Standard reset(), step(), observation_space, action_space
  • Joint optimization: Simultaneously handles AGV routing (MAPF) and job scheduling (JSSP)
  • Cost-based assignment: Hungarian algorithm for optimal task-AGV matching
  • Collision-free routing: Reservation table for conflict prevention
  • Flexible configuration: Customizable machines, AGVs, jobs, and grid layouts
  • SVG visualization: Real-time factory state rendering

Installation

pip install joint-sim

For additional features:

pip install "joint-sim[visualization]"  # Matplotlib for charts
pip install "joint-sim[optimal-solver]"  # OR-Tools for optimal JSSP
pip install "joint-sim[all]"             # All optional dependencies

Quick Start

Basic Usage

from joint_sim import JointSimGymEnv, FactoryConfig

# Create environment
config = FactoryConfig(
    n_machines=4,
    n_agvs=2,
    n_jobs=5,
    grid_size=(10, 10),
)

env = JointSimGymEnv(config, assigner_type='cost')

# Standard Gymnasium interface
obs, info = env.reset(seed=42)

for _ in range(1000):
    # Random action (use your RL policy here)
    action = env.action_space.sample()

    obs, reward, terminated, truncated, info = env.step(action)

    if terminated or truncated:
        break

print(f"Completed jobs: {info['completed_jobs']}/{info['total_jobs']}")

With Custom Policy

import numpy as np
from joint_sim import JointSimGymEnv, FactoryConfig

env = JointSimGymEnv(
    config=FactoryConfig(n_machines=4, n_agvs=2, n_jobs=5),
    assigner_type='cost'
)

obs, info = env.reset(seed=42)

def heuristic_policy(env, obs):
    """Assign nearest task to each AGV"""
    requests = env.get_transport_requests()
    n_agvs = env.config.n_agvs

    if not requests:
        return {'agv_assignments': np.full(n_agvs, -1, dtype=np.int32)}

    assignments = np.full(n_agvs, -1, dtype=np.int32)

    for i in range(min(n_agvs, len(requests))):
        assignments[i] = requests[i].job_id

    return {'agv_assignments': assignments}

while True:
    action = heuristic_policy(env, obs)
    obs, reward, terminated, truncated, info = env.step(action)

    if terminated or truncated:
        break

print(f"Total reward: {reward}, Completed: {info['completed_jobs']}")

Environment Specification

Observation Space

observation_space = Dict({
    'grid': Box(0, 3, shape=(height, width), dtype=int32),      # Factory grid
    'agv_positions': Box(0, max_size, shape=(n_agvs, 2)),       # AGV (x, y) positions
    'agv_status': Box(0, 3, shape=(n_agvs,), dtype=int32),      # 0=idle, 1=moving, 2=loading, 3=unloading
    'agv_carrying': Box(-1, n_jobs, shape=(n_agvs,), dtype=int32),  # Job ID being carried (-1=none)
    'machine_status': Box(0, 1, shape=(n_machines,), dtype=int32),  # 0=idle, 1=working
    'machine_queue_length': Box(0, n_jobs, shape=(n_machines,)),   # Queue length per machine
    'time': Box(0, max_time, shape=(), dtype=int32),            # Current timestep
})

Action Space

action_space = Dict({
    'agv_assignments': Box(-1, n_jobs-1, shape=(n_agvs,), dtype=int32),
})
# -1: No assignment (AGV stays idle)
# 0 to n_jobs-1: Assign job to AGV

Reward Function

  • Job completion bonus: +100 per completed job
  • Terminal bonus: +100 when all jobs complete

API Reference

JointSimGymEnv

env = JointSimGymEnv(
    config: FactoryConfig = None,       # Factory configuration
    assigner_type: str = 'cost',        # 'cost' or 'greedy'
    assigner_config: dict = None,       # Assigner parameters
    max_episode_steps: int = 10000,     # Maximum steps per episode
    reward_scale: float = 1.0           # Reward scaling factor
)

FactoryConfig

config = FactoryConfig(
    n_machines: int = 6,                # Number of machines
    n_agvs: int = 3,                    # Number of AGVs
    n_jobs: int = 10,                   # Number of jobs
    grid_size: Tuple[int, int] = (20, 20),  # Grid dimensions
    n_ops_per_job: Tuple[int, int] = (3, 6),  # Operations per job range
    op_duration_range: Tuple[int, int] = (3, 10),  # Operation duration range
    seed: int = 42,                     # Random seed
    max_time: int = 5000,               # Maximum simulation time
)

Key Methods

Method Description
reset(seed=None) Reset environment, returns (obs, info)
step(action) Execute action, returns (obs, reward, terminated, truncated, info)
get_transport_requests() Get list of pending transport requests
get_state() Get current FactoryState
get_metrics() Get simulation metrics
render(filepath=None) Render SVG visualization

License

MIT License - see LICENSE file for details.

Citation

If you use this environment in your research, please cite:

@software{joint_sim,
  title = {Joint-Sim: MAPF+JSSP Reinforcement Learning Environment},
  author = {Skyrim Forestsea},
  year = {2026},
  url = {https://github.com/skyrimforest/joint-sim}
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

joint_sim-0.1.1.tar.gz (77.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

joint_sim-0.1.1-py3-none-any.whl (67.1 kB view details)

Uploaded Python 3

File details

Details for the file joint_sim-0.1.1.tar.gz.

File metadata

  • Download URL: joint_sim-0.1.1.tar.gz
  • Upload date:
  • Size: 77.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.5

File hashes

Hashes for joint_sim-0.1.1.tar.gz
Algorithm Hash digest
SHA256 560fdc92b9a99394a45fb53937d0f4038f6fca1457f7365957f6dcbc59363385
MD5 b8cc50fdda89266886bded3998f834a2
BLAKE2b-256 d074951cb1fcf1cdcb94e207c1130c7ec45c0a8b2393846c1727e736fdceeea1

See more details on using hashes here.

File details

Details for the file joint_sim-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: joint_sim-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 67.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.5

File hashes

Hashes for joint_sim-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c39b78414879c7d2310bff67b41fa36ef96419a2d6aaf50b1eb9d447992daf6c
MD5 ec4a7a5b229981605d1112f9df94d9ad
BLAKE2b-256 094db91d136a854d435538fdee4da855727d2d374429040cea221f4e4e9b5824

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page