Parcourir la source

Merge branch 'release/v0.1.1'

Jérôme BUISINE il y a 4 ans
Parent
commit
db3538337f

+ 1 - 0
README.md

@@ -10,6 +10,7 @@ Optimisation generic framework built for optimization problem during thesis
 - **evaluator:** example of an evaluation function to use (you have to implement your own evaluation function)
 - **solutions:** solutions used to represent problem data
 - **operators:** mutators, crossovers update of solution. This folder also had `policies` folder to manage the way of update and use solution.
+- **checkpoints:** checkpoints folder where `Checkpoint` class is available for making checkpoint every number of evaluations.
   
 **Note:** you can pass a custom `validator` function to the algorithm in order to check is solution is always correct for your needs after an update.
 

+ 63 - 5
algorithms/Algorithm.py

@@ -4,7 +4,7 @@ import logging
 # Generic algorithm class
 class Algorithm():
 
-    def __init__(self, _initalizer, _evaluator, _operators, _policy, _validator, _maximise=True):
+    def __init__(self, _initalizer, _evaluator, _operators, _policy, _validator, _maximise=True, _parent=None):
         """
         Initialize all usefull parameters for problem to solve
         """
@@ -14,14 +14,32 @@ class Algorithm():
         self.operators = _operators
         self.validator = _validator
         self.policy = _policy
+        self.checkpoint = None
 
         # other parameters
-        self.maxEvalutations = 0 # by default
+        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):
+        self.checkpoint = _class(self, _every, _filepath)
+
+    
+    def setCheckpoint(self, _checkpoint):
+        self.checkpoint = _checkpoint
+
+
+    def resume(self):
+        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):
         """
         Reinit the whole variables
@@ -34,8 +52,31 @@ class Algorithm():
 
         # keep in memory best known solution (current solution)
         self.bestSolution = self.currentSolution
+        
+
+    def increaseEvaluation(self):
+        self.numberOfEvaluations += 1
+
+        if self.parent is not None:
+            self.parent.numberOfEvaluations += 1
+
+    
+    def getGlobalEvaluation(self):
+
+        if self.parent is not None:
+            return self.parent.numberOfEvaluations
 
-        self.numberOfEvaluations = 0
+        return self.numberOfEvaluations
+
+
+    def stop(self):
+        """
+        Global stopping criteria (check for inner algorithm too)
+        """
+        if self.parent is not None:
+            return self.parent.numberOfEvaluations >= self.parent.maxEvaluations or self.numberOfEvaluations >= self.maxEvaluations
+            
+        return self.numberOfEvaluations >= self.maxEvaluations
 
 
     def evaluate(self, solution):
@@ -92,14 +133,31 @@ class Algorithm():
         """
         Run the specific algorithm following number of evaluations to find optima
         """
-        self.maxEvalutations = _evaluations
+
+        self.maxEvaluations = _evaluations
+
         self.initRun()
 
+        # 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):
-        logging.info("-- %s evaluation %s of %s (%s%%) - BEST SCORE %s" % (type(self).__name__, self.numberOfEvaluations, self.maxEvalutations, "{0:.2f}".format((self.numberOfEvaluations) / self.maxEvalutations * 100.), self.bestSolution.fitness()))
+
+        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):

+ 17 - 7
algorithms/IteratedLocalSearch.py

@@ -12,11 +12,20 @@ class IteratedLocalSearch(Algorithm):
         # by default use of mother method to initialize variables
         super().run(_evaluations)
 
-        ls = LocalSearch(self.initializer, self.evaluator, self.operators, self.policy, self.validator, self.maximise)
+        # enable checkpoint for ILS
+        if self.checkpoint is not None:
+            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
+        if self.checkpoint is not None:
+            ls.setCheckpoint(self.checkpoint)
 
         # local search algorithm implementation
-        while self.numberOfEvaluations < self.maxEvalutations:
-            
+        while not self.stop():
+
             # create and search solution from local search
             newSolution = ls.run(_ls_evaluations)
 
@@ -24,11 +33,12 @@ class IteratedLocalSearch(Algorithm):
             if self.isBetter(newSolution):
                 self.bestSolution = newSolution
 
-            # increase number of evaluations
-            self.numberOfEvaluations += _ls_evaluations
+            # number of evaluatins increased from LocalSearch
+            # increase number of evaluations and progress are then not necessary there
+            #self.increaseEvaluation()
+            #self.progress()
 
-            self.progress()
-            self.information()            
+            self.information()          
 
         logging.info("End of %s, best solution found %s" % (type(self).__name__, self.bestSolution))
 

+ 4 - 4
algorithms/LocalSearch.py

@@ -14,8 +14,8 @@ class LocalSearch(Algorithm):
         solutionSize = self.bestSolution.size
 
         # local search algorithm implementation
-        while self.numberOfEvaluations < self.maxEvalutations:
-
+        while not self.stop():
+            
             for _ in range(solutionSize):
 
                 # update solution using policy
@@ -27,13 +27,13 @@ class LocalSearch(Algorithm):
                     self.bestSolution = newSolution
 
                 # increase number of evaluations
-                self.numberOfEvaluations += 1
+                self.increaseEvaluation()
 
                 self.progress()
                 logging.info("---- Current %s - SCORE %s" % (newSolution, newSolution.fitness()))
 
                 # stop algorithm if necessary
-                if self.numberOfEvaluations >= self.maxEvalutations:
+                if self.stop():
                     break
             
         logging.info("End of %s, best solution found %s" % (type(self).__name__, self.bestSolution))

+ 75 - 0
checkpoints/BasicCheckpoint.py

@@ -0,0 +1,75 @@
+# main imports
+import os
+import logging
+import numpy as np
+
+# module imports
+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:
+                    f.write(line)
+
+
+    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")

+ 22 - 0
checkpoints/Checkpoint.py

@@ -0,0 +1,22 @@
+# main imports
+import os
+import logging
+
+class Checkpoint():
+
+    def __init__(self, _algo, _every, _filepath):
+        self.algo = _algo
+        self.every = _every
+        self.filepath = _filepath
+
+    def run(self):
+        """
+        Check if necessary to do backup based on `_every` variable
+        """
+        pass
+
+    def load(self):
+        """
+        Load last backup line of solution and set algorithm state at this backup
+        """
+        pass

+ 0 - 0
checkpoints/__init__.py


+ 6 - 1
mainExample.py

@@ -16,6 +16,8 @@ from optimization.operators.crossovers.SimpleCrossover import SimpleCrossover
 
 from optimization.operators.policies.RandomPolicy import RandomPolicy
 
+from optimization.checkpoints.BasicCheckpoint import BasicCheckpoint
+
 # logging configuration
 logging.basicConfig(format='%(asctime)s %(message)s', filename='example.log', level=logging.DEBUG)
 
@@ -27,14 +29,17 @@ def validator(solution):
 def init():
     return BinarySolution([], 30).random(validator)
 
+filepath = "checkpoints.csv"
+
 def main():
 
     operators = [SimpleBinaryMutation(), SimpleMutation(), SimpleCrossover()]
     policy = RandomPolicy(operators)
 
     algo = ILS(init, evaluatorExample, operators, policy, validator, True)
+    algo.addCheckpoint(_class=BasicCheckpoint, _every=5, _filepath=filepath)
 
-    bestSol = algo.run(1000)
+    bestSol = algo.run(425)
 
     print("Found ", bestSol)