ソースを参照

Merge branch 'release/v1.0.9'

Jérôme BUISINE 3 年 前
コミット
c0e4430fec
51 ファイル変更11349 行追加911 行削除
  1. 137 0
      CONTRIBUTING
  2. 0 176
      CONTRIBUTING.md
  3. 6 3
      README.md
  4. 0 14
      build.sh
  5. 1 1
      docs/source/_static/css/custom.css
  6. BIN
      docs/source/_static/examples/qap/factories_qap.png
  7. BIN
      docs/source/_static/logo_macop.png~
  8. 3 2
      docs/source/conf.py
  9. 1 5
      docs/source/contributing.rst
  10. 2 2
      docs/source/documentations/algorithms.rst
  11. 7 7
      docs/source/documentations/introduction.rst
  12. 2 2
      docs/source/documentations/operators.rst
  13. 6 2
      docs/source/documentations/others.rst
  14. 1 1
      docs/source/documentations/policies.rst
  15. 2 2
      docs/source/documentations/problem.rst
  16. 1 1
      docs/source/documentations/solutions.rst
  17. 1 1
      docs/source/documentations/validator.rst
  18. 16 615
      docs/source/examples.rst
  19. 155 0
      docs/source/examples/qap/implementation.rst
  20. 24 0
      docs/source/examples/qap/index.rst
  21. 59 0
      docs/source/examples/qap/instance.rst
  22. 70 0
      docs/source/examples/qap/problem.rst
  23. 146 0
      docs/source/examples/ubqp/implementation.rst
  24. 15 0
      docs/source/examples/ubqp/index.rst
  25. 52 0
      docs/source/examples/ubqp/instance.rst
  26. 17 0
      docs/source/examples/ubqp/problem.rst
  27. 4 1
      docs/source/index.rst
  28. 202 0
      examples/instances/qap/qap_instance.txt
  29. 10008 0
      examples/instances/ubqp/ubqp_instance.txt
  30. 1 1
      examples/knapsackExample.py
  31. 1 1
      examples/knapsackMultiExample.py
  32. 80 0
      examples/qapExample.py
  33. 77 0
      examples/ubqpExample.py
  34. 16 9
      macop/algorithms/base.py
  35. 21 11
      macop/algorithms/mono.py
  36. 37 16
      macop/algorithms/multi.py
  37. 15 6
      macop/callbacks/classicals.py
  38. 24 8
      macop/callbacks/multi.py
  39. 4 2
      macop/callbacks/policies.py
  40. 111 4
      macop/evaluators/discrete/mono.py
  41. 6 3
      macop/evaluators/discrete/multi.py
  42. 1 2
      macop/operators/base.py
  43. 0 1
      macop/operators/continuous/crossovers.py
  44. 1 2
      macop/policies/base.py
  45. 1 2
      macop/policies/reinforcement.py
  46. 1 1
      macop/solutions/base.py
  47. 2 3
      macop/solutions/discrete.py
  48. 1 1
      macop/utils/progress.py
  49. 2 0
      paper.bib
  50. 8 2
      paper.md
  51. 1 1
      setup.py

+ 137 - 0
CONTRIBUTING

@@ -0,0 +1,137 @@
+How to contribute
+=====================================
+
+<p align="center">
+    <img src="https://github.com/jbuisine/macop/blob/master/docs/source/_static/logo_macop.png" alt="" width="40%">
+</p>
+
+
+
+# Welcome !
+
+Thank you for taking the time to read this guide for the package's contribution. I'm glad to know that you may bring a lot to the **Macop** package. This document will show you the good development practices used in the project and how you can easily participate in its evolution!
+
+# Table of contents
+
+1. [Submission processes](#submission-process)
+
+    1.2. [Submit an issue](#submit-an-issue)
+
+    1.1. [Pull request](#pull-request)
+
+2. [Coding conventions](#coding-conventions)
+
+    2.1. [Python conventions](#python-conventions)
+
+    2.2. [Code documentation](#code-documentation)
+
+    2.3. [Testing](#test-implementation)
+
+
+# Submission process
+
+## Submit an issue
+
+Do not hesitate to report bug or issue in [https://github.com/jbuisine/macop/issues](https://github.com/jbuisine/macop/issues) with the common template header:
+
+```
+**Package version:** X.X.X
+**Issue label:** XXXXX
+**Targeted modules:** `macop.algorithms`, `macop.policies`
+**Operating System:** Manjaro Linux
+
+**Description:** XXXXX
+```
+
+## Pull request
+
+If you have made changes to the project you have forked, you can submit a pull request in [https://github.com/jbuisine/macop/pulls](https://github.com/jbuisine/macop/pulls) in order to have your changes added inside new version of the `Macop` package. A [GitHub documentation](https://help.github.com/articles/about-pull-requests/) about pull requests is available if necessary.
+
+To enhance the package, do not hesitate to fix bug or missing feature. To do that, just submit your pull request with this common template header:
+
+```
+**Package version:** X.X.X
+**Enhancements label:** XXXXX
+**Targeted modules:** `macop.algorithms`, `macop.policies`
+**New modules:** `macop.XXXX`, `macop.algorithms.XXXX`
+
+**Description:** XXXXX
+```
+
+**Note:** the code conventions required for the approval of your changes are described below.
+
+Whatever the problem reported, I will thank you for your contribution to this project. So do not hesitate.
+
+
+# Coding conventions
+
+## Python conventions
+
+This project follows the [coding conventions](http://google.github.io/styleguide/pyguide.html) implemented by Google. To help you to format **\*.py** files, it is possible to use the [yapf](https://github.com/google/yapf/) Python package developed by Google.
+
+```
+yapf -ir -vv macop
+```
+
+**Note:** you need at least Python version >=3.7.0.
+
+## Package modules conventions
+
+As you perhaps already saw, package contains multiples modules and submodules. It's really import to be well organized package and let it intuitive to access as possible to features.
+
+`Macop` is mainly decompose into discrete and continuous optimisation. Especially if you want to offer continuous optimisation problems, modules are already available for this purpose. You can refer to the [documentation](https://jbuisine.github.io/macop) if necessary.
+
+In order to facilitate the integration of new modules, do not hesitate to let me know the name it could have beforehand in your pull request.
+
+## Code documentation
+
+In order to allow quick access to the code, the project follows the documentation conventions (docstring) proposed by Google. Here an example:
+
+```python
+''' Binary integer solution class
+
+    - store solution as a binary array (example: [0, 1, 0, 1, 1])
+    - associated size is the size of the array
+    - mainly use for selecting or not an element in a list of valuable objects
+
+    Attributes:
+       data: {ndarray} --  array of binary values
+       size: {int} -- size of binary array values
+       score: {float} -- fitness score value
+'''
+```
+
+You can generate documentation and display updates using these following commands:
+
+```
+bash build.sh
+firefox docs/build/index.html
+```
+
+## Test implementation
+
+This project uses the [doctest](https://docs.python.org/3/library/doctest.html) package which enables to write tests into documentation as shown in example below:
+
+```python
+""" Initialise binary solution using specific data
+
+    Args:
+        data: {ndarray} --  array of binary values
+        size: {int} -- size of binary array values
+
+    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._data) == 3
+    True
+    >>> # clone solution
+    >>> solution_copy = solution.clone()
+    >>> all(solution_copy._data == solution._data)
+"""
+```
+
+Moreover, tests written are displayed into generated documentation and show examples of how to use the developed features.

+ 0 - 176
CONTRIBUTING.md

@@ -1,176 +0,0 @@
-Contribution guidelines
-=====================================
-
-<p align="center">
-    Minimalist And Customisable Optimisation Package
-</p>
-
-
-# Welcome !
-
-Thank you for taking the time to read this guide for the package's contribution. I'm glad to know that you may bring a lot to the **Macop** package. This document will show you the good development practices used in the project and how you can easily participate in its evolution!
-
-# Table of contents
-
-1. [Naming conventions](#naming-conventions)
-
-    1.1. [Git naming conventions](#git-naming-conventions)
-
-    1.2. [Package modules conventions](#package-modules-conventions)
-
-2. [Coding conventions](#coding-conventions)
-
-    2.1. [Python conventions](#python-conventions)
-
-    2.3. [Code documentation](#code-documentation)
-
-    2.2. [Test implementation](#test-implementation)
-
-3. [Submission process](#submission-process)
-
-    3.1. [Build package](#build-package)
-
-    3.2. [Pull request](#pull-request)
-
-4. [Request an enhancement or report a bug](#request-an-enhancement-or-report-a-bug)
-
-# Naming conventions
-
-## Git naming conventions
-
-This project uses the naming conventions of the git branches set up by the [git-flow](https://danielkummer.github.io/git-flow-cheatsheet/) interface. To make your contribution as easy as possible to inject into the project, you will need to name your git branch as follows:
-
-```bash
-git branch feature/YourFeatureBranchName
-```
-
-Using git-flow interface:
-
-```bash
-git flow feature start YourFeatureBranchName
-```
-
-## Package modules conventions
-
-As you perhaps already saw, package contains multiples modules and submodules. It's really import to be well organized package and let it intuitive to access as possible to features.
-
-For the moment there are no precise conventions on the naming of new modules or sub-modules, it must just in a first step respect the hierarchy already in place and avoid any redundancies.
-
-In order to facilitate the integration of new modules, do not hesitate to let me know the name it could have beforehand.
-
-# Coding conventions
-
-## Python conventions
-
-This project follows the [coding conventions](http://google.github.io/styleguide/pyguide.html) implemented by Google. To help you to format **\*.py** files, it is possible to use the [yapf](https://github.com/google/yapf/) package developed by Google.
-
-Note that the **yapf** package is used during build process of **macop** package to format the whole code following these conventions.
-
-## Code documentation
-
-In order to allow quick access to the code, the project follows the documentation conventions (docstring) proposed by Google. Here an example:
-
-```python
-''' Binary integer solution class
-
-    - store solution as a binary array (example: [0, 1, 0, 1, 1])
-    - associated size is the size of the array
-    - mainly use for selecting or not an element in a list of valuable objects
-
-    Attributes:
-       data: {ndarray} --  array of binary values
-       size: {int} -- size of binary array values
-       score: {float} -- fitness score value
-'''
-```
-
-You can generate documentation and display updates using these following commands:
-
-```
-bash build.sh
-firefox docs/index.html
-```
-
-Do not forget to generate new documentation output before doing a pull request.
-
-## Test implementation
-
-This project use the [doctest](https://docs.python.org/3/library/doctest.html) package which enables to write tests into documentation as shown in example below:
-
-# TODO : add custom example
-```python
-""" Initialise binary solution using specific data
-
-    Args:
-        data: {ndarray} --  array of binary values
-        size: {int} -- size of binary array values
-
-    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._data) == 3
-    True
-    >>> # clone solution
-    >>> solution_copy = solution.clone()
-    >>> all(solution_copy._data == solution._data)
-"""
-```
-
-Moreover, tests written are displayed into generated documentation and let examples of how to use the developed function.
-
-# Submission process
-
-## Build pakcage
-
-One thing to do before submit your feature is to build the package:
-
-```bash
-python setup.py build
-```
-
-This command do a lot of thing for you:
-  - Runs the tests from documentation and raise errors if there are.
-  - Formats all **\*.py** inside *macop* folder using **yapf**.
-
-Do not forget to build documentation as explained in section [2.3](#code-documentation).
-
-Or directly use bash script which runs all you need:
-
-```bash
-bash build.sh
-```
-
-## Pull request
-
-Once you have built the package following previous instructions. You can make a pull request using GitHub. A [documentation](https://help.github.com/articles/about-pull-requests/) about pull requests is available.
-
-# Request an enhancement or report a bug
-
-To enhance the package, do not hesitate to report bug or missing feature. To do that, just submit an issue using at one of this labels:
-
-- **feature** or **idea**: for new an enhancement to develop
-- **bug:** for a detected bug
-
-You can also add your own labels too or add priority label:
-
-- prio:**low**
-- prio:**normal**
-- prio:**high**
-
-Main common required issue header:
-
-```
-Package version: X.X.X
-Issue label: XXXXX
-Priority: prio:**level**
-Targeted modules: `macop.algorithms`, `macop.policies`
-Operating System: Manjaro Linux
-
-Description: XXXXX
-```
-
-Whatever the problem reported, I will thank you for your contribution to this project. So do not hesitate.

+ 6 - 3
README.md

@@ -1,6 +1,7 @@
 # Minimalist And Customisable Optimisation Package
 
-![](https://img.shields.io/github/workflow/status/jbuisine/macop/build?style=flat-square) ![](https://img.shields.io/pypi/v/macop?style=flat-square) ![](https://img.shields.io/pypi/dm/macop?style=flat-square)
+[![status](https://joss.theoj.org/papers/9ea7d55c4fa83808f96929cb87adff3e/status.svg)](https://joss.theoj.org/papers/9ea7d55c4fa83808f96929cb87adff3e) ![](https://img.shields.io/github/workflow/status/jbuisine/macop/build?style=flat-square) ![](https://img.shields.io/pypi/v/macop?style=flat-square) ![](https://img.shields.io/pypi/dm/macop?style=flat-square)
+
 
 <p align="center">
     <img src="https://github.com/jbuisine/macop/blob/master/docs/source/_static/logo_macop.png" alt="" width="50%">
@@ -57,8 +58,10 @@ Main idea about this Python package is that it does not which doesn't implement
 Fully documentation of package with examples is [available](https://jbuisine.github.io/macop). 
 
 You can also see examples of use:
--  in the [knapsackExample.py](https://github.com/jbuisine/macop/blob/master/examples/knapsackExample.py) python file for mono-objective.
--  in the [knapsackMultiExample.py](https://github.com/jbuisine/macop/blob/master/examples/knapsackMultiExample.py) python file for multi-objective.
+-  in the [knapsackExample.py](https://github.com/jbuisine/macop/blob/master/examples/knapsackExample.py) for mono-objective instance.
+-  in the [knapsackMultiExample.py](https://github.com/jbuisine/macop/blob/master/examples/knapsackMultiExample.py) for multi-objective instance.
+-  in the [qapExample.py](https://github.com/jbuisine/macop/blob/master/examples/qapExample.py) for mono-objective QAP instance.
+-  in the [ubqpExample.py](https://github.com/jbuisine/macop/blob/master/examples/ubqpExample.py) for mono-objective UBQP problem instance.
 
 ## Add as dependency
 

+ 0 - 14
build.sh

@@ -1,14 +0,0 @@
-#! bin/bash
-
-# Format code
-echo "Use of yapf package to format code.."
-#yapf -ir -vv macop
-
-# Build rawls package
-echo "Build package..."
-python setup.py build
-python setup.py test
-
-echo "Build documentation..."
-rm -r docs/source/macop
-cd docs && make clean && make html

+ 1 - 1
docs/source/_static/css/custom.css

@@ -31,7 +31,7 @@ footer a {
     color: #009900 !important;   
 }
 
-colgroup :first-child {
+div[id=api] colgroup :first-child {
     width: 60% !important;
 }
 

BIN
docs/source/_static/examples/qap/factories_qap.png


BIN
docs/source/_static/logo_macop.png~


+ 3 - 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.8'
+version = '1.0.9'
 # The full version, including alpha/beta/rc tags
-release = 'v1.0.8'
+release = 'v1.0.9'
 
 
 # -- General configuration ---------------------------------------------------
@@ -47,6 +47,7 @@ extensions = [
     'sphinx.ext.autosummary',
     'sphinx.ext.viewcode',
     'sphinx.ext.coverage',
+    #'sphinx.ext.pngmath',
     #'autoapi.extension' 
 ]
 

+ 1 - 5
docs/source/contributing.rst

@@ -8,10 +8,6 @@ Contributing
 Using GitHub
 ------------
 
-This git project uses git-flow_ implementation. You are free to contribute to it.
-
-.. _git-flow: https://danielkummer.github.io/git-flow-cheatsheet/
-
 Please refer to the guidelines_ file if you want more information about process!
 
-.. _guidelines: https://github.com/prise-3d/macop/blob/master/CONTRIBUTING.md 
+.. _guidelines: https://github.com/prise-3d/macop/blob/master/CONTRIBUTING

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

@@ -9,7 +9,7 @@ Find local and global optima
 Overall, in an optimization process, we will seek to find the best, or the best solutions that minimize or maximize our objective function (fitness score obtained) in order to respond to our problem.
 
 .. image:: ../_static/documentation/search_space.png
-   :width:  800 px
+   :width:  95 %
    :align: center
 
 Sometimes, the search space can be very simple. A local search can provide access to the global optimum as shown in figure (a) above. 
@@ -270,7 +270,7 @@ The way to counter this problem is to allow the algorithm to exit the exploitati
 The idea is to make a leap in the search space in order to find a new local optimum which can be the global optimum. The explained process is illustrated below:
 
 .. image:: ../_static/documentation/search_space_simple.png
-   :width:  400 px
+   :width:  45 %
    :align: center
 
 

ファイルの差分が大きいため隠しています
+ 7 - 7
docs/source/documentations/introduction.rst


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

@@ -75,7 +75,7 @@ Mutator operator
 As detailed, the mutation operator consists in having a minimum impact on the current state of our solution. Here is an example of a modification that could be done for our problem.
 
 .. image:: ../_static/documentation/project_knapsack_mutator.png
-   :width:  800 px
+   :width:  90 %
    :align: center
 
 In this example we change a bit value randomly and obtain a new solution from our search space.
@@ -141,7 +141,7 @@ Crossover operator
 Inspired by Darwin's theory of evolution, crossover starts from two solutions to generate a so-called offspring solution composed of the fusion of the data of the parent solutions.
 
 .. image:: ../_static/documentation/project_knapsack_crossover.png
-   :width:  800 px
+   :width:  95%
    :align: center
 
 In this example we merge two solutions with a specific splitting criterion in order to obtain an offspring.

+ 6 - 2
docs/source/documentations/others.rst

@@ -6,11 +6,15 @@ It decomposes the original multi-objective problem into a number of single-objec
 MOEA/D is a state-of-art algorithm in aggregation-based approaches for multi-objective optimization.
 
 .. image:: ../_static/documentation/search_space_moead.png
-   :width:  400 px
+   :width:  45 %
    :align: center
 
 
 As illustrated below, the two main objectives are sub-divised into 5 single-objective optimization sub-problems in order to find the Pareto front.
 
 - ``macop.algorithms.multi.MOSubProblem`` class defines each sub-problem of MOEA/D.
-- ``macop.algorithms.multi.MOEAD`` class exploits ``MOSubProblem`` and implements MOEA/D using weighted-sum of objectives method.
+- ``macop.algorithms.multi.MOEAD`` class exploits ``MOSubProblem`` and implements MOEA/D using weighted-sum of objectives method.
+
+An example with MOEAD for knapsack problem is available in knapsackMultiExample.py_.
+
+.. _knapsackMultiExample.py: https://github.com/jbuisine/macop/blob/master/examples/knapsackMultiExample.py

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

@@ -12,7 +12,7 @@ Automated operator choice strategies have also been developed in the literature,
 The operator choice problem can be seen as the desire to find the best solution generation operator at the next evaluation that will be the most conducive to precisely improving the solution.
 
 .. image:: ../_static/documentation/operators_choice.png
-   :width:  400 px
+   :width:  45 %
    :align: center
 
 .. note::

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

@@ -12,7 +12,7 @@ The **knapsack problem** is a problem in combinatorial optimisation: Given a set
 The image below provides an illustration of the problem:
 
 .. image:: ../_static/documentation/knapsack_problem.png
-   :width: 300 px
+   :width: 40 %
    :align: center
 
 
@@ -28,7 +28,7 @@ Problem implementation
 During the whole tutorial, the example used is based on the previous illustration with:
 
 .. image:: ../_static/documentation/project_knapsack_problem.png
-   :width: 600 px
+   :width: 85 %
    :align: center
 
 

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

@@ -79,7 +79,7 @@ Knapsack solution
 A valid solution can be shown below where the sum of the object weights is 15 and the sum of the selected objects values is 8 (its fitness):
 
 .. image:: ../_static/documentation/project_knapsack_solution.png
-   :width:  800 px
+   :width:  85 %
    :align: center
 
 Its representation can be translate as a **binary array** with value:

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

@@ -10,7 +10,7 @@ Validator definition
 An invalid solution can be shown below where the sum of the object weights is greater than 15:
 
 .. image:: ../_static/documentation/project_knapsack_invalid.png
-   :width:  800 px
+   :width:  85 %
    :align: center
 
 In fact, **[1, 0, 1, 0, 0]** is an invalid solution as we have a weight of **16** which violates the knapsack capacity constraint.

ファイルの差分が大きいため隠しています
+ 16 - 615
docs/source/examples.rst


+ 155 - 0
docs/source/examples/qap/implementation.rst

@@ -0,0 +1,155 @@
+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._data):
+            for index_j, val_j in enumerate(solution._data):
+                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.
+
+.. warning::
+    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._data)) > len(set(list(solution._data))):
+            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

+ 24 - 0
docs/source/examples/qap/index.rst

@@ -0,0 +1,24 @@
+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
+
+.. toctree::
+   :maxdepth: 1
+   :numbered:
+   :caption: Contents:
+
+   problem
+   instance
+   implementation
+
+
+.. 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

+ 59 - 0
docs/source/examples/qap/instance.rst

@@ -0,0 +1,59 @@
+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.

+ 70 - 0
docs/source/examples/qap/problem.rst

@@ -0,0 +1,70 @@
+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)`.
+

+ 146 - 0
docs/source/examples/ubqp/implementation.rst

@@ -0,0 +1,146 @@
+Macop UBQP implementation
+=========================
+
+Let's see how it is possible with the use of the **Macop** package to implement and deal with this UBQP 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.BinarySolution`` type of solution within the Macop package represents exactly what one would wish for. 
+
+Let's see an example of its use:
+
+.. code:: python
+
+    from macop.solutions.discrete import BinarySolution
+    
+    solution = BinarySolution.random(10)
+    print(solution)
+
+
+The resulting solution obtained:
+
+.. code:: bash
+
+    Binary solution [1 0 1 1 1 0 0 1 1 0]
+
+
+UBQP 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 we need to maximise:
+
+- :math:`f(x)=x′Qx=\sum_{i=1}^{n}{\sum_{j=1}^{n}{q_{ij}⋅x_i⋅x_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 UBQPEvaluator(Evaluator):
+    """UBQP evaluator class which enables to compute UBQP solution using specific `_data`
+
+    - stores into its `_data` dictionary attritute required measures when computing a UBQP solution
+    - `_data['Q']` matrix of size n x n with real values data (stored as numpy array)
+    - `compute` method enables to compute and associate a score to a given UBQP solution
+    """
+
+    def compute(self, solution):
+        """Apply the computation of fitness from solution
+
+        Args:
+            solution: {Solution} -- UBQP solution instance
+    
+        Returns:
+            {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):
+                fitness += self._data['Q'][index_i, index_j] * val_i * val_j
+
+        return fitness
+
+The cost function for the Unconstrained binary quadratic problem is now well defined.
+
+.. warning::
+    The class proposed here, is available in the Macop package ``macop.evaluators.discrete.mono.UBQPEvaluator``.
+
+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 UBQP 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 BinarySolution
+    from macop.evaluators.discrete.mono import UBQPEvaluator
+
+    from macop.operators.discrete.mutators import SimpleMutation, SimpleBinaryMutation
+
+    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
+    def validator(solution):
+        return True
+
+    # define init random solution
+    def init():
+        return BinarySolution.random(n, validator)
+
+    # load UBQP instance
+    with open(ubqp_instance_file, 'r') as f:
+
+        lines = f.readlines()
+
+        # get all string floating point values of matrix
+        Q_data = ''.join([ line.replace('\n', '') for line in lines[8:] ])
+
+        # load the concatenate obtained string
+        Q_matrix = np.fromstring(Q_data, dtype=float, sep=' ').reshape(n, n)
+
+    print(f'Q_matrix shape: {Q_matrix.shape}')
+
+    # only one operator here
+    operators = [SimpleMutation(), SimpleBinaryMutation()]
+
+    # random policy
+    policy = RandomPolicy(operators)
+
+    # use of loaded data from UBQP instance
+    evaluator = UBQPEvaluator(data={'Q': Q_matrix})
+
+    # passing global evaluation param from ILS
+    hcfi = HillClimberFirstImprovment(init, evaluator, operators, policy, validator, maximise=True, verbose=True)
+    algo = ILS(init, evaluator, operators, policy, validator, localSearch=hcfi, maximise=True, verbose=True)
+
+    # run the algorithm
+    bestSol = algo.run(10000, ls_evaluations=100)
+
+    print('Solution for UBQP instance score is {}'.format(evaluator.compute(bestSol)))
+
+
+UBQP problem solving is now possible with **Macop**. As a reminder, the complete code is available in the ubqpExample.py_ file.
+
+.. _ubqpExample.py: https://github.com/jbuisine/macop/blob/master/examples/ubqpExample.py
+.. _documentation: https://jbuisine.github.io/macop/_build/html/documentations

+ 15 - 0
docs/source/examples/ubqp/index.rst

@@ -0,0 +1,15 @@
+Unconstrained Binary Quadratic Programming
+==========================================
+
+Given a collection of :math:`n` items such that each pair of items is associated with a profit value that can be positive, negative or zero, unconstrained binary quadratic programming (UBQP) seeks a subset of items that maximizes the sum of their paired values. The value of a pair is accumulated in the sum only if the two corresponding items are selected.
+
+The UBQP problem will be tackle in this example.
+
+.. toctree::
+   :maxdepth: 1
+   :numbered:
+   :caption: Contents:
+
+   problem
+   instance
+   implementation

+ 52 - 0
docs/source/examples/ubqp/instance.rst

@@ -0,0 +1,52 @@
+UBQP Problem instance generation
+================================
+
+To define our quadratic assignment problem instance, we will use the available mUBQP_ 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 available file **mubqpGenerator.R**, will be used to generate the instance (using R language).
+
+.. code:: bash
+
+    Rscript mubqpGenerator.R 0.8 1 100 5 42 ubqp_instance.txt
+
+The main parameters used for generating our UBQP instance are:
+
+- **ρ:** the objective correlation coefficient
+- **M:** the number of objective functions
+- **N:** the length of bit strings
+- **d:** the matrix density (frequency of non-zero numbers)
+- **s:** seed to use
+
+.. _mUBQP: http://mocobench.sourceforge.net/index.php?n=Problem.MUBQP
+
+.. _ubqp_instance.txt: https://github.com/jbuisine/macop/blob/master/examples/instances/ubqp/ubqp_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 = 'ubqp_instance.txt'
+
+    n = 100 # the instance size
+
+    # load UBQP instance
+    with open(ubqp_instance_file, 'r') as f:
+
+        lines = f.readlines()
+
+        # get all string floating point values of matrix
+        Q_data = ''.join([ line.replace('\n', '') for line in lines[8:] ])
+
+        # load the concatenate obtained string
+        Q_matrix = np.fromstring(Q_data, dtype=float, sep=' ').reshape(n, n)
+
+    print(f'Q_matrix {Q_matrix.shape}')
+
+.. note::
+    As we know the size of our instance and the structure of the document (header size), it is quite quick to look for the lines related to the :math:`Q` matrix.

ファイルの差分が大きいため隠しています
+ 17 - 0
docs/source/examples/ubqp/problem.rst


+ 4 - 1
docs/source/index.rst

@@ -8,7 +8,7 @@ Minimalist And Customisable Optimisation Package
 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..
+**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.
 
 .. toctree::
    :maxdepth: 1
@@ -19,6 +19,9 @@ What's **Macop** ?
    documentations/index
 
    api
+
+   examples
+
    contributing
 
 Indices and tables

+ 202 - 0
examples/instances/qap/qap_instance.txt

@@ -0,0 +1,202 @@
+facilities = 100 objectives = 1 max_distances = 80 max flows = 30 correlation = 0.000000 seed = 42
+ 0 48 19 34 32 75 64 40 66 69 72 20 39 22 19 42 62 10 11 55 76 53 45  3 50 65  6 38 75 71 71 32 27 70 40 17 50 44 24 11 39 65  3 44  9 31 67 47 80 20 42 28 40 23 47 76  6 13 57 76 42 47 27 52 79 54 46 19 12 31 51 48 69 16 17 78 54 17 76 44 19 30  3 56 36 42 75  9 61 61 65 57 44 25 47 55 26 75 50 22 
+48  0 79 18 55 19  5 25 50 39 40 35 73 62 71 49 19 49 28 40 59 21 16 25 42 48 41 25 34  7 79 64 64 30 12 35 67 10 66 51 45 35 66 46 51 69 68 19 16 40 67 60 53 37 16  8 62 49 19 24 27 57 49 30  2 70 17 73 29 19 79 60 58 25 23 39  6 76 18 34 46 62  9 29 20 60 62 12 16 49 68 46 58 45 26 62 67 67  7 68 
+19 79  0  4  9 51 74 27  1 68 27 49 41  9 19 46 65  4 33 69 27 16 39 31 49 22 63 43 36 75  8 47 49 60 71 43 44 58 64 37 30 13 43 51  1 49 55 47 53 78 62 59 10 52 47 72  1 48 49  7 56 40 60 55 10 25 41 12 56 77 76 62 36 28 32 56 48 68 31 74 33 42 77 13  4 39 78 39 64 15 22 54 44 15 67 31 59 38 28 21 
+34 18  4  0 11 58 12 68 20 60  8 52 76 78 10 23 67 63 46 28 29 17 20 57 21 34 50  1 37 45 26 79 55 32 46  3 69  1 27 42 64 30 75  1 66 74 28 76 21 37 10 34 26 64 80 13 49 10 22 75 20 54  7 23 36 66 49 43 63 50 75 24 17 68 44 10 25 30 26 42 21 66 19 57 78 43 79 10  6 32 57 46 12 74 76 55 21 16 15  3 
+32 55  9 11  0 78 35  6  9 43 62 76 79 65 39 47 70 61 77 10 73 35 68 68 73 17 76  5  7  1  8 10 41 46 74 44 66 34 58 46 48 40 62 40 62 24 26 74 61 22 46 11 19  4 74 29 36 15 29 24 60 48 69 37  2 73 24 33 78 77 54  8 26 32 69 43  2  9  7 48 40 24 38 17  8 57 22 59 19 71 11 74 61 38 35  2 41 30 49 15 
+75 19 51 58 78  0 43 35 10 64 61 24 34 61 60 51 11 37 36 76 32 22  9 53 13 24 59 20 79 54  9 72 58 64 73 65 71 31 58 79 73 15 52 10 20 38 29  9  3 65 60 55 75 79 76 20 55 71 35 57 49 21 43 37 48 18 45  4 46 10 42  7 73 35 15 77 75 33 22 30  9 76 25 22 33 16  9 39 55 44  1 46 79  7 42 28  6 13  8 38 
+64  5 74 12 35 43  0 48 22 75 39 58 34 47 56 35 27 45 37 25 22  1 79 35 25 36 70 18 36  6 72 46 12  4 73 31 80 40 50 24 26 76 69 63 78 27 10 25 18 50 35 40 79 71 37 58 28 67 58  6 11 71 33 27 49 16 47 19 57 42 61 64 71 28 54 65 35 29 62 73 28 35 52  7 63 45 15  9  4 16 57 78 50 22 26 13 37 15 80 80 
+40 25 27 68  6 35 48  0 51 11 69 45 72 55 35 22  3 40 32 40 65 40 52 30 48 17 44 22  6 46 19 12 43 36 63 63 30 74 76 57 19 35 20 59 16 65 13 30 40 29 26 37 74 38 13 47  9 34 59 71 20  4 73 24 52 80 60 66 75 36 69 15 53 32 43 61  4 13  9 26 67  1 68 24 49 40 69 66 61 69 70 77 44 61 18 26 46 60 50 73 
+66 50  1 20  9 10 22 51  0 77 20 63 63  4 33 18 64 52 78 23  2  5 37 55 45 18 11 76 37 69  9 53 71 80 61  5 64 31 50  2 13 30 63 65 60  1 59 78 70 32 69  3 72 53 14  7 63 78 27 54 72 47 48 21 72 60  4 35 53 27 12 67  8 53 68 23  8 57 40 58 76 46 35 78 44  8 62 48 35 62 31 78 58 47  3  9 62 30 38  7 
+69 39 68 60 43 64 75 11 77  0 26 55 13 55 64 15 18 21 19 22  2 36 12 53 43 30 25 50 32  6 49 48 46 71 26 80 20  4 64 43 44 62 26 61 73 64 62 37  5 63  4 28 32  3 80 75  8 22  4  4 45 68 41 53 74 24 47 73  1 23 15  3 57 58 16 59 64 68 68 37 16 73 29 40 36 52 33 57 32 18 51 44 34 15 21 38  8 78 76 47 
+72 40 27  8 62 61 39 69 20 26  0 50 69 50 55 28 30 53 44  9 43 60 79 17 42 22 74 47 44 39 70 18 55 21 29 46 39  1 59 11 67 42 12 66  2 45 36 64 43 73 14 20 28 13 54 24 15 63 51 50 69 22 32 24 52 68 49 25 58 20 12 11 21 75 55  5 34 29 38 34 49 75 59 22 49  8 39  4 67 41 77 14 35 35 47 54 20 60 45 16 
+20 35 49 52 76 24 58 45 63 55 50  0 74 55 36 53 20 39  8 17 68 59 78 73 73 19 41 31 53 24 16 57 74 75  7 71  9 68 76  8 46 58 53 78 69 59 28 23 71  1 48 49 75 65 54 21 77 19 65 51 29 45 25 38 52 30 63 73 58 31  7  2 74 77 47 67 51 43 45 66 28 13  5  8 11 26 24 79 55  6 38 52 52 58 20 15 61 58 51 72 
+39 73 41 76 79 34 34 72 63 13 69 74  0 72 42 20 17 32 25 12 28  3 71 13 80 61 10 47 54 37 36 58 80 57 21 32 22  3 51 65  5 11 57  4 18 38 28 60 70 47  8 77  6 19 32 29 26 48 63 30 30 75  8 60 31  6 80 13 45  1 34 15 17 37 24 46 38 25 21 72 30 42 50  3 44 29 13 16 71 35 29 17 42 37 59  9 31 73 52 51 
+22 62  9 78 65 61 47 55  4 55 50 55 72  0 76 64 41 17 43 51 14 31 60 50 18 35 57 30 75 50 39 51 55 19 44 29 69 27 18 27 76 44 34 14 11 66 65 64 78 22 60 44 30 79 35 76 13  7 78 55 25 15 76 30  2 50 47 67 70 56 49 63 49  8 78 21 37 54 64 53 54  7 50 34 73 67 20 25 33 67 50  5 72 22 38 28 77 36 15 46 
+19 71 19 10 39 60 56 35 33 64 55 36 42 76  0 79 23 54 56 75 73  3 65 74 50 57  9 68 68 46 24 15  9 35  3 78 37 56 27 78 12 70 63 45 18 72  3 32 60 50 39 18 77 35 77  4 33 53 74 17  9 34 54  7 35 35 29 46  4 26 65 51 30  1 23 39 10 17 72 41 24 41 23 62 63 25 10 28 44 66 50 58 17 70 78 46 50 13 64 22 
+42 49 46 23 47 51 35 22 18 15 28 53 20 64 79  0 39 56 58 62 47 66 47 43 41 49 75 33 24 22 66  9 28  8 17 24 75 35  2 53 23 22 65 33 37 74 51  8 13 11 40 28 45 71 19 50 31 43 57 59  4 72 61 69 31 61  3 32  7 11 49 74 43 32 70 73 74 76 56 35  8 31 40 41 47  4 56  3 44 65 23 51 69 44 63  8 23 17 51 47 
+62 19 65 67 70 11 27  3 64 18 30 20 17 41 23 39  0 61 24 70 19 28  8  6 53 68 80  6  9 62 60 60 42 60 22 63 55  8  6 74 78  3 39  4 67 49 61 60 36 22  3 10 41 41 35 23 50 68 79 44 50 77  9 47 80 54  8 12 65 22 63 59 28  3 16  9 64 51 35  4 37 34 41 20 78 23  3 61 51 61 68 24 57 77 30 21 56 61 61  4 
+10 49  4 63 61 37 45 40 52 21 53 39 32 17 54 56 61  0 43 72 66  9 74 14 24 44 74 39  7 56 43 50 20 28 35 79 65  1  5 60 58 25 50 36  3 25 72 46 80 37 79 28  9 66 13 47 35 62 26 63 28 59 80 44 67 49 69 53  8 42 49 33 24 39 60  5 10 34 60 15 14 57 46 21 69 40 33 48 72 65 10  7 42  5 45 78 76  6  5  9 
+11 28 33 46 77 36 37 32 78 19 44  8 25 43 56 58 24 43  0 63 38  8 80 36 69 11 64 22 77 69 73  6 38 11 21 62 75 37 59 76 11 62  7 23  1 71 47 52 33 41 71 35 25 48 67 74 70  8  9 58  8 27 64 67 78 47  5 13 17 59 47 22 23 78 23 74 24 24 75 56 40 13 49 16 79 23  4 59 54 35 30 80 63 49 28 22 69 15 16 10 
+55 40 69 28 10 76 25 40 23 22  9 17 12 51 75 62 70 72 63  0 65  5 59 59 57 37 59 75 30 37 47 32  1 44 80  9 38 76 78 29 14 27 39 68 28 57 51 63 47 72  3 64 60 48 59 50 51 41 74 60 35 16 74 26 70 42 71 26 67 52 16 46 16 49 53 60 52 48 14 43 18 17 26 31 61 33 12 69 27 40 61 41 39 49 37 47  3  8 72 58 
+76 59 27 29 73 32 22 65  2  2 43 68 28 14 73 47 19 66 38 65  0 42 72 51 30 70 63 48 70 51 18 24  7 38 60 78 80 66 39  1 53  1 51  4 61  5 77 20 25 51 23  1 20 22 24 51 47 54 67 27 71 70  5 23 15 32 35 80 60 63  4 11 28 71 20 71 71 25 11 28  8 37 48 11 52  7 80 30 69 53 18 74  9 52 76 61 69 52 13 66 
+53 21 16 17 35 22  1 40  5 36 60 59  3 31  3 66 28  9  8  5 42  0 39 73 66 62 68 10 57 14 50 39 37 30 49 50 17 63 42 48  7 31 62 44 74 75 44 12 41 72 30 62 17 38 31 21 18 34 39 27 24  4  4 59 39 72 66 25 12 30  1 11 28  1 12 11 44 70 33 34 23 24 35 26 68 30 76  5  2 65 61 46 74 63  3  3 24 16 73 38 
+45 16 39 20 68  9 79 52 37 12 79 78 71 60 65 47  8 74 80 59 72 39  0 32 32 53 56 16 45 24  9 20 68 62 62 79 77 77 52 54 77  2 70  6 23 61 50 29 47  9 79 12 27 63 39 15 77 40  9 54 18 69 50 41 61 19 19 64 11 37 16 18 49 48 39 20 20 49  4 41 34  9 28 71 35 57  8 11 27 78  6 24 66 38 43 76 68 68 23 46 
+ 3 25 31 57 68 53 35 30 55 53 17 73 13 50 74 43  6 14 36 59 51 73 32  0  8 46 44 62 34  2  2 66  2 64 72 31 80 60 25 53 53 72  7 55 64 33 63 45 74 15 49 19 48 71 41 37 78 53 14  1 63 38 74 28 62 14 31 54 25 25 42 32 72 32 56 25 42 69 13 35 60  5 64 43 26  7 55 57 50 10 43 38 64 45 15 71 30 23 27 74 
+50 42 49 21 73 13 25 48 45 43 42 73 80 18 50 41 53 24 69 57 30 66 32  8  0 16 12  6  3 19 53 20 24 72 37 65 79 17 21 63 20  7 37 12 40 12 54 65  7 71 30 12 23 65  4 36 10  7 51 79 78 75 23 75 76 20 61 78 52 67 31 62 25 13 42 72 72 68 24 65  9 77 17 28 11 71 14 65 74 77 40 18 39 19  6 31 27 69 70 23 
+65 48 22 34 17 24 36 17 18 30 22 19 61 35 57 49 68 44 11 37 70 62 53 46 16  0 23 25 79 55  4 14 54 25 46 23 52 30 15 65  4  6 77 17 19 70 51 14 40 43 12 23 18 24 59 25 44 49 19 64 40 39 26 55  3 56  7 35 23 40 53 43 23 29 73 19 53 24 17 49 11 73 62  7 59  8  5 39 38 27 11 79 26 36 43 32 20  3  2 31 
+ 6 41 63 50 76 59 70 44 11 25 74 41 10 57  9 75 80 74 64 59 63 68 56 44 12 23  0 48 45 75 21 41 49 76  2 48  5 79 70 56 71  9 37 55  6 36 74 28  1 23 24 33 55 65 18 67 22 39 21 42 18 68 25 25 63 60 76 35 59 54 61 13 55 32 19 17 65 76 64 60 68 22  4 13 42 14 19 25 78 48 40  4 31 18  2 42 25 26 61 75 
+38 25 43  1  5 20 18 22 76 50 47 31 47 30 68 33  6 39 22 75 48 10 16 62  6 25 48  0 57 47  4 41 21 50 60 30 78 49 46 62 41 80 38 60 38 22 72 44 25 10 27  2 12 49 63 35 70  8 80 34  9 41 23 13 65  7 16 72 32 65 28 31 53 11 66  9 79 75 40 60 67 63 70 24 20 30 61 45 79 48 47 59 62 46 25 62 47 77 75 32 
+75 34 36 37  7 79 36  6 37 32 44 53 54 75 68 24  9  7 77 30 70 57 45 34  3 79 45 57  0 63 72 54 33 41 25 30 60 38 50  3 61 41 72 26 77 57 26  4 21 49 51 35 28 18 40 30 78 23  8  9 32 74  1 35 66 47 39 71 13 70 51 13 67 48 31 67 24 30 30 73 51 75 43  7 25 37 33 79 30  5 19 20 69 69 38 74 60 77 17 73 
+71  7 75 45  1 54  6 46 69  6 39 24 37 50 46 22 62 56 69 37 51 14 24  2 19 55 75 47 63  0 34 68 75 11 58 28 24 40 31 58 41 69 44 23 52 79 14  8 65 38 15  4 75 30 44 36 51 41 64 23 17 77  1 17 53 38 75 80 34 21 56 28 17 31 60 79 17 61 78  3 61 29  7 12 52 39 24 50 20 54 18  7 36 57 76 16 33 36 69 16 
+71 79  8 26  8  9 72 19  9 49 70 16 36 39 24 66 60 43 73 47 18 50  9  2 53  4 21  4 72 34  0 73 66 55 43 16 50 16 28 12 37 49 15  3 61 42 67 45 40 34  1 30 10 36 69 27 77 76 58 35  1 39 36 44 33 37  8 34 47 47 65 17 36 34 80 58 45 24 72 55 39 62 78 25 33 38 62 15 32 21 19 18 46 19 24 68 73 65 70 29 
+32 64 47 79 10 72 46 12 53 48 18 57 58 51 15  9 60 50  6 32 24 39 20 66 20 14 41 41 54 68 73  0 65 66 68 68 40 56 78 63 66 48 53 49  8  3 76 72  6 63 63 74 40 78  2 62 53 20 72 21 72 47 37 34 51 68 54 74 18 13  7  8 67 10 69 74 71  2 15 46 41 66  7 11 11 15  5 35 53 35 24 30 34 17 48 42 75 26 70 25 
+27 64 49 55 41 58 12 43 71 46 55 74 80 55  9 28 42 20 38  1  7 37 68  2 24 54 49 21 33 75 66 65  0 58 70 73 21 67 36 20 65 80 28 79 72 17 10 65 11 20 46 66 25 48 57 64 69 28 49 32 78 36 59 25 54 77 39 77 56 70 12 47 18 13 31 68  2 17 39 25 32 18 72 15 20 78 69  2 54 76 35 53 78  1 28 14  9 39 76 53 
+70 30 60 32 46 64  4 36 80 71 21 75 57 19 35  8 60 28 11 44 38 30 62 64 72 25 76 50 41 11 55 66 58  0 58 49 24 34 49  7 43 39 18 61 24 43  8 44 47 34 38 49 37 25 29 74 36 27  3 12 63 16  2 20  6 34 34  7 41 39 48 73 51 53 36 55 29 64 18 32 26 75 33 30 60 47  4 26 46 48 10  2 48 72  3 74 32 44 53 38 
+40 12 71 46 74 73 73 63 61 26 29  7 21 44  3 17 22 35 21 80 60 49 62 72 37 46  2 60 25 58 43 68 70 58  0 47 20 60 30  9 61 30 51 31 80 52 51 11 24 71  3 53 35 17 21 54 75 34 41 71 74 31 27 77 73  4 68  7 27 39 10  7 74  2 16 65 47 63  6 27  1 35 11 20 40 52 67 17  5  2  3 16 52 36 27 66 56 62 42 50 
+17 35 43  3 44 65 31 63  5 80 46 71 32 29 78 24 63 79 62  9 78 50 79 31 65 23 48 30 30 28 16 68 73 49 47  0 40 38 65 43 16 45 24  1 64 79 36 38 58  6 19 16 67 36 22 24 23 79 49 17  2 30 64 53 67 76 69 67 66 56 56 69 26 22 38 55 80 69 80  1 23 24 61 71 20 13 61 18 69 10  4 62 28 44 47  1 46 35 33 72 
+50 67 44 69 66 71 80 30 64 20 39  9 22 69 37 75 55 65 75 38 80 17 77 80 79 52  5 78 60 24 50 40 21 24 20 40  0 70  3 76  7 69 55  3  3  3 60 67 65 31  7 14 21 29  6 33 54 76 18 17 18 46 30 25 59 23 69 55 19 68 49 75 61 61 29 73 28 60 18  8 46 60 52 61 45 42 42 77 39  3 37 24 20 40 38 49 40 19 64 32 
+44 10 58  1 34 31 40 74 31  4  1 68  3 27 56 35  8  1 37 76 66 63 77 60 17 30 79 49 38 40 16 56 67 34 60 38 70  0 26 37 47 52 53  6  1 41 37 61  4 33  2  9 13 22 31 80 33 20 52 61 38 46 32 48 26 71 28 52 32  6 37 18 28 71 51  2 17 16 16 17 49 69 72 15 55 33 61 68 67 77 76 22 23  6 15 59 61 58 12 62 
+24 66 64 27 58 58 50 76 50 64 59 76 51 18 27  2  6  5 59 78 39 42 52 25 21 15 70 46 50 31 28 78 36 49 30 65  3 26  0 43 40 48 20 49 11 22 65 18 62 16 48 48 75 59 24 58 13 28 51  4 66 78 44 61 27 42 21 39 37 17 46 58 29  8 64 68 68 40 63 45 44 46 53 58 23 71 66 36 32 40 35 24 55 41 77 64  9 62 72 79 
+11 51 37 42 46 79 24 57  2 43 11  8 65 27 78 53 74 60 76 29  1 48 54 53 63 65 56 62  3 58 12 63 20  7  9 43 76 37 43  0 16 70 52 73 36 37 21  3 32 11 21 10 70 58 77 70 31 48 75 14  8 59  5 17 63  6 77  1  9 38 76 69 16 50  4 51 70 71 79 79 54 78 32 71 80 61 77 60 32 31 57 63 10 78 80 12 61 44 64 62 
+39 45 30 64 48 73 26 19 13 44 67 46  5 76 12 23 78 58 11 14 53  7 77 53 20  4 71 41 61 41 37 66 65 43 61 16  7 47 40 16  0 35 19 31 33 67 10 29 50 77 59  9 76 25 76 38 46 70 47 53 32  4 19 60  3 60 75 37 38 13 28 21 17 57 77 46 72  1 48 22 40 55 64 41 51 57 18 71 42 22 61 30 73 38  7 76 12  7 38 18 
+65 35 13 30 40 15 76 35 30 62 42 58 11 44 70 22  3 25 62 27  1 31  2 72  7  6  9 80 41 69 49 48 80 39 30 45 69 52 48 70 35  0 13 33 50  6 42 53 43 60 50 53 46 30 41 73 11 79  7 26 46 13 10 50 11 30 23 67 64 40 19 13 54 40 14 28 77 70 21 30 17 63 39 64 34 80 79  4 49 46 38 69 51  1 37  2 24 42 48  8 
+ 3 66 43 75 62 52 69 20 63 26 12 53 57 34 63 65 39 50  7 39 51 62 70  7 37 77 37 38 72 44 15 53 28 18 51 24 55 53 20 52 19 13  0 15 37  7 64 45 40 51 41 31 43  5 16  7 75 46  5 10 51 34 74 80 47 48 33 33 42 30 80 80 26 47 76 54 74 45 10 10 21 60 51 22 18 13  6 37 79  1 50 70 37 37 12 63  7 63 15 71 
+44 46 51  1 40 10 63 59 65 61 66 78  4 14 45 33  4 36 23 68  4 44  6 55 12 17 55 60 26 23  3 49 79 61 31  1  3  6 49 73 31 33 15  0 59 36 71 76 78 42 52 37 30  9 15 68 53 65 35 32 59 31 64 63 48 33 34 49 65 23 60 25 59 32 73 62 42 32 20  8 51  5 77 72 58  1 21 44 23 73 35 39 78  2 57 78 40 58 33 79 
+ 9 51  1 66 62 20 78 16 60 73  2 69 18 11 18 37 67  3  1 28 61 74 23 64 40 19  6 38 77 52 61  8 72 24 80 64  3  1 11 36 33 50 37 59  0 45 24 22 11 18 37 24 60 13 10 47 48 80 69  3 44 14 19 16 79 70 43 29 26  9 79 63  9 18  3 75 75  5 75 25  6  7 63 56 35  6 30 59 24 77 11 52 44 25 35 41 64 62 12  2 
+31 69 49 74 24 38 27 65  1 64 45 59 38 66 72 74 49 25 71 57  5 75 61 33 12 70 36 22 57 79 42  3 17 43 52 79  3 41 22 37 67  6  7 36 45  0 52 24 50 55 24 23 68 34 53 28 65 46 69 35 70 47 27 26 75 41 12 37 64 27  5 28 35 48 19 23 64 77 62 15 72 71 59 74  8 63 30 52 38  4  1 36 79 60 46 32 34 63 46 80 
+67 68 55 28 26 29 10 13 59 62 36 28 28 65  3 51 61 72 47 51 77 44 50 63 54 51 74 72 26 14 67 76 10  8 51 36 60 37 65 21 10 42 64 71 24 52  0 45 27 60 59 59 60 60 11 55 26 36  7 10 66 12 69 71  8 10 80 51 54 71 69 63 57 16 32 54 63 40 56 73 46 18 10  6 61 28 29 44 61  1 26 67 44 19 71 67 26 37 60 64 
+47 19 47 76 74  9 25 30 78 37 64 23 60 64 32  8 60 46 52 63 20 12 29 45 65 14 28 44  4  8 45 72 65 44 11 38 67 61 18  3 29 53 45 76 22 24 45  0 16 40 51 68 77 16  7 11 78 46 54 60 73 39 31 24  9 45  9 79 75 76 76 79 16  8 28 60 75 13 64 31 28 47  6 14 69 26 25 31 65 69  2 49 42 32  7 19 16 64 46 33 
+80 16 53 21 61  3 18 40 70  5 43 71 70 78 60 13 36 80 33 47 25 41 47 74  7 40  1 25 21 65 40  6 11 47 24 58 65  4 62 32 50 43 40 78 11 50 27 16  0 10 53 55 76 38 66 62 58 25 26 72 68 39 43  1 72 35  7 14 78  8 22  6 71  7 22 71 37 47 25 12 62 71 60 61 54 38 42 34 64 51 21 21 73 75 74 33 16 14 38 64 
+20 40 78 37 22 65 50 29 32 63 73  1 47 22 50 11 22 37 41 72 51 72  9 15 71 43 23 10 49 38 34 63 20 34 71  6 31 33 16 11 77 60 51 42 18 55 60 40 10  0 11 15 68 69 76 52 70 40 12 64 65 38 72 34 43 15  8 67 10 47 79 51 54  3 12 53 37  8 75 69 40  7 28 31 77  7 34 77 37 28 76 39 64 58 36 33 78 60 55 65 
+42 67 62 10 46 60 35 26 69  4 14 48  8 60 39 40  3 79 71  3 23 30 79 49 30 12 24 27 51 15  1 63 46 38  3 19  7  2 48 21 59 50 41 52 37 24 59 51 53 11  0 75 71 30 27 55 15 54 38 67 62 15  1 66 46 25  9 63 10 18 73 80 17 60 19  6 75 63 20 45  5 71 17 50 46 63 66 66 68 42 34 73 57 54 53 20 57 30  1  3 
+28 60 59 34 11 55 40 37  3 28 20 49 77 44 18 28 10 28 35 64  1 62 12 19 12 23 33  2 35  4 30 74 66 49 53 16 14  9 48 10  9 53 31 37 24 23 59 68 55 15 75  0 12 71 66 80  2 39  5 64  8 62 33 16 31 34 22 70 37 56 10 19  8 31 69 28 56 74 56 69 69 61 53 46 67 28 56 80 14 35 71 71 59 17 69 76 15 73 46 19 
+40 53 10 26 19 75 79 74 72 32 28 75  6 30 77 45 41  9 25 60 20 17 27 48 23 18 55 12 28 75 10 40 25 37 35 67 21 13 75 70 76 46 43 30 60 68 60 77 76 68 71 12  0 61 67 51 36 30 49 49 18 22 14 20 54 38 72 72 29 49 29 66 32 52 58 50 55 25 58 32 38 37 50 50 40 51 35 13 73 72 50 61 57 55 25 80 41 18 32 20 
+23 37 52 64  4 79 71 38 53  3 13 65 19 79 35 71 41 66 48 48 22 38 63 71 65 24 65 49 18 30 36 78 48 25 17 36 29 22 59 58 25 30  5  9 13 34 60 16 38 69 30 71 61  0 72 32 53 58  4 22 43 21 72 50 27 21 52 80 14 34 59 42 60 41 36 19 10 41 58 59 43 68 59 35 18 32  4 59 48 32 17 24 56 69 44 37 46 54 54 34 
+47 16 47 80 74 76 37 13 14 80 54 54 32 35 77 19 35 13 67 59 24 31 39 41  4 59 18 63 40 44 69  2 57 29 21 22  6 31 24 77 76 41 16 15 10 53 11  7 66 76 27 66 67 72  0 61 73 53 41 18 55 74 30 44 63 65 13 50 68 37 24 70 12 58 23 25 40 43 58 65 20 60 34 61  1 70 16 31 78 55  5 38  3 67 26 11 44 13 51 10 
+76  8 72 13 29 20 58 47  7 75 24 21 29 76  4 50 23 47 74 50 51 21 15 37 36 25 67 35 30 36 27 62 64 74 54 24 33 80 58 70 38 73  7 68 47 28 55 11 62 52 55 80 51 32 61  0 52 36 69 15 63  9  2 10 45 43 13 31 13 33 35 29 78 52 20 19 20 34 42 16 61  5 62 62 39  8 41 75  8 38 68 72 31 66 34 35 79 11 70 36 
+ 6 62  1 49 36 55 28  9 63  8 15 77 26 13 33 31 50 35 70 51 47 18 77 78 10 44 22 70 78 51 77 53 69 36 75 23 54 33 13 31 46 11 75 53 48 65 26 78 58 70 15  2 36 53 73 52  0 72 79 24 46 49 50 50 50 70 71  8 35  4 49 29 64  1 21  7 72 22 45 48 64 15 29 75 23  1 61 43 65 34 52 80 71  6 65 43 37  2 13 80 
+13 49 48 10 15 71 67 34 78 22 63 19 48  7 53 43 68 62  8 41 54 34 40 53  7 49 39  8 23 41 76 20 28 27 34 79 76 20 28 48 70 79 46 65 80 46 36 46 25 40 54 39 30 58 53 36 72  0 56 28 20 33 53  2 54 18 72 69  4 41 23 56 58 26 60 39  4 46 45 52 74  7 24 36 46 30 21 13 51 25 43  9 60 26 70 42 21 24 16 47 
+57 19 49 22 29 35 58 59 27  4 51 65 63 78 74 57 79 26  9 74 67 39  9 14 51 19 21 80  8 64 58 72 49  3 41 49 18 52 51 75 47  7  5 35 69 69  7 54 26 12 38  5 49  4 41 69 79 56  0 46 34 48 74 77 26 15 43  2 71 41  3 62 53 23 67 15 31 17 59 23 51 73 24 75 23  7 21 65 56 79 69 39  6 76 38 40 78 17 62 21 
+76 24  7 75 24 57  6 71 54  4 50 51 30 55 17 59 44 63 58 60 27 27 54  1 79 64 42 34  9 23 35 21 32 12 71 17 17 61  4 14 53 26 10 32  3 35 10 60 72 64 67 64 49 22 18 15 24 28 46  0  5 55 55 48 44  4 18 18 61 76 69 28 68 77 65 55 65  5 44 51 11 25 76 51 22 72 68 75 68 58 29 57 29 73 37 79 12 25 60 69 
+42 27 56 20 60 49 11 20 72 45 69 29 30 25  9  4 50 28  8 35 71 24 18 63 78 40 18  9 32 17  1 72 78 63 74  2 18 38 66  8 32 46 51 59 44 70 66 73 68 65 62  8 18 43 55 63 46 20 34  5  0 75 42  2  9 22 77 38 44 18 13 29 33 66 70 54 77 11 31  2 25 41 53 39 28 27 74 32 44 78 32 64 37 35 53 39 66 29 16 22 
+47 57 40 54 48 21 71  4 47 68 22 45 75 15 34 72 77 59 27 16 70  4 69 38 75 39 68 41 74 77 39 47 36 16 31 30 46 46 78 59  4 13 34 31 14 47 12 39 39 38 15 62 22 21 74  9 49 33 48 55 75  0 40 29  8 46 28 80  2 48 16 78 31 33 54 36 27 55 28 12 23 70 43 79 68  2 51 13 41 57 32 15 54 72  9 26 80 40  3 75 
+27 49 60  7 69 43 33 73 48 41 32 25  8 76 54 61  9 80 64 74  5  4 50 74 23 26 25 23  1  1 36 37 59  2 27 64 30 32 44  5 19 10 74 64 19 27 69 31 43 72  1 33 14 72 30  2 50 53 74 55 42 40  0 61 37 14 40 20  3  5 38 64 37 75  4  1 60 78 77 69 56 32 64 39 40 64  9 36 59 41 35 73 76 48 29 14 68 28 72 34 
+52 30 55 23 37 37 27 24 21 53 24 38 60 30  7 69 47 44 67 26 23 59 41 28 75 55 25 13 35 17 44 34 25 20 77 53 25 48 61 17 60 50 80 63 16 26 71 24  1 34 66 16 20 50 44 10 50  2 77 48  2 29 61  0 50 45 45 33 12 41  5 32 76 47 48 52 42 17 50 61 45 38 24  4 12 52 14 71 17 61 44 75 67 54 76 50  9 31 21 61 
+79  2 10 36  2 48 49 52 72 74 52 52 31  2 35 31 80 67 78 70 15 39 61 62 76  3 63 65 66 53 33 51 54  6 73 67 59 26 27 63  3 11 47 48 79 75  8  9 72 43 46 31 54 27 63 45 50 54 26 44  9  8 37 50  0 73 23 74 46 52 11 13 67 66 78 50  7 75  9 28 78 63 80 46 53 60 70 76 58 76 63 51 68 65 35 17 48 15 36 27 
+54 70 25 66 73 18 16 80 60 24 68 30  6 50 35 61 54 49 47 42 32 72 19 14 20 56 60  7 47 38 37 68 77 34  4 76 23 71 42  6 60 30 48 33 70 41 10 45 35 15 25 34 38 21 65 43 70 18 15  4 22 46 14 45 73  0 17 32 27  6 71 11 29 39 73 60 61 58 31 30 72 45  2 51 64  6  2 54 49 58 71 65 79 42 71 42 29 64 71 65 
+46 17 41 49 24 45 47 60  4 47 49 63 80 47 29  3  8 69  5 71 35 66 19 31 61  7 76 16 39 75  8 54 39 34 68 69 69 28 21 77 75 23 33 34 43 12 80  9  7  8  9 22 72 52 13 13 71 72 43 18 77 28 40 45 23 17  0 67 79 29 80 64 56 31 68  1 30 13 78 19 20 36 43 32 25 17 39 62 66 77 62 50 74 76 12 46 50 34 41 17 
+19 73 12 43 33  4 19 66 35 73 25 73 13 67 46 32 12 53 13 26 80 25 64 54 78 35 35 72 71 80 34 74 77  7  7 67 55 52 39  1 37 67 33 49 29 37 51 79 14 67 63 70 72 80 50 31  8 69  2 18 38 80 20 33 74 32 67  0 44 21 42 52 54 40  9 29 33 49 38  5 24  7  6  4 67 17 68 62 37 32 62 29 55 29 16 77 50 36 11 13 
+12 29 56 63 78 46 57 75 53  1 58 58 45 70  4  7 65  8 17 67 60 12 11 25 52 23 59 32 13 34 47 18 56 41 27 66 19 32 37  9 38 64 42 65 26 64 54 75 78 10 10 37 29 14 68 13 35  4 71 61 44  2  3 12 46 27 79 44  0 26 34 24 64 21 11 59 51 25 56 32 49 42 62 52  1 61  4 24 47 29 38 77 25 19 18  7 71 17 53 10 
+31 19 77 50 77 10 42 36 27 23 20 31  1 56 26 11 22 42 59 52 63 30 37 25 67 40 54 65 70 21 47 13 70 39 39 56 68  6 17 38 13 40 30 23  9 27 71 76  8 47 18 56 49 34 37 33  4 41 41 76 18 48  5 41 52  6 29 21 26  0 46 57 32 27  9 31  2 25 54  5 40 37 31 47 38 32 14 17 33 20 51 27  8 47 36 36  4 41 64 27 
+51 79 76 75 54 42 61 69 12 15 12  7 34 49 65 49 63 49 47 16  4  1 16 42 31 53 61 28 51 56 65  7 12 48 10 56 49 37 46 76 28 19 80 60 79  5 69 76 22 79 73 10 29 59 24 35 49 23  3 69 13 16 38  5 11 71 80 42 34 46  0 49 22 60 50 19  9 57 46  6  5  7 76  4 19 11 21 73 34 56 70 15 43 27 37 64 79 79 61 38 
+48 60 62 24  8  7 64 15 67  3 11  2 15 63 51 74 59 33 22 46 11 11 18 32 62 43 13 31 13 28 17  8 47 73  7 69 75 18 58 69 21 13 80 25 63 28 63 79  6 51 80 19 66 42 70 29 29 56 62 28 29 78 64 32 13 11 64 52 24 57 49  0 34 23  2 13 60 11 21 22  3 41  3  7 17 53 75  5 61 27 49 26 65 43 23 55 70 61 12 18 
+69 58 36 17 26 73 71 53  8 57 21 74 17 49 30 43 28 24 23 16 28 28 49 72 25 23 55 53 67 17 36 67 18 51 74 26 61 28 29 16 17 54 26 59  9 35 57 16 71 54 17  8 32 60 12 78 64 58 53 68 33 31 37 76 67 29 56 54 64 32 22 34  0  8 52 34 78 77 24 14 18 18 55 22 27 79 42 43 35 64 19 59 12 17  6 67 40  3 66 37 
+16 25 28 68 32 35 28 32 53 58 75 77 37  8  1 32  3 39 78 49 71  1 48 32 13 29 32 11 48 31 34 10 13 53  2 22 61 71  8 50 57 40 47 32 18 48 16  8  7  3 60 31 52 41 58 52  1 26 23 77 66 33 75 47 66 39 31 40 21 27 60 23  8  0 78 80 10 36 54 39 74 23  2 77 37  8 62 23 48 30 72 69 78 15 52  9 15 48 72 20 
+17 23 32 44 69 15 54 43 68 16 55 47 24 78 23 70 16 60 23 53 20 12 39 56 42 73 19 66 31 60 80 69 31 36 16 38 29 51 64  4 77 14 76 73  3 19 32 28 22 12 19 69 58 36 23 20 21 60 67 65 70 54  4 48 78 73 68  9 11  9 50  2 52 78  0 62 75 54 69  8 23 51 60 41 80  8 45 14 20 22 50 61 62 80 21 34 73 54 18  4 
+78 39 56 10 43 77 65 61 23 59  5 67 46 21 39 73  9  5 74 60 71 11 20 25 72 19 17  9 67 79 58 74 68 55 65 55 73  2 68 51 46 28 54 62 75 23 54 60 71 53  6 28 50 19 25 19  7 39 15 55 54 36  1 52 50 60  1 29 59 31 19 13 34 80 62  0 48 56 30 66 70 35 30  1 52 22  5 20 44 48 65 56 78 20 79 21 52 23 36 36 
+54  6 48 25  2 75 35  4  8 64 34 51 38 37 10 74 64 10 24 52 71 44 20 42 72 53 65 79 24 17 45 71  2 29 47 80 28 17 68 70 72 77 74 42 75 64 63 75 37 37 75 56 55 10 40 20 72  4 31 65 77 27 60 42  7 61 30 33 51  2  9 60 78 10 75 48  0 77 15 76 77 31 63 27 45 70 74 64 11 39 25 65 43 80 48 33 75 52 17 53 
+17 76 68 30  9 33 29 13 57 68 29 43 25 54 17 76 51 34 24 48 25 70 49 69 68 24 76 75 30 61 24  2 17 64 63 69 60 16 40 71  1 70 45 32  5 77 40 13 47  8 63 74 25 41 43 34 22 46 17  5 11 55 78 17 75 58 13 49 25 25 57 11 77 36 54 56 77  0 68 60 59 73 22 63 22 51 26 79 19 62 63 19 47 72 47 15 38 11 46 39 
+76 18 31 26  7 22 62  9 40 68 38 45 21 64 72 56 35 60 75 14 11 33  4 13 24 17 64 40 30 78 72 15 39 18  6 80 18 16 63 79 48 21 10 20 75 62 56 64 25 75 20 56 58 58 58 42 45 45 59 44 31 28 77 50  9 31 78 38 56 54 46 21 24 54 69 30 15 68  0 11 10 35 20  3 46 70  3  6 80 60 22 46 12 25 17 79 58  2 75 22 
+44 34 74 42 48 30 73 26 58 37 34 66 72 53 41 35  4 15 56 43 28 34 41 35 65 49 60 60 73  3 55 46 25 32 27  1  8 17 45 79 22 30 10  8 25 15 73 31 12 69 45 69 32 59 65 16 48 52 23 51  2 12 69 61 28 30 19  5 32  5  6 22 14 39  8 66 76 60 11  0 73 32 28  4 46 74  4  3 42  2 75 80 62 71  7 27 28 49 62 27 
+19 46 33 21 40  9 28 67 76 16 49 28 30 54 24  8 37 14 40 18  8 23 34 60  9 11 68 67 51 61 39 41 32 26  1 23 46 49 44 54 40 17 21 51  6 72 46 28 62 40  5 69 38 43 20 61 64 74 51 11 25 23 56 45 78 72 20 24 49 40  5  3 18 74 23 70 77 59 10 73  0 60 67 39 22 37 38 32  2 76 34 67 80 44 53  1 42 48 60 46 
+30 62 42 66 24 76 35  1 46 73 75 13 42  7 41 31 34 57 13 17 37 24  9  5 77 73 22 63 75 29 62 66 18 75 35 24 60 69 46 78 55 63 60  5  7 71 18 47 71  7 71 61 37 68 60  5 15  7 73 25 41 70 32 38 63 45 36  7 42 37  7 41 18 23 51 35 31 73 35 32 60  0 29 73 73 53 22 40 32 45 32 63  1 71 13 15 52 26 68  8 
+ 3  9 77 19 38 25 52 68 35 29 59  5 50 50 23 40 41 46 49 26 48 35 28 64 17 62  4 70 43  7 78  7 72 33 11 61 52 72 53 32 64 39 51 77 63 59 10  6 60 28 17 53 50 59 34 62 29 24 24 76 53 43 64 24 80  2 43  6 62 31 76  3 55  2 60 30 63 22 20 28 67 29  0  8 32 60 15 21 35  4 45 51 68 25 45 29 18 70 45 69 
+56 29 13 57 17 22  7 24 78 40 22  8  3 34 62 41 20 21 16 31 11 26 71 43 28  7 13 24  7 12 25 11 15 30 20 71 61 15 58 71 41 64 22 72 56 74  6 14 61 31 50 46 50 35 61 62 75 36 75 51 39 79 39  4 46 51 32  4 52 47  4  7 22 77 41  1 27 63  3  4 39 73  8  0  2 27 13 68 47  7  7 20 16 12 55 28  8 72 75 48 
+36 20  4 78  8 33 63 49 44 36 49 11 44 73 63 47 78 69 79 61 52 68 35 26 11 59 42 20 25 52 33 11 20 60 40 20 45 55 23 80 51 34 18 58 35  8 61 69 54 77 46 67 40 18  1 39 23 46 23 22 28 68 40 12 53 64 25 67  1 38 19 17 27 37 80 52 45 22 46 46 22 73 32  2  0 33  3 15 64 25 38 51  4 14 79 79 39 26 59 26 
+42 60 39 43 57 16 45 40  8 52  8 26 29 67 25  4 23 40 23 33  7 30 57  7 71  8 14 30 37 39 38 15 78 47 52 13 42 33 71 61 57 80 13  1  6 63 28 26 38  7 63 28 51 32 70  8  1 30  7 72 27  2 64 52 60  6 17 17 61 32 11 53 79  8  8 22 70 51 70 74 37 53 60 27 33  0 61 67 42 78 34 66 62 72 56 59  1  3 38 69 
+75 62 78 79 22  9 15 69 62 33 39 24 13 20 10 56  3 33  4 12 80 76  8 55 14  5 19 61 33 24 62  5 69  4 67 61 42 61 66 77 18 79  6 21 30 30 29 25 42 34 66 56 35  4 16 41 61 21 21 68 74 51  9 14 70  2 39 68  4 14 21 75 42 62 45  5 74 26  3  4 38 22 15 13  3 61  0 41 24 55 27 35 79 37 33 12 19 47 68 70 
+ 9 12 39 10 59 39  9 66 48 57  4 79 16 25 28  3 61 48 59 69 30  5 11 57 65 39 25 45 79 50 15 35  2 26 17 18 77 68 36 60 71  4 37 44 59 52 44 31 34 77 66 80 13 59 31 75 43 13 65 75 32 13 36 71 76 54 62 62 24 17 73  5 43 23 14 20 64 79  6  3 32 40 21 68 15 67 41  0 61 32 30 53 26 65 40 51 52 61 30 65 
+61 16 64  6 19 55  4 61 35 32 67 55 71 33 44 44 51 72 54 27 69  2 27 50 74 38 78 79 30 20 32 53 54 46  5 69 39 67 32 32 42 49 79 23 24 38 61 65 64 37 68 14 73 48 78  8 65 51 56 68 44 41 59 17 58 49 66 37 47 33 34 61 35 48 20 44 11 19 80 42  2 32 35 47 64 42 24 61  0 56 38 16 39 66 40 18 10 36 66 62 
+61 49 15 32 71 44 16 69 62 18 41  6 35 67 66 65 61 65 35 40 53 65 78 10 77 27 48 48  5 54 21 35 76 48  2 10  3 77 40 31 22 46  1 73 77  4  1 69 51 28 42 35 72 32 55 38 34 25 79 58 78 57 41 61 76 58 77 32 29 20 56 27 64 30 22 48 39 62 60  2 76 45  4  7 25 78 55 32 56  0 19 24 53 12  8 40 29  3 77 38 
+65 68 22 57 11  1 57 70 31 51 77 38 29 50 50 23 68 10 30 61 18 61  6 43 40 11 40 47 19 18 19 24 35 10  3  4 37 76 35 57 61 38 50 35 11  1 26  2 21 76 34 71 50 17  5 68 52 43 69 29 32 32 35 44 63 71 62 62 38 51 70 49 19 72 50 65 25 63 22 75 34 32 45  7 38 34 27 30 38 19  0  9 34  6 16 35  6  2 71 78 
+57 46 54 46 74 46 78 77 78 44 14 52 17  5 58 51 24  7 80 41 74 46 24 38 18 79  4 59 20  7 18 30 53  2 16 62 24 22 24 63 30 69 70 39 52 36 67 49 21 39 73 71 61 24 38 72 80  9 39 57 64 15 73 75 51 65 50 29 77 27 15 26 59 69 61 56 65 19 46 80 67 63 51 20 51 66 35 53 16 24  9  0 42 59 22 67 79 70 39 66 
+44 58 44 12 61 79 50 44 58 34 35 52 42 72 17 69 57 42 63 39  9 74 66 64 39 26 31 62 69 36 46 34 78 48 52 28 20 23 55 10 73 51 37 78 44 79 44 42 73 64 57 59 57 56  3 31 71 60  6 29 37 54 76 67 68 79 74 55 25  8 43 65 12 78 62 78 43 47 12 62 80  1 68 16  4 62 79 26 39 53 34 42  0 54  9 78 40 67 52 57 
+25 45 15 74 38  7 22 61 47 15 35 58 37 22 70 44 77  5 49 49 52 63 38 45 19 36 18 46 69 57 19 17  1 72 36 44 40  6 41 78 38  1 37  2 25 60 19 32 75 58 54 17 55 69 67 66  6 26 76 73 35 72 48 54 65 42 76 29 19 47 27 43 17 15 80 20 80 72 25 71 44 71 25 12 14 72 37 65 66 12  6 59 54  0 12 14 38  5 49 59 
+47 26 67 76 35 42 26 18  3 21 47 20 59 38 78 63 30 45 28 37 76  3 43 15  6 43  2 25 38 76 24 48 28  3 27 47 38 15 77 80  7 37 12 57 35 46 71  7 74 36 53 69 25 44 26 34 65 70 38 37 53  9 29 76 35 71 12 16 18 36 37 23  6 52 21 79 48 47 17  7 53 13 45 55 79 56 33 40 40  8 16 22  9 12  0 57 42 58 24 74 
+55 62 31 55  2 28 13 26  9 38 54 15  9 28 46  8 21 78 22 47 61  3 76 71 31 32 42 62 74 16 68 42 14 74 66  1 49 59 64 12 76  2 63 78 41 32 67 19 33 33 20 76 80 37 11 35 43 42 40 79 39 26 14 50 17 42 46 77  7 36 64 55 67  9 34 21 33 15 79 27  1 15 29 28 79 59 12 51 18 40 35 67 78 14 57  0 70 69 25 21 
+26 67 59 21 41  6 37 46 62  8 20 61 31 77 50 23 56 76 69  3 69 24 68 30 27 20 25 47 60 33 73 75  9 32 56 46 40 61  9 61 12 24  7 40 64 34 26 16 16 78 57 15 41 46 44 79 37 21 78 12 66 80 68  9 48 29 50 50 71  4 79 70 40 15 73 52 75 38 58 28 42 52 18  8 39  1 19 52 10 29  6 79 40 38 42 70  0 80 39 23 
+75 67 38 16 30 13 15 60 30 78 60 58 73 36 13 17 61  6 15  8 52 16 68 23 69  3 26 77 77 36 65 26 39 44 62 35 19 58 62 44  7 42 63 58 62 63 37 64 14 60 30 73 18 54 13 11  2 24 17 25 29 40 28 31 15 64 34 36 17 41 79 61  3 48 54 23 52 11  2 49 48 26 70 72 26  3 47 61 36  3  2 70 67  5 58 69 80  0 37 47 
+50  7 28 15 49  8 80 50 38 76 45 51 52 15 64 51 61  5 16 72 13 73 23 27 70  2 61 75 17 69 70 70 76 53 42 33 64 12 72 64 38 48 15 33 12 46 60 46 38 55  1 46 32 54 51 70 13 16 62 60 16  3 72 21 36 71 41 11 53 64 61 12 66 72 18 36 17 46 75 62 60 68 45 75 59 38 68 30 66 77 71 39 52 49 24 25 39 37  0  5 
+22 68 21  3 15 38 80 73  7 47 16 72 51 46 22 47  4  9 10 58 66 38 46 74 23 31 75 32 73 16 29 25 53 38 50 72 32 62 79 62 18  8 71 79  2 80 64 33 64 65  3 19 20 34 10 36 80 47 21 69 22 75 34 61 27 65 17 13 10 27 38 18 37 20  4 36 53 39 22 27 46  8 69 48 26 69 70 65 62 38 78 66 57 59 74 21 23 47  5  0 
+
+ 0 25 20 23 18 30 21 19 23 21 20 15  5 12  2 16 13 26 13  6 23  1  6 17 11  8  1 26  5 10 16 27  3 18 18  3  3  9 24 26 26 11  5 16  9 26  7 27 20  1  5 10 21 15 24 18  7  8 17 18 24 30 24 20 12 16 13  5 24 11 29 23 10 19  2  6  4 26 22  2 16 30 17 23 20 29 28  2 15 23 16  1 16 30  5 19 20 13 18 23 
+25  0  1 23 13 13  8  9 30 19 19 10 20 17 30 23 30 18 29 15 12  1  6  5  8 20 25  2 10  5 18 28 28  3 20  6 18  9  7 23 11 24  6 12 24 22  7  6 18 30 16  1 17  1 30 11  5 11  3 15 12 12 25 25 15 27  3  4 21 19  4  2 27  2  8 23  9 26 16 10 11 15 19  7  9 23 28 24 30 20 23 26  3  5 28 29  9 10 25 14 
+20  1  0 15 23 18  9 29 19  5  5 22 12 17  2  6 15 24 25  6 16  3 16 24 27 27 16 17 16  2 21 14 25 11 18  4 24 19  5 18 14 12 16 17  5 30 25 26  6  3 26 19 11 19 18  4 17 11  9 23  6  2 19  5 28 27 16 25  4 20  2  5 27 28 22 12 15 29  4 13  2 11  6 10 27 21  7 24 19 25 10 27 24 29 26 24 27 19 30  9 
+23 23 15  0  8  9 21 21 22 25 10  9 17 26 16 28 15 26 17 19  3 20 17 20 16  7 26 19 18 30 22  1 12  8 20 10 27 21 22 21 10 18  6 30 17 12  2  1  2 26 24 22 18 25 28 29 16  8  9 24 23 24  6 13 23 11 20 16  8 29 17  1  4 27 30 25 29 30 16 13  3 30  7 15 27 29 23 17  9  5 18 12  1 20 21  3 13 21 12 19 
+18 13 23  8  0  1  9 11  9 28 21  6 19  2 27  8 30 20  8 15 18 25 23 23 18  1 13 13  8 10 23 15 21  4  3 20 10 14  5 30  6  4  3 19 16 13 19 28 27 17 21  5 12 13 16 20 11  3  8  1  1 24 13 29 14 23  2 11 11 12 12  2 13 20 14  9 25 22  5 16 25 18  6  4 19  3 28 15  6 22 22 12 12 15 22 13 10 10 28 14 
+30 13 18  9  1  0  4  1  9  4 15 17  1 14 15 10 30  4 24  7 29 14 29 29 30 24 26 19 14  9  6 14 26 12  4 12 15 14  6 27 27 27  2 16 17  7  9 13  7 20 17  7 26 19  2 29  5  8 11 26 21 23  3  7 16  2 11  7 27 17 12 12  3 30 21  5  5 26 16 11 27  5 27 16 16  4 12 22 23 20  9 10 25 14  1  9 19  2 17  1 
+21  8  9 21  9  4  0  4 10  8 11 22 11 15 15  4 28  7  8 30 10  2  1 19 17 19  3 23 25  8 18 19 16 28 17 23 10 19 16  3 24 10 26 23 19 21 19 27 16 22  5 30  2 10 12 25 21 16 27 17 10 22 21  1  4  5  8 26 23  2 13  3 28 13  1 23  1 12 28 16 17 28 19 13  5 20 10 28 20 12 20  8 26 15 28 27 23 13 13  1 
+19  9 29 21 11  1  4  0  9 30 30  3 29  3 22 28 22  6 28  2  7 18 16 11 17 12 17 21 14 24 25 24 28 21 24 26 27 28 19  3 24 10 24  3  8  3 22 15  5 26 28 30  7 10  3  7 10  8  7  2 23  8 29 19  2 12 11 17 13 29  5  5 26  2 13 16 30 14 22 28 19 26 19 22 12 16 10 28 21  9  3  7 17 18  3  7 30 29 11 14 
+23 30 19 22  9  9 10  9  0 23 13 20 30  3 17  1 15 30 19 21 24 15 16 25 20  8 27 12  5 16 17 29 22 11 10  8 19 11  9 11  3  4 25 12 30  1 29 14 23  5 12 28  6 27 11 15 23  1  6 11  3 25 12 10  3 14  6 26 26 17 27 10  1 15 29  9 30 12 28 17 16 21 11 29  5 11  7 21 11 30 30  7  2 10 22 22 23 16 26  5 
+21 19  5 25 28  4  8 30 23  0 14 14 20 21  7  3 27 11 30 26 17 22  8 26  2 13 15 26  3  1 25 14  7 27 13  4  4 26 19  4  4  9 21 17 19 12 14 19  8 27 13 19 15  7 22  9  5  1  6 15 20  1 24 29 21 23  7 25  3  2 17  1 28 29 29  7 23 28 19  5 17 27 22 19 15 15 14 23 10 23 25 11  4 29  1 10 17 12 13  6 
+20 19  5 10 21 15 11 30 13 14  0 12 20  2 18 13 27 22 20  6 17 24  9 19  8 24 30 21 29  1 17 30  4  5 27 17  3 30  4 17 18  3 23  3 15 23  8 11 19 30  4 29 20 17 18 16 10 27 26  3  7  1  2 14  7 29 12  7  6 23  7  4  7 17  1 28 23 23 23  7 21  4  1 15 15 11  6 15  5 30 23 26 11  8 20 20 23 23 21  4 
+15 10 22  9  6 17 22  3 20 14 12  0 18 25 15 22 14 10 11 27  2  8 19  3 22 16  7 28 10  6 15  6 11  6 17  3 28 15 27 21  6 17 15 29 19  3 10  5 22 30 16 11  4 27 25 12 29 29 30 21 19 12 29 12 18 18 13 17  9 27 27  3 30 28 20 26 22 20 30 18  1 10 27 28 14 25 15  5 11  6 21 24  4 27 27 30 27 27  9  6 
+ 5 20 12 17 19  1 11 29 30 20 20 18  0 16 30 29 20  4 13 11 24 29 11  7 28 26 16 19  6 27 28 23 13 21 11 10 27 23 20 17 17 20  5 29 20 12 23 12 15 24 13 16 17 23  5 23 25  8  7 23  7 17 13 23 13 10 23 25  3  1  7 17  1 28 20  4 18 13 14 21 19  8 30 17  6 18  8 15 27 24  3 25 29 27  6 18  2 18  8  5 
+12 17 17 26  2 14 15  3  3 21  2 25 16  0 22  9 12 25 12  7 24 26 18  3 14 11 13 25 26 27 18  7 15  8  2  2  4 22 25  1 16  7 10 13 12 20 28  8 22  8 15  9 21 24 21  3 30 29 29 20 23 21 18 20 17 26  3 12 28 28 26 22 12 23 26 29 23 16  8 27 13 19 21  8 19 19 25 13 12 28 28 10 21 28 16 23  5  2 27  6 
+ 2 30  2 16 27 15 15 22 17  7 18 15 30 22  0  5 15 19 26 22  1 20 10  8 22 28  9 16 25 11  6 24  1 27 17  8 16 15 26  3  8 27 17 27 14 26 29 10 17  1 23 28 29 26 14 30 30 16 10 11 24 26 23 11 19  1  9 22 22 17 13  8  2  1  8 29 16 18  4  3 30  7 23 23 11  8 28  7 24 27 26 17 25 22  1  4 26 10 14 20 
+16 23  6 28  8 10  4 28  1  3 13 22 29  9  5  0 22 14  9 28  8  3  4  6  9 29 26 17 17 18 27 17 11 29 22 20  1 24 16 26 17 23 16 14  8 29 22  5 21 30  6  3 16 28 11 29 14 30 23  2 17  4  2 19 19  2 27  5 22 27 25 23 10 13 21 23 28  6 11  8  8 11  3 24 28  2  4 28 27 12 15 26  3 17 27 11  2  1  6 18 
+13 30 15 15 30 30 28 22 15 27 27 14 20 12 15 22  0  4 15 22 13 18 25  4 12  5  3 15 24 27  9 12 23 19 10  5  7  4 25 13  4  9 15  4 15  4 22 26  3 13 28 20 19  1 18  1 24 28 16 10  5  7 16 17  2 21 18 13 13 10 29 21  6 15 24 18 21 16  3 28 25 17  5  1  4  9 29  2 30 10 22 19  2 16  9 12 22 28 15  1 
+26 18 24 26 20  4  7  6 30 11 22 10  4 25 19 14  4  0  8  9 10  8 23  7 18 13 15 16  8 30 29 20 25 17  3 29 11 24  2  3 28  5  3 18 18  7 12 11  3 17 21  7 21  2 19 26  7  1  9 25 16 22 28  9 15 12  8 30 19 18 16 21 27  5 23  8 11 10 15  5 21 20  1 24  3  7  1 28 20 26 30 22  1 12 21  8 15 26 23 23 
+13 29 25 17  8 24  8 28 19 30 20 11 13 12 26  9 15  8  0 23 21  6 14 15  3  3 13  1 23 22 13 11 20 27 13 12 27  3 18  3 29 10  3  7 26 13 13 30 18 27 16 24 12  4 29 22 24  4  7 12 25  7  2  5  3  1  4 30 25  1 23 15  7 21 21 20 14  5 29  9  1 26  9  8 19 19  6  9 25 27  3 29 29 11 28  3 11 13 28  9 
+ 6 15  6 19 15  7 30  2 21 26  6 27 11  7 22 28 22  9 23  0  9  1  5 21  1  1 26 16 18 17 21  4 28 10 24 14  6 18 12 29 23 10 28 19  1  8  5  2  9  8 27 24 14 16 24 26 21 14 21  1 27 13 21 26 24 20  4 14 18 30 27 13 11 24 17 19  6  1 30  2 18 22 10  4  8  2  5 23 16 21 19 17 23 22  4 16  7 20 17 28 
+23 12 16  3 18 29 10  7 24 17 17  2 24 24  1  8 13 10 21  9  0 20 24  8  8 10  7 14  3  5  4  6 11 26 22 20 12 25  7 28 15 24  3  1  9 25 10 23  9  9  9 15 25 13 25 13 29 21 30 26 20 17  1  8 23 26  8 21 19 19 19 29 13 21 12 13 21  1 29  3 17 24  7 16 30 26 19 22  2 13 22 15 19 20 30  4 29 16 18 30 
+ 1  1  3 20 25 14  2 18 15 22 24  8 29 26 20  3 18  8  6  1 20  0 27 19 30 18 22 23  2  2 25  3 30 21  8  7 14 13 20  6 21 16 17  7 16 12 23 27  8 14 25 23 24  7 28  1 23  6 26 27  4  2 29 20 18 29 16 17 27 17  6  9 14  8  8 27  7 23  6  9 27 26 13 25 24 21  6 20 19 30 27 18  7 19  7 30  6 18  5 10 
+ 6  6 16 17 23 29  1 16 16  8  9 19 11 18 10  4 25 23 14  5 24 27  0  1 22 28 18 14 16 13 24 21  8  2 30 14 29 30 27 19 11 30 12  3 22 15 17  1 13 27 23 26 16  8  4 14  5 15 26  7  1 28 14 25 29 26 28  1  7 27  9 16 24 30 16 26 21  4  5 14  5 13 20 26 30  9 18 24 10  3 29 20 17  3  6 13 28 26 15  5 
+17  5 24 20 23 29 19 11 25 26 19  3  7  3  8  6  4  7 15 21  8 19  1  0  6 27 14  8 27 11 20 14 12 29  8 27 14 13 19 29 14 17 13  7 14 30 16  4 26  3 13  1 14 20 12 25  1 18 18 26 13  5 12 19 13  6 10  3 25 30 16 21 17 16 25 29  4  6 25 17 12  8 24 23 18 25 17 13 11 10  3 19 19  7  2 26  2 17 26 10 
+11  8 27 16 18 30 17 17 20  2  8 22 28 14 22  9 12 18  3  1  8 30 22  6  0  1  5 12 22  9 22  2  5 23 30  1 26 12 14 20  5 26 20 25 30 25 19 27 20 30 28 14  9 24 22  9 10 27  2 23 28  8 29 27  1 25 13  6 27 11  4 13  4 24 30  9  7 19 30 15 16 13 15 22 22 24  7 14 27 20 25 30  3  5 25  7 29  8 14 26 
+ 8 20 27  7  1 24 19 12  8 13 24 16 26 11 28 29  5 13  3  1 10 18 28 27  1  0 23 25 12 21  7  9 12 12 12 18 19 11 10 27 29 14 25 29 23 14  4  9  8 21 12 16  6  2 19  3 28 27 19 17 28 22 17 27  5 25 23 18 28 12 27 23 11 22  7 26 27 26 10 12 27 12 29 28 17 20 11 16 11 18 30 11 16 21 25  5  8 21 21 10 
+ 1 25 16 26 13 26  3 17 27 15 30  7 16 13  9 26  3 15 13 26  7 22 18 14  5 23  0  3  2 28 20  6 16 13 11 25 24 13 21  2 20 27 16 30 20 17  2 21  9 21 28  4  5 27  4 11  3 22  3 28  3 27 21  2 22  7 29 19 17  1  3 11 26  7 18  7 19 17  4 22  5  2  4  2 30 11  9  7 12  6  2 21  6 18 22  6  6 29  7  6 
+26  2 17 19 13 19 23 21 12 26 21 28 19 25 16 17 15 16  1 16 14 23 14  8 12 25  3  0  7 23 18  4 18  2  3 19 13 28  4 29 11 15 25  8 26 20 12 25 16  5  9 20 16 29  2 19 14  6  6 15 12 10 20 23  4 26 16  5 17 26 16 24  8  5 26 11 13  5 13 21  7 28 21  9 14 20 30  3 28 21 15 13 17 22 21 29 28 11  2  3 
+ 5 10 16 18  8 14 25 14  5  3 29 10  6 26 25 17 24  8 23 18  3  2 16 27 22 12  2  7  0  1 15 13 10  2 23 14 27 21 25 12  5 19  1  9  3 15 19  7  9  8 22 26  3 12  1 18  2 26 17  5 30 11 25 13 16 11 18 15  5 25 21  3 12  5 30 24  6 27  6 17 16  4  6  6 25  8 10 18 12  2 14 17 10 27 20 26 17  9 27 15 
+10  5  2 30 10  9  8 24 16  1  1  6 27 27 11 18 27 30 22 17  5  2 13 11  9 21 28 23  1  0 30  2 10 20 16 28 13 30 20 20 18 13  7 27  5  5 18  2  7  8  2 26  4 18 21 25 29 24  5 12  7 19 20 25 14 24 10 17  1 27  9 25  7 25 22  4 29 14 28 16 30 20  6  9 10  5 26 30 23 27  1  8 14  6  1  6 25 25 21 27 
+16 18 21 22 23  6 18 25 17 25 17 15 28 18  6 27  9 29 13 21  4 25 24 20 22  7 20 18 15 30  0  1 19 17 10 18 20  7 22 23 29  4 24 27 11  6 15 25 26 27 26 28 14 15 23 21  4 11 19 13  2  5  3 19  7 30 11 13  5 11 16  9  4 20 25  5 14  5 25 28 10 26 28  7  4 10  4 13 28 14 26 26  1 23 25 24  9  3 25 16 
+27 28 14  1 15 14 19 24 29 14 30  6 23  7 24 17 12 20 11  4  6  3 21 14  2  9  6  4 13  2  1  0 14  7 13 29 11 24 17 17  2 27 24  3 28 29 15 29 11 14 13 23 26  8  5 12  5 26 22  2  9 14 17 20 16  5  6 13  6 21 19 18 22 27 12 28 16 29  7 20 22 17  6 10 27 17 27 17 27 26 28 18  7  6 23 10 15 18 20 15 
+ 3 28 25 12 21 26 16 28 22  7  4 11 13 15  1 11 23 25 20 28 11 30  8 12  5 12 16 18 10 10 19 14  0 17 12  5  4  7 26  8 20  3 11 21 18  8 16 21 16 13 26 23  8 30 20 12  2 25 22 24 13  2  2 14  2 15  5  5 28 29 15 10  2 20 15 28 29 22 19 16 20 17 18  4 22 19  7 10 17 20  6 21 30 19 20  8 24  7 16  7 
+18  3 11  8  4 12 28 21 11 27  5  6 21  8 27 29 19 17 27 10 26 21  2 29 23 12 13  2  2 20 17  7 17  0  2 22  4 30  3  4 28  5 28  2 28  5  6 26 23 13 14 12 28  6  9 11 24 15 11  7 10 28  7 18 24  9 30 29 11 23 16 26 30 16 10 30 25  7  5 15 13 20 30  1 14 15  8  3 27 15  2 16 11 27  6 15 30  8 19 15 
+18 20 18 20  3  4 17 24 10 13 27 17 11  2 17 22 10  3 13 24 22  8 30  8 30 12 11  3 23 16 10 13 12  2  0  1 10 18  2 12 29 20 12 10 24  9  3 22 15  4  8  3  4 23 22 16 24 29 14 22  2 23 14 23 23  6  4 30  1  5 17 12  5 15 20 16 14 26 24 28  5  2  8 11  1 21  7 25 25  9 24  5  4 13  7 23 11 29 19  5 
+ 3  6  4 10 20 12 23 26  8  4 17  3 10  2  8 20  5 29 12 14 20  7 14 27  1 18 25 19 14 28 18 29  5 22  1  0 24 22  3 14 14  3 16  8 30  8  1  5 19  5  1  2 20  2 27  2  5  9 17 11 10 22  6  5 22  3 26 15  6 18 14 30  4  9 18  4  5  4 17 13 13 25 23  4 23 21  3 12  1  5 25 29  5 27  1 19 14  2  9 27 
+ 3 18 24 27 10 15 10 27 19  4  3 28 27  4 16  1  7 11 27  6 12 14 29 14 26 19 24 13 27 13 20 11  4  4 10 24  0 29  4  1 22 20 10 30  9  7 17 24  1 20 17  2 10 16  9  8 15 24 18  2 26 17 14  2 17 28 22  7 14 25 18 18 25 26 18 19 20  5 11 13 30 26 25 26  3 27 25 25 12 16  2 15 28 15 10  6 28  2 15 12 
+ 9  9 19 21 14 14 19 28 11 26 30 15 23 22 15 24  4 24  3 18 25 13 30 13 12 11 13 28 21 30  7 24  7 30 18 22 29  0 30  3  3 21 21  2 14 23 28  5 18 15  9 22 29 16 25  7 11 23 23  1 12 11  1  7 30  2 10 29  1  9 21  3  8 18 25 24 11 24 20 15 22 24 23 18  4  8 19  8  1 27 22  5  9 13 25  8 21 23 21  3 
+24  7  5 22  5  6 16 19  9 19  4 27 20 25 26 16 25  2 18 12  7 20 27 19 14 10 21  4 25 20 22 17 26  3  2  3  4 30  0 26  1 12  1 18 23 14  2 16 14  7 21 30 16 14  3 11 24 12 12 21 25 14 18 12 10  2 29 15  1 10 13 23 29 29 28 18 28  6 10 26 16 21 27 13  5 28 19 18  5 30 22  4 29 25 16 20 25  5  5  6 
+26 23 18 21 30 27  3  3 11  4 17 21 17  1  3 26 13  3  3 29 28  6 19 29 20 27  2 29 12 20 23 17  8  4 12 14  1  3 26  0  5 16  6 28  1 19 30  9  2 17 24  4  9 28 20  7 14 30  6 22 24  9  3 13  3 14  7  8 15  7  3  2  8 23  2 14 13  5 15 19 30  5  9 15 10 29 18 27 17 16 28 30 25 17 18  7 18 14 16 13 
+26 11 14 10  6 27 24 24  3  4 18  6 17 16  8 17  4 28 29 23 15 21 11 14  5 29 20 11  5 18 29  2 20 28 29 14 22  3  1  5  0  9 19 15 11 13 26 27  5 22 12  4  2 20 21 17 11 12 26 11 28 18  7 29 18 27 19  6  2 28  5 15 24  5 22 25 24  4 11 15 30 29 23 12 10 13 13 27  3 13 10 24 20 29  5 24 29 18  9  7 
+11 24 12 18  4 27 10 10  4  9  3 17 20  7 27 23  9  5 10 10 24 16 30 17 26 14 27 15 19 13  4 27  3  5 20  3 20 21 12 16  9  0  4 12  6  1 24 11 13 18 20 20 15  7 14  2 24  8 25  1  1  8 13  5 30 26 28 14 16  6 19  5 25 27 23 30 30 10  8  9  6 14 25 10 21 30 12  5 23  9 18 15  9 14  1 26 18 26  5 29 
+ 5  6 16  6  3  2 26 24 25 21 23 15  5 10 17 16 15  3  3 28  3 17 12 13 20 25 16 25  1  7 24 24 11 28 12 16 10 21  1  6 19  4  0 14  5 25  7  5 22 30 30 10 19  7 19 29 14  3 27 28 15  8 11 23 22 26  1 29 23 21  7 30 28 19 12 13 16 15 21  6 22 12 22  6 14 12 19 23 17  8 12 22  3 10 22 18 19 13 30 17 
+16 12 17 30 19 16 23  3 12 17  3 29 29 13 27 14  4 18  7 19  1  7  3  7 25 29 30  8  9 27 27  3 21  2 10  8 30  2 18 28 15 12 14  0 22 25 20 15 22 14 13 13 24 24  8  6 25 22 12 29 17 18  2 26 18 27 24 21 28 25  8 22 10 23  3  3  3 12 22 24 17  5  2 17 27 13 27  6  5  4  9 21 27  1 15 27 29 28 10  7 
+ 9 24  5 17 16 17 19  8 30 19 15 19 20 12 14  8 15 18 26  1  9 16 22 14 30 23 20 26  3  5 11 28 18 28 24 30  9 14 23  1 11  6  5 22  0 21 15 26  7  9 17 21 23 20 24 23 23 29 17 28 21 15 11 12  4 21 24 23 24 28  7 18  5  2 16 11 18 25  4  5 21 23 16  9  6  3  6 28 25 15 17 13 26 14  7 29 28 11 15 19 
+26 22 30 12 13  7 21  3  1 12 23  3 12 20 26 29  4  7 13  8 25 12 15 30 25 14 17 20 15  5  6 29  8  5  9  8  7 23 14 19 13  1 25 25 21  0 25 17 10  9 26 16 16 23 13 20  5 23  5  7  8 11  5 25 21 27 26 12 26 27 24 26 14 12 10  3  2 24  4 17 15 17 25 13 19 17 11  7  6 21 15  6 19 14 21 17 25  3 17 10 
+ 7  7 25  2 19  9 19 22 29 14  8 10 23 28 29 22 22 12 13  5 10 23 17 16 19  4  2 12 19 18 15 15 16  6  3  1 17 28  2 30 26 24  7 20 15 25  0 14 13  2 17 29 21 19 22 19 17  8 21 10 16 27  8 26 27 25  1 29 22 12 17 24  6 22 29 14  6 26 24 15 18 26  2 27 16 17  1 20  6  8  2 14 11  7 17  2 29 19  3 20 
+27  6 26  1 28 13 27 15 14 19 11  5 12  8 10  5 26 11 30  2 23 27  1  4 27  9 21 25  7  2 25 29 21 26 22  5 24  5 16  9 27 11  5 15 26 17 14  0  6 12  1 28 26  5 28 10 17 19 11 26 26  2  8 21 14 21 16 17  9 29 29  4  3 14 12 10  4  1 16 13  6  1  6 20  8  2 28 18 23 19 23  4  9 23 25 14  3  8 18 24 
+20 18  6  2 27  7 16  5 23  8 19 22 15 22 17 21  3  3 18  9  9  8 13 26 20  8  9 16  9  7 26 11 16 23 15 19  1 18 14  2  5 13 22 22  7 10 13  6  0  9 29 24 24 28  5 11  5  3  5 30  8 27  9  1 19  5  8 19 25 23 13 17 26 25  8 24  2 26 29 24  1  6 10 24  6 11 27 23  6 29  5 12 19  6  6 16  6  5 26 23 
+ 1 30  3 26 17 20 22 26  5 27 30 30 24  8  1 30 13 17 27  8  9 14 27  3 30 21 21  5  8  8 27 14 13 13  4  5 20 15  7 17 22 18 30 14  9  9  2 12  9  0  6 24  8 21 30  6 14 26 12 18 29  8 26 13 13 20 23 18  3 18 18 15 14 29 15  7 15  2  7 28 22 30  7 12 29 18  7 11  2  8  6  7 20 13 23 27 10 19  3  7 
+ 5 16 26 24 21 17  5 28 12 13  4 16 13 15 23  6 28 21 16 27  9 25 23 13 28 12 28  9 22  2 26 13 26 14  8  1 17  9 21 24 12 20 30 13 17 26 17  1 29  6  0  8 29 17 17 27 23 11  3 21 17 24 25  1 18 16 28 28  9 22  6 24  5 24 25 13  6  5 16 17 27  9  8 24 28 29  6 13 15 15 11  1  5 16 11  5 30 11 19 15 
+10  1 19 22  5  7 30 30 28 19 29 11 16  9 28  3 20  7 24 24 15 23 26  1 14 16  4 20 26 26 28 23 23 12  3  2  2 22 30  4  4 20 10 13 21 16 29 28 24 24  8  0  4 15 17 12 16 21 20 15  1 15  3  1 28  7 26 12 30 30 25 17 24  9 13 29  4  5  1  9  7 25 21 19 27 22 27  7 24  7 16 14  5  2  6 12 22 29 13 29 
+21 17 11 18 12 26  2  7  6 15 20  4 17 21 29 16 19 21 12 14 25 24 16 14  9  6  5 16  3  4 14 26  8 28  4 20 10 29 16  9  2 15 19 24 23 16 21 26 24  8 29  4  0 29 21  2 28 29  3  9  6 12 21  3  9  7 23  2 18 29  1 19 17 19 15 18  7 14 21  5 15  9 25  7 27  5 22  8 24  1 18 20  9 30  3  9  4 17  4 12 
+15  1 19 25 13 19 10 10 27  7 17 27 23 24 26 28  1  2  4 16 13  7  8 20 24  2 27 29 12 18 15  8 30  6 23  2 16 16 14 28 20  7  7 24 20 23 19  5 28 21 17 15 29  0 18 14  5 20 10 22 12 16  1  8 13  6 13 28 13 19 26 15 25  6 10 28 23  3 17 21 24 25 20  3 13  9 16 21 29 13 28 29 22 20 23 20 17 20  2 24 
+24 30 18 28 16  2 12  3 11 22 18 25  5 21 14 11 18 19 29 24 25 28  4 12 22 19  4  2  1 21 23  5 20  9 22 27  9 25  3 20 21 14 19  8 24 13 22 28  5 30 17 17 21 18  0  2 14 17  3 25 30  1 29 10 24  8 25 18 19 25 26  2 27  7 12 19 27 24 17 11 23 27 19 28  2 26  5 29 25 11  9 13  3 12 23 13 15 10 20 28 
+18 11  4 29 20 29 25  7 15  9 16 12 23  3 30 29  1 26 22 26 13  1 14 25  9  3 11 19 18 25 21 12 12 11 16  2  8  7 11  7 17  2 29  6 23 20 19 10 11  6 27 12  2 14  2  0 23 10 12  7  3  2 27  2 25  6  4 10 27 25 22  5 18 14  3 23 11  9 11  7  2 24  8 10 20  2 12  1 20 23 26  7  8 23  5 21 16 16 13  8 
+ 7  5 17 16 11  5 21 10 23  5 10 29 25 30 30 14 24  7 24 21 29 23  5  1 10 28  3 14  2 29  4  5  2 24 24  5 15 11 24 14 11 24 14 25 23  5 17 17  5 14 23 16 28  5 14 23  0 18 15 29  3 24  1 21 23 26 21  1  7 23  8  1  5 10 30 28  5 16  5 25 22 11 10  2 24  2  2 27 27 24 29 24 22 24 15 25 17  8 20  5 
+ 8 11 11  8  3  8 16  8  1  1 27 29  8 29 16 30 28  1  4 14 21  6 15 18 27 27 22  6 26 24 11 26 25 15 29  9 24 23 12 30 12  8  3 22 29 23  8 19  3 26 11 21 29 20 17 10 18  0  8 14 19 13  9  1  1 13  1 27 23  8  7  6 23  1 21 22 20  4  1 11 27 20 30 29  9 30 26 20 25  2  5 20 13 17  9  5 11  5 16  2 
+17  3  9  9  8 11 27  7  6  6 26 30  7 29 10 23 16  9  7 21 30 26 26 18  2 19  3  6 17  5 19 22 22 11 14 17 18 23 12  6 26 25 27 12 17  5 21 11  5 12  3 20  3 10  3 12 15  8  0 14 21  5 13 15  2 24  6 23 26 12 19 17 15 30 18 21 16  3  3 11  5 21 12  8 22 10 20  5 23 13 10  8 27 21 28  6 10  7 21 29 
+18 15 23 24  1 26 17  2 11 15  3 21 23 20 11  2 10 25 12  1 26 27  7 26 23 17 28 15  5 12 13  2 24  7 22 11  2  1 21 22 11  1 28 29 28  7 10 26 30 18 21 15  9 22 25  7 29 14 14  0  4  9 16 16 17 30  1 21 10 21 23 20 20 25 23 17 27 16 21 23  3 10 26 13 27 24  1 13 18  4 25 10 18 14 23 14 21  8  5 14 
+24 12  6 23  1 21 10 23  3 20  7 19  7 23 24 17  5 16 25 27 20  4  1 13 28 28  3 12 30  7  2  9 13 10  2 10 26 12 25 24 28  1 15 17 21  8 16 26  8 29 17  1  6 12 30  3  3 19 21  4  0 21 27  8 15 15 10 26 14  9  8 22 10  8 17 15  7  2 10 27  2 19 22 30  7  7 26 11 24  8  4  2  4  4 16  5 22 15 28 22 
+30 12  2 24 24 23 22  8 25  1  1 12 17 21 26  4  7 22  7 13 17  2 28  5  8 22 27 10 11 19  5 14  2 28 23 22 17 11 14  9 18  8  8 18 15 11 27  2 27  8 24 15 12 16  1  2 24 13  5  9 21  0 13 18 16  9 22 26 11  5  4 10  5 10 20  6 21  7 19 24 13 21 15 25  7  9 27  9 11  5 26  2 12 19 22  6  2 11 27 11 
+24 25 19  6 13  3 21 29 12 24  2 29 13 18 23  2 16 28  2 21  1 29 14 12 29 17 21 20 25 20  3 17  2  7 14  6 14  1 18  3  7 13 11  2 11  5  8  8  9 26 25  3 21  1 29 27  1  9 13 16 27 13  0  6 24 25 25 16 19  8 13  1  3  8 11 13 10  4 11  1 30 27 14 13 28  4 27 24 15 30 28 12 30 10  5  5 25 10 12  2 
+20 25  5 13 29  7  1 19 10 29 14 12 23 20 11 19 17  9  5 26  8 20 25 19 27 27  2 23 13 25 19 20 14 18 23  5  2  7 12 13 29  5 23 26 12 25 26 21  1 13  1  1  3  8 10  2 21  1 15 16  8 18  6  0 22 30 13  9  4 30 27 28 20 16 13 17 23 25 28 13 24 22 18  8  4  9 25 17 25 27  6 26  5 18  5 23 26 12 18 19 
+12 15 28 23 14 16  4  2  3 21  7 18 13 17 19 19  2 15  3 24 23 18 29 13  1  5 22  4 16 14  7 16  2 24 23 22 17 30 10  3 18 30 22 18  4 21 27 14 19 13 18 28  9 13 24 25 23  1  2 17 15 16 24 22  0  9  7 10 23 28  6 26 28 18  1 11 12 23  4 17  1 30 13  3 23 15 21  4  1 27  4 20 29  3 27 25  4  4 11 13 
+16 27 27 11 23  2  5 12 14 23 29 18 10 26  1  2 21 12  1 20 26 29 26  6 25 25  7 26 11 24 30  5 15  9  6  3 28  2  2 14 27 26 26 27 21 27 25 21  5 20 16  7  7  6  8  6 26 13 24 30 15  9 25 30  9  0  5 20  8 12 26 15 24 23 17 12 29 20 27  3 15 24 17  1 16 27  8  8 17 30  6  4 12  9 24 10 23  9  9 10 
+13  3 16 20  2 11  8 11  6  7 12 13 23  3  9 27 18  8  4  4  8 16 28 10 13 23 29 16 18 10 11  6  5 30  4 26 22 10 29  7 19 28  1 24 24 26  1 16  8 23 28 26 23 13 25  4 21  1  6  1 10 22 25 13  7  5  0 15 29 28  1 19 16 22 22  3 16  7 22 23  9 20  8 15 16 23 11  1 29 29 20 29 11  1  5  1 25  7 30 20 
+ 5  4 25 16 11  7 26 17 26 25  7 17 25 12 22  5 13 30 30 14 21 17  1  3  6 18 19  5 15 17 13 13  5 29 30 15  7 29 15  8  6 14 29 21 23 12 29 17 19 18 28 12  2 28 18 10  1 27 23 21 26 26 16  9 10 20 15  0 16 18 22 10 18 12 16  8  9  3 28  5  5  1 11 18  6  1  1  5  2  8 28 13 30 26 18 12  8  5  9 25 
+24 21  4  8 11 27 23 13 26  3  6  9  3 28 22 22 13 19 25 18 19 27  7 25 27 28 17 17  5  1  5  6 28 11  1  6 14  1  1 15  2 16 23 28 24 26 22  9 25  3  9 30 18 13 19 27  7 23 26 10 14 11 19  4 23  8 29 16  0 15 20 29 19 29 19  2  9  7  5  9 22  4 11 28 25 24 27 10 25 12 16 29  6  2 21 18 30  9  2 23 
+11 19 20 29 12 17  2 29 17  2 23 27  1 28 17 27 10 18  1 30 19 17 27 30 11 12  1 26 25 27 11 21 29 23  5 18 25  9 10  7 28  6 21 25 28 27 12 29 23 18 22 30 29 19 25 25 23  8 12 21  9  5  8 30 28 12 28 18 15  0 21 10 10 28 30 14 11  4 13 24 23 20 10 27  6 15  2 26  4 17 21 19  8 27  3 29 21 13  5 18 
+29  4  2 17 12 12 13  5 27 17  7 27  7 26 13 25 29 16 23 27 19  6  9 16  4 27  3 16 21  9 16 19 15 16 17 14 18 21 13  3  5 19  7  8  7 24 17 29 13 18  6 25  1 26 26 22  8  7 19 23  8  4 13 27  6 26  1 22 20 21  0  4 20 25 18  6  5 13 26 30 12 17 30 14  3 12 17 13 19  7  4 10 19 21 18 25 21 14 16 25 
+23  2  5  1  2 12  3  5 10  1  4  3 17 22  8 23 21 21 15 13 29  9 16 21 13 23 11 24  3 25  9 18 10 26 12 30 18  3 23  2 15  5 30 22 18 26 24  4 17 15 24 17 19 15  2  5  1  6 17 20 22 10  1 28 26 15 19 10 29 10  4  0 27  1  6  9 17 16 25 13 12 10 14  9 20 21 15 12 20  8  7 30 10 29  4 30  4  6 26 13 
+10 27 27  4 13  3 28 26  1 28  7 30  1 12  2 10  6 27  7 11 13 14 24 17  4 11 26  8 12  7  4 22  2 30  5  4 25  8 29  8 24 25 28 10  5 14  6  3 26 14  5 24 17 25 27 18  5 23 15 20 10  5  3 20 28 24 16 18 19 10 20 27  0 22  2 26 16  1 27 24  8 22 23  4 27  5  6  5 16 14  6 21  3 23 23 19 21  3  7  6 
+19  2 28 27 20 30 13  2 15 29 17 28 28 23  1 13 15  5 21 24 21  8 30 16 24 22  7  5  5 25 20 27 20 16 15  9 26 18 29 23  5 27 19 23  2 12 22 14 25 29 24  9 19  6  7 14 10  1 30 25  8 10  8 16 18 23 22 12 29 28 25  1 22  0 18  1 22 25 19 17 28 27  1 14 24  1  3 18 11 16  1  9 24  3 14  2 20 24 15 30 
+ 2  8 22 30 14 21  1 13 29 29  1 20 20 26  8 21 24 23 21 17 12  8 16 25 30  7 18 26 30 22 25 12 15 10 20 18 18 25 28  2 22 23 12  3 16 10 29 12  8 15 25 13 15 10 12  3 30 21 18 23 17 20 11 13  1 17 22 16 19 30 18  6  2 18  0 13 28 27 21 22  4 25 15  5 14  2 16 16  9 26 22  8 19 12 23 22 11  5  6  5 
+ 6 23 12 25  9  5 23 16  9  7 28 26  4 29 29 23 18  8 20 19 13 27 26 29  9 26  7 11 24  4  5 28 28 30 16  4 19 24 18 14 25 30 13  3 11  3 14 10 24  7 13 29 18 28 19 23 28 22 21 17 15  6 13 17 11 12  3  8  2 14  6  9 26  1 13  0 11 10 28 24  7 23  9  3 11 12 12  8 24 21 11 30 22 18 30 15  5  4 17 24 
+ 4  9 15 29 25  5  1 30 30 23 23 22 18 23 16 28 21 11 14  6 21  7 21  4  7 27 19 13  6 29 14 16 29 25 14  5 20 11 28 13 24 30 16  3 18  2  6  4  2 15  6  4  7 23 27 11  5 20 16 27  7 21 10 23 12 29 16  9  9 11  5 17 16 22 28 11  0 23  6 19 16 19 25 15  9 12  2 26  9  4 15 14  7 16 25 17 15  9  7 30 
+26 26 29 30 22 26 12 14 12 28 23 20 13 16 18  6 16 10  5  1  1 23  4  6 19 26 17  5 27 14  5 29 22  7 26  4  5 24  6  5  4 10 15 12 25 24 26  1 26  2  5  5 14  3 24  9 16  4  3 16  2  7  4 25 23 20  7  3  7  4 13 16  1 25 27 10 23  0 21  4 12 28 10  8 24 15  2 14  7  8 22 28  7  4 22 23 23 24 15 17 
+22 16  4 16  5 16 28 22 28 19 23 30 14  8  4 11  3 15 29 30 29  6  5 25 30 10  4 13  6 28 25  7 19  5 24 17 11 20 10 15 11  8 21 22  4  4 24 16 29  7 16  1 21 17 17 11  5  1  3 21 10 19 11 28  4 27 22 28  5 13 26 25 27 19 21 28  6 21  0 11 14 19 21 18 20 29  9  5 23 12 22 26 15  2 24  5 30  1 17 28 
+ 2 10 13 13 16 11 16 28 17  5  7 18 21 27  3  8 28  5  9  2  3  9 14 17 15 12 22 21 17 16 28 20 16 15 28 13 13 15 26 19 15  9  6 24  5 17 15 13 24 28 17  9  5 21 11  7 25 11 11 23 27 24  1 13 17  3 23  5  9 24 30 13 24 17 22 24 19  4 11  0  4  2 26  9 30 16  1 14 13 25 29  9 18 24  2  4  1 27 14 16 
+16 11  2  3 25 27 17 19 16 17 21  1 19 13 30  8 25 21  1 18 17 27  5 12 16 27  5  7 16 30 10 22 20 13  5 13 30 22 16 30 30  6 22 17 21 15 18  6  1 22 27  7 15 24 23  2 22 27  5  3  2 13 30 24  1 15  9  5 22 23 12 12  8 28  4  7 16 12 14  4  0 19  1  5 28 26 22  9 25 10  8 25  2 28 23 13  7 23 29 10 
+30 15 11 30 18  5 28 26 21 27  4 10  8 19  7 11 17 20 26 22 24 26 13  8 13 12  2 28  4 20 26 17 17 20  2 25 26 24 21  5 29 14 12  5 23 17 26  1  6 30  9 25  9 25 27 24 11 20 21 10 19 21 27 22 30 24 20  1  4 20 17 10 22 27 25 23 19 28 19  2 19  0 17  3 22 28 18  1 19 19 13  1 20 20 19 22  1 14 26  9 
+17 19  6  7  6 27 19 19 11 22  1 27 30 21 23  3  5  1  9 10  7 13 20 24 15 29  4 21  6  6 28  6 18 30  8 23 25 23 27  9 23 25 22  2 16 25  2  6 10  7  8 21 25 20 19  8 10 30 12 26 22 15 14 18 13 17  8 11 11 10 30 14 23  1 15  9 25 10 21 26  1 17  0 10  2 30 16  1 13 12 20 26 10  6 17  8 18  5 23 21 
+23  7 10 15  4 16 13 22 29 19 15 28 17  8 23 24  1 24  8  4 16 25 26 23 22 28  2  9  6  9  7 10  4  1 11  4 26 18 13 15 12 10  6 17  9 13 27 20 24 12 24 19  7  3 28 10  2 29  8 13 30 25 13  8  3  1 15 18 28 27 14  9  4 14  5  3 15  8 18  9  5  3 10  0 14  2 16  9 24  8 11 11 12 18 21 15 30  6  8  5 
+20  9 27 27 19 16  5 12  5 15 15 14  6 19 11 28  4  3 19  8 30 24 30 18 22 17 30 14 25 10  4 27 22 14  1 23  3  4  5 10 10 21 14 27  6 19 16  8  6 29 28 27 27 13  2 20 24  9 22 27  7  7 28  4 23 16 16  6 25  6  3 20 27 24 14 11  9 24 20 30 28 22  2 14  0 13 26  3  8 22  1 13  7 21  1 20 29  6 16 12 
+29 23 21 29  3  4 20 16 11 15 11 25 18 19  8  2  9  7 19  2 26 21  9 25 24 20 11 20  8  5 10 17 19 15 21 21 27  8 28 29 13 30 12 13  3 17 17  2 11 18 29 22  5  9 26  2  2 30 10 24  7  9  4  9 15 27 23  1 24 15 12 21  5  1  2 12 12 15 29 16 26 28 30  2 13  0 21 15 25  9 11 25 24 12 25 27  4 12  7 30 
+28 28  7 23 28 12 10 10  7 14  6 15  8 25 28  4 29  1  6  5 19  6 18 17  7 11  9 30 10 26  4 27  7  8  7  3 25 19 19 18 13 12 19 27  6 11  1 28 27  7  6 27 22 16  5 12  2 26 20  1 26 27 27 25 21  8 11  1 27  2 17 15  6  3 16 12  2  2  9  1 22 18 16 16 26 21  0 12  4 26 21 21  5 11 15 17  8 28 26  2 
+ 2 24 24 17 15 22 28 28 21 23 15  5 15 13  7 28  2 28  9 23 22 20 24 13 14 16  7  3 18 30 13 17 10  3 25 12 25  8 18 27 27  5 23  6 28  7 20 18 23 11 13  7  8 21 29  1 27 20  5 13 11  9 24 17  4  8  1  5 10 26 13 12  5 18 16  8 26 14  5 14  9  1  1  9  3 15 12  0  1  6  1 17  9 20  5 22 21 22 15  2 
+15 30 19  9  6 23 20 21 11 10  5 11 27 12 24 27 30 20 25 16  2 19 10 11 27 11 12 28 12 23 28 27 17 27 25  1 12  1  5 17  3 23 17  5 25  6  6 23  6  2 15 24 24 29 25 20 27 25 23 18 24 11 15 25  1 17 29  2 25  4 19 20 16 11  9 24  9  7 23 13 25 19 13 24  8 25  4  1  0 10 10  1  4 23 12 24 13 24 15 24 
+23 20 25  5 22 20 12  9 30 23 30  6 24 28 27 12 10 26 27 21 13 30  3 10 20 18  6 21  2 27 14 26 20 15  9  5 16 27 30 16 13  9  8  4 15 21  8 19 29  8 15  7  1 13 11 23 24  2 13  4  8  5 30 27 27 30 29  8 12 17  7  8 14 16 26 21  4  8 12 25 10 19 12  8 22  9 26  6 10  0  4 21 28 24  9 15 29 26 29 24 
+16 23 10 18 22  9 20  3 30 25 23 21  3 28 26 15 22 30  3 19 22 27 29  3 25 30  2 15 14  1 26 28  6  2 24 25  2 22 22 28 10 18 12  9 17 15  2 23  5  6 11 16 18 28  9 26 29  5 10 25  4 26 28  6  4  6 20 28 16 21  4  7  6  1 22 11 15 22 22 29  8 13 20 11  1 11 21  1 10  4  0 19  2 30 30 25  3  3 24  7 
+ 1 26 27 12 12 10  8  7  7 11 26 24 25 10 17 26 19 22 29 17 15 18 20 19 30 11 21 13 17  8 26 18 21 16  5 29 15  5  4 30 24 15 22 21 13  6 14  4 12  7  1 14 20 29 13  7 24 20  8 10  2  2 12 26 20  4 29 13 29 19 10 30 21  9  8 30 14 28 26  9 25  1 26 11 13 25 21 17  1 21 19  0  8 26 16 22  4 21 23 28 
+16  3 24  1 12 25 26 17  2  4 11  4 29 21 25  3  2  1 29 23 19  7 17 19  3 16  6 17 10 14  1  7 30 11  4  5 28  9 29 25 20  9  3 27 26 19 11  9 19 20  5  5  9 22  3  8 22 13 27 18  4 12 30  5 29 12 11 30  6  8 19 10  3 24 19 22  7  7 15 18  2 20 10 12  7 24  5  9  4 28  2  8  0 11  5  3  6  6  1 23 
+30  5 29 20 15 14 15 18 10 29  8 27 27 28 22 17 16 12 11 22 20 19  3  7  5 21 18 22 27  6 23  6 19 27 13 27 15 13 25 17 29 14 10  1 14 14  7 23  6 13 16  2 30 20 12 23 24 17 21 14  4 19 10 18  3  9  1 26  2 27 21 29 23  3 12 18 16  4  2 24 28 20  6 18 21 12 11 20 23 24 30 26 11  0 24 13 15  6 30 26 
+ 5 28 26 21 22  1 28  3 22  1 20 27  6 16  1 27  9 21 28  4 30  7  6  2 25 25 22 21 20  1 25 23 20  6  7  1 10 25 16 18  5  1 22 15  7 21 17 25  6 23 11  6  3 23 23  5 15  9 28 23 16 22  5  5 27 24  5 18 21  3 18  4 23 14 23 30 25 22 24  2 23 19 17 21  1 25 15  5 12  9 30 16  5 24  0 22 30  5  4  4 
+19 29 24  3 13  9 27  7 22 10 20 30 18 23  4 11 12  8  3 16  4 30 13 26  7  5  6 29 26  6 24 10  8 15 23 19  6  8 20  7 24 26 18 27 29 17  2 14 16 27  5 12  9 20 13 21 25  5  6 14  5  6  5 23 25 10  1 12 18 29 25 30 19  2 22 15 17 23  5  4 13 22  8 15 20 27 17 22 24 15 25 22  3 13 22  0  2 17 21 17 
+20  9 27 13 10 19 23 30 23 17 23 27  2  5 26  2 22 15 11  7 29  6 28  2 29  8  6 28 17 25  9 15 24 30 11 14 28 21 25 18 29 18 19 29 28 25 29  3  6 10 30 22  4 17 15 16 17 11 10 21 22  2 25 26  4 23 25  8 30 21 21  4 21 20 11  5 15 23 30  1  7  1 18 30 29  4  8 21 13 29  3  4  6 15 30  2  0 29 19 12 
+13 10 19 21 10  2 13 29 16 12 23 27 18  2 10  1 28 26 13 20 16 18 26 17  8 21 29 11  9 25  3 18  7  8 29  2  2 23  5 14 18 26 13 28 11  3 19  8  5 19 11 29 17 20 10 16  8  5  7  8 15 11 10 12  4  9  7  5  9 13 14  6  3 24  5  4  9 24  1 27 23 14  5  6  6 12 28 22 24 26  3 21  6  6  5 17 29  0 24 14 
+18 25 30 12 28 17 13 11 26 13 21  9  8 27 14  6 15 23 28 17 18  5 15 26 14 21  7  2 27 21 25 20 16 19 19  9 15 21  5 16  9  5 30 10 15 17  3 18 26  3 19 13  4  2 20 13 20 16 21  5 28 27 12 18 11  9 30  9  2  5 16 26  7 15  6 17  7 15 17 14 29 26 23  8 16  7 26 15 15 29 24 23  1 30  4 21 19 24  0 14 
+23 14  9 19 14  1  1 14  5  6  4  6  5  6 20 18  1 23  9 28 30 10  5 10 26 10  6  3 15 27 16 15  7 15  5 27 12  3  6 13  7 29 17  7 19 10 20 24 23  7 15 29 12 24 28  8  5  2 29 14 22 11  2 19 13 10 20 25 23 18 25 13  6 30  5 24 30 17 28 16 10  9 21  5 12 30  2  2 24 24  7 28 23 26  4 17 12 14 14  0 

ファイルの差分が大きいため隠しています
+ 10008 - 0
examples/instances/ubqp/ubqp_instance.txt


+ 1 - 1
examples/knapsackExample.py

@@ -23,7 +23,7 @@ if not os.path.exists('data'):
     os.makedirs('data')
 
 # logging configuration
-logging.basicConfig(format='%(asctime)s %(message)s', filename='data/example.log', level=logging.DEBUG)
+logging.basicConfig(format='%(asctime)s %(message)s', filename='data/example_knapsack.log', level=logging.DEBUG)
 
 random.seed(42)
 

+ 1 - 1
examples/knapsackMultiExample.py

@@ -24,7 +24,7 @@ if not os.path.exists('data'):
     os.makedirs('data')
 
 # logging configuration
-logging.basicConfig(format='%(asctime)s %(message)s', filename='data/exampleMOEAD.log', level=logging.DEBUG)
+logging.basicConfig(format='%(asctime)s %(message)s', filename='data/example_MOEAD_knapsack.log', level=logging.DEBUG)
 
 random.seed(42)
 

+ 80 - 0
examples/qapExample.py

@@ -0,0 +1,80 @@
+# main imports
+import logging
+import os
+import random
+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.policies.reinforcement import UCBPolicy
+
+from macop.algorithms.mono import IteratedLocalSearch as ILS
+from macop.algorithms.mono import HillClimberFirstImprovment
+from macop.callbacks.classicals import BasicCheckpoint
+
+if not os.path.exists('data'):
+    os.makedirs('data')
+
+# logging configuration
+logging.basicConfig(format='%(asctime)s %(message)s', filename='data/example_qap.log', level=logging.DEBUG)
+
+random.seed(42)
+
+# usefull instance data
+n = 100
+qap_instance_file = 'instances/qap/qap_instance.txt'
+filepath = "data/checkpoints_qap.csv"
+
+
+# default validator
+def validator(solution):
+    if len(list(solution._data)) > len(set(list(solution._data))):
+        print("not valid")
+        return False
+    return True
+
+# define init random solution
+def init():
+    return CombinatoryIntegerSolution.random(n, validator)
+
+
+def main():
+
+    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}')
+
+    operators = [SimpleMutation()]
+    policy = RandomPolicy(operators)
+    callback = BasicCheckpoint(every=5, filepath=filepath)
+    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)
+    
+    # add callback into callback list
+    algo.addCallback(callback)
+
+    bestSol = algo.run(10000, ls_evaluations=100)
+
+    print('Solution for QAP instance score is {}'.format(evaluator.compute(bestSol)))
+
+if __name__ == "__main__":
+    main()

+ 77 - 0
examples/ubqpExample.py

@@ -0,0 +1,77 @@
+# main imports
+import logging
+import os
+import random
+import numpy as np
+
+# module imports
+from macop.solutions.discrete import BinarySolution
+from macop.evaluators.discrete.mono import UBQPEvaluator
+
+from macop.operators.discrete.mutators import SimpleMutation
+from macop.operators.discrete.mutators import SimpleBinaryMutation
+
+from macop.policies.classicals import RandomPolicy
+
+from macop.algorithms.mono import IteratedLocalSearch as ILS
+from macop.algorithms.mono import HillClimberFirstImprovment
+from macop.callbacks.classicals import BasicCheckpoint
+
+if not os.path.exists('data'):
+    os.makedirs('data')
+
+# logging configuration
+logging.basicConfig(format='%(asctime)s %(message)s', filename='data/example.log', level=logging.DEBUG)
+
+random.seed(42)
+
+# usefull instance data
+n = 100
+ubqp_instance_file = 'instances/ubqp/ubqp_instance.txt'
+filepath = "data/checkpoints_ubqp.csv"
+
+
+# default validator
+def validator(solution):
+    return True
+
+# define init random solution
+def init():
+    return BinarySolution.random(n, validator)
+
+
+filepath = "data/checkpoints.csv"
+
+def main():
+
+    # load UBQP instance
+    with open(ubqp_instance_file, 'r') as f:
+
+        lines = f.readlines()
+
+        # get all string floating point values of matrix
+        Q_data = ''.join([ line.replace('\n', '') for line in lines[8:] ])
+
+        # load the concatenate obtained string
+        Q_matrix = np.fromstring(Q_data, dtype=float, sep=' ').reshape(n, n)
+
+    print(f'Q_matrix shape: {Q_matrix.shape}')
+
+    operators = [SimpleBinaryMutation(), SimpleMutation()]
+    policy = RandomPolicy(operators)
+    callback = BasicCheckpoint(every=5, filepath=filepath)
+    evaluator = UBQPEvaluator(data={'Q': Q_matrix})
+
+    # passing global evaluation param from ILS
+    hcfi = HillClimberFirstImprovment(init, evaluator, operators, policy, validator, maximise=True, verbose=True)
+    algo = ILS(init, evaluator, operators, policy, validator, localSearch=hcfi, maximise=True, verbose=True)
+    
+    # add callback into callback list
+    algo.addCallback(callback)
+
+    bestSol = algo.run(10000, ls_evaluations=100)
+
+    print('Solution for UBQP instance score is {}'.format(evaluator.compute(bestSol)))
+
+if __name__ == "__main__":
+    main()

+ 16 - 9
macop/algorithms/base.py

@@ -127,7 +127,6 @@ class Algorithm():
         """
         self._parent = parent
 
-
     def initRun(self):
         """
         Initialize the current solution and best solution using the `initialiser` function
@@ -154,7 +153,6 @@ class Algorithm():
 
             current_algorithm._numberOfEvaluations += 1
             current_algorithm = current_algorithm._parent
-            
 
     def getGlobalEvaluation(self):
         """Get the global number of evaluation (if inner algorithm)
@@ -281,8 +279,9 @@ class Algorithm():
         else:
             self._numberOfEvaluations = 0
 
-        logging.info("Run %s with %s evaluations" %
-                     (self.__str__(), evaluations))
+        logging.info(
+            f"Run {self.__str__()} with {(self.__str__(), evaluations)} evaluations"
+        )
 
     def progress(self):
         """
@@ -293,19 +292,27 @@ class Algorithm():
                 callback.run()
 
         if self._verbose:
-            macop_progress(self, self.getGlobalEvaluation(), self.getGlobalMaxEvaluation())
+            macop_progress(self, self.getGlobalEvaluation(),
+                           self.getGlobalMaxEvaluation())
 
-        logging.info(f"-- {type(self).__name__} evaluation {self._numberOfEvaluations} of {self._maxEvaluations} ({((self._numberOfEvaluations / self._maxEvaluations) * 100):.2f}%) - BEST SCORE {self._bestSolution.fitness()}")
+        logging.info(
+            f"-- {type(self).__name__} evaluation {self._numberOfEvaluations} of {self._maxEvaluations} - BEST SCORE {self._bestSolution._score}"
+        )
 
     def end(self):
         """Display end message into `run` method
         """
-    
-        macop_text(self, f'({type(self).__name__}) Found after {self._numberOfEvaluations} evaluations \n   - {self._bestSolution}')
+
+        macop_text(
+            self,
+            f'({type(self).__name__}) Found after {self._numberOfEvaluations} evaluations \n   - {self._bestSolution}'
+        )
         macop_line(self)
 
     def information(self):
-        logging.info(f"-- Best {self._bestSolution} - SCORE {self._bestSolution.fitness()}")
+        logging.info(
+            f"-- Best {self._bestSolution} - SCORE {self._bestSolution.fitness()}"
+        )
 
     def __str__(self):
         return f"{type(self).__name__} using {type(self._bestSolution).__name__}"

+ 21 - 11
macop/algorithms/mono.py

@@ -93,7 +93,9 @@ class HillClimberFirstImprovment(Algorithm):
                 self.increaseEvaluation()
 
                 self.progress()
-                logging.info(f"---- Current {newSolution} - SCORE {newSolution.fitness()}")
+                logging.info(
+                    f"---- Current {newSolution} - SCORE {newSolution.fitness()}"
+                )
 
                 # stop algorithm if necessary
                 if self.stop():
@@ -102,8 +104,10 @@ class HillClimberFirstImprovment(Algorithm):
             # set new current solution using best solution found in this neighbor search
             self._currentSolution = self._bestSolution
 
-        logging.info(f"End of {type(self).__name__}, best solution found {self._bestSolution}")
-        
+        logging.info(
+            f"End of {type(self).__name__}, best solution found {self._bestSolution}"
+        )
+
         return self._bestSolution
 
 
@@ -191,7 +195,9 @@ class HillClimberBestImprovment(Algorithm):
                 self.increaseEvaluation()
 
                 self.progress()
-                logging.info(f"---- Current {newSolution} - SCORE {newSolution.fitness()}")
+                logging.info(
+                    f"---- Current {newSolution} - SCORE {newSolution.fitness()}"
+                )
 
                 # stop algorithm if necessary
                 if self.stop():
@@ -200,8 +206,10 @@ class HillClimberBestImprovment(Algorithm):
             # set new current solution using best solution found in this neighbor search
             self._currentSolution = self._bestSolution
 
-        logging.info(f"End of {type(self).__name__}, best solution found {self._bestSolution}")
-        
+        logging.info(
+            f"End of {type(self).__name__}, best solution found {self._bestSolution}"
+        )
+
         return self._bestSolution
 
 
@@ -268,15 +276,15 @@ class IteratedLocalSearch(Algorithm):
                  maximise=True,
                  parent=None,
                  verbose=True):
-        
-        super().__init__(initializer, evaluator, operators, policy, validator, maximise, parent, verbose)
+
+        super().__init__(initializer, evaluator, operators, policy, validator,
+                         maximise, parent, verbose)
 
         # specific local search associated with current algorithm
         self._localSearch = localSearch
         # need to attach current algorithm as parent
         self._localSearch.setParent(self)
 
-
     def run(self, evaluations, ls_evaluations=100):
         """
         Run the iterated local search algorithm using local search (EvE compromise)
@@ -319,8 +327,10 @@ class IteratedLocalSearch(Algorithm):
 
             self.information()
 
-        logging.info(f"End of {type(self).__name__}, best solution found {self._bestSolution}")
-        
+        logging.info(
+            f"End of {type(self).__name__}, best solution found {self._bestSolution}"
+        )
+
         self.end()
 
         return self._bestSolution

+ 37 - 16
macop/algorithms/multi.py

@@ -107,7 +107,7 @@ class MOEAD(Algorithm):
 
         if mu < T:
             raise ValueError('`mu` cannot be less than `T`')
-            
+
         if mu < T:
             raise ValueError('`mu` cannot be less than `T`')
 
@@ -141,14 +141,23 @@ class MOEAD(Algorithm):
         for i in range(self._mu):
 
             # compute weight sum from solution
-            sub_evaluator = WeightedSum(data={'evaluators': evaluator, 'weights': weights[i]})
+            sub_evaluator = WeightedSum(data={
+                'evaluators': evaluator,
+                'weights': weights[i]
+            })
 
             # intialize each sub problem
             # use copy of list to keep track for each sub problem
-            subProblem = MOSubProblem(i, weights[i],
-                                      initializer, sub_evaluator,
-                                      operators.copy(), policy, validator,
-                                      maximise, self, verbose=self._verbose)
+            subProblem = MOSubProblem(i,
+                                      weights[i],
+                                      initializer,
+                                      sub_evaluator,
+                                      operators.copy(),
+                                      policy,
+                                      validator,
+                                      maximise,
+                                      self,
+                                      verbose=self._verbose)
 
             self._subProblems.append(subProblem)
 
@@ -213,7 +222,8 @@ class MOEAD(Algorithm):
                 # for each neighbor of current sub problem update solution if better
                 improvment = False
                 for j in self._neighbors[i]:
-                    if spBestSolution.fitness() > self._subProblems[j]._bestSolution.fitness():
+                    if spBestSolution.fitness(
+                    ) > self._subProblems[j]._bestSolution.fitness():
 
                         # create new solution based on current new if better, computes fitness associated to new solution for sub problem
                         newSolution = spBestSolution.clone()
@@ -241,7 +251,9 @@ class MOEAD(Algorithm):
                 if self.stop():
                     break
 
-        logging.info(f"End of {type(self).__name__}, best solution found {self._population}")
+        logging.info(
+            f"End of {type(self).__name__}, best solution found {self._population}"
+        )
 
         self.end()
 
@@ -255,9 +267,12 @@ class MOEAD(Algorithm):
             for callback in self._callbacks:
                 callback.run()
 
-        macop_progress(self, self.getGlobalEvaluation(), self.getGlobalMaxEvaluation())
+        macop_progress(self, self.getGlobalEvaluation(),
+                       self.getGlobalMaxEvaluation())
 
-        logging.info(f"-- {type(self).__name__} evaluation {self._numberOfEvaluations} of {self._maxEvaluations} ({((self._numberOfEvaluations) / self._maxEvaluations * 100.):.2f}%)")
+        logging.info(
+            f"-- {type(self).__name__} evaluation {self._numberOfEvaluations} of {self._maxEvaluations} ({((self._numberOfEvaluations) / self._maxEvaluations * 100.):.2f}%)"
+        )
 
     def setNeighbors(self):
 
@@ -339,9 +354,12 @@ class MOEAD(Algorithm):
 
     def end(self):
         """Display end message into `run` method
-        """ 
+        """
 
-        macop_text(self, f'({type(self).__name__}) Found after {self._numberOfEvaluations} evaluations')
+        macop_text(
+            self,
+            f'({type(self).__name__}) Found after {self._numberOfEvaluations} evaluations'
+        )
 
         for i, solution in enumerate(self._pfPop):
             macop_text(self, f'  - [{i}] {solution._scores} : {solution}')
@@ -426,8 +444,8 @@ class MOSubProblem(Algorithm):
                  parent=None,
                  verbose=True):
 
-        super().__init__(initalizer, evaluator, operators, policy,
-                         validator, maximise, parent)
+        super().__init__(initalizer, evaluator, operators, policy, validator,
+                         maximise, parent)
 
         self._index = index
         self._weights = weights
@@ -477,8 +495,11 @@ class MOSubProblem(Algorithm):
             if self.stop():
                 break
 
-            logging.info(f"---- Current {newSolution} - SCORE {newSolution.fitness()}")
+            logging.info(
+                f"---- Current {newSolution} - SCORE {newSolution.fitness()}")
 
-            logging.info(f"End of {type(self).__name__}, best solution found {self._bestSolution}")
+            logging.info(
+                f"End of {type(self).__name__}, best solution found {self._bestSolution}"
+            )
 
         return self._bestSolution

+ 15 - 6
macop/callbacks/classicals.py

@@ -71,7 +71,8 @@ class BasicCheckpoint(Callback):
                 globalEvaluation = int(data[0])
 
                 if self._algo.getParent() is not None:
-                    self._algo.getParent()._numberOfEvaluations = globalEvaluation
+                    self._algo.getParent(
+                    )._numberOfEvaluations = globalEvaluation
                 else:
                     self._algo._numberOfEvaluations = globalEvaluation
 
@@ -83,12 +84,20 @@ class BasicCheckpoint(Callback):
 
                 self._algo._bestSolution._data = np.array(solutionData)
                 self._algo._bestSolution._score = 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}.')
+            macop_text(self._algo,
+                       f'Checkpoint found from `{self._filepath}` file.')
+            macop_text(
+                self._algo,
+                f'Restart algorithm from evaluation {self._algo._numberOfEvaluations}.'
+            )
         else:
-            macop_text(self._algo, 'No backup found... Start running algorithm from evaluation 0.')
-            logging.info("Can't load backup... Backup filepath not valid in Checkpoint")
+            macop_text(
+                self._algo,
+                'No backup found... Start running algorithm from evaluation 0.'
+            )
+            logging.info(
+                "Can't load backup... Backup filepath not valid in Checkpoint")
 
         macop_line(self._algo)

+ 24 - 8
macop/callbacks/multi.py

@@ -75,7 +75,8 @@ class MultiCheckpoint(Callback):
                         globalEvaluation = int(data[0])
 
                         if self._algo.getParent() is not None:
-                            self._algo.getParen()._numberOfEvaluations = globalEvaluation
+                            self._algo.getParen(
+                            )._numberOfEvaluations = globalEvaluation
                         else:
                             self._algo._numberOfEvaluations = globalEvaluation
 
@@ -93,11 +94,20 @@ class MultiCheckpoint(Callback):
                     self._algo._pfPop.append(self._algo._population[i])
 
             macop_line(self._algo)
-            macop_text(self._algo, f'Load of available population from `{self._filepath}`')
-            macop_text(self._algo, f'Restart algorithm from evaluation {self._algo._numberOfEvaluations}.')
+            macop_text(
+                self._algo,
+                f'Load of available population from `{self._filepath}`')
+            macop_text(
+                self._algo,
+                f'Restart algorithm from evaluation {self._algo._numberOfEvaluations}.'
+            )
         else:
-            macop_text(self._algo, 'No backup found... Start running algorithm from evaluation 0.')
-            logging.info("Can't load backup... Backup filepath not valid in Checkpoint")
+            macop_text(
+                self._algo,
+                'No backup found... Start running algorithm from evaluation 0.'
+            )
+            logging.info(
+                "Can't load backup... Backup filepath not valid in Checkpoint")
 
         macop_line(self._algo)
 
@@ -169,9 +179,15 @@ class ParetoCheckpoint(Callback):
                     self._algo._pfPop[i]._data = solutionData
                     self._algo._pfPop[i]._scores = scores
 
-            macop_text(self._algo, f'Load of available pareto front backup from `{ self._filepath}`')
-        else:           
-            macop_text(self._algo, 'No pareto front found... Start running algorithm with new pareto front population.')
+            macop_text(
+                self._algo,
+                f'Load of available pareto front backup from `{ self._filepath}`'
+            )
+        else:
+            macop_text(
+                self._algo,
+                'No pareto front found... Start running algorithm with new pareto front population.'
+            )
             logging.info("No pareto front backup used...")
 
         macop_line(self._algo)

+ 4 - 2
macop/callbacks/policies.py

@@ -75,8 +75,10 @@ class UCBCheckpoint(Callback):
                 self._algo._policy._occurences = [
                     float(f) for f in occurrencesLine.split(';')
                 ]
-            
-            macop_text(self._algo, f'Load of available UCB policy data from `{self._filepath}`')
+
+            macop_text(
+                self._algo,
+                f'Load of available UCB policy data from `{self._filepath}`')
         else:
             macop_text(self._algo, 'No UCB data found, use default UCB policy')
             logging.info("No UCB data found...")

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

@@ -1,11 +1,11 @@
-"""Knapsack evaluators classes
+"""Mono-objective evaluators classes
 """
 # main imports
 from macop.evaluators.base import Evaluator
 
 
 class KnapsackEvaluator(Evaluator):
-    """Knapsack evaluator class which enables to compute solution using specific `_data`
+    """Knapsack evaluator class which enables to compute knapsack solution using specific `_data`
 
     - stores into its `_data` dictionary attritute required measures when computing a knapsack solution
     - `_data['worths']` stores knapsack objects worths information
@@ -28,7 +28,6 @@ class KnapsackEvaluator(Evaluator):
     >>> evaluator.compute(solution)
     40
     """
-
     def compute(self, solution):
         """Apply the computation of fitness from solution
 
@@ -42,4 +41,112 @@ class KnapsackEvaluator(Evaluator):
         for index, elem in enumerate(solution._data):
             fitness += self._data['worths'][index] * elem
 
-        return fitness
+        return fitness
+
+class QAPEvaluator(Evaluator):
+    """Quadratic Assignment Problem (QAP) evaluator class which enables to compute qap solution using specific `_data`
+
+    Solutions use for this evaluator are with type of `macop.solutions.discrete.CombinatoryIntegerSolution`
+
+    - 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
+
+    Example:
+
+    >>> 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()
+    >>> 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)
+    >>> 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
+    """
+    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._data):
+            for index_j, val_j in enumerate(solution._data):
+                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`
+
+    - stores into its `_data` dictionary attritute required measures when computing a UBQP solution
+    - `_data['Q']` matrix of size n x n with real values data (stored as numpy array)
+    - `compute` method enables to compute and associate a score to a given UBQP solution
+
+    Example:
+
+    >>> 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
+    """
+
+    def compute(self, solution):
+        """Apply the computation of fitness from solution
+
+        Args:
+            solution: {Solution} -- UBQP solution instance
+    
+        Returns:
+            {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):
+                fitness += self._data['Q'][index_i, index_j] * val_i * val_j
+
+        return fitness

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

@@ -35,7 +35,6 @@ class WeightedSum(Evaluator):
     >>> weighted_score
     50.8
     """
-
     def compute(self, solution):
         """Apply the computation of fitness from solution
 
@@ -48,9 +47,13 @@ class WeightedSum(Evaluator):
         Returns:
             {float} -- weighted-sum of the fitness scores
         """
-        scores = [evaluator.compute(solution) for evaluator in self._data['evaluators']]
+        scores = [
+            evaluator.compute(solution)
+            for evaluator in self._data['evaluators']
+        ]
 
         # associate objectives scores to solution
         solution._scores = scores
 
-        return sum([scores[i] * w for i, w in enumerate(self._data['weights'])])
+        return sum(
+            [scores[i] * w for i, w in enumerate(self._data['weights'])])

+ 1 - 2
macop/operators/base.py

@@ -51,7 +51,6 @@ class Mutation(Operator):
         raise NotImplementedError
 
 
-
 class Crossover(Operator):
     """Abstract crossover extend from Operator
 
@@ -62,4 +61,4 @@ class Crossover(Operator):
         self._kind = KindOperator.CROSSOVER
 
     def apply(self, solution1, solution2=None):
-        raise NotImplementedError
+        raise NotImplementedError

+ 0 - 1
macop/operators/continuous/crossovers.py

@@ -6,4 +6,3 @@ import sys
 
 # module imports
 from macop.operators.base import Crossover
-

+ 1 - 2
macop/policies/base.py

@@ -3,7 +3,6 @@
 import logging
 from abc import abstractmethod
 
-
 from macop.operators.base import KindOperator
 
 
@@ -51,7 +50,7 @@ class Policy():
         # avoid use of crossover if only one solution is passed
         if solution2 is None and operator._kind == KindOperator.CROSSOVER:
 
-            while operator._kind == KindOperator.CROSSOVER:            
+            while operator._kind == KindOperator.CROSSOVER:
                 operator = self.select()
 
         # apply operator on solution

+ 1 - 2
macop/policies/reinforcement.py

@@ -69,7 +69,6 @@ class UCBPolicy(Policy):
         self._C = C
         self._exp_rate = exp_rate
 
-
     def select(self):
         """Select using Upper Confidence Bound the next operator to use (using acquired rewards)
 
@@ -129,7 +128,7 @@ class UCBPolicy(Policy):
         # avoid use of crossover if only one solution is passed
         if solution2 is None and operator._kind == KindOperator.CROSSOVER:
 
-            while operator._kind == KindOperator.CROSSOVER:            
+            while operator._kind == KindOperator.CROSSOVER:
                 operator = self.select()
 
         # apply operator on solution

+ 1 - 1
macop/solutions/base.py

@@ -4,6 +4,7 @@
 from abc import abstractmethod
 from copy import deepcopy
 
+
 class Solution():
     """Base abstract solution class structure
     
@@ -11,7 +12,6 @@ class Solution():
     - get size (shape) of specific data representation
     - stores the score of the solution
     """
-
     def __init__(self, data, size):
         """
         Abstract solution class constructor

+ 2 - 3
macop/solutions/discrete.py

@@ -80,12 +80,11 @@ class BinarySolution(Solution):
         return f"Binary solution {self._data}"
 
 
-
 class CombinatoryIntegerSolution(Solution):
     """
     Combinatory integer solution class
 
-    - store solution as a binary array (example: [0, 1, 0, 1, 1])
+    - store solution as a combinatory array (example: [1, 3, 0, 2])
     - associated size is the size of the array
     - mainly use for selecting or not an element in a list of valuable objects
 
@@ -221,4 +220,4 @@ class IntegerSolution(Solution):
         return solution
 
     def __str__(self):
-        return f"Integer solution {self._data}"
+        return f"Integer solution {self._data}"

+ 1 - 1
macop/utils/progress.py

@@ -79,4 +79,4 @@ def macop_progress(algo, evaluations, max):
     # go to line
     if progress >= 1.:
         print()
-        print(macop_line(algo))
+        macop_line(algo)

+ 2 - 0
paper.bib

@@ -136,6 +136,8 @@
 @misc{ceres-solver,
   author = "Sameer Agarwal and Keir Mierle and Others",
   title = "Ceres Solver",
+  version = "2.0.0",
+  year = "2020",
   howpublished = "\url{http://ceres-solver.org}",
 }
 

ファイルの差分が大きいため隠しています
+ 8 - 2
paper.md


+ 1 - 1
setup.py

@@ -73,7 +73,7 @@ class TestCommand(distutils.command.check.check):
 
 setup(
     name='macop',
-    version='1.0.8',
+    version='1.0.9',
     description='Minimalist And Customisable Optimisation Package',
     long_description=open('README.md').read(),
     long_description_content_type='text/markdown',