Skip to content

Commit

Permalink
Make MPIUtil a singleton & remove instantiation in module
Browse files Browse the repository at this point in the history
  • Loading branch information
PytLab committed Oct 25, 2018
1 parent 57a6b69 commit 6fcc7d5
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ A **G**\ enetic **A**\ lgorithm **F**\ ramework in py\ **T**\ hon
:target: https://www.python.org/downloads/release/python-351/
:alt: platform

.. image:: https://img.shields.io/badge/pypi-v0.5.4-blue.svg
.. image:: https://img.shields.io/badge/pypi-v0.6.0-blue.svg
:target: https://pypi.python.org/pypi/gaft/
:alt: versions

Expand Down
2 changes: 1 addition & 1 deletion gaft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from .engine import GAEngine

__version__ = '0.5.4'
__version__ = '0.6.0'
__author__ = 'ShaoZhengjiang <[email protected]>'

# Set root logger.
Expand Down
4 changes: 3 additions & 1 deletion gaft/components/binary_individual.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import logging

from .individual import IndividualBase
from ..mpiutil import mpi
from ..mpiutil import MPIUtil

mpi = MPIUtil()


class BinaryIndividual(IndividualBase):
Expand Down
67 changes: 35 additions & 32 deletions gaft/engine.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

''' Genetic Algorithm engine definition '''
''' Genetic Algorithm engine definition
'''

import logging
import math
Expand All @@ -15,8 +16,9 @@
from .components import IndividualBase, Population
from .plugin_interfaces.operators import Selection, Crossover, Mutation
from .plugin_interfaces.analysis import OnTheFlyAnalysis
from .mpiutil import mpi
from .mpiutil import MPIUtil

mpi = MPIUtil()

def do_profile(filename, sortby='tottime'):
''' Constructor for function profiling decorator.
Expand Down Expand Up @@ -84,7 +86,29 @@ def __set__(self, engine, value):


class GAEngine(object):
''' Class for representing a Genetic Algorithm engine.
''' Class for representing a Genetic Algorithm engine. The class is the
central object in GAFT framework for running a genetic algorithm optimization.
Once the population with individuals, a set of genetic operators and fitness
function are setup, the engine object unites these informations and provide
means for running a genetic algorthm optimization.
:param population: The Population to be reproduced in evolution iteration.
:type population: :obj:`gaft.components.Population`
:param selection: The Selection to be used for individual seleciton.
:type selection: :obj:`gaft.plugin_interfaces.operators.Selection`
:param crossover: The Crossover to be used for individual crossover.
:type crossover: :obj:`gaft.plugin_interfaces.operators.Crossover`
:param mutation: The Mutation to be used for individual mutation.
:type mutation: :obj:`gaft.plugin_interfaces.operators.Mutation`
:param fitness: The fitness calculation function for an individual in population.
:type fitness: function
:param analysis: All analysis class for on-the-fly analysis.
:type analysis: :obj:`OnTheFlyAnalysis` list
'''
# Statistical attributes for population.
fmax, fmin, fmean = StatVar('fmax'), StatVar('fmin'), StatVar('fmean')
Expand All @@ -94,30 +118,6 @@ class GAEngine(object):

def __init__(self, population, selection, crossover, mutation,
fitness=None, analysis=None):
''' Genetic algorithm engine. The class is the central object in GAFT framework
for running a genetic algorithm optimization. Once the population with
individuals, a set of genetic operators and fitness function are setup,
the engine object unites these informations and provide means for running
a genetic algorthm optimization.
:param population: The Population to be reproduced in evolution iteration.
:type population: :obj:`gaft.components.Population`
:param selection: The Selection to be used for individual seleciton.
:type selection: :obj:`gaft.plugin_interfaces.operators.Selection`
:param crossover: The Crossover to be used for individual crossover.
:type crossover: :obj:`gaft.plugin_interfaces.operators.Crossover`
:param mutation: The Mutation to be used for individual mutation.
:type mutation: :obj:`gaft.plugin_interfaces.operators.Mutation`
:param fitness: The fitness calculation function for an individual in population.
:type fitness: function
:param analysis: All analysis class for on-the-fly analysis.
:type analysis: :obj:`OnTheFlyAnalysis` list
'''
# Set logger.
logger_name = 'gaft.{}'.format(self.__class__.__name__)
self.logger = logging.getLogger(logger_name)
Expand Down Expand Up @@ -303,10 +303,12 @@ def linear_scaling(self, target='max', ksi=0.5):
:param ksi: Selective pressure adjustment value.
:type ksi: float
Note::
.. Note::
Linear Scaling:
1. arg max f(x), then f' = f - min{f(x)} + ksi;
2. arg min f(x), then f' = max{f(x)} - f(x) + ksi;
1. :math:`arg \max f(x)`, then the scaled fitness would be :math:`f - \min f(x) + {\\xi}`
2. :math:`arg \min f(x)`, then the scaled fitness would be :math:`\max f(x) - f(x) + {\\xi}`
'''
def _linear_scaling(fn):
# For original fitness calculation.
Expand Down Expand Up @@ -346,9 +348,10 @@ def dynamic_linear_scaling(self, target='max', ksi0=2, r=0.9):
value is 0.9
:type r: float in range [0.9, 0.999]
Note::
.. Note::
Dynamic Linear Scaling:
For maximizaiton, f' = f(x) - min{f(x)} + ksi^k, k is generation number.
For maximizaiton, :math:`f' = f(x) - \min f(x) + {\\xi}^{k}`, :math:`k` is generation number.
'''
def _dynamic_linear_scaling(fn):
# For original fitness calculation.
Expand Down
62 changes: 49 additions & 13 deletions gaft/mpiutil.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Utitlities for parallelizing Genetic Algorithm by using MPI interfaces
A high-level utility class for parallelizing Genetic Algorithm by using MPI interfaces
in distributed MPI environment.
'''

Expand All @@ -16,12 +16,30 @@
MPI_INSTALLED = False


class Singleton(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance


class MPIUtil(object):

__metaclass__ = Singleton

def __init__(self):
''' Wrapper class for higher level of MPI interfaces that will create a
singleton for parallelization.
'''
logger_name = 'gaft.{}'.format(self.__class__.__name__)
self._logger = logging.getLogger(logger_name)

def bcast(self, data):
''' Broadcast data to MPI processes
:param data: Data to be broadcasted
:type data: any Python object
'''
if MPI_INSTALLED:
mpi_comm = MPI.COMM_WORLD
bdata = mpi_comm.bcast(data, root=0)
Expand All @@ -31,12 +49,16 @@ def bcast(self, data):

# Wrapper for common MPI interfaces.
def barrier(self):
''' Block until all processes in the communicator have reached this routine
'''
if MPI_INSTALLED:
mpi_comm = MPI.COMM_WORLD
mpi_comm.barrier()

@property
def rank(self):
''' Get the rank of the calling process in the communicator
'''
if MPI_INSTALLED:
mpi_comm = MPI.COMM_WORLD
return mpi_comm.Get_rank()
Expand All @@ -45,6 +67,8 @@ def rank(self):

@property
def size(self):
''' Get the size of the group associated with a communicator
'''
if MPI_INSTALLED:
mpi_comm = MPI.COMM_WORLD
return mpi_comm.Get_size()
Expand All @@ -53,12 +77,19 @@ def size(self):

@property
def is_master(self):
''' If current process is the master process
'''
return self.rank == 0

# Utility methods.
def split_seq(self, sequence):
'''
Split the sequence according to rank and processor number.
''' Split the sequence according to rank and processor number.
:param sequence: Data sequence to be splitted
:type sequence: any Python object list
:return: Sub data sequence for current process
:rtype: any Python object list
'''
starts = [i for i in range(0, len(sequence), len(sequence)//self.size)]
ends = starts[1: ] + [len(sequence)]
Expand All @@ -67,8 +98,13 @@ def split_seq(self, sequence):
return sequence[start: end]

def split_size(self, size):
'''
Split a size number(int) to sub-size number.
''' Split a size number(int) to sub-size number.
:param size: The size number to be splitted.
:type size: int
:return: Sub-size for current process
:rtype: int
'''
if size < self.size:
warn_msg = ('Splitting size({}) is smaller than process ' +
Expand All @@ -87,8 +123,13 @@ def split_size(self, size):
return splited_sizes[self.rank]

def merge_seq(self, seq):
'''
Gather data in sub-process to root process.
''' Gather data in sub-process to root process.
:param seq: Sub data sequence for current process
:type seq: any Python object list
:return: Merged data sequence from all processes in a communicator
:rtype: any Python object list
'''
if self.size == 1:
return seq
Expand All @@ -98,13 +139,8 @@ def merge_seq(self, seq):
return list(chain(*merged_seq))


mpi = MPIUtil()


def master_only(func):
'''
Decorator to limit a function to be called
only in master process in MPI env.
''' Decorator to limit a function to be called only in master process in MPI env.
'''
@wraps(func)
def _call_in_master_proc(*args, **kwargs):
Expand Down
4 changes: 3 additions & 1 deletion gaft/operators/mutation/flip_bit_mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@

from random import random, uniform

from ...mpiutil import mpi
from ...mpiutil import MPIUtil
from ...plugin_interfaces.operators.mutation import Mutation
from ...components.binary_individual import BinaryIndividual
from ...components.decimal_individual import DecimalIndividual

mpi = MPIUtil()


class FlipBitMutation(Mutation):
def __init__(self, pm):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
:target: https://www.python.org/downloads/release/python-351/
:alt: platform
.. image:: https://img.shields.io/badge/pypi-v0.5.4-blue.svg
.. image:: https://img.shields.io/badge/pypi-v0.6.0-blue.svg
:target: https://pypi.python.org/pypi/gaft/
:alt: versions
Expand Down
4 changes: 3 additions & 1 deletion tests/mpiutil_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

import unittest

from gaft.mpiutil import mpi
from gaft.mpiutil import MPIUtil

mpi = MPIUtil()


class MPIUtilTest(unittest.TestCase):
Expand Down

0 comments on commit 6fcc7d5

Please sign in to comment.