Parcourir la source

Update comments and enable use of getter and setter

Jérôme BUISINE il y a 3 ans
Parent
commit
b0b1450ba9

+ 11 - 4
CONTRIBUTING

@@ -15,9 +15,11 @@ Thank you for taking the time to read this guide for the package's contribution.
 
 1. [Submission processes](#submission-process)
 
-    1.2. [Submit an issue](#submit-an-issue)
+    1.1. [Submit an issue](#submit-an-issue)
 
-    1.1. [Pull request](#pull-request)
+    1.2. [Pull request](#pull-request)
+
+    1.3. [Seek support](#seek-support)
 
 2. [Coding conventions](#coding-conventions)
 
@@ -62,6 +64,11 @@ To enhance the package, do not hesitate to fix bug or missing feature. To do tha
 
 Whatever the problem reported, I will thank you for your contribution to this project. So do not hesitate.
 
+## Seek support
+
+If you have any problem with the use of the package, issue or pull request submission, do not hesitate to let a message to [https://github.com/jbuisine/macop/discussions](https://github.com/jbuisine/macop/discussions). Especially in the question and answer section. 
+
+You can also contact me at the following email address: `contact@jeromebuisine.fr`.
 
 # Coding conventions
 
@@ -126,11 +133,11 @@ This project uses the [doctest](https://docs.python.org/3/library/doctest.html)
     >>> data = [0, 1, 0, 1, 1]
     >>> solution = BinarySolution(data, len(data))
     >>> # check data content
-    >>> sum(solution._data) == 3
+    >>> sum(solution.getData()) == 3
     True
     >>> # clone solution
     >>> solution_copy = solution.clone()
-    >>> all(solution_copy._data == solution._data)
+    >>> all(solution_copy._data == solution.getData())
 """
 ```
 

+ 19 - 4
README.md

@@ -24,9 +24,24 @@
     <img src="https://github.com/jbuisine/macop/blob/master/docs/source/_static/documentation/macop_behaviour.png" alt="" width="50%">
 </p>
 
+
+## Motivation
+
+Flexible discrete optimisation package allowing a quick implementation of your problems. In particular it meets the following needs:
+
+- **Common basis:** the interaction loop during the solution finding process proposed within the package is common to all heuristics. This allows the user to modify only a part of this interaction loop if necessary without rendering the process non-functional.
+- **Hierarchy:** a hierarchical algorithm management system is available, especially when an algorithm needs to manage local searches. This hierarchy remains transparent to the user. The main algorithm will be able to manage and control the process of searching for solutions.
+- **Flexibility:** although the algorithms are dependent on each other, it is possible that their internal management is different. This means that the ways in which solutions are evaluated and updated, for example, may be different.
+- **Abstraction:** thanks to the modular separability of the package, it is quickly possible to implement new problems, solutions representation, way to evaluate, update solutions within the package.
+- **Extensible:** the package is open to extension, i.e. it does not partition the user in these developer choices. It can just as well implement continuous optimization problems if needed while making use of the main interaction loop proposed by the package.
+- **Easy Setup:** As a Pure Python package distributed is ``pip`` installable and easy to use.
+
+
+## Content
+
 The primary advantage of using Python is that it allows you to dynamically add new members within the new implemented solution or algorithm classes. This of course does not close the possibilities of extension and storage of information within solutions and algorithms. It all depends on the need in question.
 
-## In `macop.algorihtms` module:
+### In `macop.algorihtms` module:
 
 Both single and multi-objective algorithms have been implemented for demonstration purposes. 
 
@@ -36,15 +51,15 @@ The mono-objective Iterated Local Search algorithm which aims to perform local s
 
 The main purpose of these developed algorithms is to show the possibilities of operational search algorithm implementations based on the minimalist structure of the library.
 
-## In `macop.solutions` module:
+### In `macop.solutions` module:
 
 Currently, only combinatorial solutions (discrete problem modelisation) are offered, with the well-known problem of the knapsack as an example. Of course, it's easy to add your own representations of solutions. Solutions modeling continuous problems can also be created by the anyone who wants to model his own problem.
 
-## In `macop.operators` and `macop.policies` modules:
+### In `macop.operators` and `macop.policies` modules:
 
 A few mutation and crossover operators have been implemented, however, it remains quite simple. What is interesting here is that it is possible to develop one's own strategy for choosing operators for the next evaluation. The available UCBPolicy class proposes this functionality as an example, since it will seek to propose the best operator to apply based on a method known as the Adaptive Operator Selection (AOS) via the use of the Upper Confidence Bound (UCB) algorithm. 
 
-## In `macop.callbacks` module:
+### In `macop.callbacks` module:
 
 The use of callback instance, allows both to do an action every $k$ evaluations of information, but also to reload them once the run of the algorithm is cut. Simply inherit the abstract Callback class and implement the `apply` method to backup and `load` to restore. It is possible to add as many callbacks as required. Such as an example, implemented UCBPolicy has its own callback allowing the instance to reload previously collected statistics and restart using them.
 

+ 2 - 2
docs/source/conf.py

@@ -25,9 +25,9 @@ copyright = '2021, Jérôme BUISINE'
 author = 'Jérôme BUISINE'
 
 # The short X.Y version
-version = '1.0.9'
+version = '1.0.10'
 # The full version, including alpha/beta/rc tags
-release = 'v1.0.9'
+release = 'v1.0.10'
 
 
 # -- General configuration ---------------------------------------------------

+ 13 - 0
docs/source/description.rst

@@ -17,6 +17,19 @@ Based on its generic behaviour, each **Macop** algorithm runs can be represented
 
 The package is strongly oriented on combinatorial optimisation (hence discrete optimisation) but it remains possible to extend for continuous optimisation.
 
+Motivation
+~~~~~~~~~~
+
+Flexible discrete optimisation package allowing a quick implementation of your problems. In particular it meets the following needs:
+
+- **Common basis:** the interaction loop during the solution finding process proposed within the package is common to all heuristics. This allows the user to modify only a part of this interaction loop if necessary without rendering the process non-functional.
+- **Hierarchy:** a hierarchical algorithm management system is available, especially when an algorithm needs to manage local searches. This hierarchy remains transparent to the user. The main algorithm will be able to manage and control the process of searching for solutions.
+- **Flexibility:** although the algorithms are dependent on each other, it is possible that their internal management is different. This means that the ways in which solutions are evaluated and updated, for example, may be different.
+- **Abstraction:** thanks to the modular separability of the package, it is quickly possible to implement new problems, solutions representation, way to evaluate, update solutions within the package.
+- **Extensible:** the package is open to extension, i.e. it does not partition the user in these developer choices. It can just as well implement continuous optimization problems if needed while making use of the main interaction loop proposed by the package.
+- **Easy Setup:** As a Pure Python package distributed is ``pip`` installable and easy to use.
+
+
 
 Installation
 ------------

+ 15 - 15
docs/source/documentations/algorithms.rst

@@ -34,7 +34,7 @@ We will pay attention to the different methods of which she is composed. This cl
 
 She is composed of few default attributes:
 
-- initializer: {function} -- basic function strategy to initialize solution
+- initialiser: {function} -- basic function strategy to initialise solution
 - evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
 - operators: {[Operator]} -- list of operator to use when launching algorithm
 - policy: {Policy} -- Policy instance strategy to select operators
@@ -43,7 +43,7 @@ She is composed of few default attributes:
 - verbose: {bool} -- verbose or not information about the algorithm
 - currentSolution: {Solution} -- current solution managed for current evaluation comparison
 - 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
+- callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
 - parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
 
 .. code-block:: python
@@ -51,7 +51,7 @@ She is composed of few default attributes:
     class Algorithm():
 
         def __init__(self,
-                    initializer,
+                    initialiser,
                     evaluator,
                     operators,
                     policy,
@@ -88,7 +88,7 @@ She is composed of few default attributes:
 
         def initRun(self):
             """
-            Initialize the current solution and best solution using the `initialiser` function
+            initialise the current solution and best solution using the `initialiser` function
             """
             ...
 
@@ -155,12 +155,12 @@ The ``evaluate``, ``update`` and ``isBetter`` will be used a lot when looking fo
 In particular the ``update`` function, which will call the ``policy`` instance to generate a new valid solution.
 ``isBetter`` method is also overloadable especially if the algorithm does not take any more into account than a single solution to be verified (verification via a population for example).
 
-The ``initRun`` method specify the way you intialise your algorithm (``bestSolution`` and ``currentSolution`` as example) if algorithm not already initialized.
+The ``initRun`` method specify the way you intialise your algorithm (``bestSolution`` and ``currentSolution`` as example) if algorithm not already initialised.
 
 .. note:: 
     The ``initRun`` method can also be used for intialise population of solutions instead of only one best solution, if you want to manage a genetic algorithm.
 
-Most important part is the ``run`` method. Into abstract, the ``run`` method only initialized the current number of evaluation for the algorithm based on the parent algorithm if we are into inner algorithm.
+Most important part is the ``run`` method. Into abstract, the ``run`` method only initialised the current number of evaluation for the algorithm based on the parent algorithm if we are into inner algorithm.
 It is always **mandatory** to call the parent class ``run`` method using ``super().run(evaluations)``. Then, using ``evaluations`` parameter which is the number of evaluations budget to run, we can process or continue to find solutions into search space.
 
 .. warning::
@@ -190,10 +190,10 @@ Let's implement an algorithm well known under the name of hill climber best impr
             Run a local search algorithm
             """
 
-            # by default use of mother method to initialize variables
+            # by default use of mother method to initialise variables
             super().run(evaluations)
 
-            # initialize current solution and best solution
+            # initialise current solution and best solution
             self.initRun()
 
             solutionSize = self._currentSolution._size
@@ -238,7 +238,7 @@ We will also need to define a **solution initialisation function** so that the a
     evaluator = KnapsackEvaluator(data={'worths': elements_score})
 
     # valid instance using lambda
-    validator = lambda solution: sum([ elements_weight[i] * solution._data[i] for i in range(len(solution._data))]) <= 15
+    validator = lambda solution: sum([ elements_weight[i] * solution.getData()[i] for i in range(len(solution.getData()))]) <= 15
     
     # initialiser instance using lambda with default param value
     initialiser = lambda x=5: BinarySolution.random(x, validator)
@@ -287,7 +287,7 @@ Let's called this new algorithm ``IteratedLocalSearch``:
     class IteratedLocalSearch(Algorithm):
         
         def __init__(self,
-                    initializer,
+                    initialiser,
                     evaluator,
                     operators,
                     policy,
@@ -297,7 +297,7 @@ Let's called this new algorithm ``IteratedLocalSearch``:
                     parent=None,
                     verbose=True):
             
-            super().__init__(initializer, evaluator, operators, policy, validator, maximise, parent, verbose)
+            super().__init__(initialiser, evaluator, operators, policy, validator, maximise, parent, verbose)
 
             # specific local search associated with current algorithm
             self._localSearch = localSearch
@@ -311,10 +311,10 @@ Let's called this new algorithm ``IteratedLocalSearch``:
             Run the iterated local search algorithm using local search
             """
 
-            # by default use of mother method to initialize variables
+            # by default use of mother method to initialise variables
             super().run(evaluations)
 
-            # initialize current solution
+            # initialise current solution
             self.initRun()
 
             # local search algorithm implementation
@@ -348,7 +348,7 @@ Then, we use this local search in our ``run`` method to allow a better search fo
     evaluator = KnapsackEvaluator(data={'worths': elements_score})
 
     # valid instance using lambda
-    validator = lambda solution: sum([ elements_weight[i] * solution._data[i] for i in range(len(solution._data))]) <= 15
+    validator = lambda solution: sum([ elements_weight[i] * solution.getData()[i] for i in range(len(solution.getData()))]) <= 15
     
     # initialiser instance using lambda with default param value
     initialiser = lambda x=5: BinarySolution.random(x, validator)
@@ -361,7 +361,7 @@ Then, we use this local search in our ``run`` method to allow a better search fo
     
     # maximizing algorithm (relative to knapsack problem)
     localSearch = HillClimberBestImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
-    algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
+    algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
 
     # run the algorithm using local search and get solution found 
     solution = algo.run(evaluations=100, ls_evaluations=10)

+ 7 - 7
docs/source/documentations/callbacks.rst

@@ -101,9 +101,9 @@ We are going to create our own Callback instance called ``BasicCheckpoint`` whic
 
                 # create specific line with solution data
                 solutionData = ""
-                solutionSize = len(solution._data)
+                solutionSize = len(solution.getData())
 
-                for index, val in enumerate(solution._data):
+                for index, val in enumerate(solution.getData()):
                     solutionData += str(val)
 
                     if index < solutionSize - 1:
@@ -145,12 +145,12 @@ We are going to create our own Callback instance called ``BasicCheckpoint`` whic
                     # get best solution data information
                     solutionData = list(map(int, data[1].split(' ')))
 
-                    # avoid uninitialized solution
+                    # avoid uninitialised solution
                     if self._algo._bestSolution is None:
-                        self._algo._bestSolution = self._algo._initializer()
+                        self._algo._bestSolution = self._algo.initialiser()
 
                     # set to algorithm the lastest obtained best solution
-                    self._algo._bestSolution._data = np.array(solutionData)
+                    self._algo._bestsolution.getData() = np.array(solutionData)
                     self._algo._bestSolution._score = float(data[2])
 
 
@@ -203,10 +203,10 @@ If we want to exploit this functionality, then we will need to exploit them with
             Run the iterated local search algorithm using local search
             """
 
-            # by default use of mother method to initialize variables
+            # by default use of mother method to initialise variables
             super().run(evaluations)
 
-            # initialize current solution
+            # initialise current solution
             self.initRun()
 
             # restart using callbacks backup list

+ 2 - 2
docs/source/documentations/evaluators.rst

@@ -61,13 +61,13 @@ We will define the ``KnapsackEvaluator`` class, which will therefore allow us to
 
             # `_data` contains worths array values of objects
             fitness = 0
-            for index, elem in enumerate(solution._data):
+            for index, elem in enumerate(solution.getData()):
                 fitness += self._data['worths'][index] * elem
 
             return fitness
 
 
-It is now possible to initialize our new evaluator with specific data of our problem instance:
+It is now possible to initialise our new evaluator with specific data of our problem instance:
 
 .. code-block:: python
 

+ 4 - 4
docs/source/documentations/operators.rst

@@ -105,10 +105,10 @@ The modification applied here is just a bit swapped. Let's define the ``SimpleBi
             copy_solution = solution.clone()
 
             # swicth values
-            if copy_solution._data[cell]:
-                copy_solution._data[cell] = 0
+            if copy_solution.getData()[cell]:
+                copy_solution.getData()[cell] = 0
             else:
-                copy_solution._data[cell] = 1
+                copy_solution.getData()[cell] = 1
 
             # return the new obtained solution
             return copy_solution
@@ -172,7 +172,7 @@ The first half of solution 1 will be saved and added to the second half of solut
             # copy of solution 2
             copy_solution = solution2.clone()
 
-            copy_solution._data[splitIndex:] = firstData[splitIndex:]
+            copy_solution.getData()[splitIndex:] = firstData[splitIndex:]
 
             return copy_solution
 

+ 3 - 3
docs/source/documentations/solutions.rst

@@ -47,7 +47,7 @@ Some specific methods are available:
         @staticmethod
         def random(size, validator=None):
             """
-            Initialize solution using random data with validator or not
+            initialise solution using random data with validator or not
             """
             ...
 
@@ -60,7 +60,7 @@ Some specific methods are available:
 
 From these basic methods, it is possible to manage a representation of a solution to our problem. 
 
-Allowing to initialize it randomly or not (using constructor or ``random`` method), to evaluate it (``evaluate`` method) and to check some constraints of validation of the solution (``isValid`` method).
+Allowing to initialise it randomly or not (using constructor or ``random`` method), to evaluate it (``evaluate`` method) and to check some constraints of validation of the solution (``isValid`` method).
 
 .. note::
     Only one of these methods needs specification if we create our own type of solution. This is the ``random`` method, which depends on the need of the problem.
@@ -113,7 +113,7 @@ We will also have to implement the ``random`` method to create a new random solu
 
             # create binary array of specific size using numpy random module
             data = np.random.randint(2, size=size)
-            # initialize new solution using constructor
+            # initialise new solution using constructor
             solution = BinarySolution(data, size)
 
             # check if validator is set

+ 1 - 1
docs/source/documentations/validator.rst

@@ -35,7 +35,7 @@ To avoid taking into account invalid solutions, we can define our function which
 
         for i, w in enumerate(elements_weight):
             # add weight if current object is set to 1
-            weight_sum += w * solution._data[i]
+            weight_sum += w * solution.getData()[i]
         
         # validation condition
         return weight_sum <= 15

+ 3 - 3
docs/source/examples/qap/implementation.rst

@@ -63,8 +63,8 @@ So we are going to create a class that will inherit from the abstract class ``ma
             {float} -- fitness score of solution
         """
         fitness = 0
-        for index_i, val_i in enumerate(solution._data):
-            for index_j, val_j in enumerate(solution._data):
+        for index_i, val_i in enumerate(solution.getData()):
+            for index_j, val_j in enumerate(solution.getData()):
                 fitness += self._data['F'][index_i, index_j] * self._data['D'][val_i, val_j]
 
         return fitness
@@ -105,7 +105,7 @@ If you are uncomfortable with some of the elements in the code that will follow,
 
     # default validator (check the consistency of our data, i.e. only unique element)
     def validator(solution):
-        if len(list(solution._data)) > len(set(list(solution._data))):
+        if len(list(solution.getData())) > len(set(list(solution.getData()))):
             print("not valid")
             return False
         return True

+ 2 - 2
docs/source/examples/ubqp/implementation.rst

@@ -60,8 +60,8 @@ So we are going to create a class that will inherit from the abstract class ``ma
             {float} -- fitness score of solution
         """
         fitness = 0
-        for index_i, val_i in enumerate(solution._data):
-            for index_j, val_j in enumerate(solution._data):
+        for index_i, val_i in enumerate(solution.getData()):
+            for index_j, val_j in enumerate(solution.getData()):
                 fitness += self._data['Q'][index_i, index_j] * val_i * val_j
 
         return fitness

+ 19 - 2
docs/source/index.rst

@@ -6,13 +6,16 @@ Minimalist And Customisable Optimisation Package
    :align: center
 
 What's **Macop** ?
-------------------
+~~~~~~~~~~~~~~~~~~
 
 **Macop** is a discrete optimisation Python package which not which doesn't implement every algorithm in the literature but provides the ability to quickly develop and test your own algorithm and strategies. The main objective of this package is to provide maximum flexibility, which allows for easy experimentation in implementation.
 
+
+Contents
+~~~~~~~~
+
 .. toctree::
    :maxdepth: 1
-   :caption: Contents:
 
    description
 
@@ -24,6 +27,20 @@ What's **Macop** ?
 
    contributing
 
+
+Motivation
+~~~~~~~~~~
+
+Flexible discrete optimisation package allowing a quick implementation of your problems. In particular it meets the following needs:
+
+- **Common basis:** the interaction loop during the solution finding process proposed within the package is common to all heuristics. This allows the user to modify only a part of this interaction loop if necessary without rendering the process non-functional.
+- **Hierarchy:** a hierarchical algorithm management system is available, especially when an algorithm needs to manage local searches. This hierarchy remains transparent to the user. The main algorithm will be able to manage and control the process of searching for solutions.
+- **Flexibility:** although the algorithms are dependent on each other, it is possible that their internal management is different. This means that the ways in which solutions are evaluated and updated, for example, may be different.
+- **Abstraction:** thanks to the modular separability of the package, it is quickly possible to implement new problems, solutions representation, way to evaluate, update solutions within the package.
+- **Extensible:** the package is open to extension, i.e. it does not partition the user in these developer choices. It can just as well implement continuous optimization problems if needed while making use of the main interaction loop proposed by the package.
+- **Easy Setup:** As a Pure Python package distributed is `pip` installable and easy to use.
+
+
 Indices and tables
 ------------------
 

+ 1 - 1
examples/knapsackExample.py

@@ -33,7 +33,7 @@ elements_weight = [ random.randint(2, 5) for _ in range(30) ]
 def knapsackWeight(solution):
 
     weight_sum = 0
-    for index, elem in enumerate(solution._data):
+    for index, elem in enumerate(solution.getData()):
         weight_sum += elements_weight[index] * elem
 
     return weight_sum

+ 1 - 1
examples/knapsackMultiExample.py

@@ -35,7 +35,7 @@ elements_weight = [ random.randint(90, 100) for _ in range(500) ]
 def knapsackWeight(solution):
 
     weight_sum = 0
-    for index, elem in enumerate(solution._data):
+    for index, elem in enumerate(solution.getData()):
         weight_sum += elements_weight[index] * elem
 
     return weight_sum

+ 1 - 1
examples/qapExample.py

@@ -33,7 +33,7 @@ filepath = "data/checkpoints_qap.csv"
 
 # default validator
 def validator(solution):
-    if len(list(solution._data)) > len(set(list(solution._data))):
+    if len(list(solution.getData())) > len(set(list(solution.getData()))):
         print("not valid")
         return False
     return True

+ 65 - 17
macop/algorithms/base.py

@@ -22,7 +22,7 @@ class Algorithm():
 
 
     Attributes:
-        initializer: {function} -- basic function strategy to initialize solution
+        initialiser: {function} -- basic function strategy to initialise solution
         evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
         operators: {[Operator]} -- list of operator to use when launching algorithm
         policy: {Policy} -- Policy implementation strategy to select operators
@@ -31,11 +31,11 @@ class Algorithm():
         verbose: {bool} -- verbose or not information about the algorithm
         currentSolution: {Solution} -- current solution managed for current evaluation comparison
         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
+        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
         parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
     """
     def __init__(self,
-                 initializer,
+                 initialiser,
                  evaluator,
                  operators,
                  policy,
@@ -43,13 +43,27 @@ class Algorithm():
                  maximise=True,
                  parent=None,
                  verbose=True):
+        """Basic Algorithm initialisation
+
+        Args:
+            initialiser: {function} -- basic function strategy to initialise solution
+            evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
+            operators: {[Operator]} -- list of operator to use when launching algorithm
+            policy: {Policy} -- Policy 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 optimisation problem 
+            parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
+            verbose: {bool} -- verbose or not information about the algorithm
+        """
+
+        # public members intialization
+        self.initialiser = initialiser
+        self.evaluator = evaluator
+        self.validator = validator
+        self.policy = policy
 
         # protected members intialization
-        self._initializer = initializer
-        self._evaluator = evaluator
         self._operators = operators
-        self._policy = policy
-        self._validator = validator
         self._callbacks = []
         self._bestSolution = None
         self._currentSolution = None
@@ -75,9 +89,9 @@ class Algorithm():
 
         # also track reference for policy
         if self._parent is not None:
-            self._policy.setAlgo(self.getParent())
+            self.policy.setAlgo(self.getParent())
         else:
-            self._policy.setAlgo(self)
+            self.policy.setAlgo(self)
 
     def addCallback(self, callback):
         """Add new callback to algorithm specifying usefull parameters
@@ -127,15 +141,33 @@ class Algorithm():
         """
         self._parent = parent
 
+    def getResult(self):
+        """Get the expected result of the current algorithm
+
+        By default the best solution (but can be anything you want)
+
+        Returns:
+            {object} -- expected result data of the current algorithm
+        """
+        return self._bestSolution
+
+    def setDefaultResult(self, result):
+        """Set current default result of the algorithm
+
+        Args:
+            result: {object} -- expected result data of the current algorithm
+        """
+        self._bestSolution = result
+
     def initRun(self):
         """
-        Initialize the current solution and best solution using the `initialiser` function
+        initialise the current solution and best solution using the `initialiser` function
         """
 
-        self._currentSolution = self._initializer()
+        self._currentSolution = self.initialiser()
 
         # evaluate current solution
-        self._currentSolution.evaluate(self._evaluator)
+        self._currentSolution.evaluate(self.evaluator)
         self.increaseEvaluation()
 
         # keep in memory best known solution (current solution)
@@ -167,6 +199,22 @@ class Algorithm():
 
         return self._numberOfEvaluations
 
+    def getEvaluation(self):
+        """Get the current number of evaluation
+
+        Returns:
+            {int} -- current number of evaluation
+        """
+        return self._numberOfEvaluations
+
+    def setEvaluation(self, evaluations):
+        """Set the current number of evaluation
+
+        Args:
+            evaluations: {int} -- current expected number of evaluation
+        """
+        self._numberOfEvaluations = evaluations
+
     def getGlobalMaxEvaluation(self):
         """Get the global max number of evaluation (if inner algorithm)
 
@@ -206,7 +254,7 @@ class Algorithm():
         Note: 
             if multi-objective problem this method can be updated using array of `evaluator`
         """
-        return solution.evaluate(self._evaluator)
+        return solution.evaluate(self.evaluator)
 
     def update(self, solution):
         """
@@ -221,13 +269,13 @@ class Algorithm():
         """
 
         # two parameters are sent if specific crossover solution are wished
-        sol = self._policy.apply(solution)
+        sol = self.policy.apply(solution)
 
         # compute fitness of new solution if not already computed
         if sol._score is None:
-            sol.evaluate(self._evaluator)
+            sol.evaluate(self.evaluator)
 
-        if (sol.isValid(self._validator)):
+        if (sol.isValid(self.validator)):
             return sol
         else:
             logging.info("-- New solution is not valid %s" % sol)
@@ -246,7 +294,7 @@ class Algorithm():
         Returns:
             {bool} -- `True` if better
         """
-        if not solution.isValid(self._validator):
+        if not solution.isValid(self.validator):
             return False
 
         # depending of problem to solve (maximizing or minimizing)

+ 40 - 24
macop/algorithms/mono.py

@@ -16,7 +16,7 @@ class HillClimberFirstImprovment(Algorithm):
     - And do these steps until a number of evaluation (stopping criterion) is reached.
 
     Attributes:
-        initalizer: {function} -- basic function strategy to initialize solution
+        initalizer: {function} -- basic function strategy to initialise solution
         evaluator: {Evaluator} -- evaluator instance 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
@@ -24,7 +24,8 @@ class HillClimberFirstImprovment(Algorithm):
         maximise: {bool} -- specify kind of optimisation 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
+        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
+        parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
     
     Example:
 
@@ -46,12 +47,12 @@ class HillClimberFirstImprovment(Algorithm):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=20: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
-    >>> algo = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
     >>> # run the algorithm
     >>> solution = algo.run(100)
     >>> solution._score
@@ -68,10 +69,10 @@ class HillClimberFirstImprovment(Algorithm):
             {Solution} -- best solution found
         """
 
-        # by default use of mother method to initialize variables
+        # by default use of mother method to initialise variables
         super().run(evaluations)
 
-        # initialize current solution and best solution
+        # initialise current solution and best solution
         self.initRun()
 
         solutionSize = self._currentSolution._size
@@ -119,7 +120,7 @@ class HillClimberBestImprovment(Algorithm):
     - And do these steps until a number of evaluation (stopping criterion) is reached.
 
     Attributes:
-        initalizer: {function} -- basic function strategy to initialize solution
+        initalizer: {function} -- basic function strategy to initialise solution
         evaluator: {Evaluator} -- evaluator instance 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
@@ -127,7 +128,8 @@ class HillClimberBestImprovment(Algorithm):
         maximise: {bool} -- specify kind of optimisation 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
+        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
+        parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
     
     Example:
 
@@ -149,12 +151,12 @@ class HillClimberBestImprovment(Algorithm):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=20: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
-    >>> algo = HillClimberBestImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = HillClimberBestImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
     >>> # run the algorithm
     >>> solution = algo.run(100)
     >>> solution._score
@@ -171,10 +173,10 @@ class HillClimberBestImprovment(Algorithm):
             {Solution} -- best solution found
         """
 
-        # by default use of mother method to initialize variables
+        # by default use of mother method to initialise variables
         super().run(evaluations)
 
-        # initialize current solution and best solution
+        # initialise current solution and best solution
         self.initRun()
 
         solutionSize = self._currentSolution._size
@@ -222,7 +224,7 @@ class IteratedLocalSearch(Algorithm):
     - Restart this process until stopping critirion (number of expected evaluations).
 
     Attributes:
-        initalizer: {function} -- basic function strategy to initialize solution
+        initalizer: {function} -- basic function strategy to initialise 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
@@ -231,7 +233,8 @@ class IteratedLocalSearch(Algorithm):
         currentSolution: {Solution} -- current solution managed for current evaluation
         bestSolution: {Solution} -- best solution found so far during running algorithm
         localSearch: {Algorithm} -- current local search into ILS
-        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
+        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
+        parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
     
     Example:
 
@@ -254,20 +257,20 @@ class IteratedLocalSearch(Algorithm):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=20: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
-    >>> local_search = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
-    >>> algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
+    >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
     >>> # run the algorithm
     >>> solution = algo.run(100, ls_evaluations=10)
     >>> solution._score
     137
     """
     def __init__(self,
-                 initializer,
+                 initialiser,
                  evaluator,
                  operators,
                  policy,
@@ -276,8 +279,21 @@ class IteratedLocalSearch(Algorithm):
                  maximise=True,
                  parent=None,
                  verbose=True):
+        """Iterated Local Search Algorithm initialisation with use of specific LocalSearch {Algorithm} instance
 
-        super().__init__(initializer, evaluator, operators, policy, validator,
+        Args:
+            initialiser: {function} -- basic function strategy to initialise solution
+            evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
+            operators: {[Operator]} -- list of operator to use when launching algorithm
+            policy: {Policy} -- Policy implementation strategy to select operators
+            validator: {function} -- basic function to check if solution is valid or not under some constraints
+            localSearch: {Algorithm} -- current local search into ILS
+            maximise: {bool} -- specify kind of optimisation problem 
+            parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
+            verbose: {bool} -- verbose or not information about the algorithm
+        """
+
+        super().__init__(initialiser, evaluator, operators, policy, validator,
                          maximise, parent, verbose)
 
         # specific local search associated with current algorithm
@@ -297,13 +313,13 @@ class IteratedLocalSearch(Algorithm):
             {Solution} -- best solution found
         """
 
-        # by default use of mother method to initialize variables
+        # by default use of mother method to initialise variables
         super().run(evaluations)
 
         # enable resuming for ILS
         self.resume()
 
-        # initialize current solution
+        # initialise current solution
         self.initRun()
 
         # add same callbacks

+ 89 - 41
macop/algorithms/multi.py

@@ -20,7 +20,7 @@ class MOEAD(Algorithm):
         mu: {int} -- number of sub problems
         T: {[float]} -- number of neightbors for each sub problem
         nObjectives: {int} -- number of objectives (based of number evaluator)
-        initializer: {function} -- basic function strategy to initialize solution
+        initialiser: {function} -- basic function strategy to initialise solution
         evaluator: {[function]} -- list of basic function in order to obtained fitness (multiple objectives)
         operators: {[Operator]} -- list of operator to use when launching algorithm
         policy: {Policy} -- Policy class implementation strategy to select operators
@@ -30,7 +30,8 @@ class MOEAD(Algorithm):
         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
-        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
+        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
+        parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
 
     >>> import random
     >>> # operators import
@@ -52,13 +53,13 @@ class MOEAD(Algorithm):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=20: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
     >>> # MOEAD use multi-objective, hence list of evaluators with mu=100 and T=10
-    >>> algo = MOEAD(20, 5, initializer, [evaluator1, evaluator2], operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = MOEAD(20, 5, initialiser, [evaluator1, evaluator2], operators, policy, validator, maximise=True, verbose=False)
     >>> # run the algorithm and get the pareto front obtained
     >>> pf_solutions = algo.run(100)
     >>> # check size of expected pareto
@@ -68,7 +69,7 @@ class MOEAD(Algorithm):
     def __init__(self,
                  mu,
                  T,
-                 initializer,
+                 initialiser,
                  evaluator,
                  operators,
                  policy,
@@ -76,13 +77,28 @@ class MOEAD(Algorithm):
                  maximise=True,
                  parent=None,
                  verbose=True):
+        """Multi-Ojective Evolutionary Algorithm with Scalar Decomposition initialisation
+
+        Args:
+            mu: {int} -- number of sub problems
+            T: {[float]} -- number of neightbors for each sub problem
+            initialiser: {function} -- basic function strategy to initialise solution
+            evaluator: {[function]} -- list of basic function in order to obtained fitness (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 optimisation problem
+            parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
+            verbose: {bool} -- verbose or not information about the algorithm
+        """
 
         # redefinition of constructor to well use `initRun` method
-        self._initializer = initializer
-        self._evaluator = evaluator
+        self.initialiser = initialiser
+        self.evaluator = evaluator
+        self.validator = validator
+
         self._operators = operators
-        self._policy = policy
-        self._validator = validator
+        self.policy = policy
         self._callbacks = []
 
         # by default
@@ -103,7 +119,7 @@ class MOEAD(Algorithm):
             operator.setAlgo(self)
 
         # by default track reference for policy
-        self._policy.setAlgo(self)
+        self.policy.setAlgo(self)
 
         if mu < T:
             raise ValueError('`mu` cannot be less than `T`')
@@ -114,7 +130,7 @@ class MOEAD(Algorithm):
         self._mu = mu
         self._T = T
 
-        # initialize neighbors for each sub problem
+        # initialise neighbors for each sub problem
         self.setNeighbors()
 
         weights = []
@@ -150,7 +166,7 @@ class MOEAD(Algorithm):
             # use copy of list to keep track for each sub problem
             subProblem = MOSubProblem(i,
                                       weights[i],
-                                      initializer,
+                                      initialiser,
                                       sub_evaluator,
                                       operators.copy(),
                                       policy,
@@ -161,7 +177,8 @@ class MOEAD(Algorithm):
 
             self._subProblems.append(subProblem)
 
-        self._population = [None for n in range(self._mu)]
+        self.population = [None for n in range(self._mu)]
+
         self._pfPop = []
 
         # ref point based on number of evaluators
@@ -174,7 +191,7 @@ class MOEAD(Algorithm):
 
     def initRun(self):
         """
-        Method which initialiazes or re-initializes the whole algorithm context specifically for MOEAD
+        Method which initialiazes or re-initialises the whole algorithm context specifically for MOEAD
         """
         # initialization is done during run method
         pass
@@ -190,18 +207,18 @@ class MOEAD(Algorithm):
             {Solution} -- best solution found
         """
 
-        # by default use of mother method to initialize variables
+        # by default use of mother method to initialise variables
         super().run(evaluations)
 
         # enable callback resume for MOEAD
         self.resume()
 
-        # initialize each sub problem if no backup
+        # initialise each sub problem if no backup
         for i in range(self._mu):
 
             if self._subProblems[i]._bestSolution is None:
                 self._subProblems[i].run(1)
-                self._population[i] = self._subProblems[i]._bestSolution
+                self.population[i] = self._subProblems[i]._bestSolution
 
         # if no backup for pf population
         if len(self._pfPop) == 0:
@@ -233,7 +250,7 @@ class MOEAD(Algorithm):
                         self._subProblems[j]._bestSolution = newSolution
 
                         # update population solution for this sub problem
-                        self._population[j] = newSolution
+                        self.population[j] = newSolution
 
                         improvment = True
 
@@ -252,7 +269,7 @@ class MOEAD(Algorithm):
                     break
 
         logging.info(
-            f"End of {type(self).__name__}, best solution found {self._population}"
+            f"End of {type(self).__name__}, best solution found {self.population}"
         )
 
         self.end()
@@ -301,19 +318,19 @@ class MOEAD(Algorithm):
     def updateRefPoint(self, solution):
 
         if self._maximise:
-            for i in range(len(self._evaluator)):
-                if solution._scores[i] > self._refPoint[i]:
-                    self._refPoint[i] = solution._scores[i]
+            for i in range(len(self.evaluator)):
+                if solution.scores[i] > self._refPoint[i]:
+                    self._refPoint[i] = solution.scores[i]
         else:
-            for i in range(len(self._evaluator)):
+            for i in range(len(self.evaluator)):
                 if solution.scores[i] < self._refPoint[i]:
-                    self._refPoint[i] = solution._scores[i]
+                    self._refPoint[i] = solution.scores[i]
 
     def paretoFront(self, population):
 
         paFront = []
         indexes = []
-        nObjectives = len(self._evaluator)
+        nObjectives = len(self.evaluator)
         nSolutions = len(population)
 
         # find dominated solution
@@ -333,12 +350,12 @@ class MOEAD(Algorithm):
                 nDominated = 0
 
                 # check number of dominated objectives of current solution by the others solution
-                for k in range(len(self._evaluator)):
+                for k in range(len(self.evaluator)):
                     if self._maximise:
-                        if population[i]._scores[k] < population[j]._scores[k]:
+                        if population[i].scores[k] < population[j].scores[k]:
                             nDominated += 1
                     else:
-                        if population[i]._scores[k] > population[j]._scores[k]:
+                        if population[i].scores[k] > population[j].scores[k]:
                             nDominated += 1
 
                 if nDominated == nObjectives:
@@ -352,6 +369,22 @@ class MOEAD(Algorithm):
 
         return paFront
 
+    def getResult(self):
+        """Get the expected result of the current algorithm
+
+        Returns:
+            [{Solution}] -- pareto front population
+        """
+        return self._pfPop
+
+    def setDefaultResult(self, result):
+        """Set current default result of the algorithm
+
+        Args:
+            result: {object} -- expected result data of the current algorithm
+        """
+        self._pfPop = result
+
     def end(self):
         """Display end message into `run` method
         """
@@ -362,7 +395,7 @@ class MOEAD(Algorithm):
         )
 
         for i, solution in enumerate(self._pfPop):
-            macop_text(self, f'  - [{i}] {solution._scores} : {solution}')
+            macop_text(self, f'  - [{i}] {solution.scores} : {solution}')
 
         macop_line(self)
 
@@ -371,10 +404,10 @@ class MOEAD(Algorithm):
         logging.info("-- Pareto front :")
 
         for i, solution in enumerate(self._pfPop):
-            logging.info(f"-- {i}] SCORE {solution._scores} - {solution}")
+            logging.info(f"-- {i}] SCORE {solution.scores} - {solution}")
 
     def __str__(self):
-        return f"{type(self).__name__} using {type(self._population).__name__}"
+        return f"{type(self).__name__} using {type(self.population).__name__}"
 
 
 class MOSubProblem(Algorithm):
@@ -383,7 +416,7 @@ class MOSubProblem(Algorithm):
     Attributes:
         index: {int} -- sub problem index
         weights: {[float]} -- sub problems objectives weights
-        initalizer: {function} -- basic function strategy to initialize solution
+        initalizer: {function} -- basic function strategy to initialise 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
@@ -392,7 +425,8 @@ class MOSubProblem(Algorithm):
         verbose: {bool} -- verbose or not information about the algorithm
         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
+        callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
+        parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
     
     Example:
 
@@ -416,17 +450,17 @@ class MOSubProblem(Algorithm):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=20: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
-    >>> algo = MOEAD(20, 5, initializer, [evaluator1, evaluator2], operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = MOEAD(20, 5, initialiser, [evaluator1, evaluator2], operators, policy, validator, maximise=True, verbose=False)
     >>> # weights of the sub problem
     >>> sub_problem_weights = [0.4, 0.6]
     >>> sub_evaluator = WeightedSum(data={'evaluators': [evaluator1, evaluator2], 'weights': sub_problem_weights})
     >>> # first parameter is the index of the MOSubProblem
-    >>> subProblem = MOSubProblem(0, sub_problem_weights, initializer, sub_evaluator, operators, policy, validator, maximise=True, parent=algo, verbose=False)
+    >>> subProblem = MOSubProblem(0, sub_problem_weights, initialiser, sub_evaluator, operators, policy, validator, maximise=True, parent=algo, verbose=False)
     >>> # run the algorithm
     >>> solution = subProblem.run(100)
     >>> solution._score
@@ -443,6 +477,20 @@ class MOSubProblem(Algorithm):
                  maximise=True,
                  parent=None,
                  verbose=True):
+        """Specific multi-objective sub problem used into MOEAD
+
+        Args:
+            index: {int} -- sub problem index
+            weights: {[float]} -- sub problems objectives weights
+            initalizer: {function} -- basic function strategy to initialise 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 optimisation problem 
+            parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
+            verbose: {bool} -- verbose or not information about the algorithm
+        """
 
         super().__init__(initalizer, evaluator, operators, policy, validator,
                          maximise, parent)
@@ -463,10 +511,10 @@ class MOSubProblem(Algorithm):
             {Solution} -- best solution found
         """
 
-        # by default use of mother method to initialize variables
+        # by default use of mother method to initialise variables
         super().run(evaluations)
 
-        # initialize solution if necessary
+        # initialise solution if necessary
         if self._bestSolution is None:
             self.initRun()
 
@@ -477,7 +525,7 @@ class MOSubProblem(Algorithm):
         for _ in range(evaluations):
 
             # keep reference of sub problem used
-            self._policy.setAlgo(self)
+            self.policy.setAlgo(self)
 
             # update solution using policy
             newSolution = self.update(self._bestSolution)

+ 6 - 0
macop/callbacks/base.py

@@ -16,6 +16,12 @@ class Callback():
         filepath: {str} -- file path where checkpoints will be saved
     """
     def __init__(self, every, filepath):
+        """Callback abstract initialiser
+
+        Args:
+            every: {int} -- checkpoint frequency used (based on number of evaluations)
+            filepath: {str} -- file path where checkpoints will be saved
+        """
 
         self._algo = None
         self._every = every

+ 8 - 9
macop/callbacks/classicals.py

@@ -25,7 +25,7 @@ class BasicCheckpoint(Callback):
         Check if necessary to do backup based on `every` variable
         """
         # get current best solution
-        solution = self._algo._bestSolution
+        solution = self._algo.getResult()
 
         currentEvaluation = self._algo.getGlobalEvaluation()
 
@@ -71,26 +71,25 @@ class BasicCheckpoint(Callback):
                 globalEvaluation = int(data[0])
 
                 if self._algo.getParent() is not None:
-                    self._algo.getParent(
-                    )._numberOfEvaluations = globalEvaluation
+                    self._algo.getParent().setEvaluation(globalEvaluation)
                 else:
-                    self._algo._numberOfEvaluations = globalEvaluation
+                    self._algo.setEvaluation(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()
+                if self._algo.getResult() is None:
+                    self._algo.setDefaultResult(self._algo.initialiser())
 
-                self._algo._bestsolution.setData(np.array(solutionData))
-                self._algo._bestSolution._score = float(data[2])
+                self._algo.getResult().setData(np.array(solutionData))
+                self._algo.getResult().setScore(float(data[2]))
 
             macop_line(self._algo)
             macop_text(self._algo,
                        f'Checkpoint found from `{self._filepath}` file.')
             macop_text(
                 self._algo,
-                f'Restart algorithm from evaluation {self._algo._numberOfEvaluations}.'
+                f'Restart algorithm from evaluation {self._algo.getEvaluation()}.'
             )
         else:
             macop_text(

+ 13 - 13
macop/callbacks/multi.py

@@ -25,7 +25,7 @@ class MultiCheckpoint(Callback):
         Check if necessary to do backup based on `every` variable
         """
         # get current population
-        population = self._algo._population
+        population = self._algo.population
 
         currentEvaluation = self._algo.getGlobalEvaluation()
 
@@ -48,8 +48,8 @@ class MultiCheckpoint(Callback):
 
                     line = str(currentEvaluation) + ';'
 
-                    for i in range(len(self._algo._evaluator)):
-                        line += str(solution._scores[i]) + ';'
+                    for i in range(len(self._algo.evaluator)):
+                        line += str(solution.scores[i]) + ';'
 
                     line += solutionData + ';\n'
 
@@ -80,18 +80,18 @@ class MultiCheckpoint(Callback):
                         else:
                             self._algo._numberOfEvaluations = globalEvaluation
 
-                    nObjectives = len(self._algo._evaluator)
+                    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
+                    # initialise and fill with data
+                    self._algo.population[i] = self._algo.initialiser()
+                    self._algo.population[i].setData(np.array(solutionData))
+                    self._algo.population[i].scores = scores
 
-                    self._algo._pfPop.append(self._algo._population[i])
+                    self._algo._pfPop.append(self._algo.population[i])
 
             macop_line(self._algo)
             macop_text(
@@ -149,8 +149,8 @@ class ParetoCheckpoint(Callback):
 
                     line = ''
 
-                    for i in range(len(self._algo._evaluator)):
-                        line += str(solution._scores[i]) + ';'
+                    for i in range(len(self._algo.evaluator)):
+                        line += str(solution.scores[i]) + ';'
 
                     line += solutionData + ';\n'
 
@@ -170,14 +170,14 @@ class ParetoCheckpoint(Callback):
 
                     data = line.replace(';\n', '').split(';')
 
-                    nObjectives = len(self._algo._evaluator)
+                    nObjectives = len(self._algo.evaluator)
                     scores = [float(s) for s in data[0:nObjectives]]
 
                     # get best solution data information
                     solutionData = list(map(int, data[-1].split(' ')))
 
                     self._algo._pfPop[i]._data = solutionData
-                    self._algo._pfPop[i]._scores = scores
+                    self._algo._pfPop[i].scores = scores
 
             macop_text(
                 self._algo,

+ 6 - 6
macop/callbacks/policies.py

@@ -37,20 +37,20 @@ class UCBCheckpoint(Callback):
 
                 rewardsLine = ''
 
-                for i, r in enumerate(self._algo._policy._rewards):
+                for i, r in enumerate(self._algo.policy.rewards):
                     rewardsLine += str(r)
 
-                    if i != len(self._algo._policy._rewards) - 1:
+                    if i != len(self._algo.policy.rewards) - 1:
                         rewardsLine += ';'
 
                 f.write(rewardsLine + '\n')
 
                 occurrencesLine = ''
 
-                for i, o in enumerate(self._algo._policy._occurences):
+                for i, o in enumerate(self._algo.policy.occurences):
                     occurrencesLine += str(o)
 
-                    if i != len(self._algo._policy._occurences) - 1:
+                    if i != len(self._algo.policy.occurences) - 1:
                         occurrencesLine += ';'
 
                 f.write(occurrencesLine + '\n')
@@ -69,10 +69,10 @@ class UCBCheckpoint(Callback):
                 rewardsLine = lines[0].replace('\n', '')
                 occurrencesLine = lines[1].replace('\n', '')
 
-                self._algo._policy._rewards = [
+                self._algo.policy.rewards = [
                     float(f) for f in rewardsLine.split(';')
                 ]
-                self._algo._policy._occurences = [
+                self._algo.policy.occurences = [
                     float(f) for f in occurrencesLine.split(';')
                 ]
 

+ 2 - 8
macop/evaluators/base.py

@@ -11,15 +11,9 @@ class Evaluator():
     """Abstract Evaluator class which enables to compute solution using specific `_data` 
     """
     def __init__(self, data: dict):
-                """Apply the computation of fitness from solution
-
-        Fitness is a float value for mono-objective or set of float values if multi-objective evaluation
-
+        """Initialise Evaluator instance which stores into its `_data` dictionary attritute required measures when computing a solution
         Args:
-            solution: {Solution} -- Solution instance
-
-        Return:
-            {float} -- computed solution score (float or set of float if multi-objective evaluation)
+            data: {dict} -- specific data dictionnary
         """
         self._data = data
 

+ 4 - 3
macop/evaluators/discrete/mono.py

@@ -43,6 +43,7 @@ class KnapsackEvaluator(Evaluator):
 
         return fitness
 
+
 class QAPEvaluator(Evaluator):
     """Quadratic Assignment Problem (QAP) evaluator class which enables to compute qap solution using specific `_data`
 
@@ -94,12 +95,13 @@ class QAPEvaluator(Evaluator):
         fitness = 0
         for index_i, val_i in enumerate(solution.getData()):
             for index_j, val_j in enumerate(solution.getData()):
-                fitness += self._data['F'][index_i, index_j] * self._data['D'][val_i, val_j]
+                fitness += self._data['F'][index_i,
+                                           index_j] * self._data['D'][val_i,
+                                                                      val_j]
 
         return fitness
 
 
-
 class UBQPEvaluator(Evaluator):
     """Unconstrained Binary Quadratic Programming (UBQP) evaluator class which enables to compute UBQP solution using specific `_data`
 
@@ -134,7 +136,6 @@ class UBQPEvaluator(Evaluator):
     >>> evaluator.compute(solution)
     477.0
     """
-
     def compute(self, solution):
         """Apply the computation of fitness from solution
 

+ 1 - 1
macop/evaluators/discrete/multi.py

@@ -53,7 +53,7 @@ class WeightedSum(Evaluator):
         ]
 
         # associate objectives scores to solution
-        solution._scores = scores
+        solution.scores = scores
 
         return sum(
             [scores[i] * w for i, w in enumerate(self._data['weights'])])

+ 23 - 0
macop/operators/base.py

@@ -17,6 +17,8 @@ class Operator():
     """
     @abstractmethod
     def __init__(self):
+        """Abstract Operator initialiser
+        """
         pass
 
     @abstractmethod
@@ -45,9 +47,19 @@ class Mutation(Operator):
         kind: {KindOperator} -- specify the kind of operator
     """
     def __init__(self):
+        """Mutation initialiser in order to specify kind of Operator
+        """
         self._kind = KindOperator.MUTATOR
 
     def apply(self, solution):
+        """Apply mutation over solution in order to obtained new solution
+
+        Args:
+            solution: {Solution} -- solution to use in order to create new solution
+
+        Return:
+            {Solution} -- new generated solution
+        """
         raise NotImplementedError
 
 
@@ -58,7 +70,18 @@ class Crossover(Operator):
         kind: {KindOperator} -- specify the kind of operator
     """
     def __init__(self):
+        """Crossover initialiser in order to specify kind of Operator
+        """
         self._kind = KindOperator.CROSSOVER
 
     def apply(self, solution1, solution2=None):
+        """Apply crossover using two solutions in order to obtained new solution
+
+        Args:
+            solution1: {Solution} -- the first solution to use for generating new solution
+            solution2: {Solution} -- the second solution to use for generating new solution
+
+        Return:
+            {Solution} -- new generated solution
+        """
         raise NotImplementedError

+ 12 - 12
macop/operators/discrete/crossovers.py

@@ -33,21 +33,21 @@ class SimpleCrossover(Crossover):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(20, 30) for i in range(10) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=10: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=10: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> simple_crossover = SimpleCrossover()
     >>> simple_mutation = SimpleMutation()
     >>> operators = [simple_crossover, simple_mutation]
     >>> policy = UCBPolicy(operators)
-    >>> local_search = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
-    >>> algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
+    >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
     >>> # using best solution, simple crossover is applied
     >>> best_solution = algo.run(100)
     >>> list(best_solution.getData())
     [1, 1, 0, 1, 0, 1, 1, 1, 0, 1]
-    >>> new_solution_1 = initializer()
-    >>> new_solution_2 = initializer()
+    >>> new_solution_1 = initialiser()
+    >>> new_solution_2 = initialiser()
     >>> offspring_solution = simple_crossover.apply(new_solution_1, new_solution_2)
     >>> list(offspring_solution.getData())
     [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
@@ -106,21 +106,21 @@ class RandomSplitCrossover(Crossover):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(20, 30) for i in range(10) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=10: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=10: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> random_split_crossover = RandomSplitCrossover()
     >>> simple_mutation = SimpleMutation()
     >>> operators = [random_split_crossover, simple_mutation]
     >>> policy = UCBPolicy(operators)
-    >>> local_search = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
-    >>> algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
+    >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
     >>> # using best solution, simple crossover is applied
     >>> best_solution = algo.run(100)
     >>> list(best_solution.getData())
     [1, 1, 1, 0, 1, 0, 1, 1, 1, 0]
-    >>> new_solution_1 = initializer()
-    >>> new_solution_2 = initializer()
+    >>> new_solution_1 = initialiser()
+    >>> new_solution_2 = initialiser()
     >>> offspring_solution = random_split_crossover.apply(new_solution_1, new_solution_2)
     >>> list(offspring_solution.getData())
     [0, 0, 0, 1, 1, 0, 0, 1, 0, 0]

+ 2 - 1
macop/operators/discrete/mutators.py

@@ -52,7 +52,8 @@ class SimpleMutation(Mutation):
         temp = copy_solution.getData()[firstCell]
 
         # swicth values
-        copy_solution.getData()[firstCell] = copy_solution.getData()[secondCell]
+        copy_solution.getData()[firstCell] = copy_solution.getData(
+        )[secondCell]
         copy_solution.getData()[secondCell] = temp
 
         return copy_solution

+ 6 - 1
macop/policies/base.py

@@ -14,6 +14,11 @@ class Policy():
         operators: {[Operator]} -- list of selected operators for the algorithm
     """
     def __init__(self, operators):
+        """Initialise new Policy instance using specific list of operators
+
+        Args:
+            operators: [{}] -- list of operators to use
+        """
         self._operators = operators
 
     @abstractmethod
@@ -45,7 +50,7 @@ class Policy():
 
         # default value of solution2 is current best solution
         if solution2 is None and self._algo is not None:
-            solution2 = self._algo._bestSolution
+            solution2 = self._algo.getResult()
 
         # avoid use of crossover if only one solution is passed
         if solution2 is None and operator._kind == KindOperator.CROSSOVER:

+ 27 - 16
macop/policies/reinforcement.py

@@ -47,28 +47,39 @@ class UCBPolicy(Policy):
     >>> # validator specification (based on weights of each objects)
     >>> weights = [ random.randint(5, 30) for i in range(20) ]
     >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
-    >>> # initializer function with lambda function
-    >>> initializer = lambda x=20: BinarySolution.random(x, validator)
+    >>> # initialiser function with lambda function
+    >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = UCBPolicy(operators)
-    >>> local_search = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
-    >>> algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
-    >>> policy._occurences
+    >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
+    >>> policy.occurences
     [0, 0]
     >>> solution = algo.run(100)
     >>> type(solution).__name__
     'BinarySolution'
-    >>> policy._occurences # one more due to first evaluation
+    >>> policy.occurences # one more due to first evaluation
     [51, 53]
     """
     def __init__(self, operators, C=100., exp_rate=0.5):
+        """UCB Policy initialiser
+
+        Args:
+            operators: {[Operator]} -- list of selected operators for the algorithm
+            C: {float} -- The second half of the UCB equation adds exploration, with the degree of exploration being controlled by the hyper-parameter `C`.
+            exp_rate: {float} -- exploration rate (probability to choose randomly next operator)
+        """
+
+        # private members
         self._operators = operators
-        self._rewards = [0. for o in self._operators]
-        self._occurences = [0 for o in self._operators]
         self._C = C
         self._exp_rate = exp_rate
 
+        # public members
+        self.rewards = [0. for o in self._operators]
+        self.occurences = [0 for o in self._operators]
+
     def select(self):
         """Select using Upper Confidence Bound the next operator to use (using acquired rewards)
 
@@ -76,7 +87,7 @@ class UCBPolicy(Policy):
             {Operator}: the selected operator
         """
 
-        indices = [i for i, o in enumerate(self._occurences) if o == 0]
+        indices = [i for i, o in enumerate(self.occurences) if o == 0]
 
         # random choice following exploration rate
         if np.random.uniform(0, 1) <= self._exp_rate:
@@ -88,12 +99,12 @@ class UCBPolicy(Policy):
 
             # if operator have at least be used one time
             ucbValues = []
-            nVisits = sum(self._occurences)
+            nVisits = sum(self.occurences)
 
             for i in range(len(self._operators)):
 
-                ucbValue = self._rewards[i] + self._C * math.sqrt(
-                    math.log(nVisits) / (self._occurences[i] + 0.1))
+                ucbValue = self.rewards[i] + self._C * math.sqrt(
+                    math.log(nVisits) / (self.occurences[i] + 0.1))
                 ucbValues.append(ucbValue)
 
             return self._operators[ucbValues.index(max(ucbValues))]
@@ -123,7 +134,7 @@ class UCBPolicy(Policy):
 
         # default value of solution2 is current best solution
         if solution2 is None and self._algo is not None:
-            solution2 = self._algo._bestSolution
+            solution2 = self._algo.getResult()
 
         # avoid use of crossover if only one solution is passed
         if solution2 is None and operator._kind == KindOperator.CROSSOVER:
@@ -138,7 +149,7 @@ class UCBPolicy(Policy):
             newSolution = operator.apply(solution1)
 
         # compute fitness of new solution
-        newSolution.evaluate(self._algo._evaluator)
+        newSolution.evaluate(self._algo.evaluator)
 
         # compute fitness improvment rate
         if self._algo._maximise:
@@ -151,9 +162,9 @@ class UCBPolicy(Policy):
         operator_index = self._operators.index(operator)
 
         if fir > 0:
-            self._rewards[operator_index] += fir
+            self.rewards[operator_index] += fir
 
-        self._occurences[operator_index] += 1
+        self.occurences[operator_index] += 1
 
         logging.info("---- Obtaining %s" % (newSolution))
 

+ 7 - 1
macop/solutions/base.py

@@ -74,10 +74,16 @@ class Solution():
         """
         self._data = data
 
+    def setScore(self, score):
+        """
+        Set solution score as wished
+        """
+        self._score = score
+
     @staticmethod
     def random(size, validator=None):
         """
-        Initialize solution using random data with validator or not
+        initialise solution using random data with validator or not
 
         Args:
             size: {int} -- expected solution size to generate

+ 3 - 3
macop/solutions/discrete.py

@@ -21,7 +21,7 @@ class BinarySolution(Solution):
     """
     def __init__(self, data, size):
         """
-        Initialize binary solution using specific data
+        initialise binary solution using specific data
 
         Args:
             data: {ndarray} --  array of binary values
@@ -95,7 +95,7 @@ class CombinatoryIntegerSolution(Solution):
     """
     def __init__(self, data, size):
         """
-        Initialize integer solution using specific data
+        initialise integer solution using specific data
 
         Args:
             data: {ndarray} --  array of integer values
@@ -163,7 +163,7 @@ class IntegerSolution(Solution):
     """
     def __init__(self, data, size):
         """
-        Initialize integer solution using specific data
+        initialise integer solution using specific data
 
         Args:
             data: {ndarray} --  array of binary values

+ 3 - 3
setup.py

@@ -73,14 +73,14 @@ class TestCommand(distutils.command.check.check):
 
 setup(
     name='macop',
-    version='1.0.9',
+    version='1.0.10',
     description='Minimalist And Customisable Optimisation Package',
     long_description=open('README.md').read(),
     long_description_content_type='text/markdown',
     classifiers=[
-        'Development Status :: 4 - Beta',
+        'Development Status :: 5 - Production/Stable',
         'License :: OSI Approved :: MIT License',
-        'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
         'Topic :: Scientific/Engineering',
         'Topic :: Utilities'
     ],