Skip to content

Commit

Permalink
Merge branch 'dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
nmichlo committed Oct 20, 2021
2 parents b0ec0d7 + 0a8709e commit 5d42dee
Show file tree
Hide file tree
Showing 13 changed files with 744 additions and 9 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ Ruck aims to fill the following criteria:
2. Be easily **extensible** and **debuggable**.
3. Performant while maintaining its simplicity.

## Features

Ruck has various features that will be expanded upon in time
- 📦 Modular evolutionary systems inspired by pytorch lightning
+ Helps organise code & arguably looks clean

- ➕ Multi-Objective optimisation support
+ Optionally optimised version of NSGA-II if `numba` is installed, over 65x faster

- 🏎 Optional multithreading support with `ray`, including helper functions

- 🏭 Factory methods for simple evolutionary algorithms

- 🧪 Various helper functions for selection, mutation and mating


## Citing Ruck

Please use the following citation if you use Ruck in your research:
Expand Down
2 changes: 1 addition & 1 deletion examples/multiobjective.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from matplotlib import pyplot as plt

from ruck import *
from ruck.external.deap import select_nsga2
from ruck.functional import select_nsga2


class MultiObjectiveMinimalModule(EaModule):
Expand Down
3 changes: 3 additions & 0 deletions requirements-all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ pip>=21.0
numpy>=1.19
tqdm>=4

# optional for performance improvements
numba>=0.40.0

# requirements needed for examples too
ray>=1.6.0
deap>=1.3
Expand Down
57 changes: 57 additions & 0 deletions ruck/external/_numba.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
# MIT License
#
# Copyright (c) 2021 Nathan Juraj Michlo
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~


# ========================================================================= #
# Timer #
# ========================================================================= #


def optional_njit(*args, cache=True, **kwargs):
"""
Optionally apply the numba JIT to a function if numba is installed.
- Additionally by default sets the cache=True value on
the JIT functions so that startup times are faster!
"""

def _decorator(fn):
# try import numba
try:
import numba
except ImportError:
import warnings
warnings.warn(f'Performance of {fn.__name__} will be slow. Skipping JIT compilation because numba is not installed!')
numba = None
# handle cases
if numba is not None:
fn = numba.njit(*args, cache=cache, **kwargs)(fn)
# done!
return fn
# return decorator
return _decorator


# ========================================================================= #
# lists #
# ========================================================================= #
21 changes: 18 additions & 3 deletions ruck/external/deap.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,44 @@
# SOFTWARE.
# ~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~

import warnings
from typing import Optional
from typing import Tuple
from typing import Sequence

from ruck.functional import check_selection


try:
import deap
except ImportError as e:
import warnings
warnings.warn('failed to import deap, please install it: $ pip install deap')
raise e


# ========================================================================= #
# deap helper #
# ========================================================================= #


# CONFIG: | JIT: | PYTHON:
# (in=0008 out=0004) | [OLD: 0.000176 NEW: 0.000156s SPEEDUP: 1.126655x] | [OLD: 0.000139 NEW: 0.000198s SPEEDUP: 0.699888x]
# (in=0064 out=0032) | [OLD: 0.002818 NEW: 0.000316s SPEEDUP: 8.913371x] | [OLD: 0.002732 NEW: 0.003151s SPEEDUP: 0.867194x]
# (in=0256 out=0128) | [OLD: 0.040459 NEW: 0.001258s SPEEDUP: 32.161621x] | [OLD: 0.038630 NEW: 0.045156s SPEEDUP: 0.855490x]
# (in=1024 out=0512) | [OLD: 0.672029 NEW: 0.010862s SPEEDUP: 61.872225x] | [OLD: 0.644428 NEW: 0.768074s SPEEDUP: 0.839018x]
# (in=4096 out=2048) | [OLD: 10.511867 NEW: 0.158704s SPEEDUP: 66.235660x] | [OLD: 10.326754 NEW: 12.973584s SPEEDUP: 0.795983x]


@check_selection
def select_nsga2(population, num_offspring: int, weights: Optional[Tuple[float, ...]] = None):
def select_nsga2(population, num_offspring: int, weights: Optional[Sequence[float]] = None):
"""
This is hacky... ruck doesn't yet have NSGA2
support, but we will add it in future!
"""
# this function has been deprecated
warnings.warn('`ruck.external.deap.select_nsga2` has been deprecated in favour of `ruck.functional.select_nsga2`. `ruck.external.deap` will be removed in version v0.3.0')
# checks
if num_offspring == 0:
return []
# get a fitness value to perform checks
f = population[0].fitness
# check fitness
Expand Down
20 changes: 17 additions & 3 deletions ruck/functional/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,23 @@
# SOFTWARE.
# ~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~

from ruck.functional._mate import *
from ruck.functional._mutate import *
from ruck.functional._select import *
from ruck.functional._mate import check_mating
from ruck.functional._mate import mate_crossover_1d
from ruck.functional._mate import mate_crossover_nd

from ruck.functional._mutate import check_mutation
from ruck.functional._mutate import mutate_flip_bits
from ruck.functional._mutate import mutate_flip_bit_groups

from ruck.functional._select import check_selection
from ruck.functional._select import select_best
from ruck.functional._select import select_worst
from ruck.functional._select import select_random
from ruck.functional._select import select_tournament

from ruck.functional._select_nsga import select_nsga2
from ruck.functional._select_nsga import argsort_non_dominated
from ruck.functional._select_nsga import compute_crowding_distances

# helper -- should be replaced
from ruck.functional._algorithm import *
2 changes: 1 addition & 1 deletion ruck/functional/_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

from ruck._member import Member
from ruck._member import Population
from ruck.functional import SelectFnHint
from ruck.functional._select import SelectFnHint
from ruck.functional._mate import MateFnHint
from ruck.functional._mutate import MutateFnHint
from ruck.util._iter import chained
Expand Down
Loading

0 comments on commit 5d42dee

Please sign in to comment.