currentSolution (macop.algorithms.Algorithm.Algorithm attribute)

getGlobalEvaluation() (macop.algorithms.Algorithm.Algorithm method)

getGlobalMaxEvaluation() (macop.algorithms.Algorithm.Algorithm method)

increaseEvaluation() (macop.algorithms.Algorithm.Algorithm method)

IteratedLocalSearch (class in macop.algorithms.mono.IteratedLocalSearch)

policy (macop.algorithms.mono.IteratedLocalSearch.IteratedLocalSearch attribute)

run() (macop.algorithms.mono.IteratedLocalSearch.IteratedLocalSearch method)

initalizer (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

evaluator (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

operators (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

policy (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

validator (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

maximise (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

currentSolution (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

bestSolution (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

callbacks (macop.algorithms.mono.LocalSearch.LocalSearch attribute)

index (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

weights (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

initalizer (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

evaluator (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

operators (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

policy (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

validator (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

maximise (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

currentSolution (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

bestSolution (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

callbacks (macop.algorithms.multi.MOSubProblem.MOSubProblem attribute)

algo (macop.callbacks.BasicCheckpoint.BasicCheckpoint attribute)

every (macop.callbacks.BasicCheckpoint.BasicCheckpoint attribute)

filepath (macop.callbacks.BasicCheckpoint.BasicCheckpoint attribute)

algo (macop.callbacks.MultiCheckpoint.MultiCheckpoint attribute)

every (macop.callbacks.MultiCheckpoint.MultiCheckpoint attribute)

filepath (macop.callbacks.MultiCheckpoint.MultiCheckpoint attribute)

algo (macop.callbacks.ParetoCheckpoint.ParetoCheckpoint attribute)

every (macop.callbacks.ParetoCheckpoint.ParetoCheckpoint attribute)

filepath (macop.callbacks.ParetoCheckpoint.ParetoCheckpoint attribute)

load() (macop.callbacks.ParetoCheckpoint.ParetoCheckpoint method)

run() (macop.callbacks.ParetoCheckpoint.ParetoCheckpoint method)

filepath (macop.callbacks.UCBCheckpoint.UCBCheckpoint attribute)

evaluatorExample() (in module macop.evaluators.EvaluatorExample)

RandomSplitCrossover (class in macop.operators.crossovers.RandomSplitCrossover)

SimpleCrossover (class in macop.operators.crossovers.SimpleCrossover)

kind (macop.operators.crossovers.SimpleCrossover.SimpleCrossover attribute)

apply() (macop.operators.crossovers.SimpleCrossover.SimpleCrossover method)

SimpleBinaryMutation (class in macop.operators.mutators.SimpleBinaryMutation)

SimpleMutation (class in macop.operators.mutators.SimpleMutation)

kind (macop.operators.mutators.SimpleMutation.SimpleMutation attribute)

apply() (macop.operators.mutators.SimpleMutation.SimpleMutation method)

operators (macop.operators.policies.RandomPolicy.RandomPolicy attribute)

select() (macop.operators.policies.RandomPolicy.RandomPolicy method)

operators (macop.operators.policies.UCBPolicy.UCBPolicy attribute)

exp_rate (macop.operators.policies.UCBPolicy.UCBPolicy attribute)

rewards (macop.operators.policies.UCBPolicy.UCBPolicy attribute)

occurrences (macop.operators.policies.UCBPolicy.UCBPolicy attribute)

CombinatoryIntegerSolution (class in macop.solutions.CombinatoryIntegerSolution)

data (macop.solutions.IntegerSolution.IntegerSolution attribute)

size (macop.solutions.IntegerSolution.IntegerSolution attribute)

score (macop.solutions.IntegerSolution.IntegerSolution attribute)

random() (macop.solutions.IntegerSolution.IntegerSolution method)

"""Abstract Algorithm class used as basic algorithm implementation with some specific initialization
"""

# main imports
import logging
import pkgutil
import sys
from ..utils.color import macop_text, macop_line, macop_progress

# Generic algorithm class
class Algorithm():
    """Algorithm class used as basic algorithm

    Attributes:
        initalizer: {function} -- basic function strategy to initialize solution
        evaluator: {function} -- basic function in order to obtained fitness (mono or multiple objectives)
        operators: {[Operator]} -- list of operator to use when launching algorithm
        policy: {Policy} -- Policy class implementation strategy to select operators
        validator: {function} -- basic function to check if solution is valid or not under some constraints
        maximise: {bool} -- specify kind of optimization problem 
        currentSolution: {Solution} -- current solution managed for current evaluation
        bestSolution: {Solution} -- best solution found so far during running algorithm
        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
        parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
    """
    def __init__(self,
                 _initalizer,
                 _evaluator,
                 _operators,
                 _policy,
                 _validator,
                 _maximise=True,
                 _parent=None):

        self.initializer = _initalizer
        self.evaluator = _evaluator
        self.operators = _operators
        self.policy = _policy
        self.validator = _validator
        self.callbacks = []
        self.bestSolution = None
        self.currentSolution = None

        # by default
        self.numberOfEvaluations = 0
        self.maxEvaluations = 0

        # other parameters
        self.parent = _parent  # parent algorithm if it's sub algorithm

        #self.maxEvaluations = 0 # by default
        self.maximise = _maximise

        # track reference of algo into operator (keep an eye into best solution)
        for operator in self.operators:
            operator.setAlgo(self)

        # also track reference for policy
        self.policy.setAlgo(self)

    def addCallback(self, _callback):
        """Add new callback to algorithm specifying usefull parameters

        Args:
            _callback: {Callback} -- specific Callback instance
        """
        # specify current main algorithm reference
        _callback.setAlgo(self)

        # set as new
        self.callbacks.append(_callback)

    def resume(self):
        """Resume algorithm using Callback instances
        """

        # load every callback if many things are necessary to do before running algorithm
        for callback in self.callbacks:
            callback.load()

    def initRun(self):
        """
        Initialize the current solution and best solution
        """

        self.currentSolution = self.initializer()

        # evaluate current solution
        self.currentSolution.evaluate(self.evaluator)

        # keep in memory best known solution (current solution)
        if self.bestSolution is None:
            self.bestSolution = self.currentSolution

    def increaseEvaluation(self):
        """
        Increase number of evaluation once a solution is evaluated
        """
        self.numberOfEvaluations += 1

        if self.parent is not None:
            self.parent.numberOfEvaluations += 1

    def getGlobalEvaluation(self):
        """Get the global number of evaluation (if inner algorithm)

        Returns:
            {int} -- current global number of evaluation
        """

            return self.parent.getGlobalEvaluation()

        return self.numberOfEvaluations

    def getGlobalMaxEvaluation(self):
        """Get the global max number of evaluation (if inner algorithm)

        Returns:
            {int} -- current global max number of evaluation
        """

            return self.parent.getGlobalMaxEvaluation()

        return self.maxEvaluations

    def stop(self):
        """
        Global stopping criteria (check for inner algorithm too)
        """
            return self.parent.numberOfEvaluations >= self.parent.maxEvaluations or self.numberOfEvaluations >= self.maxEvaluations

        return self.numberOfEvaluations >= self.maxEvaluations

    def evaluate(self, _solution):
        """
        Evaluate a solution using evaluator passed when intialize algorithm

        Args:
            solution: {Solution} -- solution to evaluate

        Returns: 
            fitness score of solution which is not already evaluated or changed

        Note: 
            if multi-objective problem this method can be updated using array of `evaluator`
        """
        return _solution.evaluate(self.evaluator)

    def update(self, _solution):
        """
        Apply update function to solution using specific `policy`
        Check if solution is valid after modification and returns it
        
        Args:
            solution: {Solution} -- solution to update using current policy

        Returns:
            {Solution} -- updated solution obtained by the selected operator
        """

        # two parameters are sent if specific crossover solution are wished
        sol = self.policy.apply(_solution)

        if (sol.isValid(self.validator)):
            return sol
        else:
            logging.info("-- New solution is not valid %s" % sol)
            return _solution

    def isBetter(self, _solution):
        """
        Check if solution is better than best found

        Args:
            solution: {Solution} -- solution to compare with best one

        Returns:
            {bool} -- `True` if better
        """
        # depending of problem to solve (maximizing or minimizing)
        if self.maximise:
            if _solution.fitness() > self.bestSolution.fitness():
                return True
        else:
            if _solution.fitness() < self.bestSolution.fitness():

        return False

    def run(self, _evaluations):
        """
        Run the specific algorithm following number of evaluations to find optima
        """

        # append number of max evaluation if multiple run called
        self.maxEvaluations += _evaluations

        # check if global evaluation is used or not
        if self.parent is not None and self.getGlobalEvaluation() != 0:

            # init number evaluations of inner algorithm depending of globalEvaluation
            # allows to restart from `checkpoint` last evaluation into inner algorithm
            rest = self.getGlobalEvaluation() % self.maxEvaluations
            self.numberOfEvaluations = rest

        else:
            self.numberOfEvaluations = 0

        logging.info("Run %s with %s evaluations" %
                     (self.__str__(), _evaluations))

    def progress(self):
        """
        Log progress and apply callbacks if necessary
        """
        if len(self.callbacks) > 0:
            for callback in self.callbacks:
                callback.run()

        macop_progress(self.getGlobalEvaluation(),
                       self.getGlobalMaxEvaluation())

        logging.info("-- %s evaluation %s of %s (%s%%) - BEST SCORE %s" %
                     (type(self).__name__, self.numberOfEvaluations,
                      self.maxEvaluations, "{0:.2f}".format(
                          (self.numberOfEvaluations) / self.maxEvaluations *
                          100.), self.bestSolution.fitness()))

    def end(self):
        """Display end message into `run` method
        """
        print(
            macop_text('({}) Found after {} evaluations \n   - {}'.format(
                type(self).__name__, self.numberOfEvaluations,
                self.bestSolution)))
        print(macop_line())

    def information(self):
        logging.info("-- Best %s - SCORE %s" %
                     (self.bestSolution, self.bestSolution.fitness()))

    def __str__(self):
        return "%s using %s" % (type(self).__name__, type(
            self.bestSolution).__name__)

"""Iterated Local Search Algorithm implementation
"""

# module imports
from ..Algorithm import Algorithm
from .LocalSearch import LocalSearch

class IteratedLocalSearch(Algorithm):
    """Iterated Local Search used to avoid local optima and increave EvE (Exploration vs Exploitation) compromise

    """
    def run(self, _evaluations, _ls_evaluations=100):
        """
        Run the iterated local search algorithm using local search (EvE compromise)

        Args:
            _evaluations: {int} -- number of global evaluations for ILS
            _ls_evaluations: {int} -- number of Local search evaluations (default: 100)

        Returns:
            {Solution} -- best solution found
        """

        # by default use of mother method to initialize variables
        super().run(_evaluations)

        # enable resuming for ILS
        self.resume()

        # initialize current solution
        self.initRun()

        # passing global evaluation param from ILS
        ls = LocalSearch(self.initializer,
                         self.evaluator,
                         self.operators,
                         self.policy,
                         self.validator,
                         self.maximise,
                         _parent=self)

        # add same callbacks
            ls.addCallback(callback)

        # local search algorithm implementation
        while not self.stop():

            # create and search solution from local search
            newSolution = ls.run(_ls_evaluations)

            # if better solution than currently, replace it
            if self.isBetter(newSolution):
                self.bestSolution = newSolution

            # number of evaluatins increased from LocalSearch
            # increase number of evaluations and progress are then not necessary there
            #self.increaseEvaluation()
            #self.progress()

            self.information()

        logging.info("End of %s, best solution found %s" %
                     (type(self).__name__, self.bestSolution))

        self.end()
        return self.bestSolution

"""Local Search algorithm
"""

class LocalSearch(Algorithm):
    """Local Search used as exploitation optimization algorithm

    """
        """
        Run the local search algorithm

        Args:
            _evaluations: {int} -- number of Local search evaluations
            
        Returns:
        """

        if self.parent:
            self.bestSolution = self.parent.bestSolution

        solutionSize = self.currentSolution.size

            for _ in range(solutionSize):

                # update current solution using policy
                newSolution = self.update(self.currentSolution)

                # if better solution than currently, replace it
                if self.isBetter(newSolution):
                    self.bestSolution = newSolution

                # increase number of evaluations
                self.increaseEvaluation()

                self.progress()
                logging.info("---- Current %s - SCORE %s" %
                             (newSolution, newSolution.fitness()))

                # stop algorithm if necessary
                if self.stop():
                    break

"""Multi-Ojective Evolutionary Algorithm with Scalar Decomposition algorithm
"""

import math
import numpy as np
import sys
from ...utils.color import macop_text, macop_line, macop_progress
from ...utils.modules import load_class

from .MOSubProblem import MOSubProblem

def moEvaluator(_solution, _evaluator, _weights):

    scores = [eval(_solution) for eval in _evaluator]

    # associate objectives scores to solution
    _solution.scores = scores

    return sum([scores[i] for i, w in enumerate(_weights)])

class MOEAD(Algorithm):
    """Multi-Ojective Evolutionary Algorithm with Scalar Decomposition

        mu: {int} -- number of sub problems
        T: {[float]} -- number of neightbors for each sub problem
        nObjectives: {int} -- number of objectives (based of number evaluator)
        evaluator: {[function]} -- list of basic function in order to obtained fitness (multiple objectives)
        population: [{Solution}] -- population of solution, one for each sub problem
        pfPop: [{Solution}] -- pareto front population
        weights: [[{float}]] -- random weights used for custom mu sub problems
    """
                 _mu,
                 _T,
                 _policy,

        # redefinition of constructor to well use `initRun` method

        self.nObjectives = len(_evaluator)

        # by default track reference for policy

        if _mu < _T:
            raise ValueError('`mu` cannot be less than `T`')

        self.mu = _mu
        self.T = _T

        # initialize neighbors for each sub problem
        self.setNeighbors()

        weights = []

        if self.nObjectives == 2:

            for i in range(self.mu):
                angle = math.pi / 2 * i / (self.mu - 1)
                weights.append([math.cos(angle), math.sin(angle)])

        elif self.nObjectives >= 3:

            # random weights using uniform
                w_i = np.random.uniform(0, 1, self.nObjectives)
                weights.append(w_i / sum(w_i))
        else:
            raise ValueError('Unvalid number of objectives')

        self.weights = weights

        self.subProblems = []

        for i in range(self.mu):

            # compute weight sum from solution
            sub_evaluator = lambda _solution: moEvaluator(
                _solution, _evaluator, weights[i])

            # intialize each sub problem
            # use copy of list to keep track for each sub problem
            subProblem = MOSubProblem(i, weights[i],
                                      _initalizer, sub_evaluator,
                                      _operators.copy(), _policy, _validator,
                                      _maximise, self)

            self.subProblems.append(subProblem)

        self.population = [None for n in range(self.mu)]
        self.pfPop = []

        # ref point based on number of evaluators
            self.refPoint = [0 for _ in range(self.nObjectives)]
        else:
            self.refPoint = [
                sys.float_info.max for _ in range(self.nObjectives)
            ]

        """
        Method which initialiazes or re-initializes the whole algorithm context specifically for MOEAD
        """
        # initialization is done during run method
        pass

        """

        Args:
            
        Returns:
        """

        # enable callback resume for MOEAD

        # initialize each sub problem if no backup

            if self.subProblems[i].bestSolution is None:
                self.subProblems[i].run(1)
                self.population[i] = self.subProblems[i].bestSolution

        # if no backup for pf population
        if len(self.pfPop) == 0:
                self.pfPop.append(self.subProblems[i].bestSolution)

        # MOEAD algorithm implementation

                # run 1 iteration into sub problem `i`
                spBestSolution = self.subProblems[i].bestSolution

                self.updateRefPoint(spBestSolution)

                # for each neighbor of current sub problem update solution if better
                improvment = False
                for j in self.neighbors[i]:
                    if spBestSolution.fitness(
                    ) > self.subProblems[j].bestSolution.fitness():

                        # create new solution based on current new if better, computes fitness associated to new solution for sub problem
                        class_name = type(spBestSolution).__name__

                        # dynamically load solution class if unknown
                        if class_name not in sys.modules:
                            load_class(class_name, globals())

                        newSolution = getattr(
                            globals()['macop.solutions.' + class_name],
                            class_name)(spBestSolution.data,
                                        len(spBestSolution.data))

                        # evaluate solution for new sub problem and update as best solution
                        self.subProblems[j].evaluate(newSolution)
                        self.subProblems[j].bestSolution = newSolution

                        # update population solution for this sub problem
                        self.population[j] = newSolution

                        improvment = True

                # add new solution if improvment is idenfity
                if improvment:
                    self.pfPop.append(spBestSolution)

                # update pareto front
                self.pfPop = self.paretoFront(self.pfPop)

                # add progress here

                    break

                     (type(self).__name__, self.population))

        return self.pfPop

        """
        """

        logging.info(
            "-- %s evaluation %s of %s (%s%%)" %
            (type(self).__name__, self.numberOfEvaluations,
             self.maxEvaluations, "{0:.2f}".format(
                 (self.numberOfEvaluations) / self.maxEvaluations * 100.)))

    def setNeighbors(self):

        dmin = dmax = 0

        if self.T % 2 == 1:
            dmin = -int(self.T / 2)
            dmax = int(self.T / 2) + 1
        else:
            dmin = -int(self.T / 2) + 1
            dmax = +self.T / 2

        # init neighbord list
        self.neighbors = [[] for n in range(self.mu)]

        for direction in range(0, -dmin):
            for i in range(self.T):
                self.neighbors[direction].append(i)

        for direction in range(-dmin, self.mu - dmax):
            for i in range(direction + dmin, direction + dmax):

        for direction in range(self.mu - dmax, self.mu):
            for i in range(self.mu - self.T, self.mu):

    def updateRefPoint(self, _solution):

            for i in range(len(self.evaluator)):
                if _solution.scores[i] > self.refPoint[i]:
                    self.refPoint[i] = _solution.scores[i]
        else:
                if _solution.scores[i] < self.refPoint[i]:

    def paretoFront(self, _population):

        paFront = []
        indexes = []
        nObjectives = len(self.evaluator)
        nSolutions = len(_population)

        # find dominated solution
        for i in range(nSolutions):
            for j in range(nSolutions):

                if j in indexes:
                    continue

                # check if solutions are the same
                if all([
                        _population[i].data[k] == _population[j].data[k]
                        for k in range(len(_population[i].data))
                ]):
                    continue

                nDominated = 0

                # check number of dominated objectives of current solution by the others solution
                for k in range(len(self.evaluator)):
                    if self.maximise:
                        if _population[i].scores[k] < _population[j].scores[k]:
                            nDominated += 1
                    else:
                        if _population[i].scores[k] > _population[j].scores[k]:

                if nDominated == nObjectives:
                    indexes.append(i)
                    break

        # store the non dominated solution into pareto front
            if i not in indexes:
                paFront.append(_population[i])

        return paFront

        """

        print(
            macop_text('({}) Found after {} evaluations'.format(
                type(self).__name__, self.numberOfEvaluations)))

        for i, solution in enumerate(self.pfPop):
            print('  - [{}] {} : {}'.format(i, solution.scores, solution))

        logging.info("-- Pareto front :")

            logging.info("-- %s] SCORE %s - %s" %
                         (i, solution.scores, solution))

            self.population).__name__)

"""MOEAD sub problem algorithm class
"""

class MOSubProblem(Algorithm):
    """Specific MO sub problem used into MOEAD

        index: {int} -- sub problem index
        weights: {[float]} -- sub problems objectives weights
    """
                 _index,
                 _weights,
                 _policy,

        super().__init__(_initalizer, _evaluator, _operators, _policy,
                         _validator, _maximise, _parent)

        self.index = _index
        self.weights = _weights

        """

        Args:
            _evaluations: {int} -- number of evaluations
            
        Returns:
        """

        # initialize solution if necessary
            self.initRun()

        # new operators list keep track of current sub problem
        for op in self.operators:
            op.setAlgo(self)

        for _ in range(_evaluations):

            # keep reference of sub problem used
            self.policy.setAlgo(self)

            # update solution using policy
            newSolution = self.update(self.bestSolution)

            # increase number of evaluations
            self.increaseEvaluation()

            self.progress()

            # stop algorithm if necessary
            if self.stop():
                break

            logging.info("---- Current %s - SCORE %s" %
                         (newSolution, newSolution.fitness()))

            logging.info("End of %s, best solution found %s" %
                         (type(self).__name__, self.bestSolution))

"""Basic Checkpoint class implementation
"""

import os

from .Callback import Callback
from ..utils.color import macop_text, macop_line

class BasicCheckpoint(Callback):
    """
    BasicCheckpoint is used for loading previous computations and start again after loading checkpoint

        algo: {Algorithm} -- main algorithm instance reference
        every: {int} -- checkpoint frequency used (based on number of evaluations)
        filepath: {str} -- file path where checkpoints will be saved
    """
    def run(self):
        """
        Check if necessary to do backup based on `every` variable
        """
        # get current best solution
        solution = self.algo.bestSolution

        currentEvaluation = self.algo.getGlobalEvaluation()

        # backup if necessary
        if currentEvaluation % self.every == 0:

            logging.info("Checkpoint is done into " + self.filepath)

            solutionData = ""
            solutionSize = len(solution.data)

            for index, val in enumerate(solution.data):
                solutionData += str(val)

                if index < solutionSize - 1:
                    solutionData += ' '

            line = str(currentEvaluation) + ';' + solutionData + ';' + str(
                solution.fitness()) + ';\n'

            # check if file exists
            if not os.path.exists(self.filepath):
                with open(self.filepath, 'w') as f:
                    f.write(line)
            else:
                with open(self.filepath, 'a') as f:

    def load(self):
        """
        Load last backup line of solution and set algorithm state (best solution and evaluations) at this backup
        """
        if os.path.exists(self.filepath):

            logging.info('Load best solution from last checkpoint')
            with open(self.filepath) as f:

                # get last line and read data
                lastline = f.readlines()[-1]
                data = lastline.split(';')

                # get evaluation  information
                globalEvaluation = int(data[0])

                if self.algo.parent is not None:
                    self.algo.parent.numberOfEvaluations = globalEvaluation
                else:
                    self.algo.numberOfEvaluations = globalEvaluation

                # get best solution data information
                solutionData = list(map(int, data[1].split(' ')))

                if self.algo.bestSolution is None:
                    self.algo.bestSolution = self.algo.initializer()

                self.algo.bestSolution.data = np.array(solutionData)
                self.algo.bestSolution.score = float(data[2])

            print(macop_line())
            print(
                macop_text('Checkpoint found from `{}` file.'.format(
                    self.filepath)))

            print(
                macop_text('Restart algorithm from evaluation {}.'.format(
                    self.algo.numberOfEvaluations)))

        else:
            print(
                macop_text(
                    'No backup found... Start running algorithm from evaluation 0.'
                ))
            logging.info(
                "Can't load backup... Backup filepath not valid in Checkpoint")

"""Abstract Checkpoint class
"""
import os
from abc import abstractmethod

class Callback():
    """
    Callback abstract class in order to compute some instruction every evaluation

    """
    def __init__(self, _every, _filepath):

        self.algo = None
        self.every = _every
        self.filepath = _filepath

        # build path if not already exists
        head, _ = os.path.split(self.filepath)

        if not os.path.exists(head):
            os.makedirs(head)

    def setAlgo(self, _algo):
        """Specify the main algorithm instance reference

        Args:
            _algo: {Algorithm} -- main algorithm instance reference
        """
        self.algo = _algo

    @abstractmethod
        """
        """
        pass

        """
        Load last backup line of solution and set algorithm state at this backup
        """
        pass

"""Multi Checkpoint class implementation
"""

import os

class MultiCheckpoint(Callback):
    """
    MultiCheckpoint is used for loading previous computations and start again after loading checkpoint

    """
        """
        """
        # get current population
        population = self.algo.population

            with open(self.filepath, 'w') as f:

                for solution in population:
                    solutionData = ""
                    solutionSize = len(solution.data)

                    for index, val in enumerate(solution.data):
                        solutionData += str(val)

                        if index < solutionSize - 1:
                            solutionData += ' '

                    line = str(currentEvaluation) + ';'

                    for i in range(len(self.algo.evaluator)):
                        line += str(solution.scores[i]) + ';'

                    line += solutionData + ';\n'

        """
        Load backup lines as population and set algorithm state (population and pareto front) at this backup
        """

                # read data for each line
                for i, line in enumerate(f.readlines()):

                    data = line.replace(';\n', '').split(';')

                    # only the first time
                    if i == 0:
                        # get evaluation  information
                        globalEvaluation = int(data[0])

                        if self.algo.parent is not None:
                            self.algo.parent.numberOfEvaluations = globalEvaluation
                        else:
                            self.algo.numberOfEvaluations = globalEvaluation

                    nObjectives = len(self.algo.evaluator)
                    scores = [float(s) for s in data[1:nObjectives + 1]]

                    # get best solution data information
                    solutionData = list(map(int, data[-1].split(' ')))

                    # initialize and fill with data
                    self.algo.population[i] = self.algo.initializer()
                    self.algo.population[i].data = np.array(solutionData)
                    self.algo.population[i].scores = scores

                    self.algo.pfPop.append(self.algo.population[i])

            print(
                macop_text('Load of available population from `{}`'.format(
            print(

        else:
            print(
                ))

"""Pareto front Checkpoint class implementation
"""

import os
import sys

from ..utils.modules import load_class

class ParetoCheckpoint(Callback):
    """
    Pareto checkpoint is used for loading previous computations and start again after loading checkpoint

    """
        """
        """
        pfPop = self.algo.pfPop

                for solution in pfPop:

                    line = ''

        """
        """

                # reinit pf population
                self.algo.pfPop = []

                # retrieve class name from algo
                class_name = type(self.algo.population[0]).__name__

                # dynamically load solution class if unknown
                if class_name not in sys.modules:
                    load_class(class_name, globals())

                for line in f.readlines():

                    scores = [float(s) for s in data[0:nObjectives]]

                    newSolution = getattr(
                        globals()['macop.solutions.' + class_name],
                        class_name)(solutionData, len(solutionData))
                    newSolution.scores = scores

                    self.algo.pfPop.append(newSolution)

            print(
                    'Load of available pareto front backup from `{}`'.format(
                        self.filepath)))

        else:
            print(
                    'No pareto front found... Start running algorithm with new pareto front population.'
                ))
            logging.info("No pareto front backup used...")

"""UCB policy Checkpoint class implementation
"""

import os

class UCBCheckpoint(Callback):
    """
    UCB checkpoint is used for loading previous UCB data and start again after loading checkpoint
    Need to be the same operators used during previous run

    """
        """
        """

            logging.info("UCB Checkpoint is done into " + self.filepath)

                rewardsLine = ''

                for i, r in enumerate(self.algo.policy.rewards):
                    rewardsLine += str(r)

                    if i != len(self.algo.policy.rewards) - 1:
                        rewardsLine += ';'

                f.write(rewardsLine + '\n')

                occurrencesLine = ''

                for i, o in enumerate(self.algo.policy.occurrences):
                    occurrencesLine += str(o)

                    if i != len(self.algo.policy.occurrences) - 1:
                        occurrencesLine += ';'

                f.write(occurrencesLine + '\n')

        """
        Load backup lines as rewards and occurrences for UCB
        """

            logging.info('Load UCB data')

                lines = f.readlines()
                rewardsLine = lines[0].replace('\n', '')
                occurrencesLine = lines[1].replace('\n', '')

                self.algo.policy.rewards = [
                    float(f) for f in rewardsLine.split(';')
                ]
                self.algo.policy.occurrences = [
                    float(f) for f in occurrencesLine.split(';')
                ]

            print(
                    'Load of available UCB policy data from `{}`'.format(

        else:
            print(macop_text('No UCB data found, use default UCB policy'))
            logging.info("No UCB data found...")

"""Python evaluator function example
"""

import random

elements_score = [random.randint(1, 20) for _ in range(30)]

# evaluator example
def evaluatorExample(_solution):
    """
    Evaluator's example of solution to compute fitness

    Args:
        _solution: {Solution} -- solution to evaluate

    Returns:
        {float} -- fitness score of solution
    """
    fitness = 0
    for index, elem in enumerate(_solution.data):
        fitness += elements_score[index] * elem

    return fitness

"""Abstract Operator class
"""
from enum import Enum

# enumeration which stores kind of operator
class KindOperator(Enum):
    """Enum in order to recognize kind of operators
    """
    MUTATOR = 1
    CROSSOVER = 2

class Operator():
    """Abstract Operator class which enables to update solution applying operator (computation)
    """
    def __init__(self):
        pass

    def apply(self, _solution):
        """Apply the current operator transformation

        Args:
            _solution: {Solution} -- Solution instance
        """
        pass

        """Keep into operator reference of the whole algorithm
           The reason is to better manage operator instance

        Args:
            _algo: {Algorithm} -- the algorithm reference runned
        """

"""Abstract Crossover class
"""
from ..Operator import KindOperator, Operator

# main mutation class
class Crossover(Operator):
    """Abstract crossover extend from Operator

        kind: {KindOperator} -- specify the kind of operator
    """
        self.kind = KindOperator.CROSSOVER

    def apply(self, solution):
        raise NotImplementedError

"""Crossover implementation which generated new solution by randomly splitting best solution and current solution
"""
import sys

from .Crossover import Crossover

class RandomSplitCrossover(Crossover):
    """Crossover implementation which generated new solution by randomly splitting best solution and current solution

    """
        """Create new solution based on best solution found and solution passed as parameter

        Args:
            _solution: {Solution} -- the solution to use for generating new solution

        Returns:
            {Solution} -- new generated solution
        """
        size = _solution.size

        # copy data of solution
        firstData = _solution.data.copy()
        # get best solution from current algorithm
        secondData = self.algo.bestSolution.data.copy()

        splitIndex = random.randint(0, len(secondData))

        if random.uniform(0, 1) > 0.5:
            firstData[splitIndex:(size - 1)] = firstData[splitIndex:(size - 1)]
            currentData = firstData
        else:
            secondData[splitIndex:(size - 1)] = firstData[splitIndex:(size -
                                                                      1)]
            currentData = secondData

        # create solution of same kind with new data
        class_name = type(_solution).__name__

        # dynamically load solution class if unknown
        if class_name not in sys.modules:
            load_class(class_name, globals())

        return getattr(globals()['macop.solutions.' + class_name],
                       class_name)(currentData, size)

"""Crossover implementation which generated new solution by splitting at mean size best solution and current solution
"""
import sys

# import all available solutions
# for loader, module_name, is_pkg in pkgutil.walk_packages(
#         path=[
#             str(pathlib.Path(__file__).parent.absolute()) + '/../../solutions'
#         ],
#         prefix='macop.solutions.'):
#     _module = loader.find_module(module_name).load_module(module_name)
#     globals()[module_name] = _module

class SimpleCrossover(Crossover):
    """Crossover implementation which generated new solution by splitting at mean size best solution and current solution

        kind: {Algorithm} -- specify the kind of operator
    """

        Args:

        Returns:
        """

        splitIndex = int(size / 2)

        else:
                                                                      1)]

"""Abstract Mutation class
"""

class Mutation(Operator):
    """Abstract Mutation extend from Operator

    """
        self.kind = KindOperator.MUTATOR

"""Mutation implementation for binary solution, swap bit randomly from solution
"""
import sys

from .Mutation import Mutation

class SimpleBinaryMutation(Mutation):
    """Mutation implementation for binary solution, swap bit randomly from solution

    """
        """Create new solution based on solution passed as parameter

        Args:

        Returns:
        """

        cell = random.randint(0, size - 1)

        currentData = _solution.data.copy()

        # swicth values
        if currentData[cell]:
            currentData[cell] = 0
        else:
            currentData[cell] = 1

"""Mutation implementation for binary solution, swap two bits randomly from solution
"""
import sys

class SimpleMutation(Mutation):
    """Mutation implementation for binary solution, swap two bits randomly from solution

    """

        Args:

        Returns:
        """

        firstCell = 0
        secondCell = 0

        while firstCell == secondCell:
            firstCell = random.randint(0, size - 1)
            secondCell = random.randint(0, size - 1)

        temp = currentData[firstCell]

        currentData[firstCell] = currentData[secondCell]
        currentData[secondCell] = temp

"""Abstract class which is used for applying strategy when selecting and applying operator 
"""

# define policy to choose `operator` function at current iteration
class Policy():
    """Abstract class which is used for applying strategy when selecting and applying operator 

        operators: {[Operator]} -- list of selected operators for the algorithm
    """
    def __init__(self, _operators):

    def select(self):
        """
        Select specific operator

        Returns:
            {Operator} -- selected operator
        """
        pass

        """
        Apply specific operator chosen to create new solution, computes its fitness and returns solution
        
        Args:

        Returns:
        """

        operator = self.select()

        logging.info("---- Applying %s on %s" %
                     (type(operator).__name__, _solution))

        # apply operator on solution
        newSolution = operator.apply(_solution)

        # compute fitness of new solution
        newSolution.evaluate(self.algo.evaluator)

        logging.info("---- Obtaining %s" % (_solution))

        return newSolution

        """Keep into policy reference of the whole algorithm
           The reason is to better manage the operator choices (use of rewards as example)

        Args:
        """

"""Policy class implementation which is used for selecting operator randomly
"""

from .Policy import Policy

class RandomPolicy(Policy):
    """Policy class implementation which is used for select operator randomly

    """
        """Select randomly the next operator to use

        Returns:
            {Operator}: the selected operator
        """
        # choose operator randomly
        index = random.randint(0, len(self.operators) - 1)
        return self.operators[index]

"""Policy class implementation which is used for selecting operator using Upper Confidence Bound
"""

class UCBPolicy(Policy):
    """UCB policy class which is used for applying UCB strategy when selecting and applying operator 

        C: {float} -- tradeoff between EvE parameter for UCB
        exp_rate: {float} -- exploration rate (probability to choose randomly next operator)
        rewards: {[float]} -- list of summed rewards obtained for each operator
        occurrences: {[int]} -- number of use (selected) of each operator
    """
    def __init__(self, _operators, _C=100., _exp_rate=0.5):
        self.rewards = [0. for o in self.operators]
        self.occurrences = [0 for o in self.operators]
        self.C = _C
        self.exp_rate = _exp_rate

        Returns:
        """

        indices = [i for i, o in enumerate(self.occurrences) if o == 0]

        # random choice following exploration rate
        if np.random.uniform(0, 1) <= self.exp_rate:

            index = random.choice(range(len(self.operators)))
            return self.operators[index]

        elif len(indices) == 0:

            # if operator have at least be used one time
            ucbValues = []
            nVisits = sum(self.occurrences)

            for i in range(len(self.operators)):

                ucbValue = self.rewards[i] + self.C * math.sqrt(
                    math.log(nVisits) / (self.occurrences[i] + 0.1))
                ucbValues.append(ucbValue)

            return self.operators[ucbValues.index(max(ucbValues))]

        else:
            return self.operators[random.choice(indices)]

        """
        
        Args:

        Returns:
        """

        # compute fitness improvment rate
        if self.algo.maximise:
            fir = (newSolution.fitness() -
                   _solution.fitness()) / _solution.fitness()
        else:
            fir = (_solution.fitness() -
                   newSolution.fitness()) / _solution.fitness()

        operator_index = self.operators.index(operator)

        if fir > 0:
            self.rewards[operator_index] += fir

        self.occurrences[operator_index] += 1

"""Binary solution class implementation
"""

# modules imports
from .Solution import Solution

# Solution which stores solution data as binary array
class BinarySolution(Solution):
    """
    Binary integer solution class

        data: {ndarray} --  array of binary values
        size: {int} -- size of binary array values
        score: {float} -- fitness score value
    """
    def __init__(self, _data, _size):
        """
        Initialize binary solution using specific data

        Args:
            data: {ndarray} --  array of binary values
            size: {int} -- size of binary array values
        """

        self.data = _data
        self.size = _size

    def random(self, _validator):
        """
        Intialize binary array with use of validator to generate valid random solution

        Args:
            _validator: {function} -- specific function which validates or not a solution

        Returns:
            {BinarySolution} -- new generated binary solution
        """

        self.data = np.random.randint(2, size=self.size)

        while not self.isValid(_validator):
            self.data = np.random.randint(2, size=self.size)

        return self

        return "Binary solution %s" % (self.data)

"""Combinatory integer solution class implementation
"""

# Solution which stores solution data as combinatory integer array
class CombinatoryIntegerSolution(Solution):
    """
    Combinatory integer solution class

    """
        """

        Args:
        """

        """
        Intialize combinatory integer array with use of validator to generate valid random solution

        Args:

        Returns:
            {CombinatoryIntegerSolution} -- new generated combinatory integer solution
        """

        self.data = np.random.shuffle(np.arange(self.size))

            self.data = np.random.shuffle(np.arange(self.size))

        return "Combinatory integer solution %s" % (self.data)

"""Integer solution class implementation
"""

# Solution which stores solution data as integer array
class IntegerSolution(Solution):
    """
    Integer solution class

    """
        """
        Initialize integer solution using specific data

        Args:
        """

        """
        Intialize integer array with use of validator to generate valid random solution

        Args:

        Returns:
            {IntegerSolution} -- new generated integer solution
        """

        self.data = np.random.randint(self.size, size=self.size)

            self.data = np.random.randint(self.size, size=self.size)

        return "Integer solution %s" % (self.data)