Parcourir la source

new documentation version

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

+ 0 - 1
.github/workflows/docs.yml

@@ -22,7 +22,6 @@ jobs:
     - name: Commit documentation changes
       run: |
         git clone https://github.com/jbuisine/macop.git --branch gh-pages --single-branch gh-pages
-        ls -l docs/_build
         cp -r docs/* gh-pages/docs
         cd gh-pages
         touch .nojekyll

+ 1 - 1
.github/workflows/python-app.yml

@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8]
+        python-version: [3.8, 3.9]
 
     steps:
     - uses: actions/checkout@v2

+ 1 - 1
.github/workflows/python-publish.yml

@@ -18,7 +18,7 @@ jobs:
     - name: Set up Python
       uses: actions/setup-python@v2
       with:
-        python-version: '3.x'
+        python-version: '3.8'
     - name: Install dependencies
       run: |
         python -m pip install --upgrade pip

+ 3 - 0
docs/source/conf.py

@@ -154,6 +154,9 @@ html_static_path = ['_static']
 # 'searchbox.html']``.
 #
 # html_sidebars = {}
+# html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] }
+
+html_sidebars = {'**': ['fulltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html']}
 
 # -- Options for HTMLHelp output ---------------------------------------------
 

Fichier diff supprimé car celui-ci est trop grand
+ 1329 - 0
docs/source/documentations.rst


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

@@ -193,7 +193,7 @@ We can now use the crossover operator created to generate new solutions. Here is
     # obtaining new solution using crossover
     offspring = crossover.apply(solution1, solution2)
 
-.. warning::
+.. tip::
     The developed ``SimpleCrossover`` is available into ``macop.operators.discrete.crossovers.SimpleCrossover`` in **Macop**.
     However, the choice of halves of the merged data is made randomly.
 

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

@@ -100,7 +100,7 @@ We can now use this operator choice policy to update our current solution:
     # pass two solutions in parameters in case of selected crossover operator
     new_solution = policy.apply(solution1, solution2)
 
-.. warning::
+.. caution::
     By default if ``solution2`` parameter is not provided into ``policy.apply`` method for crossover, the best solution known is used from the algorithm linked to the ``policy``.
 
 Updating solutions is therefore now possible with our policy. It is high time to dive into the process of optimizing solutions and digging into our research space.

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

@@ -62,7 +62,7 @@ We can now generate solutions randomly by passing our validation function as a p
     solution = BinarySolution.random(5, validator)
 
 
-.. warning::
+.. caution::
     If the search space for valid solutions is very small compared to the overall search space, this can involve a considerable time for validating the solution and therefore obtaining a solution.
 
 The validation of a solution is therefore now possible. In the next part we will focus on the evaluation of a solution.

+ 2 - 2
docs/source/examples.rst

@@ -9,8 +9,8 @@ Implemented problem examples
 .. toctree::
    :maxdepth: 1
 
-   examples/qap/index
-   examples/ubqp/index
+   examples/qap/global
+   examples/ubqp/global
 
 
 Available code examples

+ 303 - 0
docs/source/examples/qap/global.rst

@@ -0,0 +1,303 @@
+===============================
+Quadratric Assignment Problem
+===============================
+
+This example will deal with the use of the **Macop** package in relation to a quadratic assignment problem (QAP). We will use a known example of this problem to associate a set of facilities (:math:`F`) to a set of locations (:math:`L`).
+
+.. image:: ../../_static/examples/qap/factories_qap.png
+   :width: 50 %
+   :align: center
+   :alt: Example of QAP facilities to locations problem
+
+
+.. note:: 
+   The full code for what will be proposed in this example is available: qapExample.py_.
+
+.. _qapExample.py: https://github.com/jbuisine/macop/blob/master/examples/qapExample.py
+
+
+QAP problem definition
+======================
+
+The quadratic assignment problem (QAP) was introduced by Koopmans and Beckman in 1957 in the context of locating "indivisible economic activities". The objective of the problem is to assign a set of facilities to a set of locations in such a way as to minimize the total assignment cost. The assignment cost for a pair of facilities is a function of the flow between the facilities and the distance between the locations of the facilities.
+
+Location assignment example
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Consider a **facility location problem** with **four** facilities (and **four** locations). One possible assignment is shown in the figure below: facility 4 is assigned to location 1, facility 1 
+is assigned to location 2, facility 3 is assigned to location 3, and facility 2 is assigned to location 3. This assignment can be written as the permutation :math:`p=\{4,1,3,2\}`, 
+which means that facility 4 is assigned to location 1, facility 1 is assigned to location 2, facility 3 is assigned to location 3, and facility 2 is assigned to location 3. 
+In the figure, the line between a pair of facilities indicates that there is required flow between the facilities, and the thickness of the line increases with the value of the flow. 
+
+.. image:: ../../_static/examples/qap/factories_qap.png
+   :width: 50 %
+   :align: center
+   :alt: Example of QAP facilities to locations problem
+
+
+To calculate the assignment cost of the permutation, the required flows between facilities and the distances between locations are needed.
+
+
+.. tabularcolumns:: |p{1cm}|p{1cm}|p{1cm}|p{1cm}|
+
+.. csv-table:: flow of the current facilities
+   :header: facility `i`, facility `j`, flow( `i`\, `j` )
+   :widths: 2, 2, 3
+
+   1, 4, 4
+   3, 4, 10  
+   3, 1, 8
+   2, 1, 6  
+
+
+.. csv-table:: distances of between locations
+   :header: location `i`, location `j`, distances( `i`\, `j` )
+   :widths: 2, 2, 3
+
+   1, 2, 42
+   1, 3, 30  
+   2, 3, 41
+   3, 4, 23  
+
+
+Then, the assignment cost of the permutation can be computed as:
+
+:math:`f(1,4)⋅d(1,2)+f(3,4)⋅d(1,3)+f(1,3)⋅d(2,3)+f(3,2)⋅d(3,4)` 
+with result :math:`4⋅42+10⋅30+8⋅41+6⋅23=934`.
+
+Note that this permutation is not the optimal solution.
+
+Mathematical definition
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Sets**
+
+- :math:`N=\{1,2,⋯,n\}`
+- :math:`S_n=\phi:N→N` is the set of all permutations
+
+**Parameters**
+
+- :math:`F=(f_{ij})` is an :math:`n×n` matrix where :math:`f_{ij}` is the required flow between facilities :math:`i` and :math:`j`
+- :math:`D=(d_{ij})` is an :math:`n×n` matrix where :math:`d_{ij}` is the distance between locations :math:`i` and :math:`j`.
+
+**Optimization Problem**
+
+- :math:`min_{ϕ∈S_n}\sum_{i=1}^{n}{\sum_{j=1}^{n}{f_{ij}⋅d_{\phi(i)\phi(j)}}}`
+
+The assignment of facilities to locations is represented by a permutation :math:`\phi`, where :math:`\phi(i)` is the location to which facility :math:`i` is assigned. Each individual product :math:`f_{ij}⋅d_{\phi(i)\phi(j)}` is the cost of assigning facility :math:`i` to location :math:`\phi(i)` and facility :math:`j` to location :math:`\phi(j)`.
+
+QAP Problem instance generation
+===============================
+
+To define our quadratic assignment problem instance, we will use the available mQAP_ multi-objective quadratic problem generator. 
+
+Genration of the instance
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We will limit ourselves here to a single objective for the purposes of this example. The file **makeQAPuni.cc**, will be used to generate the instance.
+
+.. code:: bash
+
+    g++ makeQAPuni.cc -o mQAPGenerator
+    ./mQAPGenerator -n 100 -k 1 -f 30 -d 80 -s 42 > qap_instance.txt
+
+with the following parameters:
+
+- **-n** positive integer: number of facilities/locations;
+- **-k** positive integer: number of objectives;
+- **-f** positive integer: maximum flow between facilities;
+- **-d** positive integer: maximum distance between locations;
+- **-s** positive long: random seed.
+
+The generated qap_instance.txt_ file contains the two matrices :math:`F` and :math:`D` and define our instance problem.
+
+.. _mQAP: https://www.cs.bham.ac.uk/~jdk/mQAP/
+
+.. _qap_instance.txt: https://github.com/jbuisine/macop/blob/master/examples/instances/qap/qap_instance.txt
+
+
+Load data instance
+~~~~~~~~~~~~~~~~~~
+
+
+We are now going to load this instance via a Python code which will be useful to us later on:
+
+.. code:: Python
+
+    qap_instance_file = 'qap_instance.txt'
+
+    n = 100 # the instance size
+
+    with open(qap_instance_file, 'r') as f:
+        file_data = f.readlines()
+        print(f'Instance information {file_data[0]}')
+
+        D_lines = file_data[1:n + 1]
+        D_data = ''.join(D_lines).replace('\n', '')
+
+        F_lines = file_data[n:2 * n + 1]
+        F_data = ''.join(F_lines).replace('\n', '')
+
+    D_matrix = np.fromstring(D_data, dtype=float, sep=' ').reshape(n, n)
+    print(f'D matrix shape: {D_matrix.shape}')
+    F_matrix = np.fromstring(F_data, dtype=float, sep=' ').reshape(n, n)
+    print(f'F matrix shape: {F_matrix.shape}')
+
+.. note::
+    As we know the size of our instance and the structure of the document, it is quite quick to look for the lines related to the :math:`F` and :math:`D` matrices.
+
+Macop QAP implementation
+========================
+
+Let's see how it is possible with the use of the **Macop** package to implement and deal with this QAP instance problem.
+
+Solution structure definition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Firstly, we are going to use a type of solution that will allow us to define the structure of our solutions.
+
+The available ``macop.solutions.discrete.CombinatoryIntegerSolution`` type of solution within the Macop package represents exactly what one would wish for. 
+I.e. a solution that stores a sequence of integers relative to the size of the problem, the order of which is not sorted.
+
+Let's see an example of its use:
+
+.. code:: python
+
+    from macop.solutions.discrete import CombinatoryIntegerSolution
+    
+    solution = CombinatoryIntegerSolution.random(10)
+    print(solution)
+
+
+The resulting solution obtained:
+
+.. code:: bash
+
+    Combinatory integer solution [2 9 8 1 7 6 0 4 3 5]
+
+
+QAP Evaluator
+~~~~~~~~~~~~~
+
+Now that we have the structure of our solutions, and the means to generate them, we will seek to evaluate them.
+
+To do this, we need to create a new evaluator specific to our problem and the relative evaluation function:
+
+- :math:`min_{ϕ∈S_n}\sum_{i=1}^{n}{\sum_{j=1}^{n}{f_{ij}⋅d_{\phi(i)\phi(j)}}}`
+
+So we are going to create a class that will inherit from the abstract class ``macop.evalutarors.base.Evaluator``:
+
+
+.. code:: python
+
+    from macop.evaluators.base import Evaluator
+
+    class QAPEvaluator(Evaluator):
+    """QAP evaluator class which enables to compute QAP solution using specific `_data`
+
+    - stores into its `_data` dictionary attritute required measures when computing a QAP solution
+    - `_data['F']` matrix of size n x n with flows data between facilities (stored as numpy array)
+    - `_data['D']` matrix of size n x n with distances data between locations (stored as numpy array)
+    - `compute` method enables to compute and associate a score to a given QAP solution
+    """
+
+    def compute(self, solution):
+        """Apply the computation of fitness from solution
+
+        Args:
+            solution: {Solution} -- QAP solution instance
+    
+        Returns:
+            {float} -- fitness score of solution
+        """
+        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]
+
+        return fitness
+
+The cost function for the quadratic problem is now well defined.
+
+.. tip::
+    The class proposed here, is available in the Macop package ``macop.evaluators.discrete.mono.QAPEvaluator``.
+
+Running algorithm
+~~~~~~~~~~~~~~~~~
+
+Now that the necessary tools are available, we will be able to deal with our problem and look for solutions in the search space of our QAP instance.
+
+Here we will use local search algorithms already implemented in **Macop**.
+
+If you are uncomfortable with some of the elements in the code that will follow, you can refer to the more complete **Macop** documentation_ that focuses more on the concepts and tools of the package.
+
+.. code:: python
+
+    # main imports
+    import numpy as np
+
+    # module imports
+    from macop.solutions.discrete import CombinatoryIntegerSolution
+    from macop.evaluators.discrete.mono import QAPEvaluator
+
+    from macop.operators.discrete.mutators import SimpleMutation
+
+    from macop.policies.classicals import RandomPolicy
+
+    from macop.algorithms.mono import IteratedLocalSearch as ILS
+    from macop.algorithms.mono import HillClimberFirstImprovment
+
+    # usefull instance data
+    n = 100
+    qap_instance_file = 'qap_instance.txt'
+
+    # default validator (check the consistency of our data, i.e. only unique element)
+    def validator(solution):
+        if len(list(solution.getData())) > len(set(list(solution.getData()))):
+            print("not valid")
+            return False
+        return True
+
+    # define init random solution
+    def init():
+        return CombinatoryIntegerSolution.random(n, validator)
+
+    # load qap instance
+    with open(qap_instance_file, 'r') as f:
+        file_data = f.readlines()
+        print(f'Instance information {file_data[0]}')
+
+        D_lines = file_data[1:n + 1]
+        D_data = ''.join(D_lines).replace('\n', '')
+
+        F_lines = file_data[n:2 * n + 1]
+        F_data = ''.join(F_lines).replace('\n', '')
+
+    D_matrix = np.fromstring(D_data, dtype=float, sep=' ').reshape(n, n)
+    print(f'D matrix shape: {D_matrix.shape}')
+    F_matrix = np.fromstring(F_data, dtype=float, sep=' ').reshape(n, n)
+    print(f'F matrix shape: {F_matrix.shape}')
+
+    # only one operator here
+    operators = [SimpleMutation()]
+
+    # random policy even if list of solution has only one element
+    policy = RandomPolicy(operators)
+
+    # use of loaded data from QAP instance
+    evaluator = QAPEvaluator(data={'F': F_matrix, 'D': D_matrix})
+
+    # passing global evaluation param from ILS
+    hcfi = HillClimberFirstImprovment(init, evaluator, operators, policy, validator, maximise=False, verbose=True)
+    algo = ILS(init, evaluator, operators, policy, validator, localSearch=hcfi, maximise=False, verbose=True)
+
+    # run the algorithm
+    bestSol = algo.run(10000, ls_evaluations=100)
+
+    print('Solution for QAP instance score is {}'.format(evaluator.compute(bestSol)))
+
+
+QAP problem solving is now possible with **Macop**. As a reminder, the complete code is available in the qapExample.py_ file.
+
+.. _qapExample.py: https://github.com/jbuisine/macop/blob/master/examples/qapExample.py
+.. _documentation: https://jbuisine.github.io/macop/_build/html/documentations

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

@@ -71,7 +71,7 @@ So we are going to create a class that will inherit from the abstract class ``ma
 
 The cost function for the quadratic problem is now well defined.
 
-.. warning::
+.. tip::
     The class proposed here, is available in the Macop package ``macop.evaluators.discrete.mono.QAPEvaluator``.
 
 Running algorithm

Fichier diff supprimé car celui-ci est trop grand
+ 225 - 0
docs/source/examples/ubqp/global.rst


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

@@ -68,7 +68,7 @@ So we are going to create a class that will inherit from the abstract class ``ma
 
 The cost function for the Unconstrained binary quadratic problem is now well defined.
 
-.. warning::
+.. tip::
     The class proposed here, is available in the Macop package ``macop.evaluators.discrete.mono.UBQPEvaluator``.
 
 Running algorithm

+ 2 - 2
docs/source/index.rst

@@ -19,7 +19,7 @@ Contents
 
    description
 
-   documentations/index
+   documentations
 
    api
 
@@ -42,7 +42,7 @@ Flexible discrete optimisation package allowing a quick implementation of your p
 
 
 Indices and tables
-------------------
+~~~~~~~~~~~~~~~~~~
 
 * :ref:`genindex`
 * :ref:`modindex`

+ 33 - 6
macop/algorithms/mono.py

@@ -30,29 +30,38 @@ class HillClimberFirstImprovment(Algorithm):
     Example:
 
     >>> import random
+    >>>
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.classicals import RandomPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # solution and algorithm imports
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.mono import HillClimberFirstImprovment
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> problem_size = 20
     >>> worths = [ random.randint(0, 20) for i in range(problem_size) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # 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
-    >>> # initialiser function with lambda function
+    >>>
+    >>> # initialiser function for binary solution using specific solution size
     >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
+    >>>
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
     >>> algo = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>>
     >>> # run the algorithm
     >>> solution = algo.run(100)
     >>> solution._score
@@ -134,29 +143,38 @@ class HillClimberBestImprovment(Algorithm):
     Example:
 
     >>> import random
+    >>>
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.classicals import RandomPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # solution and algorithm imports
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.mono import HillClimberBestImprovment
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> problem_size = 20
     >>> worths = [ random.randint(0, 20) for i in range(problem_size) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # 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
-    >>> # initialiser function with lambda function
+    >>>
+    >>> # initialiser function for binary solution using specific solution size
     >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
+    >>>
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
     >>> algo = HillClimberBestImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
+    >>>
     >>> # run the algorithm
     >>> solution = algo.run(100)
     >>> solution._score
@@ -239,32 +257,41 @@ class IteratedLocalSearch(Algorithm):
     Example:
 
     >>> import random
+    >>>
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.classicals import RandomPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # import for solution and algorithm
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.mono import IteratedLocalSearch
     >>> from macop.algorithms.mono import HillClimberFirstImprovment
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> problem_size = 20
     >>> worths = [ random.randint(0, 20) for i in range(problem_size) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # 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
+    >>>
     >>> # 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(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
+    >>>
+    >>> # run the algorithm using specific number of evaluations for local search
     >>> solution = algo.run(100, ls_evaluations=10)
     >>> solution._score
     137

+ 26 - 4
macop/algorithms/multi.py

@@ -34,34 +34,45 @@ class MOEAD(Algorithm):
         parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
 
     >>> import random
+    >>>
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.classicals import RandomPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # solution and algorithm imports
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.multi import MOEAD
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> problem_size = 20
     >>> worths1 = [ random.randint(0, 20) for i in range(problem_size) ]
     >>> evaluator1 = KnapsackEvaluator(data={'worths': worths1})
     >>> worths2 = [ random.randint(10, 15) for i in range(problem_size) ]
     >>> evaluator2 = KnapsackEvaluator(data={'worths': worths2})
+    >>>
     >>> # 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
-    >>> # initialiser function with lambda function
+    >>>
+    >>> # initialiser function for binary solution using specific solution size
     >>> 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, 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
     >>> len(pf_solutions)
     33
@@ -431,36 +442,47 @@ class MOSubProblem(Algorithm):
     Example:
 
     >>> import random
+    >>>
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.classicals import RandomPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # solution and algorithm imports
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.multi import MOEAD, MOSubProblem
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> problem_size = 20
     >>> worths1 = [ random.randint(0, 20) for i in range(problem_size) ]
     >>> evaluator1 = KnapsackEvaluator(data={'worths': worths1})
     >>> worths2 = [ random.randint(10, 15) for i in range(problem_size) ]
     >>> evaluator2 = KnapsackEvaluator(data={'worths': worths2})
+    >>>
     >>> # 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
-    >>> # initialiser function with lambda function
+    >>>
+    >>> # initialiser function for binary solution using specific solution size
     >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
+    >>>
     >>> # operators list with crossover and mutation
     >>> operators = [SimpleCrossover(), SimpleMutation()]
     >>> policy = RandomPolicy(operators)
     >>> 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, initialiser, sub_evaluator, operators, policy, validator, maximise=True, parent=algo, verbose=False)
+    >>>
     >>> # run the algorithm
     >>> solution = subProblem.run(100)
     >>> solution._score

+ 19 - 0
macop/evaluators/discrete/mono.py

@@ -14,16 +14,20 @@ class KnapsackEvaluator(Evaluator):
     Example:
 
     >>> import random
+    >>>
     >>> # binary solution import
     >>> from macop.solutions.discrete import BinarySolution
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
     >>> solution_data = [1, 0, 0, 1, 1, 0, 1, 0]
     >>> size = len(solution_data)
     >>> solution = BinarySolution(solution_data, size)
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> worths = [ random.randint(5, 20) for i in range(size) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # compute solution score
     >>> evaluator.compute(solution)
     40
@@ -58,13 +62,17 @@ class QAPEvaluator(Evaluator):
 
     >>> import random
     >>> import numpy as np
+    >>>
     >>> # combinatory solution import
     >>> from macop.solutions.discrete import CombinatoryIntegerSolution
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import QAPEvaluator
+    >>>
     >>> # define problem data using QAP example instance
     >>> qap_instance_file = 'examples/instances/qap/qap_instance.txt'
     >>> n = 100 # problem size
+    >>>
     >>> # loading data
     >>> f = open(qap_instance_file, 'r')
     >>> file_data = f.readlines()
@@ -75,10 +83,13 @@ class QAPEvaluator(Evaluator):
     >>> D_matrix = np.fromstring(D_data, dtype=float, sep=' ').reshape(n, n)
     >>> F_matrix = np.fromstring(F_data, dtype=float, sep=' ').reshape(n, n)
     >>> f.close()    
+    >>>
     >>> # create evaluator instance using loading data
     >>> evaluator = QAPEvaluator(data={'F': F_matrix, 'D': D_matrix})
+    >>>
     >>> # create new random combinatory solution using n, the instance QAP size
     >>> solution = CombinatoryIntegerSolution.random(n)
+    >>>
     >>> # compute solution score
     >>> evaluator.compute(solution)
     6397983.0
@@ -113,25 +124,33 @@ class UBQPEvaluator(Evaluator):
 
     >>> import random
     >>> import numpy as np
+    >>>
     >>> # binary solution import
     >>> from macop.solutions.discrete import BinarySolution
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import UBQPEvaluator
+    >>>
     >>> # define problem data using UBQP example instance
     >>> ubqp_instance_file = 'examples/instances/ubqp/ubqp_instance.txt'
     >>> n = 100 # problem size
+    >>>
     >>> # loading data
     >>> f = open(ubqp_instance_file, 'r')
     >>> file_data = f.readlines()
+    >>>
     >>> # get all string floating point values of matrix
     >>> Q_data = ''.join([ line.replace('\\n', '') for line in file_data[8:] ])
     >>> # load the concatenate obtained string
     >>> Q_matrix = np.fromstring(Q_data, dtype=float, sep=' ').reshape(n, n)
     >>> f.close()    
+    >>>
     >>> # create evaluator instance using loading data
     >>> evaluator = UBQPEvaluator(data={'Q': Q_matrix})
+    >>>
     >>> # create new random combinatory solution using n, the instance QAP size
     >>> solution = BinarySolution.random(n)
+    >>>
     >>> # compute solution score
     >>> evaluator.compute(solution)
     477.0

+ 6 - 0
macop/evaluators/discrete/multi.py

@@ -13,21 +13,27 @@ class WeightedSum(Evaluator):
     - `compute` method enables to compute and associate a tuples of scores to a given solution
     
     >>> import random
+    >>>
     >>> # binary solution import
     >>> from macop.solutions.discrete import BinarySolution
+    >>>
     >>> # evaluators imports
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
     >>> from macop.evaluators.discrete.multi import WeightedSum
     >>> solution_data = [1, 0, 0, 1, 1, 0, 1, 0]
     >>> size = len(solution_data)
     >>> solution = BinarySolution(solution_data, size)
+    >>>
     >>> # evaluator 1 initialization (worths objects passed into data)
     >>> worths1 = [ random.randint(5, 20) for i in range(size) ]
     >>> evaluator1 = KnapsackEvaluator(data={'worths': worths1})
+    >>>
     >>> # evaluator 2 initialization (worths objects passed into data)
     >>> worths2 = [ random.randint(10, 15) for i in range(size) ]
     >>> evaluator2 = KnapsackEvaluator(data={'worths': worths2})
     >>> weighted_evaluator = WeightedSum(data={'evaluators': [evaluator1, evaluator2], 'weights': [0.3, 0.7]})
+    >>>
+    >>> # compute score and check with expected one
     >>> weighted_score = weighted_evaluator.compute(solution)
     >>> expected_score = evaluator1.compute(solution) * 0.3 + evaluator2.compute(solution) * 0.7
     >>> weighted_score == expected_score

+ 20 - 4
macop/operators/discrete/crossovers.py

@@ -19,22 +19,29 @@ class SimpleCrossover(Crossover):
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.reinforcement import UCBPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # solution and algorithm imports
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.mono import IteratedLocalSearch
     >>> from macop.algorithms.mono import HillClimberFirstImprovment
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> worths = [ random.randint(0, 20) for i in range(10) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # 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
-    >>> # initialiser function with lambda function
+    >>>
+    >>> # initialiser function for binary solution using specific solution size
     >>> initialiser = lambda x=10: BinarySolution.random(x, validator)
+    >>>
     >>> # operators list with crossover and mutation
     >>> simple_crossover = SimpleCrossover()
     >>> simple_mutation = SimpleMutation()
@@ -42,6 +49,7 @@ class SimpleCrossover(Crossover):
     >>> policy = UCBPolicy(operators)
     >>> 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())
@@ -92,22 +100,29 @@ class RandomSplitCrossover(Crossover):
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import RandomSplitCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.reinforcement import UCBPolicy
-    >>> # solution and algorithm
+    >>>
+    >>> # solution and algorithm imports
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.mono import IteratedLocalSearch
     >>> from macop.algorithms.mono import HillClimberFirstImprovment
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
+    >>>
     >>> # evaluator initialization (worths objects passed into data)
     >>> worths = [ random.randint(0, 20) for i in range(10) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # 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
-    >>> # initialiser function with lambda function
+    >>>
+    >>> # initialiser function for binary solution using specific solution size
     >>> initialiser = lambda x=10: BinarySolution.random(x, validator)
+    >>>
     >>> # operators list with crossover and mutation
     >>> random_split_crossover = RandomSplitCrossover()
     >>> simple_mutation = SimpleMutation()
@@ -115,6 +130,7 @@ class RandomSplitCrossover(Crossover):
     >>> policy = UCBPolicy(operators)
     >>> 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())

+ 2 - 0
macop/policies/classicals.py

@@ -20,6 +20,8 @@ class RandomPolicy(Policy):
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
     >>> from macop.policies.classicals import RandomPolicy
+    >>>
+    >>> # create policy instance and select next operator to apply using policy
     >>> policy = RandomPolicy([SimpleCrossover(), SimpleMutation()])
     >>> operator = policy.select()
     >>> type(operator).__name__

+ 14 - 2
macop/policies/reinforcement.py

@@ -23,32 +23,44 @@ class UCBPolicy(Policy):
 
     Attributes:
         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`.
+        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)
         rewards: {[float]} -- list of summed rewards obtained for each operator
         occurrences: {[int]} -- number of use (selected) of each operator
 
+    The value of attribute ``C`` will allow us to specify whether we wish to exploit or explore further in relation to our earned rewards. 
+    A low value of ``C`` (e.g. 2) will allow more exploitation, while a high value of ``C`` (e.g. 1000) will allow exploration.
+
+    The ``exp_rate`` variable avoids using an operator too much and allows to explore from time to time (especially if the variable ``C`` has a small value). Typical value for ``exp_rate`` can be 0.9.
+
     Example:
 
     >>> # operators import
     >>> from macop.operators.discrete.crossovers import SimpleCrossover
     >>> from macop.operators.discrete.mutators import SimpleMutation
+    >>>
     >>> # policy import
     >>> from macop.policies.reinforcement import UCBPolicy
+    >>>
     >>> # solution and algorithm
     >>> from macop.solutions.discrete import BinarySolution
     >>> from macop.algorithms.mono import IteratedLocalSearch
     >>> from macop.algorithms.mono import HillClimberFirstImprovment
+    >>>
     >>> # evaluator import
     >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
     >>> # evaluator initialization (worths objects passed into data)
+    >>>
     >>> worths = [ random.randint(0, 20) for i in range(20) ]
     >>> evaluator = KnapsackEvaluator(data={'worths': worths})
+    >>>
     >>> # 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
+    >>>
     >>> # 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)
@@ -62,7 +74,7 @@ class UCBPolicy(Policy):
     >>> policy.occurences # one more due to first evaluation
     [51, 53]
     """
-    def __init__(self, operators, C=100., exp_rate=0.5):
+    def __init__(self, operators, C=100., exp_rate=0.9):
         """UCB Policy initialiser
 
         Args:

+ 8 - 0
macop/solutions/discrete.py

@@ -30,9 +30,11 @@ class BinarySolution(Solution):
         Example:
 
         >>> from macop.solutions.discrete import BinarySolution
+        >>>
         >>> # build of a solution using specific data and size
         >>> data = [0, 1, 0, 1, 1]
         >>> solution = BinarySolution(data, len(data))
+        >>>
         >>> # check data content
         >>> sum(solution.getData()) == 3
         True
@@ -58,6 +60,8 @@ class BinarySolution(Solution):
         Example:
 
         >>> from macop.solutions.discrete import BinarySolution
+        >>>
+        >>> # generate random solution using specific validator
         >>> validator = lambda solution: True if sum(solution.getData()) > 5 else False
         >>> solution = BinarySolution.random(10, validator)
         >>> sum(solution.getData()) > 5
@@ -128,6 +132,8 @@ class CombinatoryIntegerSolution(Solution):
         Example:
 
         >>> from macop.solutions.discrete import CombinatoryIntegerSolution
+        >>>
+        >>> # generate random solution using specific validator
         >>> validator = lambda solution: True if sum(solution.getData()) > 5 else False
         >>> solution = CombinatoryIntegerSolution.random(5, validator)
         >>> sum(solution.getData()) > 5
@@ -201,6 +207,8 @@ class IntegerSolution(Solution):
         >>> from macop.solutions.discrete import IntegerSolution
         >>> import numpy as np
         >>> np.random.seed(42)
+        >>>
+        >>> # generate random solution using specific validator
         >>> validator = lambda solution: True if sum(solution.getData()) > 5 else False
         >>> solution = IntegerSolution.random(5, validator)
         >>> sum(solution.getData()) > 10

Fichier diff supprimé car celui-ci est trop grand
+ 8 - 3
paper.md