Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/cavalab/brush into island_GA
Browse files Browse the repository at this point in the history
  • Loading branch information
gAldeia committed Apr 25, 2024
2 parents ee5379d + f54104f commit 160b39a
Show file tree
Hide file tree
Showing 5 changed files with 593 additions and 4 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ GNU GPLv3, see [LICENSE](https://github.com/cavalab/brush/blob/master/LICENSE)

## Installation

### Installation via Python wheel and `pip` (recommended)

> **Important**: This method is only currently supported for CPython v3.11 running on the Linux x86_64 platform. Other Python versions and operating systems will be supported in the near future.
To install a prebuilt version of `pybrush`, download the most recent release of the wheel file on the [Releases page](https://github.com/cavalab/brush/releases/) (e.g., `pybrush-0.1.1-cp311-linux_x86_64.whl`; you may need to expand "Assets" to see the file). Then, navigate to the directory containing the wheel file and install it using `pip`:

```
pip install pybrush-0.1.1-cp311-linux_x86_64.whl
```

### Manual installation

<!-- start installation -->
Clone the repo:

Expand Down
5 changes: 2 additions & 3 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ name: brush
channels:
- conda-forge
dependencies:
- python # =3.8.2
- python>=3.9 #=3.8.2
- cmake #=3.18.*
- eigen #=3.4.*
- fmt
- gcc >= 12.0
- gxx >= 12.0
- ninja
- ceres-solver=2.1.0
- taskflow
- pybind11 #=2.6.2
- pybind11>=2.6.2
- pytest #=6.2.4
- pydot
- scikit-learn
Expand Down
2 changes: 1 addition & 1 deletion install
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/bash
make -C build -j 4 VERBOSE=1 $1
make -C build -j 4 VERBOSE=2 $1
105 changes: 105 additions & 0 deletions src/brush/deap_api/nsga2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from deap import tools
from deap.benchmarks.tools import diversity, convergence, hypervolume
import numpy as np
import functools


def nsga2(toolbox, NGEN, MU, CXPB, use_batch, verbosity, rnd_flt):
# NGEN = 250
# MU = 100
# CXPB = 0.9
# rnd_flt: random number generator to sample crossover prob

def calculate_statistics(ind):
on_train = ind.fitness.values
on_val = toolbox.evaluateValidation(ind)

return (*on_train, *on_val)

stats = tools.Statistics(calculate_statistics)

stats.register("avg", np.mean, axis=0)
stats.register("med", np.median, axis=0)
stats.register("std", np.std, axis=0)
stats.register("min", np.min, axis=0)
stats.register("max", np.max, axis=0)

logbook = tools.Logbook()
logbook.header = "gen", "evals", "avg (O1 train, O2 train, O1 val, O2 val)", \
"med (O1 train, O2 train, O1 val, O2 val)", \
"std (O1 train, O2 train, O1 val, O2 val)", \
"min (O1 train, O2 train, O1 val, O2 val)", \
"max (O1 train, O2 train, O1 val, O2 val)"

pop = toolbox.population(n=MU)

batch = toolbox.getBatch() # everytime this function is called, a new random batch is generated

# OBS: evaluate calls fit in the individual. It is different from using it to predict. The
# function evaluateValidation don't call the fit
fitnesses = toolbox.map(functools.partial(toolbox.evaluate, data=batch), pop)

for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit

# This is just to assign the crowding distance to the individuals
# no actual selection is done
pop = toolbox.survive(pop, len(pop))

record = stats.compile(pop)
logbook.record(gen=0, evals=len(pop), **record)

if verbosity > 0:
print(logbook.stream)

# Begin the generational process
for gen in range(1, NGEN):
# The batch will be random only if it is not the size of the entire train set.
# In this case, we dont need to reevaluate the whole pop
if (use_batch):
batch = toolbox.getBatch()
fitnesses = toolbox.map(functools.partial(toolbox.evaluate, data=batch), pop)

for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit

# Vary the population
# offspring = tools.selTournamentDCD(pop, len(pop))
parents = toolbox.select(pop, len(pop))
# offspring = [toolbox.clone(ind) for ind in offspring]
offspring = []

for ind1, ind2 in zip(parents[::2], parents[1::2]):
off1, off2 = None, None
if rnd_flt() < CXPB:
off1, off2 = toolbox.mate(ind1, ind2)
else:
off1 = toolbox.mutate(ind1)
off2 = toolbox.mutate(ind2)

# avoid inserting empty solutions
if off1 is not None: offspring.extend([off1])
if off2 is not None: offspring.extend([off2])

# archive.update(offspring)
# Evaluate the individuals with an invalid fitness
invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = toolbox.map(functools.partial(toolbox.evaluate, data=batch), invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit

# Select the next generation population
pop = toolbox.survive(pop + offspring, MU)
record = stats.compile(pop)
logbook.record(gen=gen, evals=len(offspring)+(len(pop) if use_batch else 0), **record)

if verbosity > 0:
print(logbook.stream)

if verbosity > 0:
print("Final population hypervolume is %f" % hypervolume(pop, [1000.0, 50.0]))

archive = tools.ParetoFront()
archive.update(pop)

return archive, logbook
Loading

0 comments on commit 160b39a

Please sign in to comment.