diff --git a/.travis.yml b/.travis.yml
index c6f83965..ee0d6c52 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,16 +2,12 @@ language: python
python: '3.6'
install: pip install -r requirements.txt
script: python -m unittest discover
-branches:
- only:
- - master
- - "/.*/"
deploy:
provider: pypi
skip_cleanup: true
- user: ajnebro
+ user: benhid
distributions: sdist bdist_wheel
password:
- secure: 19ogotJW3NnZNB0oiurGtSjwWFmX7qEbu7UL9ZDuPNOGToOFcNFq6o8zAUD18+rS+mZ+X5uEQOt3By0s9b1dd6pwvl7kQVj6rWyU4DLc94tVeRPqHMVItfY0t97/m/wtauxBsieVhEt7PFmqKXL90HKuBqZkrpbYd0f+6xlXCqQFbjiSTnRv2BFvpA90TxC70U7PRDF+PwjCcrkSMpj/EyR/V0CMsbMNi4zVmXXLWstneTkYgt2989CPu7MQaQXnryRqd3HUk5FSdLdoam29Gk8wrp9Q+Ah64wnCMyS4QbIEEPB1ZMlzsQLOo5OwcTMAdBxN4WPyIgeOaZDuUmni8guVf1U5f9UKds7nBf7eDiR5J+LD94ZPV3ukU2NPflCjnJNjzZpCCBS74XvupLmejwKX6qAfRKnlqdhjllRPcw36wr1zdVRj2ZIs5dlztFE+A9ZfP3qwyUXaIRtoQ2PQsjmN5VYz63al5yC6ncpvYj7Rql6VTG9TMNCRdLG91b+6EabPF+d0yZwLVi+qvmcR0WMsbRxvxSdaaxiZQxKgh/lMZ1eHB53d1W2qpTWHtXjEVD6id4KB+WN022CtjulYgFejpLHyYzJDqELFWfScsmX2e2tPeIGNuuXaQw/TIdnUWoSHDQ1AhXMVYyr1sHgwa+vh08jkINk3EkZFBnh9rN4=
+ secure: pkZQushcLk4eEq06foPEesrrtzxJz907zrDHJghTR5/OLaJ3JrhASG7sa5s1xRwWjAYpf8GWgZsUbM6YGe+r7EASoQ4VdbGZEaTYJCS0AkBawqe7K0KjX2llkgXdHRerTRoFe50mrxA5atE2kVsmaP1ZD0oZ8HzBcTmQbFy7/ljWLnVsyCgczTFHiSfLAdL1MPy1emGAPz6BtMV18b8GLdwBl2KzKVDWi1PwE/l5eG01YoaPT8Eq+/Sy3ZRg8Nx+jdr6D+/1jVNb3SChy27B4kC1EmaCcSwnUg9cGhjziAf6wfEr48+7ZfhEKHTSsJbeRRoDMrAk8f68inuXp627LaUPOBXQsItNwNwRZGfP1LnvoZUbVbEEm2fT6viRLHR1P4LqJcka/6JVD3UgobTvv4sTTaIReeIRJtWe+ofHL2PA+nLi1oRuFcexKfohjyOhreYbJ0jp9O/hWyM5Lak1yEb1CWUuiNy69VMxwm0i00uSyoTZD0LNf/0BBjbFZ02TxHlkKj+LXzI8IiBhhNWY7nRVAJi/MVow14KKmmpk+Qo+dnv+sS7r1zhiEr01akdsLH+3+cfu7K9lJNkhSG2ayoBOe/lKxxOw4G3D+Nm/mNL+Pu38WlmulWdMwbPdHDKmuBzzA2ADsnDIb5BoPwFDvghTbYBA8Slaum35CW/2yzA=
on:
branch: master
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 6afbb71f..00000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Development guidelines
-
-Contributions to the jMetalPy project are welcome. Please, take into account the following guidelines (all developers should follow these guidelines):
-
-- [Git WorkFlow](resources/pages/workflow_git.md)
-- [Follow style guide for python code: PEP8](resources/pages/code_style.md)
-- [Object-oriented programming](resources/pages/poo.md)
-- [Incorporate the new features of Python 3.5](resources/pages/features_python3.md)
-- [Respect the initial structure](resources/pages/project_structure.md)
-- [How to create auto documentation using compatible code](resources/pages/auto_doc.md)
-- [Performance analysis of Python](resources/pages/profiling.md)
-
-# Documentation
-
-To generate the documentation, install [Sphinx](http://www.sphinx-doc.org/en/master/) by running:
-
-```bash
-$ pip install sphinx
-$ pip install sphinx_rtd_theme
-```
-
-And then `cd` to `/docs` and run:
-
-```bash
-$ sphinx-apidoc -f -o source/ ../jmetal/
-$ make html
-```
\ No newline at end of file
diff --git a/README.md b/README.md
index 29a5bb6b..d6747482 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,6 @@
- [Installation](#installation)
- [Usage](#usage)
- [Features](#features)
-- [Contributing](#contributing)
- [License](#license)
## Installation
@@ -33,18 +32,22 @@ $ pip install jmetalpy
Examples of configuring and running all the included algorithms are located [in the docs](https://jmetalpy.readthedocs.io/en/latest/examples.html).
## Features
-The current release of jMetalPy (v0.5.0) contains the following components:
-
-* Algorithms: random search, NSGA-II, SMPSO, SMPSO/RP
-* Problems: ZDT1-6, DTLZ1-2, unconstrained (Kursawe, Fonseca, Schaffer, Viennet2), constrained (Srinivas, Tanaka).
-* Encodings: real, binary
-* Operators: selection (binary tournament, ranking and crowding distance, random, nary random, best solution), crossover (single-point, SBX), mutation (bit-blip, polynomial, uniform, random)
-* Quality indicators: hypervolume
-* Density estimator: crowding distance
-* Graphics: 2D/3D plotting in real-time
-
-## Contributing
-Please read [CONTRIBUTING](CONTRIBUTING.md) for details on how to contribute to the project.
+The current release of jMetalPy (v0.5.1) contains the following components:
+
+* Algorithms: random search, NSGA-II, SMPSO, SMPSO/RP.
+* Benchmark problems: ZDT1-6, DTLZ1-2, unconstrained (Kursawe, Fonseca, Schaffer, Viennet2), constrained (Srinivas, Tanaka).
+* Encodings: real, binary.
+* Operators: selection (binary tournament, ranking and crowding distance, random, nary random, best solution), crossover (single-point, SBX), mutation (bit-blip, polynomial, uniform, random).
+* Quality indicators: hypervolume.
+* Density estimator: crowding distance.
+* Graphics: Pareto front plotting (2 or more objectives).
+* Laboratory: Experiment class for performing studies.
+
+
+
+
+
+
## License
This project is licensed under the terms of the MIT - see the [LICENSE](LICENSE) file for details.
diff --git a/docs/source/NSGAII-ZDT1.png b/docs/source/NSGAII-ZDT1.png
deleted file mode 100644
index ac19d046..00000000
Binary files a/docs/source/NSGAII-ZDT1.png and /dev/null differ
diff --git a/docs/source/about.rst b/docs/source/about.rst
index c5482847..20e8b3a2 100644
--- a/docs/source/about.rst
+++ b/docs/source/about.rst
@@ -7,4 +7,5 @@ References
--------------------------------
1. J.J. Durillo, A.J. Nebro jMetal: a Java Framework for Multi-Objective Optimization. Advances in Engineering Software 42 (2011) 760-771.
-2. A.J. Nebro, J.J. Durillo, M. Vergne Redesigning the jMetal Multi-Objective Optimization Framework. GECCO (Companion) 2015, pp: 1093-1100. July 2015.
\ No newline at end of file
+2. A.J. Nebro, J.J. Durillo, M. Vergne Redesigning the jMetal Multi-Objective Optimization Framework. GECCO (Companion) 2015, pp: 1093-1100. July 2015.
+3. Nebro A.J. et al. (2018) Extending the Speed-Constrained Multi-objective PSO (SMPSO) with Reference Point Based Preference Articulation. In: Auger A., Fonseca C., Lourenço N., Machado P., Paquete L., Whitley D. (eds) Parallel Problem Solving from Nature – PPSN XV. PPSN 2018. Lecture Notes in Computer Science, vol 11101. Springer, Cham
\ No newline at end of file
diff --git a/docs/source/api/jmetal.util.rst b/docs/source/api/jmetal.util.rst
index 0e330e90..72e3ae54 100644
--- a/docs/source/api/jmetal.util.rst
+++ b/docs/source/api/jmetal.util.rst
@@ -1,14 +1,6 @@
Utils
===================
-Front file
------------------------------
-
-.. automodule:: jmetal.util.front_file
- :members:
- :undoc-members:
- :show-inheritance:
-
Graphic
--------------------------
diff --git a/docs/source/conf.py b/docs/source/conf.py
index f2abbff3..0fe35c75 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -19,9 +19,10 @@
# http://blog.rtwilson.com/how-to-make-your-sphinx-documentation-compile-with-readthedocs-when-youre-using-numpy-and-scipy/
import mock
-MOCK_MODULES = ['numpy', 'dask', 'dask.distributed', 'tqdm', 'bokeh', 'bokeh.embed', 'bokeh.front', 'bokeh.client',
- 'bokeh.io', 'bokeh.layouts','bokeh.models','bokeh.plotting', 'bokeh.resources',
- 'mpl_toolkits', 'mpl_toolkits.mplot3d', 'matplotlib', 'matplotlib.pyplot', 'matplotlib.axes']
+MOCK_MODULES = ['numpy', 'pandas',
+ 'tqdm',
+ 'plotly', 'plotly.offline',
+ 'matplotlib', 'matplotlib.pyplot', 'mpl_toolkits', 'mpl_toolkits.mplot3d']
for mod_name in MOCK_MODULES:
sys.modules[mod_name] = mock.Mock()
diff --git a/docs/source/examples.rst b/docs/source/examples.rst
index e56a766a..9062be6e 100644
--- a/docs/source/examples.rst
+++ b/docs/source/examples.rst
@@ -1,15 +1,16 @@
-Examples
-==============
+Getting started
+==================
.. toctree::
:maxdepth: 2
:caption: Algorithms:
- runner/nsgaii
- runner/smpso
+ examples/nsgaii
+ examples/smpso
.. toctree::
:maxdepth: 2
:caption: Further configuration:
- runner/observer
+ examples/observer
+ examples/experiment
diff --git a/docs/source/examples/experiment.rst b/docs/source/examples/experiment.rst
new file mode 100644
index 00000000..4ff868c0
--- /dev/null
+++ b/docs/source/examples/experiment.rst
@@ -0,0 +1,43 @@
+Experiments
+========================
+
+This is an example of an experimental study based on solving two problems of the ZDT family with two versions of the same algorithm (NSGAII).
+The hypervolume indicator is used for performance assessment.
+
+.. code-block:: python
+
+ # Configure experiment
+ problem_list = [ZDT1(), ZDT2()]
+ algorithm_list = []
+
+ for problem in problem_list:
+ algorithm_list.append(
+ ('NSGAII_A',
+ NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=25000,
+ mutation=NullMutation(),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ ))
+ )
+ algorithm_list.append(
+ ('NSGAII_B',
+ NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0 / problem.number_of_variables, distribution_index=20),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ ))
+ )
+
+ study = Experiment(algorithm_list, n_runs=2)
+ study.run()
+
+ # Compute quality indicators
+ metric_list = [HyperVolume(reference_point=[1, 1])]
+
+ print(study.compute_metrics(metric_list))
\ No newline at end of file
diff --git a/docs/source/examples/nsgaii.rst b/docs/source/examples/nsgaii.rst
new file mode 100644
index 00000000..3259a6b2
--- /dev/null
+++ b/docs/source/examples/nsgaii.rst
@@ -0,0 +1,104 @@
+NSGA-II
+========================
+
+Common imports for these examples:
+
+.. code-block:: python
+
+ from jmetal.algorithm import NSGAII
+ from jmetal.operator import Polynomial, SBX, BinaryTournamentSelection
+ from jmetal.component import RankingAndCrowdingDistanceComparator
+
+ from jmetal.util import FrontPlot
+
+NSGA-II with plotting
+------------------------------------
+
+.. code-block:: python
+
+ from jmetal.problem import ZDT1
+
+ problem = ZDT1(rf_path='resources/reference_front/ZDT1.pf')
+
+ algorithm = NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ )
+
+ algorithm.run()
+ front = algorithm.get_result()
+
+ pareto_front = FrontPlot(plot_title='NSGAII-ZDT1', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.to_html(filename='NSGAII-ZDT1')
+
+.. raw:: html
+
+
+
+
+.. code-block:: python
+
+ from jmetal.problem import DTLZ1
+
+ problem = DTLZ1(rf_path='resources/reference_front/DTLZ1.pf')
+
+ algorithm = NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=50000,
+ mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ )
+
+ algorithm.run()
+ front = algorithm.get_result()
+
+ pareto_front = FrontPlot(plot_title='NSGAII-DTLZ1', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.to_html(filename='NSGAII-DTLZ1')
+
+.. raw:: html
+
+
+
+
+
+NSGA-II stopping by time
+------------------------------------
+
+.. code-block:: python
+
+ from jmetal.problem import ZDT1
+
+
+ class NSGA2b(NSGAII):
+ def is_stopping_condition_reached(self):
+ # Re-define the stopping condition
+ return [False, True][self.get_current_computing_time() > 4]
+
+ problem = ZDT1()
+
+ algorithm = NSGA2b(
+ problem=problem,
+ population_size=100,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ )
+
+ algorithm.run()
+ front = algorithm.get_result()
diff --git a/docs/source/runner/observer.rst b/docs/source/examples/observer.rst
similarity index 100%
rename from docs/source/runner/observer.rst
rename to docs/source/examples/observer.rst
diff --git a/docs/source/examples/smpso.rst b/docs/source/examples/smpso.rst
new file mode 100644
index 00000000..7dee6bd8
--- /dev/null
+++ b/docs/source/examples/smpso.rst
@@ -0,0 +1,103 @@
+SMPSO
+========================
+
+Common imports for these examples:
+
+.. code-block:: python
+
+ from jmetal.operator import Polynomial
+
+ from jmetal.util import FrontPlot
+
+SMPSO with standard settings
+------------------------------------
+
+.. code-block:: python
+
+ from jmetal.algorithm import SMPSO
+ from jmetal.component import CrowdingDistanceArchive
+ from jmetal.problem import DTLZ1
+
+ problem = DTLZ1(number_of_objectives=5)
+
+ algorithm = SMPSO(
+ problem=problem,
+ swarm_size=100,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
+ leaders=CrowdingDistanceArchive(100)
+ )
+
+ algorithm.run()
+ front = algorithm.get_result()
+
+ pareto_front = FrontPlot(plot_title='SMPSO-DTLZ1-5', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.to_html(filename='SMPSO-DTLZ1-5')
+
+.. raw:: html
+
+
+
+
+.. code-block:: python
+
+ pareto_front = ScatterPlot(plot_title='SMPSO-DTLZ1-5-norm', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front, normalize=True)
+ pareto_front.to_html(filename='SMPSO-DTLZ1-5-norm')
+
+.. raw:: html
+
+
+
+
+SMPSO/RP with standard settings
+------------------------------------
+
+.. code-block:: python
+
+ from jmetal.algorithm import SMPSORP
+ from jmetal.component import CrowdingDistanceArchiveWithReferencePoint
+ from jmetal.problem import ZDT1
+
+ def points_to_solutions(points):
+ solutions = []
+ for i, _ in enumerate(points):
+ point = problem.create_solution()
+ point.objectives = points[i]
+ solutions.append(point)
+
+ return solutions
+
+ problem = ZDT1(rf_path='resources/reference_front/ZDT1.pf')
+
+ swarm_size = 100
+ reference_points = [[0.8, 0.2], [0.4, 0.6]]
+ archives_with_reference_points = []
+
+ for point in reference_points:
+ archives_with_reference_points.append(
+ CrowdingDistanceArchiveWithReferencePoint(swarm_size, point)
+ )
+
+ algorithm = SMPSORP(
+ problem=problem,
+ swarm_size=swarm_size,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
+ reference_points=reference_points,
+ leaders=archives_with_reference_points
+ )
+
+ algorithm.run()
+ front = algorithm.get_result()
+
+ pareto_front = FrontPlot(plot_title='SMPSORP-ZDT1', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.update(points_to_solutions(reference_points), legend='reference points')
+ pareto_front.to_html(filename='SMPSORP-ZDT1')
+
+.. raw:: html
+
+
+
\ No newline at end of file
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 586a542d..9a65b7e1 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -34,26 +34,16 @@ Via Github:
$ pip install -r requirements.txt
$ python setup.py install
-Basic usage
------------
-
-.. code-block:: python
-
- problem = ZDT1()
-
- algorithm = NSGAII(
- problem=problem,
- population_size=100,
- max_evaluations=25000,
- mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
- crossover=SBX(probability=1.0, distribution_index=20),
- selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
- )
-
- algorithm.run()
- front = algorithm.get_result()
+Features
+------------------------
+The current release of jMetalPy (v0.5.1) contains the following components:
- pareto_front = ScatterMatplotlib(plot_title='NSGAII for ZDT1', number_of_objectives=problem.number_of_objectives)
- pareto_front.plot(front, reference=problem.get_reference_front(), output='NSGAII-ZDT1', show=False)
+* Algorithms: random search, NSGA-II, SMPSO, SMPSO/RP
+* Benchmark problems: ZDT1-6, DTLZ1-2, unconstrained (Kursawe, Fonseca, Schaffer, Viennet2), constrained (Srinivas, Tanaka).
+* Encodings: real, binary
+* Operators: selection (binary tournament, ranking and crowding distance, random, nary random, best solution), crossover (single-point, SBX), mutation (bit-blip, polynomial, uniform, random)
+* Quality indicators: hypervolume
+* Density estimator: crowding distance
+* Graphics: Pareto front plotting (2 or more objectives)
+* Laboratory: Experiment class for performing studies.
-.. image:: NSGAII-ZDT1.png
diff --git a/docs/source/runner/nsgaii.rst b/docs/source/runner/nsgaii.rst
deleted file mode 100644
index 3a693b43..00000000
--- a/docs/source/runner/nsgaii.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-NSGA-II
-========================
-
-Common imports for these examples:
-
-.. code-block:: python
-
- from jmetal.algorithm import NSGAII
- from jmetal.operator import Polynomial, SBX, BinaryTournamentSelection
- from jmetal.component import RankingAndCrowdingDistanceComparator
-
- from jmetal.problem import ZDT1
-
-NSGA-II with standard settings
-------------------------------------
-
-.. code-block:: python
-
- algorithm = NSGAII(
- problem=ZDT1(),
- population_size=100,
- max_evaluations=25000,
- mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
- crossover=SBX(probability=1.0, distribution_index=20),
- selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
- )
-
- algorithm.run()
- front = algorithm.get_result()
-
-NSGA-II stopping by time
-------------------------------------
-
-.. code-block:: python
-
- class NSGA2b(NSGAII):
- def is_stopping_condition_reached(self):
- # Re-define the stopping condition
- return [False, True][self.get_current_computing_time() > 4]
-
- algorithm = NSGA2b(
- problem=ZDT1(),
- population_size=100,
- max_evaluations=25000,
- mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
- crossover=SBX(probability=1.0, distribution_index=20),
- selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
- )
-
- algorithm.run()
- front = algorithm.get_result()
diff --git a/docs/source/runner/smpso.rst b/docs/source/runner/smpso.rst
deleted file mode 100644
index 140f5535..00000000
--- a/docs/source/runner/smpso.rst
+++ /dev/null
@@ -1,59 +0,0 @@
-SMPSO
-========================
-
-Common imports:
-
-.. code-block:: python
-
- from jmetal.operator import Polynomial
-
- from jmetal.problem import ZDT1
-
-SMPSO with standard settings
-------------------------------------
-
-.. code-block:: python
-
- from jmetal.algorithm import SMPSO
- from jmetal.component import CrowdingDistanceArchive
-
- algorithm = SMPSO(
- problem=ZDT1(),
- swarm_size=100,
- max_evaluations=25000,
- mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
- leaders=CrowdingDistanceArchive(100)
- )
-
- algorithm.run()
- front = algorithm.get_result()
-
-SMPSO/RP with standard settings
-------------------------------------
-
-.. code-block:: python
-
- from jmetal.algorithm import SMPSORP
- from jmetal.component import CrowdingDistanceArchiveWithReferencePoint
-
- swarm_size = 100
-
- reference_points = [[0.8, 0.2], [0.4, 0.6]]
- archives_with_reference_points = []
-
- for point in reference_points:
- archives_with_reference_points.append(
- CrowdingDistanceArchiveWithReferencePoint(swarm_size, point)
- )
-
- algorithm = SMPSORP(
- problem=ZDT1(),
- swarm_size=swarm_size,
- max_evaluations=25000,
- mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
- reference_points=reference_points,
- leaders=archives_with_reference_points
- )
-
- algorithm.run()
- front = algorithm.get_result()
\ No newline at end of file
diff --git a/docs/source/visualization.png b/docs/source/visualization.png
new file mode 100644
index 00000000..b400c9be
Binary files /dev/null and b/docs/source/visualization.png differ
diff --git a/examples/experiment/NSGAII for ZDT1-2.py b/examples/experiment/NSGAII for ZDT1-2.py
new file mode 100644
index 00000000..e92f105b
--- /dev/null
+++ b/examples/experiment/NSGAII for ZDT1-2.py
@@ -0,0 +1,42 @@
+from jmetal.algorithm import NSGAII
+from jmetal.component.comparator import RankingAndCrowdingDistanceComparator
+from jmetal.operator import NullMutation, SBX, BinaryTournamentSelection, Polynomial
+from jmetal.problem import ZDT1, ZDT2
+from jmetal.component.quality_indicator import HyperVolume
+from jmetal.util.laboratory import Experiment
+
+# Configure experiment
+problem_list = [ZDT1(), ZDT2()]
+algorithm_list = []
+
+for problem in problem_list:
+ algorithm_list.append(
+ ('NSGAII_A',
+ NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=25000,
+ mutation=NullMutation(),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ ))
+ )
+ algorithm_list.append(
+ ('NSGAII_B',
+ NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0 / problem.number_of_variables, distribution_index=20),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ ))
+ )
+
+study = Experiment(algorithm_list, n_runs=2)
+study.run()
+
+# Compute quality indicators
+metric_list = [HyperVolume(reference_point=[1, 1])]
+
+print(study.compute_metrics(metric_list))
\ No newline at end of file
diff --git a/examples/experiment/NSGAII-SMPSO for ZDT1.py b/examples/experiment/NSGAII-SMPSO for ZDT1.py
deleted file mode 100644
index 17697176..00000000
--- a/examples/experiment/NSGAII-SMPSO for ZDT1.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from jmetal.algorithm import NSGAII, SMPSO
-from jmetal.component.archive import CrowdingDistanceArchive
-from jmetal.component.comparator import RankingAndCrowdingDistanceComparator
-from jmetal.operator import NullMutation, SBX, BinaryTournamentSelection
-from jmetal.problem import ZDT1, ZDT2
-from jmetal.component.quality_indicator import HyperVolume
-from jmetal.util.laboratory import experiment, display
-
-algorithm = [
- (NSGAII, {'population_size': 100, 'max_evaluations': 25000, 'mutation': NullMutation(), 'crossover': SBX(1.0, 20),
- 'selection': BinaryTournamentSelection(RankingAndCrowdingDistanceComparator())}),
- (NSGAII(population_size=100, max_evaluations=25000, mutation=NullMutation(), crossover=SBX(1.0, 20),
- selection=BinaryTournamentSelection(RankingAndCrowdingDistanceComparator()), problem=ZDT1()), {}),
- (SMPSO, {'swarm_size': 100, 'max_evaluations': 25000, 'mutation': NullMutation(),
- 'leaders': CrowdingDistanceArchive(100)})
-]
-metric = [HyperVolume(reference_point=[1, 1])]
-problem = [(ZDT1, {}), (ZDT2, {})]
-
-results = experiment(algorithm, metric, problem)
-display(results)
\ No newline at end of file
diff --git a/examples/experiment/SMPSO for ZDT1-2.py b/examples/experiment/SMPSO for ZDT1-2.py
new file mode 100644
index 00000000..92a74285
--- /dev/null
+++ b/examples/experiment/SMPSO for ZDT1-2.py
@@ -0,0 +1,50 @@
+from jmetal.algorithm import SMPSO
+from jmetal.component import CrowdingDistanceArchive
+from jmetal.operator import Polynomial, NullMutation
+from jmetal.problem import ZDT1, ZDT2
+from jmetal.component.quality_indicator import HyperVolume
+from jmetal.util.laboratory import Experiment
+
+# Configure experiment
+problem_list = [ZDT1(), ZDT2()]
+algorithm_list = []
+
+for problem in problem_list:
+ algorithm_list.append(
+ ('SMPSO_A',
+ SMPSO(
+ problem=problem,
+ swarm_size=100,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=0.5, distribution_index=20),
+ leaders=CrowdingDistanceArchive(100)
+ ))
+ )
+ algorithm_list.append(
+ ('SMPSO_B',
+ SMPSO(
+ problem=problem,
+ swarm_size=100,
+ max_evaluations=25000,
+ mutation=NullMutation(),
+ leaders=CrowdingDistanceArchive(100)
+ ))
+ )
+ algorithm_list.append(
+ ('SMPSO_C',
+ SMPSO(
+ problem=problem,
+ swarm_size=100,
+ max_evaluations=25000,
+ mutation=NullMutation(),
+ leaders=CrowdingDistanceArchive(100)
+ ))
+ )
+
+study = Experiment(algorithm_list, n_runs=1)
+study.run()
+
+# Compute quality indicators
+metric_list = [HyperVolume(reference_point=[1, 1])]
+
+print(study.compute_metrics(metric_list))
diff --git a/examples/multiobjective/nsgaii_dtlz1.py b/examples/multiobjective/nsgaii_dtlz1.py
new file mode 100644
index 00000000..6abec7dd
--- /dev/null
+++ b/examples/multiobjective/nsgaii_dtlz1.py
@@ -0,0 +1,39 @@
+from jmetal.algorithm import NSGAII
+from jmetal.problem import DTLZ1
+from jmetal.operator import SBX, Polynomial, BinaryTournamentSelection
+from jmetal.component import ProgressBarObserver, VisualizerObserver, RankingAndCrowdingDistanceComparator
+from jmetal.util import FrontPlot, SolutionList
+
+
+if __name__ == '__main__':
+ problem = DTLZ1(rf_path='../../resources/reference_front/DTLZ1.pf')
+
+ algorithm = NSGAII(
+ problem=problem,
+ population_size=100,
+ max_evaluations=50000,
+ mutation=Polynomial(probability=1.0 / problem.number_of_variables, distribution_index=20),
+ crossover=SBX(probability=1.0, distribution_index=20),
+ selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
+ )
+
+ progress_bar = ProgressBarObserver(step=100, maximum=50000)
+ visualizer = VisualizerObserver()
+ algorithm.observable.register(observer=progress_bar)
+ algorithm.observable.register(observer=visualizer)
+
+ algorithm.run()
+ front = algorithm.get_result()
+
+ # Plot frontier to file
+ pareto_front = FrontPlot(plot_title='NSGAII-DTLZ1', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.to_html(filename='NSGAII-DTLZ1')
+
+ # Save variables to file
+ SolutionList.print_function_values_to_file(front, 'FUN.NSGAII.DTLZ1')
+ SolutionList.print_variables_to_file(front, 'VAR.NSGAII.DTLZ1')
+
+ print('Algorithm (continuous problem): ' + algorithm.get_name())
+ print('Problem: ' + problem.get_name())
+ print('Computing time: ' + str(algorithm.total_computing_time))
diff --git a/examples/multiobjective/nsgaii_full_settings.py b/examples/multiobjective/nsgaii_zdt1.py
similarity index 62%
rename from examples/multiobjective/nsgaii_full_settings.py
rename to examples/multiobjective/nsgaii_zdt1.py
index a3de7483..a8042b37 100644
--- a/examples/multiobjective/nsgaii_full_settings.py
+++ b/examples/multiobjective/nsgaii_zdt1.py
@@ -1,12 +1,12 @@
from jmetal.algorithm import NSGAII
-from jmetal.component import VisualizerObserver, ProgressBarObserver, RankingAndCrowdingDistanceComparator
from jmetal.problem import ZDT1
from jmetal.operator import SBX, Polynomial, BinaryTournamentSelection
-from jmetal.util import ScatterMatplotlib, SolutionList
+from jmetal.component import ProgressBarObserver, RankingAndCrowdingDistanceComparator, VisualizerObserver
+from jmetal.util import FrontPlot, SolutionList
if __name__ == '__main__':
- problem = ZDT1()
+ problem = ZDT1(rf_path='../../resources/reference_front/ZDT1.pf')
algorithm = NSGAII(
problem=problem,
@@ -17,21 +17,22 @@
selection=BinaryTournamentSelection(comparator=RankingAndCrowdingDistanceComparator())
)
- observer = VisualizerObserver(problem)
progress_bar = ProgressBarObserver(step=100, maximum=25000)
- algorithm.observable.register(observer=observer)
+ visualizer = VisualizerObserver()
algorithm.observable.register(observer=progress_bar)
+ algorithm.observable.register(observer=visualizer)
algorithm.run()
front = algorithm.get_result()
# Plot frontier to file
- pareto_front = ScatterMatplotlib(plot_title='NSGAII for ZDT1', number_of_objectives=problem.number_of_objectives)
- pareto_front.plot(front, reference=problem.get_reference_front(), output='NSGAII-ZDT1', show=False)
+ pareto_front = FrontPlot(plot_title='NSGAII-ZDT1', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.to_html(filename='NSGAII-ZDT1')
# Save variables to file
- SolutionList.print_function_values_to_file(front, 'FUN.NSGAII.' + problem.get_name())
- SolutionList.print_variables_to_file(front, 'VAR.NSGAII.' + problem.get_name())
+ SolutionList.print_function_values_to_file(front, 'FUN.NSGAII.ZDT1')
+ SolutionList.print_variables_to_file(front, 'VAR.NSGAII.ZDT1')
print('Algorithm (continuous problem): ' + algorithm.get_name())
print('Problem: ' + problem.get_name())
diff --git a/examples/multiobjective/smpso_full_settings.py b/examples/multiobjective/smpso_dtlz1_5.py
similarity index 52%
rename from examples/multiobjective/smpso_full_settings.py
rename to examples/multiobjective/smpso_dtlz1_5.py
index 0b9014ea..c98fac9e 100644
--- a/examples/multiobjective/smpso_full_settings.py
+++ b/examples/multiobjective/smpso_dtlz1_5.py
@@ -1,14 +1,12 @@
from jmetal.algorithm import SMPSO
-from jmetal.component.observer import ProgressBarObserver, VisualizerObserver
-from jmetal.component.archive import CrowdingDistanceArchive
-from jmetal.problem import ZDT1
+from jmetal.problem import DTLZ1
from jmetal.operator import Polynomial
-from jmetal.util.graphic import ScatterMatplotlib
-from jmetal.util.solution_list_output import SolutionList
+from jmetal.component import ProgressBarObserver, CrowdingDistanceArchive
+from jmetal.util import FrontPlot, SolutionList
if __name__ == '__main__':
- problem = ZDT1()
+ problem = DTLZ1(number_of_objectives=5)
algorithm = SMPSO(
problem=problem,
@@ -18,21 +16,24 @@
leaders=CrowdingDistanceArchive(100)
)
- observer = VisualizerObserver(problem)
progress_bar = ProgressBarObserver(step=100, maximum=25000)
- algorithm.observable.register(observer=observer)
algorithm.observable.register(observer=progress_bar)
algorithm.run()
front = algorithm.get_result()
# Plot frontier to file
- pareto_front = ScatterMatplotlib(plot_title='SMPSO for ' + problem.get_name(), number_of_objectives=problem.number_of_objectives)
- pareto_front.plot(front, reference=problem.get_reference_front(), output='SMPSO-' + problem.get_name(), show=False)
+ pareto_front = FrontPlot(plot_title='SMPSO-DTLZ1-5', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.to_html(filename='SMPSO-DTLZ1-5')
+
+ pareto_front = FrontPlot(plot_title='SMPSO-DTLZ1-5-norm', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front, normalize=True)
+ pareto_front.to_html(filename='SMPSO-DTLZ1-5-norm')
# Save variables to file
- SolutionList.print_function_values_to_file(front, 'FUN.SMPSO.' + problem.get_name())
- SolutionList.print_variables_to_file(front, 'VAR.SMPSO.' + problem.get_name())
+ SolutionList.print_function_values_to_file(front, 'FUN.SMPSO.DTLZ1-5')
+ SolutionList.print_variables_to_file(front, 'VAR.SMPSO.DTLZ1-5')
print('Algorithm (continuous problem): ' + algorithm.get_name())
print('Problem: ' + problem.get_name())
diff --git a/examples/multiobjective/smpsorp_standard_settings.py b/examples/multiobjective/smpsorp_standard_settings.py
deleted file mode 100644
index 8d485365..00000000
--- a/examples/multiobjective/smpsorp_standard_settings.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from jmetal.algorithm import SMPSORP
-from jmetal.component.archive import CrowdingDistanceArchiveWithReferencePoint
-from jmetal.component.observer import ProgressBarObserver
-from jmetal.problem import ZDT1
-from jmetal.operator import Polynomial
-
-
-if __name__ == '__main__':
- problem = ZDT1()
- swarm_size = 100
-
- reference_points = [[0.0, 0.0]]
- archives_with_reference_points = []
-
- for point in reference_points:
- archives_with_reference_points.append(
- CrowdingDistanceArchiveWithReferencePoint(swarm_size, point)
- )
-
- algorithm = SMPSORP(
- problem=problem,
- swarm_size=swarm_size,
- max_evaluations=25000,
- mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
- reference_points=reference_points,
- leaders=archives_with_reference_points
- )
-
- progress_bar = ProgressBarObserver(step=swarm_size, maximum=25000)
- algorithm.observable.register(progress_bar)
-
- algorithm.run()
-
- print('Algorithm (continuous problem): ' + algorithm.get_name())
- print('Problem: ' + problem.get_name())
- print('Computing time: ' + str(algorithm.total_computing_time))
diff --git a/examples/multiobjective/smpsorp_zdt1.py b/examples/multiobjective/smpsorp_zdt1.py
new file mode 100644
index 00000000..6af6cb5f
--- /dev/null
+++ b/examples/multiobjective/smpsorp_zdt1.py
@@ -0,0 +1,56 @@
+from jmetal.algorithm import SMPSORP
+from jmetal.component import ProgressBarObserver, VisualizerObserver, CrowdingDistanceArchiveWithReferencePoint
+from jmetal.problem import ZDT1
+from jmetal.operator import Polynomial
+from jmetal.util import FrontPlot
+
+
+def points_to_solutions(points):
+ solutions = []
+ for i, _ in enumerate(points):
+ point = problem.create_solution()
+ point.objectives = points[i]
+ solutions.append(point)
+
+ return solutions
+
+
+if __name__ == '__main__':
+ problem = ZDT1(rf_path='../../resources/reference_front/ZDT1.pf')
+
+ swarm_size = 100
+
+ reference_points = [[0.5, 0.5], [0.2, 0.8]]
+ archives_with_reference_points = []
+
+ for point in reference_points:
+ archives_with_reference_points.append(
+ CrowdingDistanceArchiveWithReferencePoint(swarm_size, point)
+ )
+
+ algorithm = SMPSORP(
+ problem=problem,
+ swarm_size=swarm_size,
+ max_evaluations=25000,
+ mutation=Polynomial(probability=1.0/problem.number_of_variables, distribution_index=20),
+ reference_points=reference_points,
+ leaders=archives_with_reference_points
+ )
+
+ progress_bar = ProgressBarObserver(step=100, maximum=25000)
+ visualizer = VisualizerObserver()
+ algorithm.observable.register(observer=progress_bar)
+ algorithm.observable.register(observer=visualizer)
+
+ algorithm.run()
+ front = algorithm.get_result()
+
+ # Plot frontier to file
+ pareto_front = FrontPlot(plot_title='SMPSORP-ZDT1', axis_labels=problem.obj_labels)
+ pareto_front.plot(front, reference_front=problem.reference_front)
+ pareto_front.update(points_to_solutions(reference_points), legend='reference points')
+ pareto_front.to_html(filename='SMPSORP-ZDT1')
+
+ print('Algorithm (continuous problem): ' + algorithm.get_name())
+ print('Problem: ' + problem.get_name())
+ print('Computing time: ' + str(algorithm.total_computing_time))
\ No newline at end of file
diff --git a/jmetal/__init__.py b/jmetal/__init__.py
index 213468e7..0dcbe600 100644
--- a/jmetal/__init__.py
+++ b/jmetal/__init__.py
@@ -12,7 +12,7 @@
# create a file handler
file_handler = logging.FileHandler('jmetalpy.log', delay=True)
-file_handler.setLevel(logging.INFO)
+file_handler.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler()
# create a logging format
diff --git a/jmetal/algorithm/__init__.py b/jmetal/algorithm/__init__.py
index 839f7f76..258d9252 100644
--- a/jmetal/algorithm/__init__.py
+++ b/jmetal/algorithm/__init__.py
@@ -1,9 +1,10 @@
from .multiobjective.nsgaii import NSGAII
from .multiobjective.smpso import SMPSO, SMPSORP
from .singleobjective.evolutionaryalgorithm import ElitistEvolutionStrategy, NonElitistEvolutionStrategy
+from .multiobjective.randomSearch import RandomSearch
__all__ = [
'NSGAII',
'SMPSO', 'SMPSORP',
- 'ElitistEvolutionStrategy', 'NonElitistEvolutionStrategy'
+ 'ElitistEvolutionStrategy', 'NonElitistEvolutionStrategy','RandomSearch'
]
diff --git a/jmetal/algorithm/multiobjective/nsgaii.py b/jmetal/algorithm/multiobjective/nsgaii.py
index 5f8d99cc..746c4b4c 100644
--- a/jmetal/algorithm/multiobjective/nsgaii.py
+++ b/jmetal/algorithm/multiobjective/nsgaii.py
@@ -36,14 +36,14 @@ def __init__(self,
NSGA-II is a genetic algorithm (GA), i.e. it belongs to the evolutionary algorithms (EAs)
family. The implementation of NSGA-II provided in jMetalPy follows the evolutionary
- algorithm template described in the algorithm module (:py:mod:`algorithm`).
+ algorithm template described in the algorithm module (:py:mod:`jmetal.core.algorithm`).
:param problem: The problem to solve.
:param population_size: Size of the population.
:param max_evaluations: Maximum number of evaluations/iterations.
- :param mutation: Mutation operator (see :py:mod:`mutation`).
- :param crossover: Crossover operator (see :py:mod:`crosover`).
- :param selection: Selection operator (see :py:mod:`selection`).
+ :param mutation: Mutation operator (see :py:mod:`jmetal.operator.mutation`).
+ :param crossover: Crossover operator (see :py:mod:`jmetal.operator.crossover`).
+ :param selection: Selection operator (see :py:mod:`jmetal.operator.selection`).
:param evaluator: An evaluator object to evaluate the individuals of the population.
"""
super(NSGAII, self).__init__(
diff --git a/jmetal/algorithm/multiobjective/smpso.py b/jmetal/algorithm/multiobjective/smpso.py
index 26830c9d..bac7e31f 100644
--- a/jmetal/algorithm/multiobjective/smpso.py
+++ b/jmetal/algorithm/multiobjective/smpso.py
@@ -92,7 +92,8 @@ def update_progress(self) -> None:
observable_data = {'evaluations': self.evaluations,
'computing time': self.get_current_computing_time(),
- 'population': self.leaders.solution_list}
+ 'population': self.leaders.solution_list,
+ 'reference_front': self.problem.reference_front}
self.observable.notify_all(**observable_data)
@@ -264,7 +265,8 @@ def update_progress(self) -> None:
observable_data = {'evaluations': self.evaluations,
'computing time': self.get_current_computing_time(),
- 'population': self.get_result() + reference_points}
+ 'population': self.get_result() + reference_points,
+ 'reference_front': self.problem.reference_front}
self.observable.notify_all(**observable_data)
diff --git a/jmetal/algorithm/singleobjective/evolutionaryalgorithm.py b/jmetal/algorithm/singleobjective/evolutionaryalgorithm.py
index 12c60201..825b902a 100644
--- a/jmetal/algorithm/singleobjective/evolutionaryalgorithm.py
+++ b/jmetal/algorithm/singleobjective/evolutionaryalgorithm.py
@@ -134,7 +134,8 @@ def update_progress(self):
observable_data = {'evaluations': self.evaluations,
'computing time': self.get_current_computing_time(),
- 'population': self.population}
+ 'population': self.population,
+ 'reference_front': self.problem.reference_front}
self.observable.notify_all(**observable_data)
diff --git a/jmetal/component/observer.py b/jmetal/component/observer.py
index 5a8ff12e..4e791089 100644
--- a/jmetal/component/observer.py
+++ b/jmetal/component/observer.py
@@ -3,8 +3,7 @@
from tqdm import tqdm
-from jmetal.core.problem import Problem
-from jmetal.util.graphic import ScatterMatplotlib
+from jmetal.util.graphic import ScatterStreaming
from jmetal.core.observable import Observer
from jmetal.util.solution_list_output import SolutionList
@@ -22,6 +21,11 @@
class ProgressBarObserver(Observer):
def __init__(self, step: int, maximum: int, desc: str= 'Progress') -> None:
+ """ Show a smart progress meter with the number of evaluations and computing time.
+
+ :param step: Initial counter value.
+ :param maximum: Number of expected iterations.
+ :param desc: Prefix for the progressbar. """
self.progress_bar = tqdm(total=maximum, initial=step, ascii=True, desc=desc)
self.progress = step
self.step = step
@@ -38,6 +42,9 @@ def update(self, *args, **kwargs):
class BasicAlgorithmObserver(Observer):
def __init__(self, frequency: float = 1.0) -> None:
+ """ Show the number of evaluations, best fitness and computing time.
+
+ :param frequency: Display frequency. """
self.display_frequency = frequency
def update(self, *args, **kwargs):
@@ -56,15 +63,18 @@ def update(self, *args, **kwargs):
class WriteFrontToFileObserver(Observer):
def __init__(self, output_directory) -> None:
+ """ Write function values of the front into files.
+
+ :param output_directory: Output directory. Each front will be saved on a file `FUN.x`. """
self.counter = 0
self.directory = output_directory
if os.path.isdir(self.directory):
- jMetalPyLogger.warning('Directory {0} exists. Removing contents.'.format(self.directory))
+ jMetalPyLogger.warning('Directory {} exists. Removing contents.'.format(self.directory))
for file in os.listdir(self.directory):
os.remove('{0}/{1}'.format(self.directory, file))
else:
- jMetalPyLogger.warning('Directory {0} does not exist. Creating it.'.format(self.directory))
+ jMetalPyLogger.warning('Directory {} does not exist. Creating it.'.format(self.directory))
os.mkdir(self.directory)
def update(self, *args, **kwargs):
@@ -76,18 +86,19 @@ def update(self, *args, **kwargs):
class VisualizerObserver(Observer):
- def __init__(self, problem: Problem, replace: bool=True) -> None:
+ def __init__(self, replace: bool=True) -> None:
self.display_frequency = 1.0
self.replace = replace
- self.reference = problem.get_reference_front()
- self.plot = ScatterMatplotlib('jMetalPy', problem.number_of_objectives)
+ self.plot = ScatterStreaming(plot_title='jMetalPy')
def update(self, *args, **kwargs):
computing_time = kwargs['computing time']
evaluations = kwargs['evaluations']
+
front = kwargs['population']
+ reference_front = kwargs['reference_front']
title = '{0}, Eval: {1}, Time: {2}'.format('VisualizerObserver', evaluations, computing_time)
if (evaluations % self.display_frequency) == 0:
- self.plot.update(front, self.reference, new_title=title, persistence=self.replace)
+ self.plot.update(front, reference_front, rename_title=title, persistence=self.replace)
diff --git a/jmetal/component/quality_indicator.py b/jmetal/component/quality_indicator.py
index 4ce33951..9892ab15 100644
--- a/jmetal/component/quality_indicator.py
+++ b/jmetal/component/quality_indicator.py
@@ -22,6 +22,9 @@ class Metric:
def get_name(self) -> str:
return self.__class__.__name__
+ @abstractmethod
+ def compute(self, front: List[Solution]):
+ pass
class HyperVolume(Metric):
""" Hypervolume computation based on variant 3 of the algorithm in the paper:
@@ -171,7 +174,7 @@ def _sort_by_dimension(self, nodes, i):
# build a list of tuples of (point[i], node)
decorated = [(node.cargo[i], node) for node in nodes]
# sort by this value
- decorated.sort()
+ decorated.sort(key=lambda n: n[0])
# write back to original list
nodes[:] = [node for (_, node) in decorated]
diff --git a/jmetal/component/test/test_quality_indicator.py b/jmetal/component/test/test_quality_indicator.py
index 1874f4c3..a24ab7be 100644
--- a/jmetal/component/test/test_quality_indicator.py
+++ b/jmetal/component/test/test_quality_indicator.py
@@ -28,11 +28,11 @@ def test_should_hypervolume_return_5_0(self):
self.assertEqual(5.0, value)
def test_should_hypervolume_return_the_correct_value_when_applied_to_the_ZDT1_reference_front(self):
- problem = ZDT1()
+ problem = ZDT1(rf_path='resources/reference_front/ZDT1.pf')
reference_point = [1, 1]
hv = HyperVolume(reference_point)
- value = hv.compute(problem.get_reference_front())
+ value = hv.compute(problem.reference_front)
self.assertAlmostEqual(0.666, value, delta=0.001)
diff --git a/jmetal/core/problem.py b/jmetal/core/problem.py
index da10044b..cdf283ee 100644
--- a/jmetal/core/problem.py
+++ b/jmetal/core/problem.py
@@ -1,11 +1,9 @@
from abc import ABCMeta, abstractmethod
-from os.path import dirname, join
+from typing import Generic, TypeVar, List
from pathlib import Path
-from typing import Generic, TypeVar
import random
from jmetal.core.solution import BinarySolution, FloatSolution, IntegerSolution
-from jmetal.util.front_file import read_front_from_file_as_solutions
S = TypeVar('S')
@@ -18,18 +16,52 @@ class Problem(Generic[S]):
MINIMIZE = -1
MAXIMIZE = 1
- def __init__(self):
- self.number_of_variables = None
- self.number_of_objectives = None
- self.number_of_constraints = None
- self.obj_directions = []
+ def __init__(self, reference_front_path: str):
+ self.number_of_variables: int = None
+ self.number_of_objectives: int = None
+ self.number_of_constraints: int = None
- @abstractmethod
- def evaluate(self, solution: S) -> S:
- """ Evaluate a solution.
+ self.obj_directions: List[int] = []
+ self.obj_labels: List[str] = []
- :return: Evaluated solution. """
- pass
+ self.reference_front: List[S] = None
+ if reference_front_path:
+ self.reference_front = self.read_front_from_file_as_solutions(reference_front_path)
+
+ @staticmethod
+ def read_front_from_file(file_path: str) -> List[List[float]]:
+ """ Reads a front from a file and returns a list.
+
+ :return: List of solution points. """
+ front = []
+ if Path(file_path).is_file():
+ with open(file_path) as file:
+ for line in file:
+ vector = [float(x) for x in line.split()]
+ front.append(vector)
+ else:
+ raise Exception('Reference front file was not found at {}'.format(file_path))
+
+ return front
+
+ @staticmethod
+ def read_front_from_file_as_solutions(file_path: str) -> List[S]:
+ """ Reads a front from a file and returns a list of solution objects.
+
+ :return: List of solution objects. """
+ front = []
+ if Path(file_path).is_file():
+ with open(file_path) as file:
+ for line in file:
+ vector = [float(x) for x in line.split()]
+ solution = FloatSolution(2, 2, 0, [], [])
+ solution.objectives = vector
+
+ front.append(solution)
+ else:
+ raise Exception('Reference front file was not found at {}'.format(file_path))
+
+ return front
@abstractmethod
def create_solution(self) -> S:
@@ -38,25 +70,15 @@ def create_solution(self) -> S:
:return: Solution. """
pass
- def evaluate_constraints(self, solution: S):
- pass
-
- def get_reference_front(self) -> list:
- """ Get the reference front to the problem (if any).
- This method read front files (.pf) located in `jmetal/problem/reference_front/`, which must have the same
- name as the problem.
-
- :return: Front."""
- reference_front_path = 'problem/reference_front/{0}.pf'.format(self.get_name())
-
- front = []
- file_path = dirname(join(dirname(__file__)))
- computed_path = join(file_path, reference_front_path)
+ @abstractmethod
+ def evaluate(self, solution: S) -> S:
+ """ Evaluate a solution. For any new problem inheriting from :class:`Problem`, this method should be replaced.
- if Path(computed_path).is_file():
- front = read_front_from_file_as_solutions(computed_path)
+ :return: Evaluated solution. """
+ pass
- return front
+ def evaluate_constraints(self, solution: S):
+ pass
def get_name(self) -> str:
return self.__class__.__name__
@@ -67,12 +89,14 @@ class BinaryProblem(Problem[BinarySolution]):
__metaclass__ = ABCMeta
- @abstractmethod
- def evaluate(self, solution: BinarySolution) -> BinarySolution:
+ def __init__(self, rf_path: str = None):
+ super(BinaryProblem, self).__init__(reference_front_path=rf_path)
+
+ def create_solution(self) -> BinarySolution:
pass
@abstractmethod
- def create_solution(self) -> BinarySolution:
+ def evaluate(self, solution: BinarySolution) -> BinarySolution:
pass
@@ -81,15 +105,11 @@ class FloatProblem(Problem[FloatSolution]):
__metaclass__ = ABCMeta
- def __init__(self):
- super(FloatProblem, self).__init__()
+ def __init__(self, rf_path: str = None):
+ super(FloatProblem, self).__init__(reference_front_path=rf_path)
self.lower_bound = None
self.upper_bound = None
- @abstractmethod
- def evaluate(self, solution: FloatSolution) -> FloatSolution:
- pass
-
def create_solution(self) -> FloatSolution:
new_solution = FloatSolution(self.number_of_variables, self.number_of_objectives, self.number_of_constraints,
self.lower_bound, self.upper_bound)
@@ -98,21 +118,21 @@ def create_solution(self) -> FloatSolution:
return new_solution
+ @abstractmethod
+ def evaluate(self, solution: FloatSolution) -> FloatSolution:
+ pass
+
class IntegerProblem(Problem[IntegerSolution]):
""" Class representing integer problems. """
__metaclass__ = ABCMeta
- def __init__(self):
- super(IntegerProblem, self).__init__()
+ def __init__(self, rf_path: str = None):
+ super(IntegerProblem, self).__init__(reference_front_path=rf_path)
self.lower_bound = None
self.upper_bound = None
- @abstractmethod
- def evaluate(self, solution: IntegerSolution) -> IntegerSolution:
- pass
-
def create_solution(self) -> IntegerSolution:
new_solution = IntegerSolution(
self.number_of_variables,
@@ -125,3 +145,7 @@ def create_solution(self) -> IntegerSolution:
for i in range(self.number_of_variables)]
return new_solution
+
+ @abstractmethod
+ def evaluate(self, solution: IntegerSolution) -> IntegerSolution:
+ pass
diff --git a/jmetal/core/solution.py b/jmetal/core/solution.py
index 2ead41d7..6f568bdb 100644
--- a/jmetal/core/solution.py
+++ b/jmetal/core/solution.py
@@ -1,3 +1,4 @@
+from abc import ABCMeta
from typing import List, Generic, TypeVar
BitSet = List[bool]
@@ -7,10 +8,13 @@
class Solution(Generic[S]):
""" Class representing solutions """
+ __metaclass__ = ABCMeta
+
def __init__(self, number_of_variables: int, number_of_objectives: int, number_of_constraints: int = 0):
self.number_of_objectives = number_of_objectives
self.number_of_variables = number_of_variables
self.number_of_constraints = number_of_constraints
+
self.objectives = [0.0 for _ in range(self.number_of_objectives)]
self.variables = [[] for _ in range(self.number_of_variables)]
self.attributes = {}
@@ -28,7 +32,7 @@ def __str__(self) -> str:
class BinarySolution(Solution[BitSet]):
""" Class representing float solutions """
- def __init__(self, number_of_variables: int, number_of_objectives: int, number_of_constraints=0):
+ def __init__(self, number_of_variables: int, number_of_objectives: int, number_of_constraints: int=0):
super(BinarySolution, self).__init__(number_of_variables, number_of_objectives, number_of_constraints)
def __copy__(self):
diff --git a/jmetal/core/test/test_problem.py b/jmetal/core/test/test_problem.py
index 62954395..c77be09f 100644
--- a/jmetal/core/test/test_problem.py
+++ b/jmetal/core/test/test_problem.py
@@ -5,8 +5,8 @@
class FloatProblemTestCases(unittest.TestCase):
- class DummyFloatProblem(FloatProblem):
+ class DummyFloatProblem(FloatProblem):
def evaluate(self, solution: FloatSolution) -> FloatSolution:
pass
@@ -38,8 +38,8 @@ def test_should_create_solution_create_a_valid_solution(self) -> None:
class IntegerProblemTestCases(unittest.TestCase):
- class DummyIntegerProblem(IntegerProblem):
+ class DummyIntegerProblem(IntegerProblem):
def evaluate(self, solution: IntegerSolution) -> IntegerSolution:
pass
diff --git a/jmetal/core/test/test_solution.py b/jmetal/core/test/test_solution.py
index dd28c356..f150ff40 100644
--- a/jmetal/core/test/test_solution.py
+++ b/jmetal/core/test/test_solution.py
@@ -1,30 +1,7 @@
import copy
import unittest
-from jmetal.core.solution import BinarySolution, FloatSolution, Solution, IntegerSolution
-
-
-class SolutionTestCase(unittest.TestCase):
-
- def test_should_constructor_create_a_non_null_object(self) -> None:
- solution = Solution[int](3, 2)
- self.assertIsNotNone(solution)
-
- def test_should_constructor_create_a_valid_solution_of_ints(self) -> None:
- solution = Solution[int](3, 2)
- self.assertEqual(3, solution.number_of_variables)
- self.assertEqual(2, solution.number_of_objectives)
- self.assertEqual(0, solution.number_of_constraints)
-
- def test_should_constructor_create_a_valid_solution_of_floats(self) -> None:
- solution = Solution[float](3, 2, 5)
- self.assertEqual(3, solution.number_of_variables)
- self.assertEqual(2, solution.number_of_objectives)
- self.assertEqual(5, solution.number_of_constraints)
-
- def test_should_constructor_create_a_non_null_objective_list(self) -> None:
- solution = Solution[float](3, 2)
- self.assertIsNotNone(solution.objectives)
+from jmetal.core.solution import BinarySolution, FloatSolution, IntegerSolution
class BinarySolutionTestCase(unittest.TestCase):
@@ -100,17 +77,17 @@ def test_should_constructor_create_a_non_null_object(self) -> None:
self.assertIsNotNone(solution)
def test_should_default_constructor_create_a_valid_solution(self) -> None:
- solution = IntegerSolution(2, 3, 2, [0.0, 0.5], [1.0, 2.0])
+ solution = IntegerSolution(2, 3, 2, [0, 5], [1, 2])
self.assertEqual(2, solution.number_of_variables)
self.assertEqual(3, solution.number_of_objectives)
self.assertEqual(2, solution.number_of_constraints)
self.assertEqual(2, len(solution.variables))
self.assertEqual(3, len(solution.objectives))
- self.assertEqual([0.0, 0.5], solution.lower_bound)
- self.assertEqual([1.0, 2.0], solution.upper_bound)
+ self.assertEqual([0, 5], solution.lower_bound)
+ self.assertEqual([1, 2], solution.upper_bound)
def test_should_copy_work_properly(self) -> None:
- solution = IntegerSolution(2, 3, 2, [0.0, 0.5], [1.0, 2.0])
+ solution = IntegerSolution(2, 3, 2, [0, 5], [1, 2])
solution.variables = [1, 2]
solution.objectives = [0.16, -2.34, 9.25]
solution.attributes["attr"] = "value"
diff --git a/jmetal/operator/crossover.py b/jmetal/operator/crossover.py
index 5967d125..ff6e4be3 100644
--- a/jmetal/operator/crossover.py
+++ b/jmetal/operator/crossover.py
@@ -6,7 +6,7 @@
from jmetal.core.solution import Solution, FloatSolution, BinarySolution
"""
-.. module:: crosover
+.. module:: crossover
:platform: Unix, Windows
:synopsis: Module implementing crossover operators.
diff --git a/jmetal/problem/multiobjective/constrained.py b/jmetal/problem/multiobjective/constrained.py
index c0cf034e..a87fc71b 100644
--- a/jmetal/problem/multiobjective/constrained.py
+++ b/jmetal/problem/multiobjective/constrained.py
@@ -15,12 +15,15 @@
class Srinivas(FloatProblem):
""" Class representing problem Srinivas. """
- def __init__(self):
- super(Srinivas, self).__init__()
+ def __init__(self, rf_path: str=None):
+ super(Srinivas, self).__init__(rf_path=rf_path)
self.number_of_objectives = 2
self.number_of_variables = 2
self.number_of_constraints = 2
+ self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
+
self.lower_bound = [-20.0 for _ in range(self.number_of_variables)]
self.upper_bound = [20.0 for _ in range(self.number_of_variables)]
@@ -63,12 +66,15 @@ def get_name(self):
class Tanaka(FloatProblem):
""" Class representing problem Tanaka """
- def __init__(self):
- super(Tanaka, self).__init__()
+ def __init__(self, rf_path: str=None):
+ super(Tanaka, self).__init__(rf_path=rf_path)
self.number_of_objectives = 2
self.number_of_variables = 2
self.number_of_constraints = 2
+ self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
+
self.lower_bound = [10e-5 for _ in range(self.number_of_variables)]
self.upper_bound = [pi for _ in range(self.number_of_variables)]
diff --git a/jmetal/problem/multiobjective/dtlz.py b/jmetal/problem/multiobjective/dtlz.py
index 2a908641..00ac7c42 100644
--- a/jmetal/problem/multiobjective/dtlz.py
+++ b/jmetal/problem/multiobjective/dtlz.py
@@ -18,22 +18,21 @@ class DTLZ1(FloatProblem):
.. note:: Unconstrained problem. The default number of variables and objectives are, respectively, 7 and 3.
"""
- def __init__(self, number_of_variables: int = 7, number_of_objectives=3):
+ def __init__(self, number_of_variables: int = 7, number_of_objectives=3, rf_path: str=None):
""" :param number_of_variables: number of decision variables of the problem.
+ :param rf_path: Path to the reference front file (if any). Default to None.
"""
- super(DTLZ1, self).__init__()
+ super(DTLZ1, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = number_of_objectives
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE] * number_of_objectives
+ self.obj_labels = ['$ f_{} $'.format(i) for i in range(number_of_objectives)]
self.lower_bound = self.number_of_variables * [0.0]
self.upper_bound = self.number_of_variables * [1.0]
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
k = self.number_of_variables - self.number_of_objectives + 1
@@ -63,22 +62,21 @@ class DTLZ2(FloatProblem):
.. note:: Unconstrained problem. The default number of variables and objectives are, respectively, 12 and 3.
"""
- def __init__(self, number_of_variables: int = 12, number_of_objectives=3):
+ def __init__(self, number_of_variables: int = 12, number_of_objectives=3, rf_path: str=None):
""":param number_of_variables: number of decision variables of the problem
+ :param rf_path: Path to the reference front file (if any). Default to None.
"""
- super(DTLZ2, self).__init__()
+ super(DTLZ2, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = number_of_objectives
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE] * number_of_objectives
+ self.obj_labels = ['$ f_{} $'.format(i) for i in range(number_of_objectives)]
self.lower_bound = self.number_of_variables * [0.0]
self.upper_bound = self.number_of_variables * [1.0]
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
k = self.number_of_variables - self.number_of_objectives + 1
diff --git a/jmetal/problem/multiobjective/unconstrained.py b/jmetal/problem/multiobjective/unconstrained.py
index cd2ade73..1d2cc9d2 100644
--- a/jmetal/problem/multiobjective/unconstrained.py
+++ b/jmetal/problem/multiobjective/unconstrained.py
@@ -15,13 +15,14 @@
class Kursawe(FloatProblem):
""" Class representing problem Kursawe. """
- def __init__(self, number_of_variables: int = 3):
- super(Kursawe, self).__init__()
+ def __init__(self, number_of_variables: int=3, rf_path: str=None):
+ super(Kursawe, self).__init__(rf_path=rf_path)
self.number_of_objectives = 2
self.number_of_variables = number_of_variables
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = [-5.0 for _ in range(number_of_variables)]
self.upper_bound = [5.0 for _ in range(number_of_variables)]
@@ -49,13 +50,14 @@ def get_name(self):
class Fonseca(FloatProblem):
- def __init__(self):
- super(Fonseca, self).__init__()
+ def __init__(self, rf_path: str=None):
+ super(Fonseca, self).__init__(rf_path=rf_path)
self.number_of_variables = 3
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = self.number_of_variables * [-4]
self.upper_bound = self.number_of_variables * [4]
@@ -76,13 +78,14 @@ def get_name(self):
class Schaffer(FloatProblem):
- def __init__(self):
- super(Schaffer, self).__init__()
+ def __init__(self, rf_path: str=None):
+ super(Schaffer, self).__init__(rf_path=rf_path)
self.number_of_variables = 1
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = [-100000]
self.upper_bound = [100000]
@@ -104,13 +107,14 @@ def get_name(self):
class Viennet2(FloatProblem):
- def __init__(self):
- super(Viennet2, self).__init__()
+ def __init__(self, rf_path: str=None):
+ super(Viennet2, self).__init__(rf_path=rf_path)
self.number_of_variables = 2
self.number_of_objectives = 3
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)', 'f(z)']
self.lower_bound = self.number_of_variables * [-4]
self.upper_bound = self.number_of_variables * [4]
diff --git a/jmetal/problem/multiobjective/zdt.py b/jmetal/problem/multiobjective/zdt.py
index 8e05124a..5ca0dc31 100644
--- a/jmetal/problem/multiobjective/zdt.py
+++ b/jmetal/problem/multiobjective/zdt.py
@@ -19,23 +19,21 @@ class ZDT1(FloatProblem):
.. note:: Continuous problem having a convex Pareto front
"""
- def __init__(self, number_of_variables: int = 30):
+ def __init__(self, number_of_variables: int=30, rf_path: str=None):
+ """ :param number_of_variables: Number of decision variables of the problem.
+ :param rf_path: Path to the reference front file (if any). Default to None.
"""
- :param number_of_variables: Number of decision variables of the problem.
- """
- super(ZDT1, self).__init__()
+ super(ZDT1, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = self.number_of_variables * [0.0]
self.upper_bound = self.number_of_variables * [1.0]
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
g = self.__eval_g(solution)
h = self.__eval_h(solution.variables[0], g)
@@ -62,26 +60,27 @@ def get_name(self):
class ZDT2(FloatProblem):
- """ Problem ZDT2
+ """ Problem ZDT2.
.. note:: Bi-objective unconstrained problem. The default number of variables is 30.
.. note:: Continuous problem having a non-convex Pareto front
"""
- def __init__(self, number_of_variables: int = 30):
- super(ZDT2, self).__init__()
+ def __init__(self, number_of_variables: int = 30, rf_path: str=None):
+ """ :param number_of_variables: Number of decision variables of the problem.
+ :param rf_path: Path to the reference front file (if any). Default to None.
+ """
+ super(ZDT2, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = self.number_of_variables * [0.0]
self.upper_bound = self.number_of_variables * [1.0]
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
g = self.__eval_g(solution)
h = self.__eval_h(solution.variables[0], g)
@@ -108,26 +107,27 @@ def get_name(self):
class ZDT3(FloatProblem):
- """ Problem ZDT3
+ """ Problem ZDT3.
.. note:: Bi-objective unconstrained problem. The default number of variables is 30.
.. note:: Continuous problem having a partitioned Pareto front
"""
- def __init__(self, number_of_variables: int = 30):
- super(ZDT3, self).__init__()
+ def __init__(self, number_of_variables: int = 30, rf_path: str=None):
+ """ :param number_of_variables: Number of decision variables of the problem.
+ :param rf_path: Path to the reference front file (if any). Default to None.
+ """
+ super(ZDT3, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = self.number_of_variables * [0.0]
self.upper_bound = self.number_of_variables * [1.0]
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
g = self.__eval_g(solution)
h = self.__eval_h(solution.variables[0], g)
@@ -153,28 +153,29 @@ def get_name(self):
class ZDT4(FloatProblem):
- """ Problem ZDT4
+ """ Problem ZDT4.
.. note:: Bi-objective unconstrained problem. The default number of variables is 10.
.. note:: Continuous multi-modal problem having a convex Pareto front
"""
- def __init__(self, number_of_variables: int = 10):
- super(ZDT4, self).__init__()
+ def __init__(self, number_of_variables: int = 10, rf_path: str=None):
+ """ :param number_of_variables: Number of decision variables of the problem.
+ :param rf_path: Path to the reference front file (if any). Default to None.
+ """
+ super(ZDT4, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = self.number_of_variables * [-5.0]
self.upper_bound = self.number_of_variables * [5.0]
self.lower_bound[0] = 0.0
self.upper_bound[0] = 1.0
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
g = self.__eval_g(solution)
h = self.__eval_h(solution.variables[0], g)
@@ -202,26 +203,27 @@ def get_name(self):
class ZDT6(FloatProblem):
- """ Problem ZDT6
+ """ Problem ZDT6.
.. note:: Bi-objective unconstrained problem. The default number of variables is 10.
.. note:: Continuous problem having a non-convex Pareto front
"""
- def __init__(self, number_of_variables: int = 10):
- super(ZDT6, self).__init__()
+ def __init__(self, number_of_variables: int = 10, rf_path: str=None):
+ """ :param number_of_variables: Number of decision variables of the problem.
+ :param rf_path: Path to the reference front file (if any). Default to None.
+ """
+ super(ZDT6, self).__init__(rf_path=rf_path)
self.number_of_variables = number_of_variables
self.number_of_objectives = 2
self.number_of_constraints = 0
self.obj_directions = [self.MINIMIZE, self.MINIMIZE]
+ self.obj_labels = ['f(x)', 'f(y)']
self.lower_bound = self.number_of_variables * [0.0]
self.upper_bound = self.number_of_variables * [1.0]
- FloatSolution.lower_bound = self.lower_bound
- FloatSolution.upper_bound = self.upper_bound
-
def evaluate(self, solution: FloatSolution) -> FloatSolution:
g = self.__eval_g(solution)
h = self.__eval_h(solution.variables[0], g)
diff --git a/jmetal/problem/singleobjective/unconstrained.py b/jmetal/problem/singleobjective/unconstrained.py
index f27bcaf4..36ea0649 100644
--- a/jmetal/problem/singleobjective/unconstrained.py
+++ b/jmetal/problem/singleobjective/unconstrained.py
@@ -14,13 +14,16 @@
class OneMax(BinaryProblem):
- def __init__(self, number_of_bits: int = 256):
- super(OneMax, self).__init__()
+ def __init__(self, number_of_bits: int=256, rf_path: str=None):
+ super(OneMax, self).__init__(rf_path=rf_path)
self.number_of_bits = number_of_bits
self.number_of_objectives = 1
self.number_of_variables = 1
self.number_of_constraints = 0
+ self.obj_directions = [self.MINIMIZE]
+ self.obj_labels = ['f(x)']
+
def evaluate(self, solution: BinarySolution) -> BinarySolution:
counter_of_ones = 0
for bits in solution.variables[0]:
@@ -43,12 +46,15 @@ def get_name(self) -> str:
class Sphere(FloatProblem):
- def __init__(self, number_of_variables: int = 10):
- super(Sphere, self).__init__()
+ def __init__(self, number_of_variables: int=10, rf_path: str=None):
+ super(Sphere, self).__init__(rf_path=rf_path)
self.number_of_objectives = 1
self.number_of_variables = number_of_variables
self.number_of_constraints = 0
+ self.obj_directions = [self.MINIMIZE]
+ self.obj_labels = ['f(x)']
+
self.lower_bound = [-5.12 for _ in range(number_of_variables)]
self.upper_bound = [5.12 for _ in range(number_of_variables)]
diff --git a/jmetal/util/__init__.py b/jmetal/util/__init__.py
index 8869c71d..68f54013 100644
--- a/jmetal/util/__init__.py
+++ b/jmetal/util/__init__.py
@@ -1,11 +1,9 @@
-from .front_file import read_front_from_file, read_front_from_file_as_solutions
-from .graphic import ScatterBokeh, ScatterMatplotlib
-from .laboratory import experiment, display
+from .graphic import FrontPlot, ScatterStreaming
+from .laboratory import Experiment
from .solution_list_output import SolutionList
__all__ = [
- 'read_front_from_file', 'read_front_from_file_as_solutions',
- 'ScatterBokeh', 'ScatterMatplotlib',
- 'experiment', 'display',
+ 'FrontPlot', 'ScatterStreaming',
+ 'Experiment',
'SolutionList'
]
diff --git a/jmetal/util/front_file.py b/jmetal/util/front_file.py
deleted file mode 100644
index 362007cb..00000000
--- a/jmetal/util/front_file.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from jmetal.core.solution import FloatSolution
-
-"""
-.. module:: Front file
- :platform: Unix, Windows
- :synopsis: Utils to read reference frontiers from files.
-
-.. moduleauthor:: Antonio J. Nebro
-"""
-
-
-def read_front_from_file(file_name: str):
- """ Reads a front from a file and returns a list.
- """
- front = []
- with open(file_name) as file:
- for line in file:
- vector = [float(x) for x in line.split()]
- front.append(vector)
- return front
-
-
-def read_front_from_file_as_solutions(file_path: str):
- """ Reads a front from a file and returns a list of solution objects.
-
- :return: List of solution objects.
- """
- front = []
- with open(file_path) as file:
- for line in file:
- vector = [float(x) for x in line.split()]
- solution = FloatSolution(2, 2, 0, [], [])
- solution.objectives = vector
-
- front.append(solution)
-
- return front
diff --git a/jmetal/util/graphic.py b/jmetal/util/graphic.py
index 0619ce03..41a6c0bd 100644
--- a/jmetal/util/graphic.py
+++ b/jmetal/util/graphic.py
@@ -1,29 +1,21 @@
import logging
-import warnings
from abc import ABCMeta
-from typing import TypeVar, List, Tuple
-
-from bokeh.embed import file_html
-from bokeh.resources import CDN
-from bokeh.client import ClientSession
-from bokeh.io import curdoc, reset_output
-from bokeh.layouts import column, row
-from bokeh.models import HoverTool, ColumnDataSource, TapTool, CustomJS, WheelZoomTool
-from bokeh.plotting import Figure
-from mpl_toolkits.mplot3d import Axes3D
-import matplotlib.pyplot as plt
-
-from jmetal.core.solution import Solution
+from string import Template
+from typing import TypeVar, List
-warnings.filterwarnings("ignore", ".*GUI is implemented.*")
+import matplotlib.pyplot as plt
+from mpl_toolkits.mplot3d import Axes3D
+from plotly import graph_objs as go
+from plotly.offline import plot
+from pandas import DataFrame, merge
jMetalPyLogger = logging.getLogger('jMetalPy')
S = TypeVar('S')
"""
-.. module:: Visualization
+.. module:: visualization
:platform: Unix, Windows
- :synopsis: Classes for plotting solutions.
+ :synopsis: Classes for plotting fronts.
.. moduleauthor:: Antonio Benítez-Hidalgo
"""
@@ -33,129 +25,117 @@ class Plot:
__metaclass__ = ABCMeta
- def __init__(self, plot_title: str, number_of_objectives: int,
- xaxis_label: str='', yaxis_label: str='', zaxis_label: str=''):
+ def __init__(self, plot_title: str, axis_labels: list):
self.plot_title = plot_title
- self.number_of_objectives = number_of_objectives
+ self.axis_labels = axis_labels
+
+ self.number_of_objectives: int = None
- self.xaxis_label = xaxis_label
- self.yaxis_label = yaxis_label
- self.zaxis_label = zaxis_label
+ @staticmethod
+ def get_objectives(front: List[S]) -> DataFrame:
+ """ Get objectives for each solution of the front.
- def get_objectives(self, front: List[S]) -> Tuple[list, list, list]:
+ :param front: List of solutions.
+ :return: Pandas dataframe with one column for each objective and one row for each solution. """
if front is None:
raise Exception('Front is none!')
- points = list(solution.objectives for solution in front)
+ return DataFrame(list(solution.objectives for solution in front))
- x_values, y_values = [point[0] for point in points], [point[1] for point in points]
- try:
- z_values = [point[2] for point in points]
- except IndexError:
- z_values = [0]*len(points)
-
- return x_values, y_values, z_values
+class ScatterStreaming(Plot):
+ def __init__(self, plot_title: str, axis_labels: list = None):
+ """ Creates a new :class:`ScatterStreaming` instance. Suitable for problems with 2 or 3 objectives in streaming.
-class ScatterMatplotlib(Plot):
+ :param plot_title: Title of the diagram.
+ :param axis_labels: List of axis labels. """
+ super(ScatterStreaming, self).__init__(plot_title, axis_labels)
- def __init__(self, plot_title: str, number_of_objectives: int):
- """ Creates a new :class:`ScatterPlot` instance. Suitable for problems with 2 or 3 objectives.
+ import warnings
+ warnings.filterwarnings("ignore", ".*GUI is implemented.*")
- :param plot_title: Title of the scatter diagram.
- :param number_of_objectives: Number of objectives to be used (2D/3D).
- """
- super(ScatterMatplotlib, self).__init__(plot_title, number_of_objectives)
-
- # Initialize a plot
self.fig = plt.figure()
self.sc = None
self.axis = None
- self.__initialize()
-
- def __initialize(self) -> None:
- """ Initialize the scatter plot for the first time. """
- jMetalPyLogger.info("Generating plot...")
+ def plot(self, front: List[S], reference_front: List[S], filename: str = '', show: bool = True) -> None:
+ """ Plot a front of solutions (2D or 3D).
- # Initialize a plot
- self.fig.canvas.set_window_title('jMetalPy')
+ :param front: List of solutions.
+ :param reference_front: Reference solution list (if any).
+ :param filename: If specified, save the plot into a file.
+ :param show: If True, show the final diagram (default to True). """
+ objectives = self.get_objectives(front)
- if self.number_of_objectives == 2:
- self.axis = self.fig.add_subplot(111)
-
- # Stylize axis
- self.axis.spines['top'].set_visible(False)
- self.axis.spines['right'].set_visible(False)
- self.axis.get_xaxis().tick_bottom()
- self.axis.get_yaxis().tick_left()
- else:
- self.axis = Axes3D(self.fig)
- self.axis.autoscale(enable=True, axis='both')
-
- self.axis.set_autoscale_on(True)
- self.axis.autoscale_view(True, True, True)
+ # Initialize plot
+ self.number_of_objectives = objectives.shape[1]
+ self.__initialize()
- # Style options
- self.axis.grid(color='#f0f0f5', linestyle='-', linewidth=1, alpha=0.5)
- self.fig.suptitle(self.plot_title, fontsize=13)
+ if reference_front:
+ jMetalPyLogger.info('Reference front found')
+ ref_objectives = self.get_objectives(reference_front)
- jMetalPyLogger.info("Plot initialized")
+ if self.number_of_objectives == 2:
+ self.__plot(ref_objectives[0], ref_objectives[1], None,
+ color='#323232', marker='*', markersize=3)
+ else:
+ self.__plot(ref_objectives[0], ref_objectives[1], ref_objectives[2],
+ color='#323232', marker='*', markersize=3)
- def __plot(self, x_values, y_values, z_values, color: str = '#98FB98', marker: str = 'o', msize: int = 3):
if self.number_of_objectives == 2:
- self.sc, = self.axis.plot(x_values, y_values,
- color=color, marker=marker, markersize=msize, ls='None', picker=10)
+ self.__plot(objectives[0], objectives[1], None, color='#98FB98', marker='o', markersize=3)
else:
- self.sc, = self.axis.plot(x_values, y_values, z_values,
- color=color, marker=marker, markersize=msize, ls='None', picker=10)
-
- def plot(self, front: List[S], reference: List[S], output: str= '', show: bool=True) -> None:
- if reference:
- jMetalPyLogger.info('Reference front found')
- ref_x_values, ref_y_values, ref_z_values = self.get_objectives(reference)
- self.__plot(ref_x_values, ref_y_values, ref_z_values, color='#323232', marker='*')
+ self.__plot(objectives[0], objectives[1], objectives[2], color='#98FB98', marker='o', markersize=3)
- x_values, y_values, z_values = self.get_objectives(front)
- self.__plot(x_values, y_values, z_values)
-
- if output:
- self.__save(output)
+ if filename:
+ self.fig.savefig(filename, format='png', dpi=200)
if show:
- self.fig.canvas.mpl_connect('pick_event', lambda event: self.__pick_handler(event, front))
+ self.fig.canvas.mpl_connect('pick_event', lambda event: self.__pick_handler(front, event))
plt.show()
- def update(self, front: List[S], reference: List[S], new_title: str= '', persistence: bool=True) -> None:
+ def update(self, front: List[S], reference_front: List[S], rename_title: str = '',
+ persistence: bool = True) -> None:
+ """ Update an already created plot.
+
+ :param front: List of solutions.
+ :param reference_front: Reference solution list (if any).
+ :param rename_title: New title of the plot.
+ :param persistence: If True, keep old points; else, replace them with new values.
+ """
if self.sc is None:
- jMetalPyLogger.warning("Plot is none! Generating first plot...")
- self.plot(front, reference, show=False)
+ jMetalPyLogger.warning('Plot must be initialized first.')
+ self.plot(front, reference_front, show=False)
+ return
- x_values, y_values, z_values = self.get_objectives(front)
+ objectives = self.get_objectives(front)
if persistence:
# Replace with new points
- self.sc.set_data(x_values, y_values)
+ self.sc.set_data(objectives[0], objectives[1])
if self.number_of_objectives == 3:
- self.sc.set_3d_properties(z_values)
+ self.sc.set_3d_properties(objectives[2])
else:
# Add new points
- self.__plot(x_values, y_values, z_values)
+ if self.number_of_objectives == 2:
+ self.__plot(objectives[0], objectives[1], None, color='#98FB98', marker='o', markersize=3)
+ else:
+ self.__plot(objectives[0], objectives[1], objectives[2], color='#98FB98', marker='o', markersize=3)
# Also, add event handler
event_handler = \
- self.fig.canvas.mpl_connect('pick_event', lambda event: self.__pick_handler(event, front))
+ self.fig.canvas.mpl_connect('pick_event', lambda event: self.__pick_handler(front, event))
# Update title with new times and evaluations
- self.fig.suptitle(new_title, fontsize=13)
+ self.fig.suptitle(rename_title, fontsize=13)
# Re-align the axis
self.axis.relim()
self.axis.autoscale_view(True, True, True)
- # Draw
try:
+ # Draw
self.fig.canvas.draw()
except KeyboardInterrupt:
pass
@@ -165,22 +145,43 @@ def update(self, front: List[S], reference: List[S], new_title: str= '', persist
# Disconnect the pick event for the next update
self.fig.canvas.mpl_disconnect(event_handler)
- def __save(self, file_name: str, fmt: str = 'png', dpi: int = 200):
- supported_formats = ["eps", "jpeg", "jpg", "pdf", "pgf", "png", "ps",
- "raw", "rgba", "svg", "svgz", "tif", "tiff"]
+ def __initialize(self) -> None:
+ """ Initialize the scatter plot for the first time. """
+ jMetalPyLogger.info('Generating plot')
- if fmt not in supported_formats:
- raise Exception('{0} is not a valid format! Use one of these instead: {0}'.format(fmt, supported_formats))
+ # Initialize a plot
+ self.fig.canvas.set_window_title('jMetalPy')
+
+ if self.number_of_objectives == 2:
+ self.axis = self.fig.add_subplot(111)
- self.fig.savefig(file_name + '.' + fmt, format=fmt, dpi=dpi)
+ # Stylize axis
+ self.axis.spines['top'].set_visible(False)
+ self.axis.spines['right'].set_visible(False)
+ self.axis.get_xaxis().tick_bottom()
+ self.axis.get_yaxis().tick_left()
+ elif self.number_of_objectives == 3:
+ self.axis = Axes3D(self.fig)
+ self.axis.autoscale(enable=True, axis='both')
+ else:
+ raise Exception('Number of objectives must be either 2 or 3')
+
+ self.axis.set_autoscale_on(True)
+ self.axis.autoscale_view(True, True, True)
+
+ # Style options
+ self.axis.grid(color='#f0f0f5', linestyle='-', linewidth=1, alpha=0.5)
+ self.fig.suptitle(self.plot_title, fontsize=13)
- def __retrieve_info(self, x_val: float, y_val: float, solution: Solution) -> None:
- jMetalPyLogger.info("Output file: " + '{0}-{1}'.format(x_val, y_val))
+ jMetalPyLogger.info('Plot initialized')
- with open('{0}-{1}'.format(x_val, y_val), 'w') as of:
- of.write(solution.__str__())
+ def __plot(self, x_values, y_values, z_values, **kwargs) -> None:
+ if self.number_of_objectives == 2:
+ self.sc, = self.axis.plot(x_values, y_values, ls='None', picker=10, **kwargs)
+ else:
+ self.sc, = self.axis.plot(x_values, y_values, z_values, ls='None', picker=10, **kwargs)
- def __pick_handler(self, event, front: List[S]):
+ def __pick_handler(self, front: List[S], event):
""" Handler for picking points from the plot. """
line, ind = event.artist, event.ind[0]
x, y = line.get_xdata(), line.get_ydata()
@@ -191,132 +192,209 @@ def __pick_handler(self, event, front: List[S]):
if solution.objectives[0] == x[ind] and solution.objectives[1] == y[ind]), None)
if sol is not None:
- self.__retrieve_info(x[ind], y[ind], sol)
+ with open('{0}-{1}'.format(x[ind], y[ind]), 'w') as of:
+ of.write(sol.__str__())
else:
jMetalPyLogger.warning('Solution is none')
return True
-class ScatterBokeh(Plot):
+class FrontPlot(Plot):
- def __init__(self, plot_title: str, number_of_objectives: int, ws_url: str='localhost:5006'):
- super(ScatterBokeh, self).__init__(plot_title, number_of_objectives)
+ def __init__(self, plot_title: str, axis_labels: list = None):
+ """ Creates a new :class:`FrontPlot` instance. Suitable for problems with 2 or more objectives.
- if self.number_of_objectives == 2:
- self.source = ColumnDataSource(data=dict(x=[], y=[], str=[]))
- elif self.number_of_objectives == 3:
- self.source = ColumnDataSource(data=dict(x=[], y=[], z=[], str=[]))
- else:
- raise Exception('Wrong number of objectives: {0}'.format(number_of_objectives))
+ :param plot_title: Title of the graph.
+ :param axis_labels: List of axis labels. """
+ super(FrontPlot, self).__init__(plot_title, axis_labels)
- self.client = ClientSession(websocket_url='ws://{0}/ws'.format(ws_url))
- self.doc = curdoc()
- self.doc.title = plot_title
- self.figure_xy = None
- self.figure_xz = None
- self.figure_yz = None
+ self.figure: go.Figure = None
+ self.layout = None
+ self.data = []
- self.__initialize()
+ def plot(self, front: List[S], reference_front: List[S] = None, normalize: bool = False) -> None:
+ """ Plot a front of solutions (2D, 3D or parallel coordinates).
- def __initialize(self) -> None:
- """ Set-up tools for plot. """
- code = '''
- selected = source.selected['1d']['indices'][0]
- var str = source.front.str[selected]
- alert(str)
- '''
-
- callback = CustomJS(args=dict(source=self.source), code=code)
- self.plot_tools = [TapTool(callback=callback), WheelZoomTool(), 'save', 'pan',
- HoverTool(tooltips=[('index', '$index'), ('(x,y)', '($x, $y)')])]
-
- def plot(self, front: List[S], reference: List[S]=None, output: str= '', show: bool=True) -> None:
- # This is important to purge front (if any) between calls
- reset_output()
-
- # Set up figure
- self.figure_xy = Figure(output_backend='webgl',
- sizing_mode='scale_width',
- title=self.plot_title,
- tools=self.plot_tools)
- self.figure_xy.scatter(x='x', y='y', legend='solution', fill_alpha=0.7, source=self.source)
- self.figure_xy.xaxis.axis_label = self.xaxis_label
- self.figure_xy.yaxis.axis_label = self.yaxis_label
-
- x_values, y_values, z_values = self.get_objectives(front)
-
- if self.number_of_objectives == 2:
- # Plot reference solution list (if any)
- if reference:
- ref_x_values, ref_y_values, _ = self.get_objectives(reference)
- self.figure_xy.line(x=ref_x_values, y=ref_y_values, legend='reference', color='green')
-
- # Push front to server
- self.source.stream({'x': x_values, 'y': y_values, 'str': [s.__str__() for s in front]})
- self.doc.add_root(column(self.figure_xy))
- else:
- # Add new figures for each axis
- self.figure_xz = Figure(title='xz', output_backend='webgl',
- sizing_mode='scale_width', tools=self.plot_tools)
- self.figure_xz.scatter(x='x', y='z', legend='solution', fill_alpha=0.7, source=self.source)
- self.figure_xz.xaxis.axis_label = self.xaxis_label
- self.figure_xz.yaxis.axis_label = self.zaxis_label
-
- self.figure_yz = Figure(title='yz', output_backend='webgl',
- sizing_mode='scale_width', tools=self.plot_tools)
- self.figure_yz.scatter(x='y', y='z', legend='solution', fill_alpha=0.7, source=self.source)
- self.figure_yz.xaxis.axis_label = self.yaxis_label
- self.figure_yz.yaxis.axis_label = self.zaxis_label
-
- # Plot reference solution list (if any)
- if reference:
- ref_x_values, ref_y_values, ref_z_values = self.get_objectives(reference)
- self.figure_xy.line(x=ref_x_values, y=ref_y_values, legend='reference', color='green')
- self.figure_xz.line(x=ref_x_values, y=ref_z_values, legend='reference', color='green')
- self.figure_yz.line(x=ref_y_values, y=ref_z_values, legend='reference', color='green')
-
- # Push front to server
- self.source.stream({'x': x_values, 'y': y_values, 'z': z_values, 'str': [s.__str__() for s in front]})
- self.doc.add_root(row(self.figure_xy, self.figure_xz, self.figure_yz))
-
- self.client.push(self.doc)
-
- if output:
- self.__save(output)
- if show:
- self.client.show()
-
- def update(self, front: List[S], reference: List[S], new_title: str= '', persistence: bool=False) -> None:
- # Check if plot has not been initialized first
- if self.figure_xy is None:
- self.plot(front, reference)
-
- if not persistence:
- rollover = len(front)
- else:
- rollover = None
-
- self.figure_xy.title.text = new_title
- x_values, y_values, z_values = self.get_objectives(front)
+ :param front: List of solutions.
+ :param reference_front: Reference solution list (if any).
+ :param normalize: Normalize the input front between 0 and 1 (for problems with more than 3 objectives). """
+ self.__initialize()
- if self.number_of_objectives == 2:
- self.source.stream({'x': x_values, 'y': y_values, 'str': [s.__str__() for s in front]},
- rollover=rollover)
+ if reference_front:
+ objectives = self.get_objectives(reference_front)
+ trace = self.__generate_trace(objectives=objectives, legend='reference front', normalize=normalize,
+ color='rgb(2, 130, 242)')
+ self.data.append(trace)
+
+ objectives = self.get_objectives(front)
+ metadata = list(solution.__str__() for solution in front)
+ trace = self.__generate_trace(objectives=objectives, metadata=metadata, legend='front', normalize=normalize,
+ symbol='diamond-open')
+ self.data.append(trace)
+
+ self.figure = go.Figure(data=self.data, layout=self.layout)
+
+ def update(self, data: List[S], normalize: bool = False, legend: str = '') -> None:
+ """ Update an already created graph with new data.
+
+ :param data: List of solutions to be included.
+ :param legend: Legend to be included.
+ :param normalize: Normalize the input front between 0 and 1 (for problems with more than 3 objectives). """
+ if self.figure is None:
+ jMetalPyLogger.warning('Plot must be initialized first.')
+ self.plot(data, reference_front=None, normalize=normalize)
+ return
+
+ objectives = self.get_objectives(data)
+ new_data = self.__generate_trace(objectives=objectives, legend=legend, normalize=normalize,
+ color='rgb(255, 170, 0)')
+ self.data.append(new_data)
+
+ self.figure = go.Figure(data=self.data, layout=self.layout)
+
+ def to_html(self, filename: str='front') -> str:
+ """ Export the graph to an interactive HTML (solutions can be selected to show some metadata).
+
+ :param filename: Output file name.
+ :return: Script as string. """
+ html_string = '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ''' + self.export(include_plotlyjs=False) + '''
+
+
+ '''
+
+ with open(filename + '.html', 'w') as outf:
+ outf.write(html_string)
+
+ return html_string
+
+ def export(self, filename: str = '', include_plotlyjs: bool = False) -> str:
+ """ Export as a `div` for embedding the graph in an HTML file.
+
+ :param filename: Output file name (if desired, default to None).
+ :param include_plotlyjs: If True, include plot.ly JS script (default to False).
+ :return: Script as string. """
+ script = plot(self.figure, output_type='div', include_plotlyjs=include_plotlyjs, show_link=False)
+
+ if filename:
+ with open(filename + '.html', 'w') as outf:
+ outf.write(script)
+
+ return script
+
+ def __initialize(self):
+ """ Initialize the graph for the first time. """
+ jMetalPyLogger.info('Generating graph')
+
+ self.layout = go.Layout(
+ margin=dict(l=80, r=80, b=80, t=150),
+ height=800,
+ title=self.plot_title,
+ scene=dict(
+ xaxis=dict(title=self.axis_labels[0:1][0] if self.axis_labels[0:1] else None),
+ yaxis=dict(title=self.axis_labels[1:2][0] if self.axis_labels[1:2] else None),
+ zaxis=dict(title=self.axis_labels[2:3][0] if self.axis_labels[2:3] else None)
+ ),
+ hovermode='closest'
+ )
+
+ def __generate_trace(self, objectives: DataFrame, metadata: list = None, legend: str = '', normalize: bool = False,
+ **kwargs):
+ number_of_objectives = objectives.shape[1]
+
+ if normalize:
+ objectives = (objectives - objectives.min()) / (objectives.max() - objectives.min())
+
+ marker = dict(
+ color='rgb(127, 127, 127)',
+ size=3,
+ symbol='x',
+ line=dict(
+ color='rgb(204, 204, 204)',
+ width=1
+ ),
+ opacity=0.8
+ )
+ marker.update(**kwargs)
+
+ if number_of_objectives == 2:
+ trace = go.Scattergl(
+ x=objectives[0],
+ y=objectives[1],
+ mode='markers',
+ marker=marker,
+ name=legend,
+ customdata=metadata
+ )
+ elif number_of_objectives == 3:
+ trace = go.Scatter3d(
+ x=objectives[0],
+ y=objectives[1],
+ z=objectives[2],
+ mode='markers',
+ marker=marker,
+ name=legend,
+ customdata=metadata
+ )
else:
- self.source.stream({'x': x_values, 'y': y_values, 'z': z_values, 'str': [s.__str__() for s in front]},
- rollover=rollover)
-
- def __save(self, file_name: str):
- # env = Environment(loader=FileSystemLoader(BASE_PATH + '/util/'))
- # env.filters['json'] = lambda obj: Markup(json.dumps(obj))
-
- html = file_html(models=self.doc, resources=CDN)
- with open(file_name + '.html', 'w') as of:
- of.write(html)
-
- def disconnect(self):
- if self.is_connected():
- self.client.close()
-
- def is_connected(self) -> bool:
- return self.client.connected
+ dimensions = list()
+ for column in objectives:
+ dimensions.append(
+ dict(range=[0, 1],
+ label=self.axis_labels[column:column+1][0] if self.axis_labels[column:column+1] else None,
+ values=objectives[column])
+ )
+
+ trace = go.Parcoords(
+ line=dict(color='blue'),
+ dimensions=dimensions,
+ name=legend,
+ )
+
+ return trace
+
+ def __save(self, filename: str = 'front', show: bool = False) -> None:
+ """ Save the graph. """
+ plot(self.figure, filename=filename + '.html', auto_open=show, show_link=False)
diff --git a/jmetal/util/laboratory.py b/jmetal/util/laboratory.py
index db00fb6f..15092c6f 100644
--- a/jmetal/util/laboratory.py
+++ b/jmetal/util/laboratory.py
@@ -12,51 +12,57 @@
"""
-def experiment(algorithm_list: list, metric_list: list, problem_list: list, g_params: dict=None, m_workers: int=3):
- """ :param algorithm_list: List of algorithms as Tuple(Algorithm, dic() with parameters).
- :param metric_list: List of metrics.
- :param problem_list: List of problems as Tuple(Problem, dic() with parameters).
- :param g_params: Global parameters (will override those from algorithm_list).
- :param m_workers: Maximum number of workers for ProcessPoolExecutor.
- :return: Stats.
- """
-
- with ProcessPoolExecutor(max_workers=m_workers) as pool:
- result = dict()
-
- for p_index, (problem, problem_params) in enumerate(problem_list):
- if isinstance(problem, type):
- jMetalPyLogger.debug('Problem is not instantiated by default')
- problem = problem(**problem_params)
-
- for a_index, (algorithm, algorithm_params) in enumerate(algorithm_list):
- if g_params:
- algorithm_params.update(g_params)
- if isinstance(algorithm, type):
- jMetalPyLogger.debug('Algorithm {} is not instantiated by default'.format(algorithm))
- algorithm_list[a_index] = (algorithm(problem=problem, **algorithm_params), {})
-
- jMetalPyLogger.info('Running experiment: problem {0}, algorithm {1}'.format(problem, algorithm))
-
- pool.submit(algorithm_list[a_index][0].run())
+class Experiment:
- jMetalPyLogger.debug('Waiting')
-
- # Wait until all computation is done for this problem
- pool.shutdown(wait=True)
+ def __init__(self, algorithm_list: list, n_runs: int = 1, m_workers: int = 6):
+ """ :param algorithm_list: List of algorithms as Tuple(Algorithm, dic() with parameters).
+ :param m_workers: Maximum number of workers for ProcessPoolExecutor. """
+ self.algorithm_list = algorithm_list
- for algorithm, _ in algorithm_list:
- front = algorithm.get_result()
- result[algorithm.get_name()] = {'front': front,
- 'problem': algorithm.problem.get_name(),
- 'time': algorithm.total_computing_time}
+ self.n_runs = n_runs
+ self.m_workers = m_workers
+ self.experiment_list = list()
- for metric in metric_list:
- result[algorithm.get_name()].setdefault('metric', dict()).update({metric.get_name(): metric.compute(front)})
+ def run(self) -> None:
+ """ Run the experiment. """
+ self.__configure_algorithm_list()
- return result
+ with ProcessPoolExecutor(max_workers=self.m_workers) as pool:
+ for name, algorithm, n_run in self.experiment_list:
+ jMetalPyLogger.info('Running experiment {0}'.format(
+ name, algorithm.problem, n_run)
+ )
+ pool.submit(algorithm.run())
-def display(table: dict):
- for k, v in table.items():
- print('{0}: {1}'.format(k, v['metric']))
+ # Wait until all computation is done for this problem
+ jMetalPyLogger.debug('Waiting')
+ pool.shutdown(wait=True)
+
+ def compute_metrics(self, metric_list: list) -> dict:
+ """ :param metric_list: List of metrics. Each metric should inherit from :py:class:`Metric` or, at least,
+ contain a method `compute`. """
+ results = dict()
+
+ for name, algorithm, n_run in self.experiment_list:
+ results[name] = {}
+
+ for metric in metric_list:
+ results[name].setdefault('metric', dict()).update(
+ {metric.get_name(): metric.compute(algorithm.get_result())}
+ )
+
+ return results
+
+ def export_to_file(self, base_directory: str = 'experiment', function_values_filename: str = 'FUN',
+ variables_filename: str = 'VAR'):
+ for tag, algorithm, run in self.experiment_list:
+ # todo Save VAR and FUN to files
+ pass
+
+ def __configure_algorithm_list(self):
+ """ Configure the algorithm list, by making a triple of (name, algorithm, run). """
+ for n_run in range(self.n_runs):
+ for tag, algorithm in self.algorithm_list:
+ name = '{0}.{1}.{2}'.format(tag, algorithm.problem.get_name(), n_run)
+ self.experiment_list.append((name, algorithm, n_run))
diff --git a/jmetal/util/test/__init__.py b/jmetal/util/test/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/requirements.txt b/requirements.txt
index 14dac7a2..25d55821 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
+numpy==1.13.1
+pandas
tqdm==4.23.4
-bokeh==0.12.16
-pytest==3.1.2
PyHamcrest==1.9.0
mockito==1.0.11
matplotlib==2.0.2
-numpy==1.13.1
\ No newline at end of file
+plotly
\ No newline at end of file
diff --git a/jmetal/problem/reference_front/DTLZ1.pf b/resources/reference_front/DTLZ1.pf
similarity index 100%
rename from jmetal/problem/reference_front/DTLZ1.pf
rename to resources/reference_front/DTLZ1.pf
diff --git a/jmetal/problem/reference_front/ZDT1.pf b/resources/reference_front/ZDT1.pf
similarity index 100%
rename from jmetal/problem/reference_front/ZDT1.pf
rename to resources/reference_front/ZDT1.pf
diff --git a/jmetal/problem/reference_front/ZDT2.pf b/resources/reference_front/ZDT2.pf
similarity index 100%
rename from jmetal/problem/reference_front/ZDT2.pf
rename to resources/reference_front/ZDT2.pf
diff --git a/jmetal/problem/reference_front/ZDT3.pf b/resources/reference_front/ZDT3.pf
similarity index 100%
rename from jmetal/problem/reference_front/ZDT3.pf
rename to resources/reference_front/ZDT3.pf
diff --git a/jmetal/problem/reference_front/ZDT4.pf b/resources/reference_front/ZDT4.pf
similarity index 100%
rename from jmetal/problem/reference_front/ZDT4.pf
rename to resources/reference_front/ZDT4.pf
diff --git a/jmetal/problem/reference_front/ZDT6.pf b/resources/reference_front/ZDT6.pf
similarity index 100%
rename from jmetal/problem/reference_front/ZDT6.pf
rename to resources/reference_front/ZDT6.pf
diff --git a/setup.py b/setup.py
index ee867b5a..fe462e60 100644
--- a/setup.py
+++ b/setup.py
@@ -7,12 +7,12 @@
setup(
name='jmetalpy',
- version='0.5.0',
+ version='0.5.1',
description='JMetalPy. Python version of the jMetal framework',
author='Antonio J. Nebro',
author_email='antonio@lcc.uma.es',
- maintainer='Antonio J. Nebro',
- maintainer_email='antonio@lcc.uma.es',
+ maintainer='Antonio J. Nebro, Antonio Benítez-Hidalgo',
+ maintainer_email='antonio@lcc.uma.es, antonio.b@uma.es',
license='MIT',
url='https://github.com/jMetal/jMetalPy',
long_description=open('README.md').read(),
@@ -25,14 +25,14 @@
'Programming Language :: Python :: 3.6'
],
install_requires=[
+ 'tqdm',
'numpy',
+ 'pandas',
+ 'plotly',
'matplotlib==2.0.2',
- 'bokeh==0.12.16',
- 'tqdm'
],
tests_require=[
'mockito'
'PyHamcrest',
- 'pytest'
]
)