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. + +

+
+ Visualization +
+

## 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' ] )