Skip to content
This repository has been archived by the owner on Apr 13, 2024. It is now read-only.

Commit

Permalink
Merge pull request #56 from PasaOpasen/7v
Browse files Browse the repository at this point in the history
v6.8.1
  • Loading branch information
PasaOpasen authored Apr 24, 2022
2 parents 0880b95 + 6aae10b commit 580568e
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 13 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ version](https://badge.fury.io/py/geneticalgorithm2.svg)](https://pypi.org/proje
- [Installation](#installation)
- [Updates information](#updates-information)
- [**Future**](#future)
- [6.8.1 patch](#681-patch)
- [6.8.0 minor update](#680-minor-update)
- [6.7.7 refactor](#677-refactor)
- [6.7.6 bug fix](#676-bug-fix)
Expand Down Expand Up @@ -133,6 +134,12 @@ pip3 install geneticalgorithm2
- `function_timeout` and `function` will be moved to `run()` method
- new stop criteria callbacks (min std, max functions evaluations)

## 6.8.1 patch

- printing progress bar to `'stderr'` or `'stdout'` or `None` (disable) by choice (`progress_bar_stream` argument of `run()`), deprecated `disable_progress_bar`
- little speed up
- new `geneticalgorithm2.vectorized_set_function` set function, which can be faster for big populations

## 6.8.0 minor update

- remove `crossover_probability` model parameter because of it has no sense to exist (and 1.0 value is better than others, take a look at [results](/tests/output/sense_of_crossover_prob__no_sense.png)). This parameter came from `geneticalgorithm` old package and did`t change before.
Expand Down Expand Up @@ -344,7 +351,7 @@ model = ga(function, dimension = 3,
# all of this parameters are default
result = model.run(
no_plot = False,
disable_progress_bar = False,
progress_bar_stream = 'stdout',
disable_printing = False,

set_function = None,
Expand Down Expand Up @@ -605,7 +612,7 @@ The main method if **run()**. It has parameters:

* **no_plot** (`bool`) - do not plot results using matplotlib by default

* **disable_progress_bar** (`bool`) - do not show progress bar (also it can be faster by 10-20 seconds)
* **progress_bar_stream** (`Optional[str]`) - `'stdout'` to print progress bar to `stdout`, `'stderr'` for `stderr`, `None` to disable progress bar (also it can be faster by 10-20 seconds)

* **disable_printing** (`bool`) - don't print any text (except progress bar)

Expand Down
46 changes: 38 additions & 8 deletions geneticalgorithm2/geneticalgorithm2.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def __init__(
self.checked_reports: List[Tuple[str, Callable[[np.ndarray], None]]] = None

self.population_size: int = None
self.progress_stream = None


# input algorithm's parameters
Expand Down Expand Up @@ -353,8 +354,8 @@ def __progress(self, count: int, total: int, status: str = ''):
percents = round(100.0 * part, 1)
bar = '|' * filled_len + '_' * (geneticalgorithm2.PROGRESS_BAR_LEN - filled_len)

sys.stdout.write('\r%s %s%s %s' % (bar, percents, '%', status))
sys.stdout.flush()
self.progress_stream.write('\r%s %s%s %s' % (bar, percents, '%', status))
self.progress_stream.flush()

def __str__(self):
return f"Genetic algorithm object with parameters {self.param}"
Expand Down Expand Up @@ -400,8 +401,11 @@ def _set_mutation_indexes(self, mutation_indexes: Optional[Sequence[int]]):
def run(
self,
no_plot: bool = False,
disable_progress_bar: bool = False,
disable_printing: bool = False,
progress_bar_stream: Optional[str] = 'stdout',

# deprecated
disable_progress_bar: bool = False,

set_function: Optional[Callable[[np.ndarray], np.ndarray]] = None,
apply_function_to_parents: bool = False,
Expand Down Expand Up @@ -431,7 +435,7 @@ def run(
"""
@param no_plot <boolean> - do not plot results using matplotlib by default
@param disable_progress_bar <boolean> - do not show progress bar
@param progress_bar_stream -- 'stdout', 'stderr' or None to disable progress bar
@param set_function : 2D-array -> 1D-array function, which applyes to matrix of population (size (samples, dimention))
to estimate their values
Expand Down Expand Up @@ -468,6 +472,12 @@ def run(
@param seed (None/int) - random seed (None if doesn't matter)
"""

if disable_progress_bar:
warnings.warn(
f"disable_progress_bar is deprecated and will be removed in version 7, use probress_bar_stream=None to disable progress bar"
)
progress_bar_stream = None

start_generation = Generation.from_object(self.dim, start_generation)


Expand All @@ -485,8 +495,17 @@ def run(
# randomstate = np.random.default_rng(random.randint(0, 1000) if seed is None else seed)
# self.randomstate = randomstate

show_progress = (lambda t, t2, s: self.__progress(t, t2, status=s)) if not disable_progress_bar else (lambda t, t2, s: None)

show_progress = (lambda t, t2, s: self.__progress(t, t2, status=s)) if progress_bar_stream is not None else (lambda t, t2, s: None)
if progress_bar_stream is not None:
if progress_bar_stream == 'stdout':
self.progress_stream = sys.stdout
elif progress_bar_stream == 'stderr':
self.progress_stream = sys.stderr
else:
raise Exception(
f"wrong value {progress_bar_stream} of progress_bar_stream, must be 'stdout'/'stderr'/None"
)

stop_by_val = (lambda best_f: False) if stop_when_reached is None else (lambda best_f: best_f <= stop_when_reached)

t = 0
Expand Down Expand Up @@ -829,15 +848,16 @@ def revolution(pop: np.ndarray, scores: np.ndarray, stagnation_count: int) -> Tu
parents_slice = slice(None, self.parents_count)
pop[parents_slice] = par[parents_slice]
scores[parents_slice] = par_scores[parents_slice]


DO_MUTATION = self.needs_mutation
for k in range(self.parents_count, self.population_size, 2):
r1, r2 = get_parents_inds()
pvar1 = par[r1]
pvar2 = par[r2]

ch1, ch2 = self.crossover(pvar1, pvar2)

if self.needs_mutation:
if DO_MUTATION:
ch1 = self.mut(ch1)
ch2 = self.mut_middle(ch2, pvar1, pvar2)

Expand Down Expand Up @@ -984,6 +1004,16 @@ def func(matrix: np.ndarray):
return np.array([function_for_set(matrix[i]) for i in range(matrix.shape[0])])
return func

@staticmethod
def vectorized_set_function(function_for_set: Callable[[np.ndarray], float]):
"""
works like default, but faster for big populations and slower for little
function_for_set just applyes to each row of population
"""

func = np.vectorize(function_for_set, signature='(n)->()')
return func

@staticmethod
def set_function_multiprocess(function_for_set: Callable[[np.ndarray], float], n_jobs: int = -1):
"""
Expand Down
5 changes: 4 additions & 1 deletion geneticalgorithm2/plotting_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def plot_several_lines(
save_as: Optional[str] = None,
dpi: int = 200
):
# import matplotlib
# matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator

Expand Down Expand Up @@ -50,7 +52,8 @@ def plot_pop_scores(scores, title: str = 'Population scores', save_as: Optional[
"""
plots scores (numeric values) as sorted bars
"""

# import matplotlib
# matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import Normalize
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setuptools.setup(
name="geneticalgorithm2",
version="6.8.0",
version="6.8.1",
author="Demetry Pascal",
author_email="qtckpuhdsa@gmail.com",
maintainer='Demetry Pascal',
Expand Down
2 changes: 1 addition & 1 deletion tests/disable_messages_and_printing.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def f(X):

result = model.run(
no_plot = True,
disable_progress_bar=True,
progress_bar_stream=None,
disable_printing=True
)

Expand Down
31 changes: 31 additions & 0 deletions tests/progress_bar_streams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

import sys
sys.path.append('..')

import numpy as np
from geneticalgorithm2 import geneticalgorithm2 as ga

def f(X):
return np.sum(X)

varbound = (
(0.5, 1.5),
(1, 100),
(-100, 1)
)

vartype = ('real', 'real', 'int')

model = ga(
function=f, dimension=len(vartype),
variable_type=vartype,
variable_boundaries=varbound
)

# old!!
model.run(disable_progress_bar=True)


model.run(progress_bar_stream=None)
model.run(progress_bar_stream='stdout')
model.run(progress_bar_stream='stderr')
82 changes: 82 additions & 0 deletions tests/set_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@

import math
import numpy as np
from geneticalgorithm2 import geneticalgorithm2 as ga


def f_slow(X):
"""
slow function
"""
a = X[0]
b = X[1]
c = X[2]
s = 0
for i in range(10000):
s += math.sin(a * i) + math.sin(b * i) + math.cos(c * i)

return s

rg = np.arange(10000)
def f_fast(X):
"""
fast function
"""
a, b, c = X
return (np.sin(rg*a) + np.sin(rg*b) + np.cos(rg * c)).sum()


algorithm_param = {'max_num_iteration': 50,
'population_size': 100,
'mutation_probability': 0.1,
'elit_ratio': 0.01,
'parents_portion': 0.3,
'crossover_type': 'uniform',
'mutation_type': 'uniform_by_center',
'selection_type': 'roulette',
'max_iteration_without_improv': None}

varbound = [(-10, 10)] * 3

model = ga(function=f_slow, dimension=3,
variable_type='real',
variable_boundaries=varbound,
algorithm_parameters=algorithm_param)

######## compare parallel and normal with slow function

%time model.run()
# Wall time: 34.7s

%time model.run(set_function=ga.set_function_multiprocess(f_slow, n_jobs=3))
# Wall time: 23 s


######## compare default and vectorized on fast func and small pop

model = ga(function=f_fast, dimension=3,
variable_type='real',
variable_boundaries=varbound,
algorithm_parameters=algorithm_param)

%timeit model.run(set_function=ga.default_set_function(f_fast), no_plot=True, progress_bar_stream=None, disable_printing=True)
# 1.41 s ± 4.79 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit model.run(set_function=ga.vectorized_set_function(f_fast), no_plot=True, progress_bar_stream=None, disable_printing=True)
# 1.42 s ± 10.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

######## compare default and vectorized on fast func and big pop
algorithm_param['population_size'] = 1500
algorithm_param['max_num_iteration'] = 15
model = ga(function=f_fast, dimension=3,
variable_type='real',
variable_boundaries=varbound,
algorithm_parameters=algorithm_param)

%timeit model.run(set_function=ga.default_set_function(f_fast), no_plot=True, progress_bar_stream=None, disable_printing=True)
# 6.63 s ± 229 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit model.run(set_function=ga.vectorized_set_function(f_fast), no_plot=True, progress_bar_stream=None, disable_printing=True)
# 6.47 s ± 87.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


0 comments on commit 580568e

Please sign in to comment.