diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f9a6fe0..9ac9ec7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,33 +3,18 @@ ### Summary Describe the purpose and key changes of this PR. -### Change Type -- [ ] Bug fix -- [ ] New feature -- [ ] Breaking change -- [ ] Documentation -- [ ] Refactor -- [ ] Performance -- [ ] Other: - -### Related Issues -- Fixes: # -- Related: # - ### What Was Changed - -### Testing +### Quality Control - [ ] Added/updated tests - [ ] All tests pass locally - [ ] Linter and type checks pass -### Checklist -- [ ] Follows style guidelines -- [ ] Self-reviewed -- [ ] Code is commented where needed -- [ ] Documentation updated -- [ ] No new warnings +### Related Issues +- Fixes: # +- Related: # + ### Screenshots / Notes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31af69b..ac73f15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,9 @@ jobs: - name: Install dependencies run: uv sync + - name: Run linting + run: uv run ruff check . + - name: Run type checking run: uv run mypy src/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 0c33965..b720e6e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,11 +3,12 @@ "version": "0.2.0", "configurations": [ { - "name": "Curr File", + "name": "cartpole", "type": "debugpy", "request": "launch", - "program": "${workspaceFolder}/src/cumind/__main__.py", - "console": "integratedTerminal" + "program": "${workspaceFolder}/src/cartpole.py", + "console": "integratedTerminal", + "stopOnEntry": true }, { "name": "CuMind", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9ccd6f..ae8e013 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,7 +240,7 @@ def test_select_action_training_mode(self): agent = Agent(config) action = agent.select_action(observation, training=True) assert isinstance(action, int) - assert 0 <= action < config.action_space_size + assert 0 <= action < config.env_action_space_size ``` ## Project Structure diff --git a/Release_Notes.txt b/Release_Notes.txt index c5cb835..68995bb 100644 --- a/Release_Notes.txt +++ b/Release_Notes.txt @@ -2,6 +2,15 @@ CuMind Release Notes ------------------------------------------------------------------- Document all technical changes introduced in this release as concise bullet points below. +v0.1.8 (2025-07-18) +---------------------- +Tarek Ibrahim (68) +- Major configuration refactor: unified and simplified config, key, logger management +- Refactored agent, core, and data modules for consistency with new config structure +- Replaced explicit relative imports for absolute imports +- Added standard output coloring for logs +- Enhanced code quality: 126 passing tests, 86% code coverage +- Decoupled neural network code in core/ v0.1.7 (2025-07-15) ---------------------- diff --git a/checkpoints/CartPole-v1/20250713-141205/episode_01200.pkl b/checkpoints/CartPole-v1/20250713-141205/episode_01200.pkl deleted file mode 100644 index c7d2f52..0000000 Binary files a/checkpoints/CartPole-v1/20250713-141205/episode_01200.pkl and /dev/null differ diff --git a/configuration.json b/configuration.json index 79383e2..b2b573e 100644 --- a/configuration.json +++ b/configuration.json @@ -1,54 +1,72 @@ { - "Network architecture": { - "hidden_dim": 128, - "num_blocks": 2, - "conv_channels": 32 - }, - "Training": { - "batch_size": 64, - "learning_rate": 0.01, - "weight_decay": 0.0001, - "target_update_frequency": 250, - "checkpoint_interval": 50, - "num_episodes": 1220, - "train_frequency": 2, - "checkpoint_root_dir": "checkpoints" - }, - "MCTS": { - "num_simulations": 25, - "c_puct": 1.25, - "dirichlet_alpha": 0.25, - "exploration_fraction": 0.25 - }, - "Environment": { - "env_name": "CartPole-v1", - "action_space_size": 2, - "observation_shape": [ - 4 - ] - }, - "Self-Play": { - "num_unroll_steps": 5, - "td_steps": 10, - "discount": 0.997 - }, - "Memory": { - "memory_capacity": 2000, - "min_memory_size": 100, - "min_memory_pct": 0.1, - "per_alpha": 0.6, - "per_epsilon": 1e-06, - "per_beta": 0.4 - }, - "Data Types": { - "model_dtype": "float32", - "action_dtype": "int32", - "target_dtype": "float32" - }, - "Device": { - "device_type": "cpu" - }, - "Other": { + "CuMind": { + "networks": { + "hidden_dim": 128 + }, + "representation": { + "type": "cumind.core.resnet.ResNet", + "num_blocks": 2, + "conv_channels": 32, + "seed": 42 + }, + "dynamics": { + "type": "cumind.core.mlp.MLPWithEmbedding", + "num_blocks": 2, + "seed": 42 + }, + "prediction": { + "type": "cumind.core.mlp.MLPDual", + "seed": 42 + }, + "memory": { + "type": "cumind.data.memory.MemoryBuffer", + "capacity": 2000, + "min_size": 100, + "min_pct": 0.1, + "per_alpha": 0.6, + "per_epsilon": 1e-06, + "per_beta": 0.4 + }, + "training": { + "optimizer": "optax.adamw", + "batch_size": 64, + "learning_rate": 0.01, + "weight_decay": 0.0001, + "target_update_frequency": 250, + "checkpoint_interval": 50, + "num_episodes": 1220, + "train_frequency": 2, + "checkpoint_root_dir": "checkpoints" + }, + "mcts": { + "num_simulations": 25, + "c_puct": 1.25, + "dirichlet_alpha": 0.25, + "exploration_fraction": 0.25 + }, + "env": { + "name": "CartPole-v1", + "action_space_size": 2, + "observation_shape": [4] + }, + "selfplay": { + "num_unroll_steps": 5, + "td_steps": 10, + "discount": 0.997 + }, + "dtypes": { + "model": "float32", + "action": "int32", + "target": "float32" + }, + "logging": { + "dir": "logs", + "level": "INFO", + "console": true, + "timestamps": false, + "tqdm": false + }, + "device": "cpu", "seed": 42 } -} \ No newline at end of file +} diff --git a/cumind.svg b/cumind.svg new file mode 100644 index 0000000..a8ce2ee --- /dev/null +++ b/cumind.svg @@ -0,0 +1,529 @@ + + + + + + +G + + + +cumind___main__ + +cumind. +__main__ + + + +cumind_agent + +cumind.agent + + + +cumind_cli + +cumind.cli + + + +cumind_agent->cumind_cli + + + + +cumind_data_self_play + +cumind. +data. +self_play + + + +cumind_agent->cumind_data_self_play + + + + + + +cumind_agent_agent + +cumind. +agent. +agent + + + +cumind_agent_runner + +cumind. +agent. +runner + + + +cumind_agent_agent->cumind_agent_runner + + + + + + +cumind_agent_trainer + +cumind. +agent. +trainer + + + +cumind_agent_agent->cumind_agent_trainer + + + + + +cumind_agent_agent->cumind_data_self_play + + + + + +cumind_agent_runner->cumind_cli + + + + +cumind_agent_trainer->cumind_agent_runner + + + + + +cumind_cli->cumind___main__ + + + + + +cumind_core + +cumind.core + + + +cumind_core->cumind_agent_agent + + + + + + +cumind_core->cumind_agent_trainer + + + + + +cumind_core_blocks + +cumind. +core. +blocks + + + +cumind_core_encoder + +cumind. +core. +encoder + + + +cumind_core_blocks->cumind_core_encoder + + + + + +cumind_core_resnet + +cumind. +core. +resnet + + + +cumind_core_encoder->cumind_core_resnet + + + + + +cumind_core_mcts + +cumind. +core. +mcts + + + +cumind_core_mcts->cumind_agent_agent + + + + +cumind_core_network + +cumind. +core. +network + + + +cumind_core_network->cumind_agent_agent + + + + +cumind_core_network->cumind_agent_trainer + + + + +cumind_core_network->cumind_core_mcts + + + + + +cumind_data + +cumind.data + + + +cumind_data->cumind_agent_trainer + + + + +cumind_data_memory + +cumind. +data. +memory + + + +cumind_data_memory->cumind_agent_trainer + + + + + +cumind_data_memory->cumind_data_self_play + + + + + +cumind_data_self_play->cumind_agent_trainer + + + + + + +cumind_utils + +cumind.utils + + + +cumind_utils->cumind_agent_agent + + + + + +cumind_utils->cumind_agent_runner + + + + + +cumind_utils->cumind_agent_trainer + + + + +cumind_utils->cumind_cli + + + + + + + + + +cumind_utils->cumind_core_blocks + + + + + + +cumind_utils->cumind_core_encoder + + + + + + +cumind_utils->cumind_core_mcts + + + + + +cumind_utils->cumind_core_network + + + + + +cumind_utils->cumind_data_memory + + + + + +cumind_utils->cumind_data_self_play + + + + + + +cumind_utils_checkpoint + +cumind. +utils. +checkpoint + + + +cumind_utils_checkpoint->cumind_agent_runner + + + + + +cumind_utils_checkpoint->cumind_agent_trainer + + + + + +cumind_utils_checkpoint->cumind_cli + + + + + + +cumind_utils_config + +cumind. +utils. +config + + + +cumind_utils_config->cumind_agent_agent + + + + + + +cumind_utils_config->cumind_agent_runner + + + + + +cumind_utils_config->cumind_agent_trainer + + + + + +cumind_utils_config->cumind_cli + + + + + +cumind_utils_config->cumind_core_mcts + + + + +cumind_utils_logger + +cumind. +utils. +logger + + + +cumind_utils_logger->cumind_agent_agent + + + + + +cumind_utils_logger->cumind_agent_runner + + + + + +cumind_utils_logger->cumind_agent_trainer + + + + + +cumind_utils_logger->cumind_cli + + + + +cumind_utils_logger->cumind_core_blocks + + + + + +cumind_utils_logger->cumind_core_encoder + + + + + +cumind_utils_logger->cumind_core_mcts + + + + + + + +cumind_utils_logger->cumind_core_network + + + + + +cumind_utils_logger->cumind_data_memory + + + + + + + + +cumind_utils_logger->cumind_data_self_play + + + + +cumind_utils_logger->cumind_utils_checkpoint + + + + + + + + +cumind_utils_logger->cumind_utils_config + + + + + + +cumind_utils_prng + +cumind. +utils. +prng + + + +cumind_utils_logger->cumind_utils_prng + + + + + +cumind_utils_prng->cumind_agent_agent + + + + +cumind_utils_prng->cumind_utils_config + + + + + +cumind_utils_resolve + +cumind. +utils. +resolve + + + +cumind_utils_resolve->cumind_utils_config + + + + + +numpy + +numpy + + + +numpy->cumind_agent_agent + + + + + +numpy->cumind_core_mcts + + + + + +numpy->cumind_data_memory + + + + + diff --git a/docs/GPU_PERFORMANCE_TIPS.md b/docs/GPU_PERFORMANCE_TIPS.md index 3e7bb41..400d7d7 100644 --- a/docs/GPU_PERFORMANCE_TIPS.md +++ b/docs/GPU_PERFORMANCE_TIPS.md @@ -223,7 +223,7 @@ from cumind import Agent, Config def benchmark_inference(config: Config, num_runs: int = 1000): """Benchmark inference speed on GPU.""" agent = Agent(config) - observation = jnp.ones(config.observation_shape) + observation = jnp.ones(config.env_observation_shape) # Warm-up for _ in range(10): @@ -248,7 +248,7 @@ if __name__ == "__main__": ] for config in configs: - print(f"\nTesting with dtype: {config.model_dtype}") + print(f"\nTesting with dtype: {config.dtypes_model}") benchmark_inference(config) ``` diff --git a/docs/ONBOARDING.md b/docs/ONBOARDING.md new file mode 100644 index 0000000..1527bf6 --- /dev/null +++ b/docs/ONBOARDING.md @@ -0,0 +1,156 @@ +# CuMind Development Environment Setup Guide + +This guide walks you through setting up the CuMind development environment on **macOS** and **Windows (WSL)** using GitHub, VSCode or Cursor, and `uv` for Python dependency management. + +## 1. Platform Prerequisites + +CuMind requires a Linux-based development environment. + +To achieve this on your machine: + +### On Windows: + +- Install WSL and Ubuntu by following the [official Microsoft guide](https://learn.microsoft.com/en-us/windows/wsl/install) +- Recommended: WSL 2 and Ubuntu 22.04 +- After installation, open "Ubuntu" from the Start Menu to launch your WSL terminal +- Use a code editor such as [VSCode](https://code.visualstudio.com/) or [Cursor](https://cursor.so) that supports WSL. + If you're using VSCode, install the **Remote - WSL** extension — you'll use it to open and work with the project from inside your WSL environment after cloning the repository. + - Additional resource: [VSCode: Developing in WSL](https://code.visualstudio.com/docs/remote/wsl) + +### On macOS: + +- No WSL needed — the built-in Terminal provides a compatible Unix-based environment +- Use any local code editor such as [VSCode](https://code.visualstudio.com/) or [Cursor](https://cursor.so) + +## 2. Setup Python & `uv` + +### 1. **Install Python** + +Ensure Python 3.12 or higher is installed: + +```bash +python3 --version +``` + +If not installed or the version is too old, install: + +- #### On WSL: + + Install Python and pip: + + ```bash + sudo apt update + sudo apt install python3 python3-pip + ``` + +- #### On macOS: + + ```bash + brew install python + ``` + +### 2. **Install `uv`** + +Install the Python dependency manager `uv` using one of two ways: + +- Using pip/pipx: + + ```bash + pip install uv + # or + pipx install uv + ``` + +- Using the standalone installer: + + ```bash + curl -LsSf https://astral.sh/uv/install.sh | sh + ``` + + To update, run: + + ```bash + uv self update + ``` + +More details: [uv documentation](https://astral.sh/uv/) + +## 4. Clone Repo and Setup Git Authentication + +You can clone the repo using either **SSH** or **HTTPS**. + +### Option 1: SSH (Recommended) + +1. Generate a new SSH key (if you don't have one already): + +```bash +ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "your_email@example.com" +``` + +2. Copy your public key: + +```bash +cat ~/.ssh/id_ed25519.pub +``` + +3. Add it to GitHub: + [https://github.com/settings/ssh/new](https://github.com/settings/ssh/new) + +4. Test your connection: + +```bash +ssh -T git@github.com +``` + +5. Clone the repo: + +```bash +git clone git@github.com:carletonai/CuMind.git +``` + +> If you already have an SSH key and have it registered with GitHub, skip steps 1–3. + +### Option 2: HTTPS + +Clone the repo using: + +```bash +git clone https://github.com/carletonai/CuMind.git +``` + +You’ll be prompted to enter your GitHub **username** and a **personal access token** (PAT) instead of a password. +Generate a PAT here: [https://github.com/settings/tokens](https://github.com/settings/tokens) + +## 5. Set Up and Run the Project + +Once the repo is cloned, navigate to it and install dependencies: + +```bash +cd CuMind +uv sync +``` + +Test out running a specific file to make sure your setup is working: + +`uv run python src/cartpole.py` + +To run the full application: + +`uv run python -m cumind --config configuration.json` + +## 6. Test Your Git Setup + +Make sure you can push a new branch: + +```bash +git checkout -b test-branch +git push origin test-branch +``` + +Once confirmed, please delete the test branch: + +`git push origin --delete test-branch` + +## You’re Ready! + +You should now be able to develop and contribute to CuMind locally. If you run into issues, feel free to reach out on Discord. diff --git a/plots/episode_loss.png b/plots/episode_loss.png new file mode 100644 index 0000000..dee2107 Binary files /dev/null and b/plots/episode_loss.png differ diff --git a/plots/policy_loss.png b/plots/policy_loss.png new file mode 100644 index 0000000..7601168 Binary files /dev/null and b/plots/policy_loss.png differ diff --git a/plots/reward.png b/plots/reward.png new file mode 100644 index 0000000..d634f1b Binary files /dev/null and b/plots/reward.png differ diff --git a/plots/reward_loss.png b/plots/reward_loss.png new file mode 100644 index 0000000..fad0de1 Binary files /dev/null and b/plots/reward_loss.png differ diff --git a/plots/total_loss.png b/plots/total_loss.png new file mode 100644 index 0000000..cb2b791 Binary files /dev/null and b/plots/total_loss.png differ diff --git a/plots/value_loss.png b/plots/value_loss.png new file mode 100644 index 0000000..d51a08e Binary files /dev/null and b/plots/value_loss.png differ diff --git a/pyproject.toml b/pyproject.toml index 679bf54..0cfab56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,8 +3,8 @@ # ========================= [project] name = "cumind" -version = "0.1.7" -description = "CUAIS CuMind implementation" +version = "0.1.8" +description = "A JAX-based CuMind is a JAX-based RL framework inspired by Google DeepMind. Achieve's superhuman performance in complex domains without pretraining nor prior knowledge of their rules." readme = "README.md" requires-python = ">=3.12" dependencies = [ @@ -60,7 +60,7 @@ target-version = "py312" [tool.ruff.lint] select = ["E", "F", "I", "N", "W"] # Errors, pyFlakes, Import, Naming, Warnings -ignore = ["E203", "E501", "F401"] +ignore = ["E203", "E501"] # Mypy configuration [tool.mypy] diff --git a/scripts/exportconfig.py b/scripts/exportconfig.py old mode 100644 new mode 100755 index c037f1c..6f81f87 --- a/scripts/exportconfig.py +++ b/scripts/exportconfig.py @@ -1,10 +1,5 @@ -from cumind.config import Config - - -def main() -> None: - config = Config() - config.to_json("configuration.json") - +#!./.venv/bin/python3.12 +from cumind.utils.config import cfg if __name__ == "__main__": - main() + cfg.save("configuration.json") diff --git a/scripts/plot.py b/scripts/plot.py new file mode 100755 index 0000000..d99b557 --- /dev/null +++ b/scripts/plot.py @@ -0,0 +1,128 @@ +#!./.venv/bin/python3.12 +import datetime +import os +import re + +import matplotlib.pyplot as plt + + +def parse_log_file(filepath): + episode_re = re.compile(r"(?P