Skip to content

Commit

Permalink
Merge branch 'best_per_generation'
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeaw committed Jun 22, 2020
2 parents b91a523 + c2224f9 commit cb16b4b
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 51 deletions.
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Change Log
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - yyyy-mm-dd

N/A

### Added

### Changed

### Fixed

## [0.6.0] - 2020-06-21

### Added
- core GA now returns an array with fitness value of the fittest individual from each generation which can be accessed from the GAO property `GAO.best_fitness_per_generation`.

### Fixed
- Bug fix in core GA for sorting the population before selection and mating.

## [0.5.0] - 2020-06-20

### Added
- Optional progress bar to monitor passage of generations during GAO run that is only displayed if [tqdm](https://github.com/tqdm/tqdm) is installed.
- Optional [multiprocessing](https://docs.python.org/2/library/multiprocessing.html) based parallelism when evaluating the fitness function over the population during a GAO run.

27 changes: 19 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

![Python version badge](https://img.shields.io/badge/python-3.6,3.7-blue.svg)
[![license](https://img.shields.io/github/license/blakeaw/GAlibrate.svg)](LICENSE)
![version](https://img.shields.io/badge/version-0.5.0-orange.svg)
[![release](https://img.shields.io/github/release-pre/blakeaw/GAlibrate.svg)](https://github.com/blakeaw/GAlibrate/releases/tag/v0.5.0)
![version](https://img.shields.io/badge/version-0.6.0-orange.svg)
[![release](https://img.shields.io/github/release-pre/blakeaw/GAlibrate.svg)](https://github.com/blakeaw/GAlibrate/releases/tag/v0.6.0)
[![anaconda cloud](https://anaconda.org/blakeaw/galibrate/badges/version.svg)](https://anaconda.org/blakeaw/galibrate)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/6cdd91c06b11458384becb85db9adb15)](https://www.codacy.com/app/blakeaw1102/GAlibrate?utm_source=github.com&utm_medium=referral&utm_content=blakeaw/GAlibrate&utm_campaign=Badge_Grade)
[![DOI](https://zenodo.org/badge/197295657.svg)](https://zenodo.org/badge/latestdoi/197295657)
Expand All @@ -17,24 +17,29 @@
Although **GAlibrate** provides a general framework for running continuous
genetic algorithm-based optimizations, it was created with systems biology models in mind. It therefore supplies additional tools for working with biological models in the [PySB](http://pysb.org/) format.

### What's new in version 0.5.0
### What's new in

#### version 0.6.0
* core GA now returns an array with fitness value of the fittest individual from each generation which can be accessed from the GAO property `GAO.best_fitness_per_generation`.
* Bug fix in core GA for sorting the population before selection and mating.

#### version 0.5.0
* Optional progress bar to monitor passage of generations during GAO run that is only displayed if [tqdm](https://github.com/tqdm/tqdm) is installed
* Optional [multiprocessing](https://docs.python.org/2/library/multiprocessing.html) based parallelism when evaluating the fitness function over the population during a GAO run.


## Table of Contents

1. [Install](#install)
1. [pip install](#pip-install)
2. [conda install](#conda-install)
3. [Recomended additional software](#recomended-additional-software)
2. [License](#license)
3. [Documentation and Usage](#documentation-and-usage)
3. [Change Log](#change-log)
4. [Documentation and Usage](#documentation-and-usage)
1. [Quick Overview](#quick-overview)
2. [Examples](#examples)
4. [Contact](#contact)
5. [Citing](#citing)
5. [Contact](#contact)
6. [Citing](#citing)

------

Expand All @@ -53,7 +58,7 @@ Note that `galibrate` has the following core dependencies:
### pip install
You can install the latest release of the `galibrate` package using `pip` sourced from the GitHub repo:
```
pip install -e git+https://github.com/blakeaw/GAlibrate@v0.5.0#egg=galibrate
pip install -e git+https://github.com/blakeaw/GAlibrate@v0.6.0#egg=galibrate
```
However, this will not automatically install the core dependencies. You will have to do that separately:
```
Expand Down Expand Up @@ -97,6 +102,12 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file

------

# Change Log

See: [CHANGELOG](CHANGELOG.md)

------

# Documentation and Usage

### Quick Overview
Expand Down
16 changes: 15 additions & 1 deletion galibrate/gao.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ def __init__(self, sampled_parameters, fitness_function, population_size,
if population_size < 4:
population_size = 4
self.population_size = int(population_size/4.) * 4
if self.population_size != population_size:
warn_string = "--Population size was adjusted from {} to {} to make it a multiple of four--".format(population_size, self.population_size)
warnings.warn(warn_string)
self.generations = generations
self.mutation_rate = mutation_rate
#self.survival_rate = survival_rate
Expand All @@ -66,6 +69,7 @@ def __init__(self, sampled_parameters, fitness_function, population_size,
self._last_generation = None
self._fittest_chromosome = None
self._fittest_fitness = None
self._best_fitness_per_generation = None

return

Expand All @@ -79,7 +83,7 @@ def run(self, verbose=False, nprocs=1):
"""
sp_locs = np.array([sampled_parameter.loc for sampled_parameter in self.sampled_parameters])
sp_widths = np.array([sampled_parameter.width for sampled_parameter in self.sampled_parameters])
last_gen_chromosomes = run_gao.run_gao(self.population_size, self._n_sp,
last_gen_chromosomes, best_fitness_per_generation = run_gao.run_gao(self.population_size, self._n_sp,
sp_locs, sp_widths,
self.generations, self.mutation_rate,
self.fitness_function, nprocs)
Expand All @@ -93,4 +97,14 @@ def run(self, verbose=False, nprocs=1):
fittest_fitness = fitnesses[fittest_idx]
self._fittest_chromosome = fittest_chromosome
self._fittest_fitness = fittest_fitness
best_fitness_per_generation[-1] = fittest_fitness
self._best_fitness_per_generation = best_fitness_per_generation
return fittest_chromosome, fittest_fitness

@property
def best(self):
return self._fittest_chromosome, self._fittest_fitness

@property
def best_fitness_per_generation(self):
return self._best_fitness_per_generation
26 changes: 6 additions & 20 deletions galibrate/run_gao_cython.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def run_gao(int pop_size, int n_sp, np.ndarray[np.double_t, ndim=1] locs,
# Initialize
cdef np.ndarray[np.double_t, ndim=2, mode='c'] chromosomes = random_population(pop_size, n_sp, locs, widths)
new_chromosome = np.zeros([pop_size, n_sp], dtype=np.double)
best_fitness_per_generation = np.zeros(n_gen+1, dtype=np.double)
if nprocs > 1:
def evaluate_fitnesses(fitness_func, chromosomes, pop_size, i_n_new, fitnesses, nprocs):
new_fitnesses = par_fitness_eval(fitness_func, chromosomes, i_n_new, nprocs)
Expand All @@ -47,40 +48,25 @@ def run_gao(int pop_size, int n_sp, np.ndarray[np.double_t, ndim=1] locs,

fitnesses_idxs = np.zeros([pop_size, 2], dtype=np.double)
_fill_fitness_idxs(pop_size, fitnesses, fitnesses_idxs)
#for i_mp in range(pop_size):
# fitnesses_idxs[i_mp][0] = fitnesses[i_mp]
# fitnesses_idxs[i_mp][1] = i_mp

# Selection
fitnesses_idxs_sort = np.sort(fitnesses_idxs, axis=0)
ind = np.argsort(fitnesses_idxs[:,0])
fitnesses_idxs_sort = fitnesses_idxs[ind]
best_fitness_per_generation[i_gen] = fitnesses_idxs_sort[-1,0]
survivors = fitnesses_idxs_sort[pop_size/2:]
# Move over the survivors
_move_over_survivors(pop_size, survivors, chromosomes, new_chromosome)
#for i_mp in range(pop_size/2):
# new_chromosome[i_mp] = chromosomes[int(survivors[i_mp][1])][:]
mating_pairs = choose_mating_pairs(survivors, pop_size)
# Generate children
_generate_children(pop_size, n_sp, i_n_new, mating_pairs, chromosomes, new_chromosome)
# for i_mp in range(pop_size/4):
# i_mate1_idx = mating_pairs[i_mp][0]
# i_mate2_idx = mating_pairs[i_mp][1]
# chromosome1 = chromosomes[i_mate1_idx,:]
# chromosome2 = chromosomes[i_mate2_idx,:]
# # Crossover and update the chromosomes
# children = crossover(chromosome1, chromosome2, n_sp)
# child1 = children[0,:]
# child2 = children[1, :]
# new_chromosome[i_n_new] = child1
# i_n_new = i_n_new + 1
# new_chromosome[i_n_new] = child2
# i_n_new = i_n_new + 1
# Replace the old population with the new one
#chromosomes = new_chromosome.copy()
double_deepcopy_2d(chromosomes, new_chromosome, pop_size, n_sp)
# Mutation
if i_gen < (n_gen-1):
mutation(chromosomes, locs, widths, pop_size, n_sp, mutation_rate)
_copy_survivor_fitnesses(pop_size, survivors, fitnesses)
return chromosomes
return chromosomes, best_fitness_per_generation

@cython.boundscheck(False)
@cython.wraparound(False)
Expand Down
26 changes: 7 additions & 19 deletions galibrate/run_gao_numba.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def run_gao(pop_size, n_sp, locs, widths, n_gen,
# Initialize
chromosomes = random_population(pop_size, n_sp, locs, widths)
new_chromosome = np.zeros((pop_size, n_sp))
best_fitness_per_generation = np.zeros(n_gen+1)
if nprocs > 1:
def evaluate_fitnesses(fitness_func, chromosomes, pop_size, i_n_new, fitnesses, nprocs):
new_fitnesses = par_fitness_eval(fitness_func, chromosomes, i_n_new, nprocs)
Expand All @@ -36,42 +37,30 @@ def evaluate_fitnesses(fitness_func, chromosomes, pop_size, i_n_new, fitnesses,
fitnesses = np.zeros(pop_size)
fitnesses = evaluate_fitnesses(fitness_func, chromosomes, pop_size, 0, fitnesses, nprocs)
else:
fitnesses = evaluate_fitnesses(fitness_func, chromosomes, pop_size, i_n_new, fitnesses, nprocs)
fitnesses = evaluate_fitnesses(fitness_func, chromosomes, pop_size, 0, fitnesses, nprocs)


fitnesses_idxs = np.zeros((pop_size, 2), dtype=np.double)
fitnesses_idxs = _fill_fitness_idxs(pop_size, fitnesses, fitnesses_idxs)

# Selection
fitnesses_idxs_sort = np.sort(fitnesses_idxs, axis=0)
ind = np.argsort(fitnesses_idxs[:,0])
fitnesses_idxs_sort = fitnesses_idxs[ind]
best_fitness_per_generation[i_gen] = fitnesses_idxs_sort[-1,0]
survivors = fitnesses_idxs_sort[int(pop_size/2):]
# Move over the survivors
new_chromosome = _move_over_survivors(pop_size, survivors, chromosomes, new_chromosome)
#for i_mp in range(int(pop_size/2)):
# new_chromosome[i_mp] = chromosomes[int(survivors[i_mp][1])][:]
# Choose the mating pairs
mating_pairs = choose_mating_pairs(survivors, pop_size)
# Generate children
new_chromosome = _generate_children(pop_size, n_sp, i_n_new, mating_pairs, chromosomes, new_chromosome)
# for i_mp in range(int(pop_size/4)):
# i_mate1_idx = mating_pairs[i_mp][0]
# i_mate2_idx = mating_pairs[i_mp][1]
# chromosome1 = chromosomes[i_mate1_idx,:]
# chromosome2 = chromosomes[i_mate2_idx,:]
# # Crossover and update the chromosomes
# children = crossover(chromosome1, chromosome2, n_sp)
# child1 = children[0,:]
# child2 = children[1, :]
# new_chromosome[i_n_new] = child1
# i_n_new = i_n_new + 1
# new_chromosome[i_n_new] = child2
# i_n_new = i_n_new + 1
# Replace the old population with the new one
chromosomes = new_chromosome.copy()
# Mutation
if i_gen < (n_gen-1):
mutation(chromosomes, locs, widths, pop_size, n_sp, mutation_rate)
fitnesses = _copy_survivor_fitnesses(pop_size, survivors, fitnesses)
return chromosomes
return chromosomes, best_fitness_per_generation

@numba.njit(cache=True)
def _fill_fitness_idxs(pop_size, fitnesses, fitnesses_idxs):
Expand Down Expand Up @@ -152,7 +141,6 @@ def crossover(c1, c2, n_sp):

crossover_point = int(n_sp * np.random.random())
crossover_beta = np.random.random()
#cdef np.ndarray[np.double_t, ndim=2] children = np.zeros([2, n_sp], dtype=np.double)
children = np.zeros((2, n_sp), dtype=np.double)
x1 = c1[crossover_point]
x2 = c2[crossover_point]
Expand Down
7 changes: 5 additions & 2 deletions galibrate/run_gao_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def run_gao(pop_size, n_sp, locs, widths, n_gen,
# Initialize
chromosomes = random_population(pop_size, n_sp, locs, widths)
new_chromosome = np.zeros([pop_size, n_sp], dtype=np.double)
best_fitness_per_generation = np.zeros(n_gen+1)
if nprocs > 1:
def evaluate_fitnesses(fitness_func, chromosomes, nprocs):
return par_fitness_eval(fitness_func, chromosomes, 0, nprocs)
Expand All @@ -33,7 +34,9 @@ def evaluate_fitnesses(fitness_func, chromosomes, nprocs):
fitnesses_idxs[i_mp][0] = fitnesses[i_mp]
fitnesses_idxs[i_mp][1] = i_mp
# Selection
fitnesses_idxs_sort = np.sort(fitnesses_idxs, axis=0)
ind = np.argsort(fitnesses_idxs[:,0])
fitnesses_idxs_sort = fitnesses_idxs[ind]
best_fitness_per_generation[i_gen] = fitnesses_idxs_sort[-1,0]
survivors = fitnesses_idxs_sort[int(pop_size/2):]
# Move over the survivors
for i_mp in range(int(pop_size/2)):
Expand All @@ -59,7 +62,7 @@ def evaluate_fitnesses(fitness_func, chromosomes, nprocs):
if i_gen < (n_gen-1):
mutation(chromosomes, locs, widths, pop_size, n_sp, mutation_rate)

return chromosomes
return chromosomes, best_fitness_per_generation

def random_population(pop_size, n_sp,
locs, widths):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from distutils.core import setup

setup(name='galibrate',
version='0.5.0',
version='0.6.0',
description='Python toolkit for continuous Genetic Algorithm optimization.',
author='Blake A. Wilson',
author_email='[email protected]',
Expand Down

0 comments on commit cb16b4b

Please sign in to comment.