Parcourir la source

new package name : macop; use of new policy for operators

Jérôme BUISINE il y a 3 ans
Parent
commit
df8251b5ac
65 fichiers modifiés avec 1236 ajouts et 148 suppressions
  1. 34 0
      .github/workflows/python-app.yml
  2. 31 0
      .github/workflows/python-publish.yml
  3. 2 0
      .gitignore
  4. 168 0
      CONTRIBUTING.md
  5. 10 3
      README.md
  6. 13 0
      build.sh
  7. 85 0
      checkpoints.csv
  8. 0 0
      docs/.nojekyll
  9. 22 0
      docs/Makefile
  10. 13 0
      docs/_autoapi_templates/index.rst
  11. 1 0
      docs/_autoapi_templates/python/attribute.rst
  12. 45 0
      docs/_autoapi_templates/python/class.rst
  13. 7 0
      docs/_autoapi_templates/python/data.rst
  14. 1 0
      docs/_autoapi_templates/python/exception.rst
  15. 14 0
      docs/_autoapi_templates/python/function.rst
  16. 15 0
      docs/_autoapi_templates/python/method.rst
  17. 96 0
      docs/_autoapi_templates/python/module.rst
  18. 1 0
      docs/_autoapi_templates/python/package.rst
  19. 13 0
      docs/index.html
  20. BIN
      docs/source/_static/logo_macop.png
  21. 213 0
      docs/source/conf.py
  22. 17 0
      docs/source/contributing.rst
  23. 72 0
      docs/source/description.rst
  24. 9 0
      docs/source/examples.rst
  25. 27 0
      docs/source/index.rst
  26. 14 0
      docs/source/macop.rst
  27. 16 0
      docs/source/macop/macop.algorithms.rst
  28. 16 0
      docs/source/macop/macop.checkpoints.rst
  29. 16 0
      docs/source/macop/macop.evaluators.rst
  30. 16 0
      docs/source/macop/macop.operators.rst
  31. 16 0
      docs/source/macop/macop.solutions.rst
  32. BIN
      logo_macop.png
  33. 0 0
      macop/__init__.py
  34. 35 30
      algorithms/Algorithm.py
  35. 14 7
      algorithms/IteratedLocalSearch.py
  36. 9 8
      algorithms/LocalSearch.py
  37. 0 0
      macop/algorithms/__init__.py
  38. 8 8
      checkpoints/BasicCheckpoint.py
  39. 2 2
      checkpoints/Checkpoint.py
  40. 0 0
      macop/checkpoints/__init__.py
  41. 2 2
      evaluators/EvaluatorExample.py
  42. 0 0
      macop/evaluators/__init__.py
  43. 33 0
      macop/operators/Operator.py
  44. 0 0
      macop/operators/__init__.py
  45. 8 0
      macop/operators/crossovers/Crossover.py
  46. 32 0
      macop/operators/crossovers/RandomSplitCrossover.py
  47. 7 6
      operators/crossovers/SimpleCrossover.py
  48. 0 0
      macop/operators/crossovers/__init__.py
  49. 11 0
      macop/operators/mutators/Mutation.py
  50. 2 2
      operators/mutators/SimpleBinaryMutation.py
  51. 2 4
      operators/mutators/SimpleMutation.py
  52. 0 0
      macop/operators/mutators/__init__.py
  53. 7 14
      operators/policies/Policy.py
  54. 2 5
      operators/policies/RandomPolicy.py
  55. 0 0
      macop/operators/policies/__init__.py
  56. 0 4
      solutions/BinarySolution.py
  57. 0 4
      solutions/CombinatoryIntegerSolution.py
  58. 0 4
      solutions/IntegerSolution.py
  59. 1 7
      solutions/Solution.py
  60. 0 0
      macop/solutions/__init__.py
  61. 10 9
      mainExample.py
  62. 0 7
      operators/Operator.py
  63. 0 11
      operators/crossovers/Crossover.py
  64. 0 11
      operators/mutators/Mutation.py
  65. 48 0
      setup.py

+ 34 - 0
.github/workflows/python-app.yml

@@ -0,0 +1,34 @@
+# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: build
+
+on:
+  push:
+    branches: [ master, develop ]
+  pull_request:
+    branches: [ master, develop ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.5, 3.6, 3.7, 3.8]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+    - name: Test with autodoc and build package
+      run: |
+        python setup.py build
+        python setup.py install
+        python setup.py test

+ 31 - 0
.github/workflows/python-publish.yml

@@ -0,0 +1,31 @@
+# This workflows will upload a Python Package using Twine when a release is created
+# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
+
+name: pypi
+
+on:
+  push:
+    branches: [ master ]
+
+jobs:
+  deploy:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python
+      uses: actions/setup-python@v2
+      with:
+        python-version: '3.x'
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install setuptools wheel twine
+    - name: Build and publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        python setup.py sdist bdist_wheel
+        twine upload dist/*

+ 2 - 0
.gitignore

@@ -58,3 +58,5 @@ docs/_build/
 # PyBuilder
 target/
 
+example.log
+checkpoint.csv

+ 168 - 0
CONTRIBUTING.md

@@ -0,0 +1,168 @@
+Contribution guidelines
+=====================================
+
+<p align="center">
+    Minimalist And Customizable Optimization 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 IPFML 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
+'''Divide image into equal size blocks
+
+  Args:
+      image: PIL Image or Numpy array
+      block: tuple (width, height) representing the size of each dimension of the block
+      pil: block type returned (default True)
+
+  Returns:
+      list containing all 2D Numpy blocks (in RGB or not)
+
+  Raises:
+      ValueError: If `image_width` or `image_heigt` are not compatible to produce correct block sizes
+'''
+```
+
+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
+"""Cauchy noise filter to apply on image
+
+  Args:
+      image: image used as input (2D or 3D image representation)
+      n: used to set importance of noise [1, 999]
+      identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+      distribution_interval: set the distribution interval of normal law distribution (default (0, 1))
+      k: variable that specifies the amount of noise to be taken into account in the output image (default 0.0002)
+
+  Returns:
+      2D Numpy array with Cauchy noise applied
+
+  Example:
+
+  >>> from ipfml.filters.noise import cauchy_noise
+  >>> import numpy as np
+  >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+  >>> noisy_image = cauchy_noise(image, 10)
+  >>> noisy_image.shape
+  (100, 100)
+"""
+```
+
+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**
+
+Whatever the problem reported, I will thank you for your contribution to this project. So do not hesitate.

+ 10 - 3
README.md

@@ -1,4 +1,11 @@
-# Thesis-OptimizationModules
+# Minimalist And Customizable Optimizations Package
+
+![](https://github.com/prise-3d/macop/workflows/build/badge.svg) ![](https://github.com/prise-3d/macop/workflows/pypi/badge.svg)
+
+<p align="center">
+    <img src="https://github.com/prise-3d/rawls/blob/master/logo_macop.png" alt="" width="50%">
+</p>
+
 
 ## Description
 
@@ -16,12 +23,12 @@ Optimisation generic framework built for optimization problem during thesis
 
 ## How to use ?
 
-You can see an example of use in the `mainExample.py` python file. You need to clone this repository with `optimization` folder name to get it works.
+You can see an example of use in the `mainExample.py` python file.
 
 ## Add as dependency
 
 ```bash
-git submodule add https://github.com/prise-3d/Thesis-OptimizationModules.git optimization
+git submodule add https://github.com/prise-3d/macop.git
 ```
 
 ## License

+ 13 - 0
build.sh

@@ -0,0 +1,13 @@
+#! 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
+
+echo "Build documentation..."
+rm -r docs/source/macop
+cd docs && make clean && make html

+ 85 - 0
checkpoints.csv

@@ -0,0 +1,85 @@
+5;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+10;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+15;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+20;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+25;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+30;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+35;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+40;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+45;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+50;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+55;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+60;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+65;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+70;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+75;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+80;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+85;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+90;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+95;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+100;0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1;234;
+105;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+110;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+115;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+120;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+125;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+130;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+135;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+140;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+145;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+150;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+155;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+160;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+165;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+170;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+175;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+180;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+185;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+190;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+195;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+200;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+205;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+210;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+215;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+220;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+225;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+230;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+235;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+240;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+245;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+250;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+255;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+260;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+265;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+270;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+275;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+280;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+285;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+290;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+295;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+300;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+305;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+310;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+315;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+320;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+325;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+330;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+335;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+340;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+345;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+350;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+355;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+360;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+365;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+370;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+375;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+380;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+385;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+390;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+395;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+400;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+405;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+410;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+415;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+420;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;
+425;0 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1;386;

algorithms/__init__.py → docs/.nojekyll


+ 22 - 0
docs/Makefile

@@ -0,0 +1,22 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+SOURCEDIR     = source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+rst:
+	sphinx-apidoc -o ./source ../rawls/
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

+ 13 - 0
docs/_autoapi_templates/index.rst

@@ -0,0 +1,13 @@
+Documentation
+=============
+
+
+.. toctree::
+   :titlesonly:
+
+   {% for page in pages %}
+   {% if page.top_level_object and page.display %}
+   {{ page.include_path }}
+   {% endif %}
+   {% endfor %}
+

+ 1 - 0
docs/_autoapi_templates/python/attribute.rst

@@ -0,0 +1 @@
+{% extends "python/data.rst" %}

+ 45 - 0
docs/_autoapi_templates/python/class.rst

@@ -0,0 +1,45 @@
+{% if obj.display %}
+.. py:{{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %}
+
+
+   {% if obj.bases %}
+   {% if "show-inheritance" in autoapi_options %}
+   Bases: {% for base in obj.bases %}:class:`{{ base }}`{% if not loop.last %}, {% endif %}{% endfor %}
+   {% endif %}
+
+
+   {% if "show-inheritance-diagram" in autoapi_options and obj.bases != ["object"] %}
+   .. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }}
+      :parts: 1
+      {% if "private-members" in autoapi_options %}:private-bases:{% endif %}
+
+   {% endif %}
+   {% endif %}
+   {% if obj.docstring %}
+   {{ obj.docstring|prepare_docstring|indent(3) }}
+   {% endif %}
+   {% if "inherited-members" in autoapi_options %}
+   {% set visible_classes = obj.classes|selectattr("display")|list %}
+   {% else %}
+   {% set visible_classes = obj.classes|rejectattr("inherited")|selectattr("display")|list %}
+   {% endif %}
+   {% for klass in visible_classes %}
+   {{ klass.rendered|indent(3) }}
+   {% endfor %}
+   {% if "inherited-members" in autoapi_options %}
+   {% set visible_attributes = obj.attributes|selectattr("display")|list %}
+   {% else %}
+   {% set visible_attributes = obj.attributes|rejectattr("inherited")|selectattr("display")|list %}
+   {% endif %}
+   {% for attribute in visible_attributes %}
+   {{ attribute.rendered|indent(3) }}
+   {% endfor %}
+   {% if "inherited-members" in autoapi_options %}
+   {% set visible_methods = obj.methods|selectattr("display")|list %}
+   {% else %}
+   {% set visible_methods = obj.methods|rejectattr("inherited")|selectattr("display")|list %}
+   {% endif %}
+   {% for method in visible_methods %}
+   {{ method.rendered|indent(3) }}
+   {% endfor %}
+{% endif %}

+ 7 - 0
docs/_autoapi_templates/python/data.rst

@@ -0,0 +1,7 @@
+{% if obj.display %}
+.. {{ obj.type }}:: {{ obj.name }}
+   {%+ if obj.value is not none or obj.annotation is not none %}:annotation:{% if obj.annotation %} :{{ obj.annotation }}{% endif %}{% if obj.value is not none %} = {{ obj.value }}{% endif %}{% endif %}
+
+
+   {{ obj.docstring|prepare_docstring|indent(3) }}
+{% endif %}

+ 1 - 0
docs/_autoapi_templates/python/exception.rst

@@ -0,0 +1 @@
+{% extends "python/class.rst" %}

+ 14 - 0
docs/_autoapi_templates/python/function.rst

@@ -0,0 +1,14 @@
+{% if obj.display %}
+.. function:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
+
+   {% if sphinx_version >= (2, 1) %}
+   {% for property in obj.properties %}
+   :{{ property }}:
+   {% endfor %}
+   {% endif %}
+
+   {% if obj.docstring %}
+   {{ obj.docstring|prepare_docstring|indent(3) }}
+   {% else %}
+   {% endif %}
+{% endif %}

+ 15 - 0
docs/_autoapi_templates/python/method.rst

@@ -0,0 +1,15 @@
+{%- if obj.display %}
+{% if sphinx_version >= (2, 1) %}
+.. method:: {{ obj.short_name }}({{ obj.args }})
+   {% for property in obj.properties %}
+   :{{ property }}:
+   {% endfor %}
+
+{% else %}
+.. {{ obj.method_type }}:: {{ obj.short_name }}({{ obj.args }})
+{% endif %}
+
+   {% if obj.docstring %}
+   {{ obj.docstring|prepare_docstring|indent(3) }}
+   {% endif %}
+{% endif %}

+ 96 - 0
docs/_autoapi_templates/python/module.rst

@@ -0,0 +1,96 @@
+{% if not obj.display %}
+:orphan:
+
+{% endif %}
+:mod:`{{ obj.name }}`
+======={{ "=" * obj.name|length }}
+
+.. py:module:: {{ obj.name }}
+
+{% if obj.docstring %}
+.. autoapi-nested-parse::
+
+   {{ obj.docstring|prepare_docstring|indent(3) }}
+
+{% endif %}
+
+{% block subpackages %}
+{% set visible_subpackages = obj.subpackages|selectattr("display")|list %}
+{% if visible_subpackages %}
+
+.. toctree::
+   :titlesonly:
+   :maxdepth: 3
+
+{% for subpackage in visible_subpackages %}
+   {{ subpackage.short_name }}/index.rst
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+{% block submodules %}
+{% set visible_submodules = obj.submodules|selectattr("display")|list %}
+{% if visible_submodules %}
+
+.. toctree::
+   :titlesonly:
+   :maxdepth: 1
+
+{% for submodule in visible_submodules %}
+   {{ submodule.short_name }}/index.rst
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+{% block content %}
+{% if obj.all is not none %}
+{% set visible_children = obj.children|selectattr("short_name", "in", obj.all)|list %}
+{% elif obj.type is equalto("package") %}
+{% set visible_children = obj.children|selectattr("display")|list %}
+{% else %}
+{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %}
+{% endif %}
+{% if visible_children %}
+
+{{ "-" * obj.type|length }}---------
+
+{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %}
+{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %}
+{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %}
+{% block classes %}
+{% if visible_classes %}
+Classes
+~~~~~~~
+
+.. autoapisummary::
+
+{% for klass in visible_classes %}
+   {{ klass.id }}
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+
+{% block functions %}
+{% if visible_functions %}
+Functions
+~~~~~~~~~
+
+.. autoapisummary::
+
+{% for function in visible_functions %}
+   {{ function.id }}
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+{% endif %}
+{% for obj_item in visible_children %}
+{{ obj_item.rendered|indent(0) }}
+{% endfor %}
+{% endif %}
+{% endblock %}

+ 1 - 0
docs/_autoapi_templates/python/package.rst

@@ -0,0 +1 @@
+{% extends "python/module.rst" %}

+ 13 - 0
docs/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>rawls documentation</title>
+    <meta http-equiv="refresh" content="0; url=./build/html/index.html" />
+</head>
+<body>
+
+</body>
+</html> 
+
+
+

BIN
docs/source/_static/logo_macop.png


+ 213 - 0
docs/source/conf.py

@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/master/config
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+sys.path.insert(0, os.path.abspath('../../../macop'))
+
+# -- Project information -----------------------------------------------------
+
+project = 'macop'
+copyright = '2020, Jérôme BUISINE'
+author = 'Jérôme BUISINE'
+
+# The short X.Y version
+version = '0.1.2'
+# The full version, including alpha/beta/rc tags
+release = 'v0.1.2'
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.doctest',
+    'sphinx.ext.coverage',
+    'sphinx.ext.napoleon',
+    'sphinx.ext.autosummary',
+    'sphinx.ext.viewcode',
+    'sphinx.ext.coverage',
+    #'autoapi.extension' 
+]
+
+# autoapi_add_toctree_entry = True
+# autoapi_template_dir = '_autoapi_templates'
+# autoapi_dirs = ['../../rawls']
+
+autosummary_generate = True
+autodoc_default_flags = ['members']
+autodoc_member_order = 'groupwise'
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = None
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+html_theme_options = {
+    'canonical_url': '',
+    #'analytics_id': 'UA-XXXXXXX-1',
+    'logo_only': False,
+    'display_version': True,
+    'prev_next_buttons_location': 'bottom',
+    'style_external_links': False,
+    #'vcs_pageview_mode': '',
+    # Toc options
+    'collapse_navigation': True,
+    'sticky_navigation': True,
+    'navigation_depth': 4,
+    'includehidden': True,
+    'titles_only': False
+}
+
+html_context = {
+  'display_github': True,
+  'github_user': 'prise-3d',
+  'github_repo': 'macop',
+  'github_version': 'master/docs/source/'
+}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself.  Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'macopdoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'macop.tex', 'macop Documentation',
+     'Jérôme BUISINE', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'macop', 'macop Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'macop', 'macop Documentation',
+     author, 'macop', 'Minimalist And Customizable Optimization Package',
+     'Miscellaneous'),
+]
+
+
+# -- Options for Epub output -------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+
+# -- Extension configuration -------------------------------------------------

+ 17 - 0
docs/source/contributing.rst

@@ -0,0 +1,17 @@
+Contributing
+=====================================
+
+.. image:: _static/logo_macop.png
+   :width: 350 px
+   :align: center
+
+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 

+ 72 - 0
docs/source/description.rst

@@ -0,0 +1,72 @@
+Description
+=====================================
+
+.. image:: _static/logo_macop.png
+   :width: 350 px
+   :align: center
+
+
+Context
+------------
+
+Minimalist And Customizable Optimization Package
+
+
+Installation
+------------
+
+Just install package using `pip` Python package manager: 
+
+.. code:: bash
+   
+   pip install macop
+
+
+How to use ?
+------------
+
+Load all `macop` implemented features:
+
+.. code:: python
+    
+   from macop.algorithms.IteratedLocalSearch import IteratedLocalSearch as ILS
+   from macop.solutions.BinarySolution import BinarySolution
+   from macop.evaluators.EvaluatorExample import evaluatorExample
+
+   from macop.operators.mutators.SimpleMutation import SimpleMutation
+   from macop.operators.mutators.SimpleBinaryMutation import SimpleBinaryMutation
+   from macop.operators.crossovers.SimpleCrossover import SimpleCrossover
+   from macop.operators.crossovers.RandomSplitCrossover import RandomSplitCrossover
+
+   from macop.operators.policies.RandomPolicy import RandomPolicy
+
+   from macop.checkpoints.BasicCheckpoint import BasicCheckpoint
+
+   # logging configuration
+   logging.basicConfig(format='%(asctime)s %(message)s', filename='example.log', level=logging.DEBUG)
+
+   # default validator
+   def validator(solution):
+      return True
+
+   # define init random solution
+   def init():
+      return BinarySolution([], 30).random(validator)
+
+   filepath = "checkpoints.csv"
+
+   def main():
+
+      operators = [SimpleBinaryMutation(), SimpleMutation(), SimpleCrossover(), RandomSplitCrossover()]
+      policy = RandomPolicy(operators)
+
+      algo = ILS(init, evaluatorExample, operators, policy, validator, True)
+      algo.addCheckpoint(_class=BasicCheckpoint, _every=5, _filepath=filepath)
+
+      bestSol = algo.run(425)
+
+      print("Found ", bestSol)
+
+
+   if __name__ == "__main__":
+      main()

+ 9 - 0
docs/source/examples.rst

@@ -0,0 +1,9 @@
+Examples
+=====================================
+
+Some examples are already available into documentation. You can find here some others and results of use of `macop` package.
+
+Processing example
+--------------------
+
+Available soon...

+ 27 - 0
docs/source/index.rst

@@ -0,0 +1,27 @@
+Minimalist And Customizable Optimization Package
+================================================
+
+.. image:: _static/logo_macop.png
+   :width: 450 px
+   :align: center
+
+What's `macop` ?
+=================
+
+`macop` is a Python package developed during a thesis project.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Contents:
+
+   description
+   macop
+   examples
+   contributing
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`

+ 14 - 0
docs/source/macop.rst

@@ -0,0 +1,14 @@
+Documentation
+=============
+
+macop
+-------------------
+
+.. autosummary::
+   :toctree: macop
+
+   macop.algorithms
+   macop.checkpoints
+   macop.evaluators
+   macop.operators
+   macop.solutions

+ 16 - 0
docs/source/macop/macop.algorithms.rst

@@ -0,0 +1,16 @@
+macop.algorithms
+================
+
+.. automodule:: macop.algorithms
+
+   
+   
+   
+
+   
+   
+   
+
+   
+   
+   

+ 16 - 0
docs/source/macop/macop.checkpoints.rst

@@ -0,0 +1,16 @@
+macop.checkpoints
+=================
+
+.. automodule:: macop.checkpoints
+
+   
+   
+   
+
+   
+   
+   
+
+   
+   
+   

+ 16 - 0
docs/source/macop/macop.evaluators.rst

@@ -0,0 +1,16 @@
+macop.evaluators
+================
+
+.. automodule:: macop.evaluators
+
+   
+   
+   
+
+   
+   
+   
+
+   
+   
+   

+ 16 - 0
docs/source/macop/macop.operators.rst

@@ -0,0 +1,16 @@
+macop.operators
+===============
+
+.. automodule:: macop.operators
+
+   
+   
+   
+
+   
+   
+   
+
+   
+   
+   

+ 16 - 0
docs/source/macop/macop.solutions.rst

@@ -0,0 +1,16 @@
+macop.solutions
+===============
+
+.. automodule:: macop.solutions
+
+   
+   
+   
+
+   
+   
+   
+
+   
+   
+   

BIN
logo_macop.png


checkpoints/__init__.py → macop/__init__.py


+ 35 - 30
algorithms/Algorithm.py

@@ -1,10 +1,17 @@
 # main imports
 import logging
 
+
 # Generic algorithm class
 class Algorithm():
-
-    def __init__(self, _initalizer, _evaluator, _operators, _policy, _validator, _maximise=True, _parent=None):
+    def __init__(self,
+                 _initalizer,
+                 _evaluator,
+                 _operators,
+                 _policy,
+                 _validator,
+                 _maximise=True,
+                 _parent=None):
         """
         Initialize all usefull parameters for problem to solve
         """
@@ -15,44 +22,46 @@ class Algorithm():
         self.validator = _validator
         self.policy = _policy
         self.checkpoint = None
+        self.bestSolution = None
 
         # other parameters
-        self.parent = _parent # parent algorithm if it's sub algorithm
+        self.parent = _parent  # parent algorithm if it's sub algorithm
         #self.maxEvaluations = 0 # by default
         self.maximise = _maximise
 
         self.initRun()
 
-
     def addCheckpoint(self, _class, _every, _filepath):
         self.checkpoint = _class(self, _every, _filepath)
 
-    
     def setCheckpoint(self, _checkpoint):
         self.checkpoint = _checkpoint
 
-
     def resume(self):
         if self.checkpoint is None:
-            raise ValueError("Need to `addCheckpoint` or `setCheckpoint` is you want to use this process")
+            raise ValueError(
+                "Need to `addCheckpoint` or `setCheckpoint` is you want to use this process"
+            )
         else:
             print('Checkpoint loading is called')
             self.checkpoint.load()
 
-
     def initRun(self):
         """
         Reinit the whole variables
         """
 
+        # add track reference of algo into operator (keep an eye into best solution)
+        for operator in self.operators:
+            operator.setAlgo(self)
+
         self.currentSolution = self.initializer()
-        
+
         # evaluate current solution
         self.currentSolution.evaluate(self.evaluator)
 
         # keep in memory best known solution (current solution)
         self.bestSolution = self.currentSolution
-        
 
     def increaseEvaluation(self):
         self.numberOfEvaluations += 1
@@ -60,7 +69,6 @@ class Algorithm():
         if self.parent is not None:
             self.parent.numberOfEvaluations += 1
 
-    
     def getGlobalEvaluation(self):
 
         if self.parent is not None:
@@ -68,16 +76,14 @@ class Algorithm():
 
         return self.numberOfEvaluations
 
-
     def stop(self):
         """
         Global stopping criteria (check for inner algorithm too)
         """
         if self.parent is not None:
             return self.parent.numberOfEvaluations >= self.parent.maxEvaluations or self.numberOfEvaluations >= self.maxEvaluations
-            
-        return self.numberOfEvaluations >= self.maxEvaluations
 
+        return self.numberOfEvaluations >= self.maxEvaluations
 
     def evaluate(self, solution):
         """
@@ -89,8 +95,7 @@ class Algorithm():
         """
         return solution.evaluate(self.evaluator)
 
-
-    def update(self, solution, secondSolution=None):
+    def update(self, solution):
         """
         Apply update function to solution using specific `policy`
 
@@ -101,15 +106,14 @@ class Algorithm():
         """
 
         # two parameters are sent if specific crossover solution are wished
-        sol = self.policy.apply(solution, secondSolution)
+        sol = self.policy.apply(solution)
 
-        if(sol.isValid(self.validator)):
+        if (sol.isValid(self.validator)):
             return sol
         else:
             logging.info("-- New solution is not valid %s" % sol)
             return solution
 
-
     def isBetter(self, solution):
         """
         Check if solution is better than best found
@@ -128,7 +132,6 @@ class Algorithm():
         # by default
         return False
 
-
     def run(self, _evaluations):
         """
         Run the specific algorithm following number of evaluations to find optima
@@ -140,7 +143,7 @@ class Algorithm():
 
         # check if global evaluation is used or not
         if self.parent is not None and self.getGlobalEvaluation() != 0:
-            
+
             # init number evaluations of inner algorithm depending of globalEvaluation
             # allows to restart from `checkpoint` last evaluation into inner algorithm
             rest = self.getGlobalEvaluation() % self.maxEvaluations
@@ -149,22 +152,24 @@ class Algorithm():
         else:
             self.numberOfEvaluations = 0
 
-        logging.info("Run %s with %s evaluations" % (self.__str__(), _evaluations))
-
+        logging.info("Run %s with %s evaluations" %
+                     (self.__str__(), _evaluations))
 
     def progress(self):
 
         if self.checkpoint is not None:
             self.checkpoint.run()
 
-        logging.info("-- %s evaluation %s of %s (%s%%) - BEST SCORE %s" % (type(self).__name__, self.numberOfEvaluations, self.maxEvaluations, "{0:.2f}".format((self.numberOfEvaluations) / self.maxEvaluations * 100.), self.bestSolution.fitness()))
-
+        logging.info("-- %s evaluation %s of %s (%s%%) - BEST SCORE %s" %
+                     (type(self).__name__, self.numberOfEvaluations,
+                      self.maxEvaluations, "{0:.2f}".format(
+                          (self.numberOfEvaluations) / self.maxEvaluations *
+                          100.), self.bestSolution.fitness()))
 
     def information(self):
-        logging.info("-- Best %s - SCORE %s" % (self.bestSolution, self.bestSolution.fitness()))
-
+        logging.info("-- Best %s - SCORE %s" %
+                     (self.bestSolution, self.bestSolution.fitness()))
 
     def __str__(self):
-        return "%s using %s" % (type(self).__name__, type(self.bestSolution).__name__)
-
-
+        return "%s using %s" % (type(self).__name__, type(
+            self.bestSolution).__name__)

+ 14 - 7
algorithms/IteratedLocalSearch.py

@@ -1,12 +1,12 @@
-# main imports 
+# main imports
 import logging
 
 # module imports
 from .Algorithm import Algorithm
-from.LocalSearch import LocalSearch
+from .LocalSearch import LocalSearch
 
-class IteratedLocalSearch(Algorithm):
 
+class IteratedLocalSearch(Algorithm):
     def run(self, _evaluations, _ls_evaluations=100):
 
         # by default use of mother method to initialize variables
@@ -17,8 +17,14 @@ class IteratedLocalSearch(Algorithm):
             self.resume()
 
         # passing global evaluation param from ILS
-        ls = LocalSearch(self.initializer, self.evaluator, self.operators, self.policy, self.validator, self.maximise, _parent=self)
-        
+        ls = LocalSearch(self.initializer,
+                         self.evaluator,
+                         self.operators,
+                         self.policy,
+                         self.validator,
+                         self.maximise,
+                         _parent=self)
+
         # set same checkpoint if exists
         if self.checkpoint is not None:
             ls.setCheckpoint(self.checkpoint)
@@ -38,8 +44,9 @@ class IteratedLocalSearch(Algorithm):
             #self.increaseEvaluation()
             #self.progress()
 
-            self.information()          
+            self.information()
 
-        logging.info("End of %s, best solution found %s" % (type(self).__name__, self.bestSolution))
+        logging.info("End of %s, best solution found %s" %
+                     (type(self).__name__, self.bestSolution))
 
         return self.bestSolution

+ 9 - 8
algorithms/LocalSearch.py

@@ -4,8 +4,8 @@ import logging
 # module imports
 from .Algorithm import Algorithm
 
-class LocalSearch(Algorithm):
 
+class LocalSearch(Algorithm):
     def run(self, _evaluations):
 
         # by default use of mother method to initialize variables
@@ -15,12 +15,11 @@ class LocalSearch(Algorithm):
 
         # local search algorithm implementation
         while not self.stop():
-            
+
             for _ in range(solutionSize):
 
                 # update solution using policy
-                # send random solution as second parameter for mutation
-                newSolution = self.update(self.bestSolution, self.initializer())
+                newSolution = self.update(self.bestSolution)
 
                 # if better solution than currently, replace it
                 if self.isBetter(newSolution):
@@ -30,12 +29,14 @@ class LocalSearch(Algorithm):
                 self.increaseEvaluation()
 
                 self.progress()
-                logging.info("---- Current %s - SCORE %s" % (newSolution, newSolution.fitness()))
+                logging.info("---- Current %s - SCORE %s" %
+                             (newSolution, newSolution.fitness()))
 
                 # stop algorithm if necessary
                 if self.stop():
                     break
-            
-        logging.info("End of %s, best solution found %s" % (type(self).__name__, self.bestSolution))
 
-        return self.bestSolution
+        logging.info("End of %s, best solution found %s" %
+                     (type(self).__name__, self.bestSolution))
+
+        return self.bestSolution

evaluators/__init__.py → macop/algorithms/__init__.py


+ 8 - 8
checkpoints/BasicCheckpoint.py

@@ -6,14 +6,13 @@ import numpy as np
 # module imports
 from .Checkpoint import Checkpoint
 
-class BasicCheckpoint(Checkpoint):
 
+class BasicCheckpoint(Checkpoint):
     def __init__(self, _algo, _every, _filepath):
         self.algo = _algo
         self.every = _every
         self.filepath = _filepath
 
-
     def run(self):
 
         # get current best solution
@@ -35,7 +34,8 @@ class BasicCheckpoint(Checkpoint):
                 if index < solutionSize - 1:
                     solutionData += ' '
 
-            line = str(currentEvaluation) + ';' + solutionData + ';' + str(solution.fitness()) + ';\n'
+            line = str(currentEvaluation) + ';' + solutionData + ';' + str(
+                solution.fitness()) + ';\n'
 
             # check if file exists
             if not os.path.exists(self.filepath):
@@ -45,7 +45,6 @@ class BasicCheckpoint(Checkpoint):
                 with open(self.filepath, 'a') as f:
                     f.write(line)
 
-
     def load(self):
 
         if os.path.exists(self.filepath):
@@ -56,8 +55,8 @@ class BasicCheckpoint(Checkpoint):
                 # get last line and read data
                 lastline = f.readlines()[-1]
                 data = lastline.split(';')
-                
-                # get evaluation  information 
+
+                # get evaluation  information
                 globalEvaluation = int(data[0])
 
                 if self.algo.parent is not None:
@@ -67,9 +66,10 @@ class BasicCheckpoint(Checkpoint):
 
                 # get best solution data information
                 solutionData = list(map(int, data[1].split(' ')))
-                
+
                 self.algo.bestSolution.data = np.array(solutionData)
                 self.algo.bestSolution.score = float(data[2])
         else:
             print('No backup found... Start running')
-            logging.info("Can't load backup... Backup filepath not valid in Checkpoint")
+            logging.info(
+                "Can't load backup... Backup filepath not valid in Checkpoint")

+ 2 - 2
checkpoints/Checkpoint.py

@@ -2,8 +2,8 @@
 import os
 import logging
 
-class Checkpoint():
 
+class Checkpoint():
     def __init__(self, _algo, _every, _filepath):
         self.algo = _algo
         self.every = _every
@@ -19,4 +19,4 @@ class Checkpoint():
         """
         Load last backup line of solution and set algorithm state at this backup
         """
-        pass
+        pass

operators/__init__.py → macop/checkpoints/__init__.py


+ 2 - 2
evaluators/EvaluatorExample.py

@@ -4,5 +4,5 @@ def evaluatorExample(solution):
     fitness = 0
     for index, elem in enumerate(solution.data):
         fitness = fitness + (elem * index)
-    
-    return fitness
+
+    return fitness

operators/crossovers/__init__.py → macop/evaluators/__init__.py


+ 33 - 0
macop/operators/Operator.py

@@ -0,0 +1,33 @@
+# main imports
+from enum import Enum
+
+
+# enumeration which stores kind of operator
+class KindOperator(Enum):
+    MUTATOR = 1
+    CROSSOVER = 2
+
+
+class Operator():
+    def __init__(self):
+        pass
+
+    def apply(self, solution):
+        """Apply the current operator transformation
+
+        Args:
+            solution (Solution): Solution instance
+
+        Raises:
+            NotImplementedError: if method not implemented into child class
+        """
+        raise NotImplementedError
+
+    def setAlgo(self, algo):
+        """Keep into operator reference of the whole algorithm
+           The reason is to better manage operator instance
+
+        Args:
+            algo (Algorithm): the algorithm reference runned
+        """
+        self.algo = algo

operators/mutators/__init__.py → macop/operators/__init__.py


+ 8 - 0
macop/operators/crossovers/Crossover.py

@@ -0,0 +1,8 @@
+# module imports
+from ..Operator import KindOperator, Operator
+
+
+# main mutation class
+class Crossover(Operator):
+    def __init__(self):
+        self.kind = KindOperator.CROSSOVER

+ 32 - 0
macop/operators/crossovers/RandomSplitCrossover.py

@@ -0,0 +1,32 @@
+# main imports
+import random
+import sys
+
+# module imports
+from .Crossover import Crossover
+
+from ...solutions.BinarySolution import BinarySolution
+from ...solutions.Solution import Solution
+
+
+class RandomSplitCrossover(Crossover):
+    def apply(self, solution):
+        size = solution.size
+
+        # copy data of solution
+        firstData = solution.data.copy()
+        # get best solution from current algorithm
+        secondData = self.algo.bestSolution.data.copy()
+
+        splitIndex = random.randint(0, len(secondData))
+
+        if random.uniform(0, 1) > 0.5:
+            firstData[splitIndex:(size - 1)] = firstData[splitIndex:(size - 1)]
+            currentData = firstData
+        else:
+            secondData[splitIndex:(size - 1)] = firstData[splitIndex:(size -
+                                                                      1)]
+            currentData = secondData
+
+        # create solution of same kind with new data
+        return globals()[type(solution).__name__](currentData, size)

+ 7 - 6
operators/crossovers/SimpleCrossover.py

@@ -10,22 +10,23 @@ from ...solutions.Solution import Solution
 
 
 class SimpleCrossover(Crossover):
-
-    def apply(self, solution, secondSolution=None):
+    def apply(self, solution):
         size = solution.size
 
         # copy data of solution
         firstData = solution.data.copy()
-        secondData = secondSolution.data.copy()
+        # get best solution from current algorithm
+        secondData = self.algo.bestSolution.data.copy()
 
         splitIndex = int(size / 2)
-        
+
         if random.uniform(0, 1) > 0.5:
             firstData[splitIndex:(size - 1)] = firstData[splitIndex:(size - 1)]
             currentData = firstData
         else:
-            secondData[splitIndex:(size - 1)] = firstData[splitIndex:(size - 1)]
+            secondData[splitIndex:(size - 1)] = firstData[splitIndex:(size -
+                                                                      1)]
             currentData = secondData
 
         # create solution of same kind with new data
-        return globals()[type(solution).__name__](currentData, size)
+        return globals()[type(solution).__name__](currentData, size)

operators/policies/__init__.py → macop/operators/crossovers/__init__.py


+ 11 - 0
macop/operators/mutators/Mutation.py

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

+ 2 - 2
operators/mutators/SimpleBinaryMutation.py

@@ -8,8 +8,8 @@ from .Mutation import Mutation
 from ...solutions.BinarySolution import BinarySolution
 from ...solutions.Solution import Solution
 
-class SimpleBinaryMutation(Mutation):
 
+class SimpleBinaryMutation(Mutation):
     def apply(self, solution):
         size = solution.size
 
@@ -25,4 +25,4 @@ class SimpleBinaryMutation(Mutation):
             currentData[cell] = 1
 
         # create solution of same kind with new data
-        return globals()[type(solution).__name__](currentData, size)
+        return globals()[type(solution).__name__](currentData, size)

+ 2 - 4
operators/mutators/SimpleMutation.py

@@ -10,7 +10,6 @@ from ...solutions.Solution import Solution
 
 
 class SimpleMutation(Mutation):
-
     def apply(self, solution):
         size = solution.size
 
@@ -21,7 +20,7 @@ class SimpleMutation(Mutation):
         currentData = solution.data.copy()
 
         while firstCell == secondCell:
-            firstCell = random.randint(0, size - 1) 
+            firstCell = random.randint(0, size - 1)
             secondCell = random.randint(0, size - 1)
 
         temp = currentData[firstCell]
@@ -29,7 +28,6 @@ class SimpleMutation(Mutation):
         # swicth values
         currentData[firstCell] = currentData[secondCell]
         currentData[secondCell] = temp
-        
+
         # create solution of same kind with new data
         return globals()[type(solution).__name__](currentData, size)
-

solutions/__init__.py → macop/operators/mutators/__init__.py


+ 7 - 14
operators/policies/Policy.py

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

+ 2 - 5
operators/policies/RandomPolicy.py

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

+ 0 - 0
macop/operators/policies/__init__.py


+ 0 - 4
solutions/BinarySolution.py

@@ -7,7 +7,6 @@ from .Solution import Solution
 
 # Solution which stores solution data as binary array
 class BinarySolution(Solution):
-
     def __init__(self, _data, _size):
         """
         Initialize data of solution using specific data
@@ -19,7 +18,6 @@ class BinarySolution(Solution):
         self.data = _data
         self.size = _size
 
-
     def random(self, _validator):
         """
         Intialize binary array using size solution data
@@ -34,7 +32,5 @@ class BinarySolution(Solution):
 
         return self
 
-
     def __str__(self):
         return "Binary solution %s" % (self.data)
-        

+ 0 - 4
solutions/CombinatoryIntegerSolution.py

@@ -7,7 +7,6 @@ from .Solution import Solution
 
 # Solution which stores solution data as combinatory integer array
 class CombinatoryIntegerSolution(Solution):
-
     def __init__(self, _data, _size):
         """
         Initialize data of solution using specific data
@@ -19,7 +18,6 @@ class CombinatoryIntegerSolution(Solution):
         self.data = _data
         self.size = _size
 
-
     def random(self, _validator):
         """
         Intialize combinatory integer array using size solution data
@@ -34,7 +32,5 @@ class CombinatoryIntegerSolution(Solution):
 
         return self
 
-
     def __str__(self):
         return "Combinatory integer solution %s" % (self.data)
-        

+ 0 - 4
solutions/IntegerSolution.py

@@ -7,7 +7,6 @@ from .Solution import Solution
 
 # Solution which stores solution data as integer array
 class IntegerSolution(Solution):
-
     def __init__(self, _data, _size):
         """
         Initialize data of solution using specific data
@@ -19,7 +18,6 @@ class IntegerSolution(Solution):
         self.data = _data
         self.size = _size
 
-
     def random(self, _validator):
         """
         Intialize integer array using size solution data
@@ -34,7 +32,5 @@ class IntegerSolution(Solution):
 
         return self
 
-
     def __str__(self):
         return "Integer solution %s" % (self.data)
-        

+ 1 - 7
solutions/Solution.py

@@ -1,6 +1,5 @@
-# Generic solution class 
+# Generic solution class
 class Solution():
-
     def __init__(self, _data, _size):
         """
         Initialize data of solution using specific data
@@ -10,7 +9,6 @@ class Solution():
         self.data = _data
         self.size = _size
         self.score = None
-        
 
     def isValid(self, _validator):
         """
@@ -18,7 +16,6 @@ class Solution():
         """
         return _validator(self)
 
-
     def evaluate(self, _evaluator):
         """
         Evaluate function using specific `_evaluator`
@@ -26,20 +23,17 @@ class Solution():
         self.score = _evaluator(self)
         return self.score
 
-
     def fitness(self):
         """
         Returns fitness score
         """
         return self.score
 
-
     def random(self, _validator):
         """
         Initialize solution using random data
         """
         raise NotImplementedError
 
-
     def __str__(self):
         print("Generic solution with ", self.data)

+ 0 - 0
macop/solutions/__init__.py


+ 10 - 9
mainExample.py

@@ -6,17 +6,18 @@ import logging
 # Note: you need to import from folder dependency name
 # examples: `from optimization.solutions.BinarySolution import BinarySolution`
 
-from optimization.algorithms.IteratedLocalSearch import IteratedLocalSearch as ILS
-from optimization.solutions.BinarySolution import BinarySolution
-from optimization.evaluators.EvaluatorExample import evaluatorExample
+from macop.algorithms.IteratedLocalSearch import IteratedLocalSearch as ILS
+from macop.solutions.BinarySolution import BinarySolution
+from macop.evaluators.EvaluatorExample import evaluatorExample
 
-from optimization.operators.mutators.SimpleMutation import SimpleMutation
-from optimization.operators.mutators.SimpleBinaryMutation import SimpleBinaryMutation
-from optimization.operators.crossovers.SimpleCrossover import SimpleCrossover
+from macop.operators.mutators.SimpleMutation import SimpleMutation
+from macop.operators.mutators.SimpleBinaryMutation import SimpleBinaryMutation
+from macop.operators.crossovers.SimpleCrossover import SimpleCrossover
+from macop.operators.crossovers.RandomSplitCrossover import RandomSplitCrossover
 
-from optimization.operators.policies.RandomPolicy import RandomPolicy
+from macop.operators.policies.RandomPolicy import RandomPolicy
 
-from optimization.checkpoints.BasicCheckpoint import BasicCheckpoint
+from macop.checkpoints.BasicCheckpoint import BasicCheckpoint
 
 # logging configuration
 logging.basicConfig(format='%(asctime)s %(message)s', filename='example.log', level=logging.DEBUG)
@@ -33,7 +34,7 @@ filepath = "checkpoints.csv"
 
 def main():
 
-    operators = [SimpleBinaryMutation(), SimpleMutation(), SimpleCrossover()]
+    operators = [SimpleBinaryMutation(), SimpleMutation(), SimpleCrossover(), RandomSplitCrossover()]
     policy = RandomPolicy(operators)
 
     algo = ILS(init, evaluatorExample, operators, policy, validator, True)

+ 0 - 7
operators/Operator.py

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

+ 0 - 11
operators/crossovers/Crossover.py

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

+ 0 - 11
operators/mutators/Mutation.py

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

+ 48 - 0
setup.py

@@ -0,0 +1,48 @@
+from setuptools import setup
+import distutils.command.check
+
+class TestCommand(distutils.command.check.check):
+    """Custom test command."""
+
+    def run(self):
+
+        # run tests using doctest
+        import doctest
+        
+        # folders
+        # from macop.algorithms import Algorithm
+
+        print("==============================")
+        print("Runs test command...")
+
+        # pass test using doctest
+        #doctest.testmod(Algorithm)
+
+        distutils.command.check.check.run(self)
+
+
+setup(
+    name='macop',
+    version='0.1.2',
+    description='Minimalist And Customizable Optimization Package',
+    long_description=open('README.md').read(),
+    long_description_content_type='text/markdown',
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'License :: OSI Approved :: MIT License',
+        'Programming Language :: Python :: 3.7',
+        'Topic :: Scientific/Engineering',
+        'Topic :: Utilities'
+    ],
+    url='https://github.com/prise-3d/macop',
+    author='Jérôme BUISINE',
+    author_email='jerome.buisine@univ-littoral.fr',
+    license='MIT',
+    packages=['macop', 'macop.algorithms', 'macop.checkpoints', 'macop.evaluators', 'macop.operators', 'macop.solutions'],
+    install_requires=[
+        'numpy',
+    ],
+    cmdclass={
+        'test': TestCommand,
+    },
+    zip_safe=False)