Skip to content

Commit 11acb48

Browse files
authored
Merge pull request #157 from EMI-Group/evoxtorch-dev-bh
EvoXTorch project structure changes
2 parents 2ce3988 + 551bba5 commit 11acb48

File tree

200 files changed

+1665
-119629
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

200 files changed

+1665
-119629
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Please describe your pull request here
88
A quick checklist, please check what applies to your pull request (put "x" in the brackets)
99
-->
1010

11-
- [ ] I have formatted my Python code with `black`.
11+
- [ ] I have formatted my Python code with `ruff`.
1212
- [ ] I have good commit messages.
1313
- If adding new algorithms, problems, operators:
1414
- [ ] Added related test cases.

.github/workflows/python-package.yml

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,11 @@ jobs:
2929
- name: Install build dependencies
3030
run: |
3131
python -m pip install --upgrade pip
32-
python -m pip install build wheel setuptools pytest
33-
- name: Build and install package Python 3.10 and above
32+
python -m pip install build wheel setuptools
33+
- name: Build and install package
3434
run: |
3535
output=$(python -m build --wheel)
36-
if [[ "${{ matrix.python-version }}" == "3.12" ]]; then
37-
# Exclude envpool when testing with Python 3.12
38-
pip install dist/${output##* }[test]
39-
pip install gymnasium ray gpjax plotly pandas tensorflow-datasets grain brax plotly pandas
40-
else
41-
pip install dist/${output##* }[full,test]
42-
fi
43-
- name: Test with pytest Python 3.10 and above
36+
pip install dist/${output##* }[test]
37+
- name: Test with unittest
4438
run: |
45-
if [[ "${{ matrix.python-version }}" == "3.12" ]]; then
46-
pytest -k 'not test_envpool_cartpole'
47-
else
48-
pytest
49-
fi
39+
python -m unittest

.github/workflows/ruff-check.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: Ruff
2+
on: [ push, pull_request ]
3+
jobs:
4+
ruff:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v4
8+
- uses: astral-sh/ruff-action@v3

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,5 @@ cython_debug/
166166

167167
# Test
168168
tests
169-
evox
170169
**/_future/*
171-
data
170+
data/MNIST

.pre-commit-config.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# See https://pre-commit.com for more information
2+
# See https://pre-commit.com/hooks.html for more hooks
3+
repos:
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v3.2.0
6+
hooks:
7+
- id: trailing-whitespace
8+
- id: end-of-file-fixer
9+
- id: check-yaml
10+
- id: check-added-large-files
11+
- repo: https://github.com/astral-sh/ruff-pre-commit
12+
# Ruff version.
13+
rev: v0.8.5
14+
hooks:
15+
# Run the linter.
16+
- id: ruff
17+
args: [ --fix ]
18+
# Run the formatter.
19+
- id: ruff-format

README.md

Lines changed: 15 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
---
2626

27-
Building upon [JAX](https://github.com/google/jax) and [Ray](https://github.com/ray-project/ray), EvoX offers a comprehensive suite of **50+ Evolutionary Algorithms (EAs)** and a wide range of **100+ Benchmark Problems/Environments**, all benefiting from distributed GPU-acceleration. It facilitates efficient exploration of complex optimization landscapes, effective tackling of black-box optimization challenges, and deep dives into neuroevolution with [Brax](https://github.com/google/brax). With a foundation in functional programming and hierarchical state management, EvoX offers a user-friendly and modular experience. For more details, please refer to our [Paper](https://arxiv.org/abs/2301.12457) and [Documentation](https://evox.readthedocs.io/en/latest/) / [文档](https://evox.readthedocs.io/zh/latest/).
27+
Building upon PyTorch, EvoX offers a comprehensive suite of **50+ Evolutionary Algorithms (EAs)** and a wide range of **100+ Benchmark Problems/Environments**, all benefiting from distributed GPU-acceleration. It facilitates efficient exploration of complex optimization landscapes, effective tackling of black-box optimization challenges, and deep dives into neuroevolution with [Brax](https://github.com/google/brax). With a foundation in functional programming and hierarchical state management, EvoX offers a user-friendly and modular experience. For more details, please refer to our [Paper](https://arxiv.org/abs/2301.12457) and [Documentation](https://evox.readthedocs.io/en/latest/) / [文档](https://evox.readthedocs.io/zh/latest/).
2828

2929
## Key Features
3030

@@ -63,107 +63,33 @@ For a comprehensive list and further details of all algorithms, please check the
6363

6464
### Benchmark Problems/Environments
6565

66-
| Category | Problems/Environments |
67-
| -------------- | ----------------------------------- |
68-
| Numerical | DTLZ, LSMOP, MaF, ZDT, CEC'22, ... |
66+
| Category | Problems/Environments |
67+
| ----------------- | ----------------------------------- |
68+
| Numerical | DTLZ, LSMOP, MaF, ZDT, CEC'22, ... |
6969
| Neuroevolution/RL | Brax, Gym, TorchVision Dataset, ... |
7070

7171
For a comprehensive list and further details of all benchmark problems/environments, please check the [API Documentation](https://evox.readthedocs.io/en/latest/api/problems/index.html).
7272

7373

7474
## Setting Up EvoX
7575

76-
77-
## Prerequisites
78-
79-
- **Python**: Version 3.12 (or higher)
80-
- **CUDA**: Version 12.1 (or higher)
81-
- **PyTorch**: Version 2.5.0 (or higher recommended)
82-
8376
Install `evox` effortlessly via `pip`:
8477
```bash
8578
pip install evox
8679
```
8780

88-
**Note**: To setup EvoX with **GPU acceleration** capabilities, you will need to setup **JAX** first. For detials, please refer to our comprehensive [Installation Guide](https://evox.readthedocs.io/en/latest/guide/install/index.html). Additionally, you can watch our **instructional videos**:
89-
90-
🎥 [EvoX Installation Guide (Linux)](https://youtu.be/fa2s1Jl-Fy0)
91-
92-
🎥 [EvoX Installation Guide (Windows)](https://youtu.be/7f8Uz1rqvn8)
93-
94-
🎥 [EvoX 安装指南 (Linux)](https://www.bilibili.com/video/BV1Zt421c7GN)
95-
96-
🎥 [EvoX 安装指南 (Windows)](https://www.bilibili.com/video/BV1Bb421h7bG)
97-
98-
99-
100-
## Quick Start
101-
102-
Kickstart your journey with EvoX in just a few simple steps:
103-
1. **Import necessary modules**:
104-
```python
105-
import evox
106-
from evox import algorithms, problems, workflows
107-
```
108-
2. **Configure an algorithm and define a problem**:
109-
```python
110-
pso = algorithms.PSO(
111-
lb=jnp.full(shape=(2,), fill_value=-32),
112-
ub=jnp.full(shape=(2,), fill_value=32),
113-
pop_size=100,
114-
)
115-
ackley = problems.numerical.Ackley()
116-
```
117-
3. **Compose and initialize the workflow**:
118-
```python
119-
workflow = workflows.StdWorkflow(pso, ackley)
120-
key = jax.random.PRNGKey(42)
121-
state = workflow.init(key)
122-
```
123-
4. **Run the workflow**:
124-
```python
125-
# Execute the workflow for 100 iterations
126-
for i in range(100):
127-
state = workflow.step(state)
128-
```
129-
130-
## Use-cases and Applications
131-
132-
Try out ready-to-play examples in your browser with Colab:
133-
134-
| Example | Link |
135-
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
136-
| Basic Usage | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/EMI-Group/evox/blob/main/docs/source/guide/basics/1-start.ipynb) |
137-
| Numerical Optimization | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/EMI-Group/evox/blob/main/docs/source/example/pso_ackley.ipynb) |
138-
| Neuroevolution with Gym | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/EMI-Group/evox/blob/main/docs/source/example/gym_classic_control.ipynb) |
139-
| Neuroevolution with Brax | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/EMI-Group/evox/blob/main/docs/source/guide/basics/2-problems.ipynb) |
140-
| Custom Algorithm/Problem | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/EMI-Group/evox/blob/main/docs/source/example/custom_algorithm_and_problem.ipynb) |
141-
142-
For more use-cases and applications, pleae check out [Example Directory](https://evox.readthedocs.io/en/latest/example/index.html).
143-
144-
145-
## Unit Test Commands
81+
## Run Unit Test
14682

83+
1. prepare the test environment by installing the required packages (e.g., `torch`) in your Python environment
84+
2. run the following command in the **root directory of the project**: `pip install -e .`, which will install the `evox` package in editable mode
85+
3. run unittest:
14786
```shell
148-
python ./unit_test/algorithms/pso_variants/test_clpso.py
149-
python ./unit_test/algorithms/pso_variants/test_cso.py
150-
python ./unit_test/algorithms/pso_variants/test_dms_pso_el.py
151-
python ./unit_test/algorithms/pso_variants/test_fs_pso.py
152-
python ./unit_test/algorithms/pso_variants/test_pso.py
153-
python ./unit_test/algorithms/pso_variants/test_sl_pso_gs.py
154-
python ./unit_test/algorithms/pso_variants/test_sl_pso_us.py
155-
156-
python ./unit_test/core/test_jit_util.py
157-
python ./unit_test/core/test_module.py
158-
159-
python ./unit_test/problems/test_hpo_wrapper.py
160-
python ./unit_test/problems/test_supervised_learning.py
161-
162-
python ./unit_test/utils/test_jit_fix.py
163-
python ./unit_test/utils/test_parameters_and_vector.py
164-
python ./unit_test/utils/test_while.py
165-
166-
python ./unit_test/workflows/test_std_workflow.py
87+
# run all tests
88+
python -m unittest
89+
# run tests in [path], e.g. python -m unittest unit_test/core/test_jit_util.py
90+
python -m unittest [path-to-test-file]
91+
# run a specific test method or module, e.g. python -m unittest unit_test.core.test_jit_util.TestJitUtil.test_single_eval
92+
python -m unittest [path-to-method-or-module]
16793
```
16894

16995
## Community & Support
@@ -195,4 +121,4 @@ If you use EvoX in your research and want to cite it in your work, please use:
195121

196122
## Star History
197123

198-
[![Star History Chart](https://api.star-history.com/svg?repos=EMI-Group/evox&type=Date)](https://star-history.com/#EMI-Group/evox&Date)
124+
[![Star History Chart](https://api.star-history.com/svg?repos=EMI-Group/evox&type=Date)](https://star-history.com/#EMI-Group/evox&Date)

benchmarks/pso.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""Benchmark the performance of PSO algorithm in EvoX."""
2+
3+
import time
4+
5+
import torch
6+
from torch.profiler import ProfilerActivity, profile
7+
8+
from evox.algorithms import PSO
9+
from evox.core import Problem, jit, use_state, vmap
10+
from evox.workflows import StdWorkflow
11+
12+
13+
def run_pso():
14+
class Sphere(Problem):
15+
def __init__(self):
16+
super().__init__()
17+
18+
def evaluate(self, pop: torch.Tensor):
19+
return (pop**2).sum(-1)
20+
21+
algo = PSO(pop_size=10, lb=-10 * torch.ones(3), ub=10 * torch.ones(3))
22+
prob = Sphere()
23+
24+
torch.set_default_device("cuda" if torch.cuda.is_available() else "cpu")
25+
print(torch.get_default_device())
26+
workflow = StdWorkflow()
27+
workflow.setup(algo, prob)
28+
workflow.init_step()
29+
workflow.step()
30+
state_step = use_state(lambda: workflow.step)
31+
vmap_state_step = vmap(state_step)
32+
print(vmap_state_step.init_state(2))
33+
state = state_step.init_state()
34+
jit_state_step = jit(state_step, trace=True, example_inputs=(state,))
35+
state = state_step.init_state()
36+
t = time.time()
37+
with profile(
38+
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
39+
record_shapes=True,
40+
profile_memory=True,
41+
) as prof:
42+
for _ in range(1000):
43+
workflow.step()
44+
print(prof.key_averages().table())
45+
torch.cuda.synchronize()
46+
t = time.time()
47+
with profile(
48+
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
49+
record_shapes=True,
50+
profile_memory=True,
51+
) as prof:
52+
for _ in range(1000):
53+
state = jit_state_step(state)
54+
print(prof.key_averages().table())
55+
torch.cuda.synchronize()
56+
print(time.time() - t)
57+
58+
59+
if __name__ == "__main__":
60+
run_pso()

benchmarks/test_base.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import os
2+
3+
import torch
4+
from torch.profiler import ProfilerActivity, profile
5+
6+
from src.core import Algorithm, Problem, jit, use_state, vmap
7+
from src.workflows import EvalMonitor, StdWorkflow
8+
9+
10+
class Sphere(Problem):
11+
def __init__(self):
12+
super().__init__()
13+
14+
def evaluate(self, pop: torch.Tensor):
15+
return (pop**2).sum(-1)
16+
17+
18+
def test(
19+
algo: Algorithm,
20+
print_path: str | None = None,
21+
profiling: bool = True,
22+
test_trace: bool = True,
23+
):
24+
torch.set_default_device("cuda" if torch.cuda.is_available() else "cpu")
25+
print("Current device: ", torch.get_default_device())
26+
27+
monitor = EvalMonitor(full_fit_history=False, full_sol_history=False)
28+
prob = Sphere()
29+
workflow = StdWorkflow()
30+
workflow.setup(algo, prob)
31+
# test trace step
32+
if test_trace:
33+
state_step = use_state(lambda: workflow.step)
34+
state = state_step.init_state()
35+
jit_state_step = jit(state_step, trace=True, example_inputs=(state,))
36+
vmap_state_step = vmap(state_step)
37+
vmap_state_step = jit(
38+
vmap_state_step,
39+
trace=True,
40+
lazy=False,
41+
example_inputs=(vmap_state_step.init_state(3),),
42+
)
43+
# print
44+
if print_path is not None:
45+
with open(os.path.join(print_path, "script.md"), "w") as ff:
46+
ff.write(workflow.step.inlined_graph.__str__())
47+
if test_trace:
48+
with open(os.path.join(print_path, "trace.md"), "w") as ff:
49+
ff.write(jit_state_step.inlined_graph.__str__())
50+
with open(os.path.join(print_path, "vmap.md"), "w") as ff:
51+
ff.write(vmap_state_step.inlined_graph.__str__())
52+
# profile
53+
workflow = StdWorkflow()
54+
workflow.setup(algo, prob, monitor)
55+
workflow.init_step()
56+
print("Initial best fitness:", workflow.get_submodule("monitor").topk_fitness)
57+
if profiling:
58+
with profile(
59+
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
60+
record_shapes=True,
61+
profile_memory=True,
62+
) as prof:
63+
for _ in range(1000):
64+
workflow.step()
65+
print(prof.key_averages().table())
66+
else:
67+
for _ in range(1000):
68+
workflow.step()
69+
print("Final best fitness:", workflow.get_submodule("monitor").topk_fitness)
70+
torch.cuda.synchronize()
71+
if test_trace:
72+
workflow = StdWorkflow()
73+
workflow.setup(algo, prob, monitor)
74+
workflow.init_step()
75+
state_step = use_state(lambda: workflow.step)
76+
state = state_step.init_state()
77+
print("Initial best fitness:", state["self.algorithm._monitor_.topk_fitness"])
78+
jit_state_step = jit(state_step, trace=True, example_inputs=(state,))
79+
if profiling:
80+
with profile(
81+
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
82+
record_shapes=True,
83+
profile_memory=True,
84+
) as prof:
85+
for _ in range(1000):
86+
state = jit_state_step(state)
87+
print(prof.key_averages().table())
88+
else:
89+
for _ in range(1000):
90+
state = jit_state_step(state)
91+
print("Final best fitness:", state["self.algorithm._monitor_.topk_fitness"])
92+
torch.cuda.synchronize()

benchmarks/utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Benchmark the performance of utils functions in EvoX"""
2+
3+
import torch
4+
from torch.profiler import ProfilerActivity, profile
5+
6+
from evox.core import jit
7+
from evox.utils import switch
8+
9+
10+
def run_switch():
11+
x = torch.tensor([1, 0, 1], dtype=torch.int)
12+
y = torch.tensor([[2.0, 2.5], [3.0, 3.5], [4.0, 4.5]]).T.split(1, dim=0)
13+
y = [a.squeeze(0) for a in y]
14+
basic_switch = jit(switch, trace=False)
15+
z = basic_switch(x, y)
16+
print(z)
17+
18+
x = torch.randint(low=0, high=10, size=(1000, 10000), dtype=torch.int, device="cuda")
19+
y = [torch.rand(1000, 10000, device="cuda") for _ in range(10)]
20+
vmap_switch = jit(switch, trace=False)
21+
with profile(
22+
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
23+
record_shapes=True,
24+
profile_memory=True,
25+
) as prof:
26+
for _ in range(1000):
27+
z = vmap_switch(x, y)
28+
print(prof.key_averages().table())
29+
30+
31+
if __name__ == "__main__":
32+
run_switch()

0 commit comments

Comments
 (0)