Преглед на файлове

Merge branch 'release/v0.0.9'

Jérôme BUISINE преди 4 години
родител
ревизия
6dcd32014a

+ 1 - 1
README.md

@@ -9,7 +9,7 @@ Optimisation generic framework built for optimization problem during thesis
 - **algorithms:** generic and implemented OR algorithms
 - **evaluator:** example of an evaluation function to use (you have to implement your own evaluation function)
 - **solutions:** solutions used to represent problem data
-- **updators:** mutators, crossovers update of solution. This folder also had `policies` folder to manage the way of update and use solution.
+- **operators:** mutators, crossovers update of solution. This folder also had `policies` folder to manage the way of update and use solution.
   
 **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.
 

+ 7 - 6
algorithms/Algorithm.py

@@ -4,14 +4,14 @@ import logging
 # Generic algorithm class
 class Algorithm():
 
-    def __init__(self, _initalizer, _evaluator, _updators, _policy, _validator, _maximise=True):
+    def __init__(self, _initalizer, _evaluator, _operators, _policy, _validator, _maximise=True):
         """
         Initialize all usefull parameters for problem to solve
         """
 
         self.initializer = _initalizer
         self.evaluator = _evaluator
-        self.updators = _updators
+        self.operators = _operators
         self.validator = _validator
         self.policy = _policy
 
@@ -49,7 +49,7 @@ class Algorithm():
         return solution.evaluate(self.evaluator)
 
 
-    def update(self, solution):
+    def update(self, solution, secondSolution=None):
         """
         Apply update function to solution using specific `policy`
 
@@ -59,7 +59,8 @@ class Algorithm():
             updated solution
         """
 
-        sol = self.policy.apply(solution)
+        # two parameters are sent if specific crossover solution are wished
+        sol = self.policy.apply(solution, secondSolution)
 
         if(sol.isValid(self.validator)):
             return sol
@@ -98,11 +99,11 @@ class Algorithm():
 
 
     def progress(self):
-        logging.info("-- %s evaluation n°%s of %s, %s%% - %s" % (type(self).__name__, self.numberOfEvaluations, self.maxEvalutations, "{0:.2f}".format((self.numberOfEvaluations) / self.maxEvalutations * 100.), self.bestSolution.fitness()))
+        logging.info("-- %s evaluation n°%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()))
 
 
     def information(self):
-        logging.info("-- Best solution %s with score of %s" % (self.bestSolution, self.bestSolution.fitness()))
+        logging.info("-- Best solution %s - SCORE %s" % (self.bestSolution, self.bestSolution.fitness()))
 
 
     def __str__(self):

+ 1 - 1
algorithms/IteratedLocalSearch.py

@@ -12,7 +12,7 @@ class IteratedLocalSearch(Algorithm):
         # by default use of mother method to initialize variables
         super().run(_evaluations)
 
-        ls = LocalSearch(self.initializer, self.evaluator, self.updators, self.policy, self.validator, self.maximise)
+        ls = LocalSearch(self.initializer, self.evaluator, self.operators, self.policy, self.validator, self.maximise)
 
         # local search algorithm implementation
         while self.numberOfEvaluations < self.maxEvalutations:

+ 3 - 2
algorithms/LocalSearch.py

@@ -19,7 +19,8 @@ class LocalSearch(Algorithm):
             for _ in range(solutionSize):
 
                 # update solution using policy
-                newSolution = self.update(self.bestSolution)
+                # send random solution as second parameter for mutation
+                newSolution = self.update(self.bestSolution, self.initializer())
 
                 # if better solution than currently, replace it
                 if self.isBetter(newSolution):
@@ -29,7 +30,7 @@ class LocalSearch(Algorithm):
                 self.numberOfEvaluations += 1
 
                 self.progress()
-                logging.info("-- Found solution %s with score of %s" % (newSolution, newSolution.fitness()))
+                logging.info("---- Current %s - SCORE %s" % (newSolution, newSolution.fitness()))
 
                 # stop algorithm if necessary
                 if self.numberOfEvaluations >= self.maxEvalutations:

+ 9 - 6
mainExample.py

@@ -10,8 +10,11 @@ from optimization.algorithms.IteratedLocalSearch import IteratedLocalSearch as I
 from optimization.solutions.BinarySolution import BinarySolution
 from optimization.evaluators.EvaluatorExample import evaluatorExample
 
-from optimization.updators.mutators.SimpleMutation import SimpleMutation, SimpleBinaryMutation
-from optimization.updators.policies.RandomPolicy import RandomPolicy
+from optimization.operators.mutators.SimpleMutation import SimpleMutation
+from optimization.operators.mutators.SimpleBinaryMutation import SimpleBinaryMutation
+from optimization.operators.crossovers.SimpleCrossover import SimpleCrossover
+
+from optimization.operators.policies.RandomPolicy import RandomPolicy
 
 # logging configuration
 logging.basicConfig(format='%(asctime)s %(message)s', filename='example.log', level=logging.DEBUG)
@@ -26,12 +29,12 @@ def init():
 
 def main():
 
-    updators = [SimpleBinaryMutation, SimpleMutation]
-    policy = RandomPolicy(updators)
+    operators = [SimpleBinaryMutation(), SimpleMutation(), SimpleCrossover()]
+    policy = RandomPolicy(operators)
 
-    algo = ILS(init, evaluatorExample, updators, policy, validator, True)
+    algo = ILS(init, evaluatorExample, operators, policy, validator, True)
 
-    bestSol = algo.run(100000)
+    bestSol = algo.run(1000)
 
     print("Found ", bestSol)
 

+ 7 - 0
operators/Operator.py

@@ -0,0 +1,7 @@
+# main imports
+from enum import Enum
+
+# enumeration which stores kind of operator
+class Operator(Enum):
+    MUTATOR = 1
+    CROSSOVER = 2

updators/__init__.py → operators/__init__.py


+ 11 - 0
operators/crossovers/Crossover.py

@@ -0,0 +1,11 @@
+# module imports
+from ..Operator import Operator
+
+# main mutation class
+class Crossover():
+
+    def __init__(self):
+        self.kind = Operator.CROSSOVER
+
+    def apply(self, solution, secondSolution=None):
+        raise NotImplementedError

+ 31 - 0
operators/crossovers/SimpleCrossover.py

@@ -0,0 +1,31 @@
+# main imports
+import random
+import sys
+
+# module imports
+from .Crossover import Crossover
+
+from ...solutions.BinarySolution import BinarySolution
+from ...solutions.Solution import Solution
+
+
+class SimpleCrossover(Crossover):
+
+    def apply(self, solution, secondSolution=None):
+        size = solution.size
+
+        # copy data of solution
+        firstData = solution.data.copy()
+        secondData = secondSolution.data.copy()
+
+        splitIndex = int(size / 2)
+        
+        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
+        return globals()[type(solution).__name__](currentData, size)

updators/crossovers/__init__.py → operators/crossovers/__init__.py


+ 11 - 0
operators/mutators/Mutation.py

@@ -0,0 +1,11 @@
+# module imports
+from ..Operator import Operator
+
+# main mutation class
+class Mutation():
+
+    def __init__(self):
+        self.kind = Operator.MUTATOR
+
+    def apply(self, solution):
+        raise NotImplementedError

+ 28 - 0
operators/mutators/SimpleBinaryMutation.py

@@ -0,0 +1,28 @@
+# main imports
+import random
+import sys
+
+# module imports
+from .Mutation import Mutation
+
+from ...solutions.BinarySolution import BinarySolution
+from ...solutions.Solution import Solution
+
+class SimpleBinaryMutation(Mutation):
+
+    def apply(self, solution):
+        size = solution.size
+
+        cell = random.randint(0, size - 1)
+
+        # copy data of solution
+        currentData = solution.data.copy()
+
+        # swicth values
+        if currentData[cell]:
+            currentData[cell] = 0
+        else:
+            currentData[cell] = 1
+
+        # create solution of same kind with new data
+        return globals()[type(solution).__name__](currentData, size)

+ 35 - 0
operators/mutators/SimpleMutation.py

@@ -0,0 +1,35 @@
+# main imports
+import random
+import sys
+
+# module imports
+from .Mutation import Mutation
+
+from ...solutions.BinarySolution import BinarySolution
+from ...solutions.Solution import Solution
+
+
+class SimpleMutation(Mutation):
+
+    def apply(self, solution):
+        size = solution.size
+
+        firstCell = 0
+        secondCell = 0
+
+        # copy data of solution
+        currentData = solution.data.copy()
+
+        while firstCell == secondCell:
+            firstCell = random.randint(0, size - 1) 
+            secondCell = random.randint(0, size - 1)
+
+        temp = currentData[firstCell]
+
+        # swicth values
+        currentData[firstCell] = currentData[secondCell]
+        currentData[secondCell] = temp
+        
+        # create solution of same kind with new data
+        return globals()[type(solution).__name__](currentData, size)
+

updators/mutators/__init__.py → operators/mutators/__init__.py


+ 40 - 0
operators/policies/Policy.py

@@ -0,0 +1,40 @@
+# main imports
+import logging
+
+# module imports
+from ..Operator import Operator
+
+# define policy to choose `operator` function at current iteration
+class Policy():
+
+    # here you can define your statistical variables for choosing next operator to apply
+
+    def __init__(self, _operators):
+        self.operators = _operators
+
+
+    def select(self):
+        """
+        Select specific operator to solution and returns solution
+        """
+        raise NotImplementedError
+        
+    def apply(self, solution, secondSolution=None):
+        """
+        Apply specific operator chosen to solution and returns solution
+        """
+        
+        operator = self.select()
+
+        logging.info("---- Applying %s on %s" % (type(operator).__name__, solution))
+
+        # check kind of operator
+        if operator.kind == Operator.CROSSOVER:
+            newSolution = operator.apply(solution, secondSolution)
+        
+        if operator.kind == Operator.MUTATOR:
+            newSolution = operator.apply(solution)
+
+        logging.info("---- Obtaining %s" % (solution))
+
+        return newSolution

+ 16 - 0
operators/policies/RandomPolicy.py

@@ -0,0 +1,16 @@
+# main imports
+import random
+
+# module imports
+from .Policy import Policy
+
+class RandomPolicy(Policy):
+
+    def select(self):  
+
+        # choose operator randomly
+        index = random.randint(0, len(self.operators) - 1)
+        return self.operators[index]
+
+
+        

updators/policies/__init__.py → operators/policies/__init__.py


+ 3 - 1
solutions/BinarySolution.py

@@ -27,6 +27,8 @@ class BinarySolution(Solution):
         Use of validator to generate valid random solution
         """
 
+        self.data = np.random.randint(2, size=self.size)
+
         while not self.isValid(_validator):
             self.data = np.random.randint(2, size=self.size)
 
@@ -34,5 +36,5 @@ class BinarySolution(Solution):
 
 
     def __str__(self):
-        return "Binary solution %s of size %s" % (self.data, self.size)
+        return "Binary solution %s" % (self.data)
         

+ 3 - 1
solutions/CombinatoryIntegerSolution.py

@@ -27,6 +27,8 @@ class CombinatoryIntegerSolution(Solution):
         Use of validator to generate valid random solution
         """
 
+        self.data = np.random.shuffle(np.arange(self.size))
+
         while not self.isValid(_validator):
             self.data = np.random.shuffle(np.arange(self.size))
 
@@ -34,5 +36,5 @@ class CombinatoryIntegerSolution(Solution):
 
 
     def __str__(self):
-        return "Combinatory integer solution %s of size %s" % (self.data, self.size)
+        return "Combinatory integer solution %s" % (self.data)
         

+ 3 - 1
solutions/IntegerSolution.py

@@ -27,6 +27,8 @@ class IntegerSolution(Solution):
         Use of validator to generate valid random solution
         """
 
+        self.data = np.random.randint(self.size, size=self.size)
+
         while not self.isValid(_validator):
             self.data = np.random.randint(self.size, size=self.size)
 
@@ -34,5 +36,5 @@ class IntegerSolution(Solution):
 
 
     def __str__(self):
-        return "Integer solution %s of size %s" % (self.data, self.size)
+        return "Integer solution %s" % (self.data)
         

+ 1 - 8
solutions/Solution.py

@@ -10,14 +10,7 @@ class Solution():
         self.data = _data
         self.size = _size
         self.score = None
-    
-
-    def apply(self, _updator):
-        """
-        Apply custom modification of solution and return the transformed solution
-        """
-        return _updator(self)
-
+        
 
     def isValid(self, _validator):
         """

+ 0 - 49
updators/mutators/SimpleMutation.py

@@ -1,49 +0,0 @@
-# main imports
-import random
-import sys
-
-# module imports
-from ...solutions.BinarySolution import BinarySolution
-from ...solutions.Solution import Solution
-
-def SimpleBinaryMutation(solution):
-    size = solution.size
-
-    cell = random.randint(0, size - 1)
-
-    # copy data of solution
-    currentData = solution.data.copy()
-
-    # swicth values
-    if currentData[cell]:
-        currentData[cell] = 0
-    else:
-        currentData[cell] = 1
-
-    # create solution of same kind with new data
-    return globals()[type(solution).__name__](currentData, size)
-
-
-def SimpleMutation(solution):
-
-    size = solution.size
-
-    firstCell = 0
-    secondCell = 0
-
-    # copy data of solution
-    currentData = solution.data.copy()
-
-    while firstCell == secondCell:
-        firstCell = random.randint(0, size - 1) 
-        secondCell = random.randint(0, size - 1)
-
-    temp = currentData[firstCell]
-
-    # swicth values
-    currentData[firstCell] = currentData[secondCell]
-    currentData[secondCell] = temp
-    
-    # create solution of same kind with new data
-    return globals()[type(solution).__name__](currentData, size)
-

+ 0 - 13
updators/policies/Policy.py

@@ -1,13 +0,0 @@
-# define policy to choose `updator` function at current iteration
-class Policy():
-
-    # here you can define your statistical variables for choosing next operator to apply
-
-    def __init__(self, _updators):
-        self.updators = _updators
-
-    def apply(self, solution):
-        """
-        Apply specific updator to solution and returns solution
-        """
-        raise NotImplementedError

+ 0 - 17
updators/policies/RandomPolicy.py

@@ -1,17 +0,0 @@
-# main imports
-import random
-
-# module imports
-from .Policy import Policy
-
-class RandomPolicy(Policy):
-
-    def apply(self, solution):  
-
-        # TODO : implement for mutator (need two parameters)
-
-        # choose updator randomly
-        index = random.randint(0, len(self.updators) - 1)
-        updator = self.updators[index]
-        
-        return solution.apply(updator)