1
Fork 0

Merge pull request #4 from SELab-3-2026/chore/setup-dev-environment

chore: Configure development environment
This commit is contained in:
Cedric Mekeirle 2026-03-21 20:15:26 +01:00 committed by GitHub
commit af9cf1fdc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 581 additions and 11 deletions

View file

@ -0,0 +1,20 @@
# Architecture and Code Formatting Rules
When writing or modifying code in this project, adhere strictly to the following rules:
## 1. Core Frameworks & Tooling
- **PPO & CleanRL**: Proximal Policy Optimization (PPO) is the baseline algorithm. Use CleanRL as the starting framework for PPO, ensuring adaptation for continuous action spaces.
- **JAX / Flax**: All Artificial Neural Network (ANN) controller architectures must be implemented using Flax (neural networks in JAX). Ensure full compatibility with the JAX/Flax ecosystem.
- **MuJoCo**: The simulation environment uses a MuJoCo brittle star. Ensure that XML structures (sensors, actuators, joints, morphology) respect realistic constraints and adhere to the project requirements.
## 2. Clean Code Principles
- **Naming Conventions**: Variables and functions must have consistent, intention-revealing names. A descriptive name is universally preferred over a short name with an explanatory comment.
- **Single Responsibility Function Design**: Functions must be modular. Minimize arguments and completely avoid boolean flag arguments that control execution behavior.
- **Commenting**: Code explains the "how". Comments are strictly reserved for explaining the "why".
- **No Commented-out Code**: The AI must **never** generate commented-out or dead code. Delete it using version control instead.
- **YAGNI & Complexity Management**: You Aren't Gonna Need It. Avoid premature optimization or unnecessary abstraction. Only introduce complexity with documented justification. Break large functions into small, testable blocks.
- **No Notebooks for Core Logic**: Jupyter Notebooks are explicitly forbidden for general software development as they discourage modularity. They should only be used for prototyping, tutorials, or post-processing analysis.
## 3. Formatting & Linting
- **Ruff**: Output perfectly formatted code adhering to the Google style standard. Always format and lint the code using `ruff` (see `pyproject.toml` and `ruff.toml`).
- **Separation of Concerns**: Configuration code must be completely separated from implementation logic. Core logic must never be mixed with scripts or notebooks.

15
.agents/rules/general.md Normal file
View file

@ -0,0 +1,15 @@
# General AI Agent Rules
When assisting with this project, the AI Agent must strictly abide by these overarching operational rules:
## 1. Scientific Integrity
- **No Hallucinations**: You must never fabricate results, hallucinate citations, or generate false empirical claims. Do not guess what happened if a process fails; rely strictly on outputs and logs.
## 2. Agent Operational Constraints
- **Absolute Paths**: Always use absolute paths when making tool calls or reading/writing files.
- **Refactoring Guardrails**: Do not commence massive files/directory refactors or major system migrations without explicitly communicating the plan and asking for user clarification or approval first.
- **No Boilerplate Feedback**: The AI must not produce generic boilerplate summaries or overly generic advice. Ensure all outputs are completely contextual, robust, and well-reasoned.
## 3. Communication
- **Artifacts and UI**: Use artifacts (like `implementation_plan.md` or `task.md`) appropriately for tracking design phases and updates.
- **Explicit Documenting**: If performing design choices, list them explicitly. Keep the output focused on the exact task.

View file

@ -0,0 +1,25 @@
# Git Workflow & Repository Structure Rules
When performing Git operations and managing the repository layout, follow these rules:
## 1. Committing Practices
- **Frequent & Small**: Produce small, logical commits instead of massive monolithic ones.
- **Conventional Commits**: Commit messages must adhere to the Conventional Commits specification (e.g., `feat: ...`, `fix: ...`, `refactor: ...`).
- **Single Functionality**: Each commit should relate to exactly one piece of functionality or distinct structural change.
## 2. Branching & Merging
- **Branch `dev`**: The `dev` branch is the primary integration branch for pushing and merging code.
- **Branch `main`**: Only stable, finalized releases may be pushed to `main`.
- **Feature Branches**: Organize distinct work into logical feature branches when pushing to the remote server, maintaining an organized Git history.
## 3. Artifact Management & Exclusions
- **LFS Only**: Data files, trained models, and large datasets must **never** be committed directly to Git. Ensure they are tracked with Git Large File Storage (LFS). **CRITICAL**: Every developer and AI agent must have `git-lfs` installed locally for the repository hooks to successfully pull these large files. Run `git lfs install` after cloning or setting up your environment.
## 4. Repository Layout Strictness
Ensure generated code is meticulously placed in the correct directories:
- `src/` for algorithms, network designs, and core agent modules.
- `env/` for MuJoCo wrappers and environment definitions.
- `config/` for experiment configurations (using json, gin, or yaml).
- `experiments/` for executable scripts.
- `docs/` for ReadTheDocs or Doxygen documentation, and decision logs.
- `tests/` for unit tests and verification scripts.

14
.agents/rules/method.md Normal file
View file

@ -0,0 +1,14 @@
# Methodology and Process Rules
The agent must adhere to the following scientific and operational practices, focused on robustness and reproducibility:
## 1. Scientific Context & Methodology
- **Research Focus**: Maintain focus on the project's objective: studying how controller modularity affects learning speed, coordination, and fault tolerance in brittle-star locomotion.
- **Hypothesis-Driven Design**: Base execution on clear hypotheses. Document all design decisions prior to implementation (in `/docs/decisions/` or via Artifacts/Plans).
- **Scaffolding Approach**: Start development with simple setups before scaling to complex environments and varying morphologies.
- **Value of Negative Results**: Understand that a controller failing to learn locomotion, when coupled with a thorough analysis of the failure, holds strong scientific value. Do not artificially force a positive result.
## 2. Reproducibility Protection
- **Dependency Management (uv)**: This project strictly prefers `uv`. Do **not** manually modify the `uv.lock` file. Add dependencies via `uv add <package>` and sync environments via `uv sync --frozen` (or via devcontainers).
- **Consistent Initialization**: The AI must avoid hidden randomness. Ensure that all runs use consistent seed initialization.
- **Experiment Tracking**: Use Weights & Biases (wandb) for tracking and logging all experiments and parameters. Ensure every experiment-friendly parameter is properly logged.

View file

@ -0,0 +1,24 @@
---
name: Lint and Format Code
description: Instructions for checking code style and formatting using Ruff.
---
# Skill: Lint and Format Code
The goal of this skill is to enforce the project's adherence to the Google style standard and to maintain high code quality across the python source files.
## Instructions
1. **Format Code**: To automatically format all Python files according to the `ruff.toml` specifications:
```bash
uv run ruff format .
```
2. **Check for Lints / Auto-fix**: To check the repository for style violations and automatically fix safe corrections:
```bash
uv run ruff check --fix .
```
## Important Considerations
- The `ruff.toml` file at the root handles all lint and format configuration. Do not ignore configurations when applying fixes.
- If Ruff points out complex errors that cannot be auto-fixed, analyze the code and manually address the violations, prioritizing descriptive naming and adherence to the single-responsibility principle.

View file

@ -0,0 +1,25 @@
---
name: Run Tests
description: Instructions for executing the project test suite to verify code correctness.
---
# Skill: Run Tests
The goal of this skill is to verify that the project is functioning correctly after development changes.
## Instructions
1. **Verify Environment**: The project operates dynamically with hardware acceleration (GPU via JAX) and uses `uv` for dependency/execution management.
2. **Execute Tests**:
- To run basic verification (e.g., checking JAX initialization and hardware detection), run the test script:
```bash
uv run python -m tests.test_jax_init
```
- If a broader suite of modular tests is added (e.g., `pytest`), execute tests in the `tests/` directory with:
```bash
uv run pytest tests/
```
## Important Considerations
- If you are running tests inside the local environment without a Devcontainer, verify whether `uv sync --frozen` (for CPU) or `uv sync --frozen --extra cuda` (for GPU) has been executed to avoid import errors.
- Do not run bare `python ...` without `uv run` locally, unless you are strictly operating inside a pre-activated `.venv` inside a Devcontainer.

3
.commitlintrc.json Normal file
View file

@ -0,0 +1,3 @@
{
"extends": ["@commitlint/config-conventional"]
}

30
.devcontainer/Dockerfile Normal file
View file

@ -0,0 +1,30 @@
FROM mcr.microsoft.com/devcontainers/python:3.12
# Install uv for dependency management
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Install system dependencies for JAX, OpenGL, and Mujoco
RUN apt-get update && apt-get install -y --no-install-recommends \
libgl1 \
libosmesa6-dev \
libglew-dev \
libglfw3 \
patchelf \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Set environment variables for hardware acceleration and rendering
ENV LD_LIBRARY_PATH=/usr/lib/nvidia
ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility,graphics
# Set the workspace path (defaults to /workspaces/project but can be overridden)
ARG WORKSPACE_PATH=/workspaces/project
ENV WORKSPACE_PATH=$WORKSPACE_PATH
ENV PATH="$WORKSPACE_PATH/.venv/bin:$PATH"
# Ensure the workspace and .venv directories exist and are owned by the vscode user
# This prevents permission errors when the postCreateCommand runs uv sync
RUN mkdir -p $WORKSPACE_PATH/.venv && chown -R vscode:vscode $WORKSPACE_PATH
USER vscode
WORKDIR $WORKSPACE_PATH

View file

@ -0,0 +1,49 @@
{
"name": "Brittle Star JAX/CUDA",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
"WORKSPACE_PATH": "/workspaces/${localWorkspaceFolderBasename}"
}
},
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"charliermarsh.ruff",
"ms-python.vscode-pylance",
"ms-toolsai.jupyter"
],
"settings": {
"python.defaultInterpreterPath": "${containerWorkspaceFolder}/.venv/bin/python",
"python.analysis.localRoot": "${containerWorkspaceFolder}",
"python.analysis.extraPaths": [
"${containerWorkspaceFolder}/.venv/lib/python3.12/site-packages"
]
}
},
"jetbrains": {
"plugins": [
"Pythonid",
"fleet.python",
"com.koxudaxi.ruff"
]
}
},
"remoteUser": "vscode",
"runArgs": [
"--gpus",
"all"
],
// Ensure the .venv persists using a named volume for performance and parity
"mounts": [
"source=${localWorkspaceFolderBasename}-venv,target=${containerWorkspaceFolder}/.venv,type=volume"
],
// Invoke the hardware-aware sync script
"postCreateCommand": "bash ./.devcontainer/post-create.sh",
"features": {
"ghcr.io/devcontainers/features/common-utils:1": {}
}
}

View file

@ -0,0 +1,18 @@
#!/bin/bash
set -e
# Detect if a GPU is available via nvidia-smi.
# This works for Linux hosts and Windows (WSL2) with NVIDIA Container Toolkit.
# On Mac (Apple Silicon) or systems without NVIDIA GPUs, this will skip the 'cuda' extra.
if command -v nvidia-smi &> /dev/null && nvidia-smi &> /dev/null; then
echo "GPU detected. Syncing with 'cuda' extra..."
uv sync --frozen --extra cuda
else
echo "No GPU detected or nvidia-smi failed. Syncing without 'cuda' extra..."
uv sync --frozen
fi
echo "Environment synced successfully."
# Install pre-commit hooks so they are active in the devcontainer
uv run pre-commit install

14
.gitattributes vendored Normal file
View file

@ -0,0 +1,14 @@
# Model weights
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
# Media
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.avi filter=lfs diff=lfs merge=lfs -text
# Datasets
*.csv filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text

23
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: Lint
on:
pull_request:
types: [opened, synchronize, ready_for_review]
branches:
- main
- dev
jobs:
ruff:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Ruff check
uses: astral-sh/ruff-action@v1
with:
args: "check"
- name: Run Ruff format check
uses: astral-sh/ruff-action@v1
with:
args: "format --check"

31
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,31 @@
name: Test
on:
pull_request:
types: [opened, synchronize, ready_for_review]
branches:
- main
- dev
jobs:
pytest:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "latest"
- name: Setup python
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: Install dependencies
run: uv sync --frozen
- name: Run tests with pytest
run: uv run pytest tests/

20
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,20 @@
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.16.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ["@commitlint/config-conventional"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: no-commit-to-branch
args: ['--branch', 'main', '--branch', 'dev']

35
docs/CONTRIBUTING.md Normal file
View file

@ -0,0 +1,35 @@
# Contribution Guidelines
This document outlines the contribution protocols for the scientific software engineering project focusing on bio-inspired control architectures for brittle-star-like robots. The primary objective of this project is to produce scientific insight, rather than a commercial product.
## 1. Scientific Context & Methodology
* **Research Focus:** The goal is to study how controller modularity affects learning speed, coordination, and fault tolerance in brittle-star locomotion.
* **Hypothesis-Driven Design:** Clear hypotheses must dictate a structured methodology and rigorous evaluation. All design decisions must be formally documented prior to implementation.
* **Scaffolding Approach:** Development must start with simple setups before progressively increasing the complexity of environments and morphologies.
* **Evaluation of Results:** Negative results possess scientific validity when thoroughly analyzed. If a controller fails to learn locomotion, providing a comprehensive analysis of the failure is considered a strong scientific contribution.
* **Reproducibility:** Contributors must utilize fixed library versions. Configuration systems (such as json, gin, or yaml) must be employed to ensure reproducible runs.
## 2. Clean Code & Code Quality
Code readability is paramount, as code is read far more frequently than it is written.
* **Naming Conventions:** Variables and functions must utilize consistent, intention-revealing names. A long, descriptive name is strictly preferred over a short name accompanied by a comment.
* **Function Design:** Functions must be modular and adhere to the single responsibility principle. Arguments must be minimized, and boolean flag arguments controlling behavior should be avoided.
* **Commenting:** Code must document the "how," while comments are strictly reserved for documenting the "why". Commented-out code is prohibited and must be deleted via version control.
* **YAGNI:** Contributors must adhere to the "You Aren't Gonna Need It" (YAGNI) principle and actively avoid premature optimization.
* **Notebooks:** Jupyter Notebooks are strictly limited to quick prototyping, tutorials, demonstrations, or post-processing analysis. They are explicitly forbidden for general software development because they discourage modularity.
## 3. Version Control & Repository Structure
* **Git Practices:** Commits must be frequent and small. Each commit should relate to exactly one piece of functionality.
* **Branching Strategy:** The `dev` branch serves as the integration branch for pushing and merging code. Only stable releases may be pushed to the `main` branch.
* **Artifact Management:** Data files, trained models, and large datasets must never be committed directly to Git. Git Large File Storage (LFS) must be used for tracking large files. **All developers must have `git-lfs` installed locally** (see `DEVELOPMENT.md` for setup).
* **Repository Layout:** The repository must maintain the following core directories: `src/` for algorithms, `env/` for MuJoCo wrappers, `config/` for experiment configurations, `experiments/` for scripts, `docs/` for Doxygen or ReadTheDocs documentation, and `tests/` for unit tests.
## 4. Architecture & Tooling
* **Algorithms & Frameworks:** Proximal Policy Optimization (PPO) is the recommended baseline algorithm. CleanRL should be used as a starting point and adapted for continuous action spaces. All Artificial Neural Network (ANN) controller architectures must be implemented using Flax.
* **Simulation:** The simulation environment utilizes a MuJoCo brittle star. XML MuJoCo structures must remain realistic and respect morphological constraints.
* **Experiment Tracking:** Weights & Biases (wandb) must be utilized for tracking and logging all experiments.
* **Code Styling:** All code must conform to the chosen style guide (i.e. Google standard). This is enforced using build tools and pre-commit hooks such as flake8, black, or isort.

61
docs/DEVELOPMENT.md Normal file
View file

@ -0,0 +1,61 @@
# Development Guide
This guide outlines how to set up the development environment for this project, prioritizing **reproducible builds**, **environment parity**, and **cross-hardware compatibility**.
## Reproducibility &uv
This project uses [uv](https://github.com/astral-sh/uv) to manage dependencies and virtual environments. The `uv.lock` file is the absolute source of truth for package versions and must always be committed.
### Source of Truth
- **Never modify `uv.lock` manually.**
- To add a dependency, run `uv add <package>`.
- To update dependencies, run `uv lock --upgrade`.
- To sync your environment with the lockfile, run `uv sync --frozen`.
## Git LFS (Critical)
**All developers must have Git LFS installed locally.** This repository tracks model weights (`.pt`, `.safetensors`, etc.), recordings (`.mp4`), and datasets using Git LFS.
- **Setup:** Run `git lfs install` after cloning this repository. If you are using the `.devcontainer` or `flake.nix`, LFS is typically available automatically.
- If you clone without LFS installed, run `git lfs pull` after installation to fetch the actual data files instead of the small pointer files.
## Devcontainer Setup (Recommended)
The devcontainer provides an identical experience to local development but with all system dependencies pre-configured. It automatically detects your hardware (GPU vs CPU) and syncs the appropriate dependencies.
### Prerequisites
- Docker Desktop or Docker Engine.
- [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) (for GPU support).
### Setup for VS Code
1. Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.
2. Open the project and click **Reopen in Container**.
3. On first launch, the `post-create.sh` script will:
- Detect if an NVIDIA GPU is available via `nvidia-smi`.
- Run `uv sync --frozen --extra cuda` if a GPU is found.
- Run `uv sync --frozen` otherwise.
4. The environment is stored in a **named volume** for `.venv` to ensure persistence and performance.
### Setup for JetBrains IDEs
1. The IDE will detect the `.devcontainer/devcontainer.json` file.
2. The environment is pre-configured to point to `/workspaces/project/.venv`.
3. The hardware-aware sync will run automatically during container creation.
## Local Development (Alternative)
If you prefer not to use Docker:
1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/).
2. Run `uv sync --frozen` (CPU) or `uv sync --frozen --extra cuda` (GPU).
## Hardware Acceleration (JAX)
Verify your setup by running the JAX initialization test:
```bash
uv run pytest tests/test_jax_init.py
```
In the devcontainer, this will succeed on both CPU and GPU. A `GpuDevice` is expected if a GPU is detected and the `cuda` extra was installed.

View file

@ -29,6 +29,10 @@
# Editor of your choice
(nix-jetbrains-plugins.lib.buildIdeWithPlugins pkgs "pycharm" pluginList)
];
shellHook = ''
uv run pre-commit install
'';
};
});
}

View file

@ -8,7 +8,7 @@ dependencies = [
"biorobot==0.4.2",
"evosax==0.2.0",
"ipykernel==7.2.0",
"jax[cuda13]==0.9.0.1",
"jax==0.9.0.1",
"matplotlib==3.10.8",
"mediapy==1.2.6",
"pyopengl>=3.1.10",
@ -16,7 +16,14 @@ dependencies = [
"wandb==0.24.2",
]
[project.optional-dependencies]
cuda = [
"jax[cuda13]==0.9.0.1",
]
[dependency-groups]
dev = [
"pre-commit>=4.0.0",
"pytest>=8.0.0",
"ruff>=0.15.2",
]

View file

@ -1,3 +1,4 @@
[lint]
extend-select = [
# "PLC0103", # invalid-name
# "PLC0104", # disallowed-name
@ -40,10 +41,10 @@ extend-select = [
"PLC2401", # non-ascii-name
# "PLC2403", # non-ascii-module-import
# "PLC2503", # bad-file-encoding
"PLC2801", # unnecessary-dunder-call
# "PLC2801", # unnecessary-dunder-call (requires preview)
# "PLC3001", # unnecessary-lambda-assignment
"PLC3002", # unnecessary-direct-lambda-call
"E999", # syntax-error
# "E999", # syntax-error (removed from ruff)
# "PLE0011", # unrecognized-inline-option
# "PLE0013", # bad-plugin-value
# "PLE0014", # bad-configuration-section
@ -130,7 +131,7 @@ extend-select = [
# "PLE1137", # unsupported-assignment-operation
# "PLE1138", # unsupported-delete-operation
# "PLE1139", # invalid-metaclass
"PLE1141", # dict-iter-missing-items
# "PLE1141", # dict-iter-missing-items (requires preview)
"PLE1142", # await-outside-async
# "PLE1143", # unhashable-member
# "PLE1144", # invalid-slice-step
@ -168,7 +169,7 @@ extend-select = [
# "PLE3102", # positional-only-arguments-expected
# "PLE3701", # invalid-field-call
# "PLE4702", # modified-iterating-dict
"PLE4703", # modified-iterating-set
# "PLE4703", # modified-iterating-set (requires preview)
# "PLF0001", # fatal
# "PLF0002", # astroid-error
# "PLF0010", # parse-error
@ -210,7 +211,7 @@ extend-select = [
# "PLW0238", # unused-private-member
# "PLW0239", # overridden-final-method
# "PLW0240", # subclassed-final-class
"PLW0244", # redefined-slots-in-subclass
# "PLW0244", # redefined-slots-in-subclass (requires preview)
"PLW0245", # super-without-brackets
# "PLW0246", # useless-parent-delegation
# "PLW0301", # unnecessary-semicolon
@ -274,7 +275,7 @@ extend-select = [
"PLW1508", # invalid-envvar-default
"PLW1509", # subprocess-popen-preexec-fn
# "PLW1510", # subprocess-run-check
"PLW1514", # unspecified-encoding
# "PLW1514", # unspecified-encoding (requires preview)
# "PLW1515", # forgotten-debug-statement
# "PLW1518", # method-cache-max-size-none
"PLW2101", # useless-with-lock
@ -298,7 +299,7 @@ extend-select = [
# "PLW4906", # deprecated-attribute
]
ignore = [
extend-ignore = [
# "PLC0116", # missing-function-docstring
# "PLC0200", # consider-using-enumerate
# "PLC0305", # trailing-newlines

7
tests/test_jax_init.py Normal file
View file

@ -0,0 +1,7 @@
import jax
def test_jax_initializes():
"""Verify that JAX initializes and exposes at least one device."""
devices = jax.devices()
assert len(devices) > 0, "JAX should expose at least one device"

150
uv.lock generated
View file

@ -14,7 +14,7 @@ dependencies = [
{ name = "biorobot" },
{ name = "evosax" },
{ name = "ipykernel" },
{ name = "jax", extra = ["cuda13"] },
{ name = "jax" },
{ name = "matplotlib" },
{ name = "mediapy" },
{ name = "pyopengl" },
@ -22,8 +22,15 @@ dependencies = [
{ name = "wandb" },
]
[package.optional-dependencies]
cuda = [
{ name = "jax", extra = ["cuda13"] },
]
[package.dev-dependencies]
dev = [
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "ruff" },
]
@ -32,16 +39,22 @@ requires-dist = [
{ name = "biorobot", specifier = "==0.4.2" },
{ name = "evosax", specifier = "==0.2.0" },
{ name = "ipykernel", specifier = "==7.2.0" },
{ name = "jax", extras = ["cuda13"], specifier = "==0.9.0.1" },
{ name = "jax", specifier = "==0.9.0.1" },
{ name = "jax", extras = ["cuda13"], marker = "extra == 'cuda'", specifier = "==0.9.0.1" },
{ name = "matplotlib", specifier = "==3.10.8" },
{ name = "mediapy", specifier = "==1.2.6" },
{ name = "pyopengl", specifier = ">=3.1.10" },
{ name = "pyopengl-accelerate", specifier = ">=3.1.10" },
{ name = "wandb", specifier = "==0.24.2" },
]
provides-extras = ["cuda"]
[package.metadata.requires-dev]
dev = [{ name = "ruff", specifier = ">=0.15.2" }]
dev = [
{ name = "pre-commit", specifier = ">=4.0.0" },
{ name = "pytest", specifier = ">=8.0.0" },
{ name = "ruff", specifier = ">=0.15.2" },
]
[[package]]
name = "absl-py"
@ -151,6 +164,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
]
[[package]]
name = "cfgv"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.4"
@ -285,6 +307,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
]
[[package]]
name = "distlib"
version = "0.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
]
[[package]]
name = "dm-control"
version = "1.0.37"
@ -406,6 +437,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/05/2c/ffc08c54c05cdce6fbed2aeebc46348dbe180c6d2c541c7af7ba0aa5f5f8/Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae", size = 2511, upload-time = "2023-02-27T18:28:39.447Z" },
]
[[package]]
name = "filelock"
version = "3.25.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" },
]
[[package]]
name = "flax"
version = "0.12.2"
@ -511,6 +551,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/3f/efeb7c6801c46e11bd666a5180f0d615f74f72264212f74f39586c6fda9d/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-manylinux_2_28_x86_64.whl", hash = "sha256:ce6724bb7cb3d0543dcba17206dce909f94176e68220b8eafee72e9f92bcf542", size = 243522, upload-time = "2026-01-28T05:58:03.517Z" },
{ url = "https://files.pythonhosted.org/packages/cf/b9/b04c3aa0aad2870cfe799f32f8b59789c98e1816bbce9e83f4823c5b840b/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-win32.whl", hash = "sha256:fca724a21a372731edb290841edd28a9fb1ee490f833392752844ac807c0086a", size = 552682, upload-time = "2026-01-28T05:58:05.649Z" },
{ url = "https://files.pythonhosted.org/packages/bd/e1/6d6816b296a529ac9b897ad228b1e084eb1f92319e96371880eebdc874a6/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-win_amd64.whl", hash = "sha256:823c0bd7770977d4b10e0ed0aef2f3682276b7c88b8b65cfc540afce5951392f", size = 559464, upload-time = "2026-01-28T05:58:07.261Z" },
{ url = "https://files.pythonhosted.org/packages/8c/a8/d4dab8a58fc2e6981fc7a58c4e56ba9d777fb24931cec6a22152edbb3540/glfw-2.10.0-py2.py3-none-macosx_10_6_intel.whl", hash = "sha256:a0d1f29f206219cc291edfb6cace663a86da2470632551c998e3db82d48ea177", size = 105288, upload-time = "2026-03-10T17:21:19.929Z" },
{ url = "https://files.pythonhosted.org/packages/14/61/68d35e001872a7705112418da236fa2418d4f2e5419f8b2837f9b81bb3da/glfw-2.10.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:d28d6f3ef217e64e35dc6fd0a7acb4cec9bfe7cd14dd9b35a7228a87002de154", size = 102139, upload-time = "2026-03-10T17:21:21.645Z" },
{ url = "https://files.pythonhosted.org/packages/4e/e1/ca5984081aaae07c9d371cb11dc4e4ff603510678ed9b73e58b6c351fe63/glfw-2.10.0-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:f968b522bb6a0e04aaf4dcac30a476d7229308bb2bac406a60587debb5a61e29", size = 229998, upload-time = "2026-03-10T17:21:23.549Z" },
{ url = "https://files.pythonhosted.org/packages/fa/c4/82ac75fdcfba2896da7a573c0fc7f8ceb8f77ead6866d500d06c32f1c464/glfw-2.10.0-py2.py3-none-manylinux2014_x86_64.whl", hash = "sha256:68cf3752bdadb6f4bc0a876247c28c88c7251ac39f8af076ed938fdfd71e72dd", size = 241944, upload-time = "2026-03-10T17:21:26.102Z" },
{ url = "https://files.pythonhosted.org/packages/e3/96/9f691823cca5eb6a08f346bd0ff03b78032db9370b509a1e9c8976fb20a5/glfw-2.10.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:44d98de5dbf8f727e0cb29f9b29d29528ea7570f2e6f42f8430a69df05f12b48", size = 231009, upload-time = "2026-03-10T17:21:28.481Z" },
{ url = "https://files.pythonhosted.org/packages/3f/93/977b9e679e356871d428ae7a1139ec767dd5177bed58a6344b4d2199e00f/glfw-2.10.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cca5158d62189e08792b1ae54f92307a282921a0e7783315b467e21b0a381c88", size = 243480, upload-time = "2026-03-10T17:21:30.538Z" },
{ url = "https://files.pythonhosted.org/packages/f9/bd/cea9569c8f2188b0a104472951420434a3e1f5cf26f5836ef9d7227a1a30/glfw-2.10.0-py2.py3-none-win32.whl", hash = "sha256:5e024509989740e8e7b86cc4aab508195495f79879072b0e1f68bd036a2916ad", size = 552641, upload-time = "2026-03-10T17:21:32.653Z" },
{ url = "https://files.pythonhosted.org/packages/cc/9b/4366ad3e1c0688146c70aa6143584d6a8d88583b9390f106250e25a3d5cd/glfw-2.10.0-py2.py3-none-win_amd64.whl", hash = "sha256:7f787ee8645781f10e8800438ce4357ab38c573ffb191aba380c1e72eba6311c", size = 559423, upload-time = "2026-03-10T17:21:34.766Z" },
]
[[package]]
@ -537,6 +585,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" },
]
[[package]]
name = "identify"
version = "2.6.18"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" },
]
[[package]]
name = "idna"
version = "3.11"
@ -568,6 +625,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
]
[[package]]
name = "iniconfig"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
[[package]]
name = "ipykernel"
version = "7.2.0"
@ -983,6 +1049,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
]
[[package]]
name = "nodeenv"
version = "1.10.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
]
[[package]]
name = "numpy"
version = "2.4.2"
@ -1285,6 +1360,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "pre-commit"
version = "4.5.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cfgv" },
{ name = "identify" },
{ name = "nodeenv" },
{ name = "pyyaml" },
{ name = "virtualenv" },
]
sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
]
[[package]]
name = "prompt-toolkit"
version = "3.0.52"
@ -1439,6 +1539,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
]
[[package]]
name = "pytest"
version = "9.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
@ -1451,6 +1567,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "python-discovery"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
{ name = "platformdirs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
@ -1786,6 +1915,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" },
]
[[package]]
name = "virtualenv"
version = "21.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "distlib" },
{ name = "filelock" },
{ name = "platformdirs" },
{ name = "python-discovery" },
]
sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" },
]
[[package]]
name = "wandb"
version = "0.24.2"