currentSolution (macop.algorithms.Algorithm.Algorithm attribute)

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

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

IteratedLocalSearch (class in macop.algorithms.IteratedLocalSearch)

initalizer (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

evaluator (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

operators (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

policy (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

validator (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

maximise (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

checkpoint (macop.algorithms.IteratedLocalSearch.IteratedLocalSearch attribute)

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

currentSolution (macop.algorithms.LocalSearch.LocalSearch attribute)

bestSolution (macop.algorithms.LocalSearch.LocalSearch attribute)

_evaluations (macop.algorithms.LocalSearch.LocalSearch attribute)

load() (macop.checkpoints.BasicCheckpoint.BasicCheckpoint method)

run() (macop.checkpoints.BasicCheckpoint.BasicCheckpoint method)

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

# main imports
import logging

# 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
        checkpoint: {Checkpoint} -- Checkpoint class implementation to keep track of algorithm and restart
        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.checkpoint = None
        self.bestSolution = None

        # other parameters
        self.parent = _parent  # parent algorithm if it's sub algorithm
        #self.maxEvaluations = 0 # by default
        self.maximise = _maximise

        self.initRun()

    def addCheckpoint(self, _class, _every, _filepath):
        """Add checkpoint to algorithm specifying usefull parameters

        Attributes:
            _class: {class} -- Checkpoint class type
            _every: {int} -- checkpoint frequency based on evaluations
            _filepath: {str} -- file path where checkpoints will be saved
        """
        self.checkpoint = _class(self, _every, _filepath)

    def setCheckpoint(self, _checkpoint):
        """Set checkpoint instance directly

            _checkpoint: {Checkpoint} -- checkpoint instance
        """
        self.checkpoint = _checkpoint

    def resume(self):
        """Resume algorithm using checkpoint instance

        Raises:
            ValueError: No checkpoint initialize (use `addCheckpoint` or `setCheckpoint` is you want to use this process)
        """
        if self.checkpoint is None:
            raise ValueError(
                "Need to `addCheckpoint` or `setCheckpoint` is you want to use this process"
            )
        else:
            print('Checkpoint loading is called')
            self.checkpoint.load()

    def initRun(self):
        """
        Method which initialiazes or re-initializes the whole algorithm context: operators, current solution, best solution (by default current solution)
        """

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

        self.currentSolution = self.initializer()

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

        # reinitialize policy
        # if self.parent is not None:
        #     self.policy = globals()[type(self.policy).__name__]()

        # keep in memory best known solution (current solution)
        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.numberOfEvaluations

        return self.numberOfEvaluations

    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):
        """
        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

        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

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

        # by default
        return False

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

        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 checkpoint if necessary
        """
        if self.checkpoint is not None:
            self.checkpoint.run()

        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 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 avoir 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)

            _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 checkpoint for ILS
            self.resume()

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

        # set same checkpoint if exists
            ls.setCheckpoint(self.checkpoint)

        # 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))

        return self.bestSolution

"""Local Search algorithm
"""

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

    """
        """
        Run the local search algorithm

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

        solutionSize = self.bestSolution.size

            for _ in range(solutionSize):

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

                # 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

import os
import numpy as np

from .Checkpoint import Checkpoint

class BasicCheckpoint(Checkpoint):
    def __init__(self, _algo, _every, _filepath):
        self.algo = _algo
        self.every = _every
        self.filepath = _filepath

    def run(self):

        # 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):

        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(' ')))

                self.algo.bestSolution.data = np.array(solutionData)
                self.algo.bestSolution.score = float(data[2])
        else:
            print('No backup found... Start running')
            logging.info(
                "Can't load backup... Backup filepath not valid in Checkpoint")

"""Abstract Checkpoint class
"""
import os

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

        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
    """

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

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

        """
        Check if necessary to do backup based on `_every` variable
        """
        pass

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

from enum import Enum

# enumeration which stores kind of operator
class KindOperator(Enum):
    MUTATOR = 1
    CROSSOVER = 2

class Operator():
    def __init__(self):
        pass

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

        Args:
            solution (Solution): Solution instance

        Raises:
            NotImplementedError: if method not implemented into child class
        """
        raise NotImplementedError

    def setAlgo(self, algo):
        """Keep into operator reference of the whole algorithm
           The reason is to better manage operator instance

        Args:
            algo (Algorithm): the algorithm reference runned
        """
        self.algo = algo