diff --git a/.github/img/2d-f12010.png b/.github/img/2d-f12010.png new file mode 100644 index 0000000..6937489 Binary files /dev/null and b/.github/img/2d-f12010.png differ diff --git a/.github/img/3d-f12010.png b/.github/img/3d-f12010.png new file mode 100644 index 0000000..7813911 Binary files /dev/null and b/.github/img/3d-f12010.png differ diff --git a/CITATION.cff b/CITATION.cff index 9c1aba7..cba994e 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,7 +5,7 @@ authors: given-names: "Nguyen" orcid: "https://orcid.org/0000-0001-9994-8747" title: "Opfunu: An Open-source Python Library for Optimization Benchmark Functions" -version: 1.0.1 +version: 1.0.4 doi: 10.5281/zenodo.3620960 date-released: 2022-07-13 url: "https://github.com/thieu1995/opfunu" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..faacb26 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,95 @@ +# Contributing to Opfunu + +We appreciate your interest in contributing to Opfunu! This guide details how to contribute in a way that is efficient +for everyone. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [How Can I Contribute?](#how-can-i-contribute) +- [Pull Requests](#pull-requests) +- [Issue Tracking](#issue-tracking) + +## Code of Conduct + +All contributors are expected to adhere to the project's [Code of Conduct](CODE_OF_CONDUCT.md). Please read the document before contributing. + +## Getting Started + +1. Fork the project repository and clone your fork: + + ``` + git clone https://github.com/thieu1995/opfunu.git + ``` + +2. Create a new branch for your changes: + + ``` + git checkout -b name-of-your-branch + ``` + +3. Make your changes and commit them: + + ``` + git commit -m "Detailed commit message" + ``` + +4. Push your changes to your fork: + + ``` + git push origin name-of-your-branch + ``` + +5. Create a pull request from your branch to the Opfunu main branch. + +## How Can I Contribute? + +Here are some ways to contribute: + +- Improve documentation +- Fix bugs or add new features +- Write tutorials or blog posts +- Review code submissions +- Test the application and report issues + +However, before contributing, make sure that the unit tests pass and that new functionality is covered by unit tests. +The unit tests can be run using `pytest`. Change working directory to opfunu and then use: + +```python +# Test CEC-based functions +python -m pytest tests/cec_based + +# Test Name-based functions +python -m pytest tests/name_based +``` + +Or you can test all files by: + +```python +python -m pytest +``` + + +## Pull Requests + +[Pull requests](https://github.com/thieu1995/opfunu/pulls) are the best way to propose changes to the codebase. We +actively welcome your pull requests: + +1. Fork the repo and create your branch from `main`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Issue that pull request! + +## Issue Tracking + +We use [GitHub issues](https://github.com/thieu1995/opfunu/issues) to track public bugs and requests. Please ensure +your description is clear and has sufficient instructions to be able to reproduce the issue. + +## Any questions? + +Don't hesitate to contact us if you have any questions. Contact [@thieu1995](mailto:nguyenthieu2102@gmail.com) +or ask your question on issues. + +Thank you for your contributions! diff --git a/ChangeLog.md b/ChangeLog.md index f6d1093..6bff3d5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,15 +1,30 @@ -# Version 1.0.4 +# Version 1.0.5 + Fix schaffer_f7_func to match the cec implementation and reference document + Fix zakharov_func to match the cec implementation and reference document + Added a direct conversion of the lunacek_bi_rastrigin_cec_func + Added tests for cec functions comparing outputs to the reference implementations --------------------------------------------------------------------- +# Version 1.0.4 + ++ Fix p value in F10 and F17 of CEC-2017 ++ Add plot_latex to Benchmark class. ++ User can use draw_latex from opfunu to draw their latex equation. ++ Update examples for draw latex function + +--------------------------------------------------------------------- + # Version 1.0.3 + Optimized katsuura_func performance, at 1M ndim > 80x speedup ++ Add plot_2d, plot_3d to Benchmark class. ++ User can use draw_2d, draw_3d from opfunu to draw their function. ++ Add tutorial on how to integrate with other optimization frameworks like Mealpy, Opytimizer, Niapy ++ Update examples and update documentations ++ Update citation and paper (Got published at Journal of Open Research Software) --------------------------------------------------------------------- + # Version 1.0.2 + Fix modified_schwefel_func() in operator.py diff --git a/README.md b/README.md index fa67e85..5e1a77a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ --- -[![GitHub release](https://img.shields.io/badge/release-1.0.2-yellow.svg)](https://github.com/thieu1995/opfunu/releases) +[![GitHub release](https://img.shields.io/badge/release-1.0.4-yellow.svg)](https://github.com/thieu1995/opfunu/releases) [![Wheel](https://img.shields.io/pypi/wheel/gensim.svg)](https://pypi.python.org/pypi/opfunu) [![PyPI version](https://badge.fury.io/py/opfunu.svg)](https://badge.fury.io/py/opfunu) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/opfunu.svg) @@ -34,6 +34,28 @@ optimization benchmark functions. Contains all CEC competition functions from 20 * **Dependencies:** numpy, matplotlib +# Citation Request + +Please include these citations if you plan to use this library: + +- LaTeX: + +```bibtex + @article{Van_Thieu_2024_Opfunu, + author = {Van Thieu, Nguyen}, + title = {Opfunu: An Open-source Python Library for Optimization Benchmark Functions}, + doi = {10.5334/jors.508}, + journal = {Journal of Open Research Software}, + month = {May}, + year = {2024} + } +``` + +- APA: + + Van Thieu, N. (2024). Opfunu: An Open-source Python Library for Optimization Benchmark Functions. Journal of Open Research Software, 12(1), 8. https://doi.org/10.5334/jors.508 + + # Installation and Usage ### Install with pip @@ -57,44 +79,13 @@ $ python >>> opfunu.CEC_DATABASE # List all cec_based functions >>> opfunu.ALL_DATABASE # List all functions in this library ->>> opfunu.get_functions_by_classname("CEC2014") +>>> opfunu.get_functions_by_classname("MiShra04") >>> opfunu.get_functions_based_classname("2015") ->>> opfunu.get_functions_by_ndim(30) ->>> opfunu.get_functions_based_ndim(2) ->>> opfunu.get_all_named_functions() ->>> opfunu.get_all_cec_functions() ->>> opfunu.get_functions() ->>> opfunu.get_cecs() -``` +>>> opfunu.get_functions_by_ndim(2) +>>> opfunu.get_functions_based_ndim(50) -### Lib's structure - -```code - -docs -examples -opfunu - cec_based - cec.py - cec2005.py - cec2008.py - ... - cec2021.py - cec2022.py - name_based - a_func.py - b_func.py - ... - y_func.py - z_func.py - utils - operator.py - validator.py - visualize.py - __init__.py - benchmark.py -README.md -setup.py +>>> opfunu.get_name_based_functions(ndim=10, continuous=True) +>>> opfunu.get_cec_based_functions(ndim=2) ``` Let's go through some examples. @@ -124,7 +115,6 @@ func.evaluate(func.create_solution()) #### 2nd way ```python - import opfunu funcs = opfunu.get_functions_by_classname("F12014") @@ -135,57 +125,91 @@ func.evaluate(func.create_solution()) all_funcs_2014 = opfunu.get_functions_based_classname("2014") print(all_funcs_2014) - ``` -For more usage examples please look at [examples](/examples) folder. +### How to draw 2D, 3D + +Two ways if you want to draw functions that available in Opfunu. + +```python +from opfunu.cec_based import F12010 +f0 = F12010() +# Visualize opfunu function using method in object +f0.plot_2d(selected_dims=(2, 3), n_points=300, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, title="Contour map of the F1 CEC 2010 function", + x_label=None, y_label=None, figsize=(10, 8), filename="2d-f12010", exts=(".png", ".pdf"), verbose=True) -# Get helps (questions, problems) +f0.plot_3d(selected_dims=(1, 6), n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, title="3D visualization of the F1 CEC 2010 function", + x_label=None, y_label=None, figsize=(10, 8), filename="3d-f12010", exts=(".png", ".pdf"), verbose=True) -* Official source code repo: https://github.com/thieu1995/opfunu -* Official document: https://opfunu.readthedocs.io/ -* Download releases: https://pypi.org/project/opfunu/ -* Issue tracker: https://github.com/thieu1995/opfunu/issues -* Notable changes log: https://github.com/thieu1995/opfunu/blob/master/ChangeLog.md -* Examples with different version: https://github.com/thieu1995/opfunu/blob/master/examples.md -* Official chat group: https://t.me/+fRVCJGuGJg1mNDg1 +## Visualize opfunu function using utility function +from opfunu import draw_2d, draw_3d -* This project also related to our another projects which are optimization and machine learning. Check it here: - * https://github.com/thieu1995/metaheuristics - * https://github.com/thieu1995/mealpy - * https://github.com/thieu1995/mafese - * https://github.com/thieu1995/pfevaluator - * https://github.com/thieu1995/MetaCluster - * https://github.com/thieu1995/enoppy - * https://github.com/thieu1995/permetrics - * https://github.com/aiir-team +draw_2d(f0.evaluate, f0.lb, f0.ub, selected_dims=(2, 3), n_points=300) +draw_3d(f0.evaluate, f0.lb, f0.ub, selected_dims=(2, 3), n_points=300) +``` + + + + + +
+ F1-2010 CEC 2D +

F1-2010 CEC 2D

+
+ F1-2010 CEC 3D +

F1-2010 CEC 3D

+
-## Cite Us -If you are using opfunu in your project, we would appreciate citations: +### How to draw Latex -```code -@software{thieu_nguyen_2020_3711682, - author = {Nguyen Van Thieu}, - title = {Opfunu: An Open-source Python Library for Optimization Benchmark Functions}, - year = 2020, - publisher = {Zenodo}, - doi = {10.5281/zenodo.3620960}, - url = {https://doi.org/10.5281/zenodo.3620960.} -} -``` +Two ways if you want to draw latex equation. +```python +from opfunu.cec_based import F12010 +from opfunu.name_based import Ackley02 +from opfunu.utils.visualize import draw_latex -## References +f0 = F12010() +f1 = Ackley02() -```code -1. http://benchmarkfcns.xyz/fcns -2. https://en.wikipedia.org/wiki/Test_functions_for_optimization -3. https://www.cs.unm.edu/~neal.holts/dga/benchmarkFunction/ -4. http://www.sfu.ca/~ssurjano/optimization.html -5. A Literature Survey of Benchmark Functions For Global Optimization Problems (2013) -6. Problem Definitions and Evaluation Criteria for the CEC 2014 Special Session and Competition on Single Objective Real-Parameter Numerical Optimization +## Plot using function inside the object +f0.plot_latex(f0.latex_formula, figsize=(8, 3), dpi=500, title="Latex equation", exts=(".png", ".pdf"), verbose=True) +f1.plot_latex(f1.latex_formula_global_optimum, figsize=(8, 3), dpi=500, title="Global optimum", verbose=True) + +## Plot using module +draw_latex(f0.latex_formula_bounds, title="Boundary for Function") +draw_latex(f1.latex_formula_dimension, title=None) ``` + + + +For more usage examples please look at [examples](/examples) folder. + + + +# Contributing + +There are lots of ways how you can contribute to Permetrics's development, and you are welcome to join in! For example, +you can report problems or make feature requests on the [issues](/issues) pages. To facilitate contributions, +please check for the guidelines in the [CONTRIBUTING.md](/CONTRIBUTING.md) file. + + +# Official channels + +* [Official source code repository](https://github.com/thieu1995/opfunu) +* [Official document](https://opfunu.readthedocs.io/) +* [Download releases](https://pypi.org/project/opfunu/) +* [Issue tracker](https://github.com/thieu1995/opfunu/issues) +* [Notable changes log](/ChangeLog.md) +* [Official discussion group](https://t.me/+fRVCJGuGJg1mNDg1) + + +--- + +Developed by: [Thieu](mailto:nguyenthieu2102@gmail.com?Subject=Opfunu_QUESTIONS) @ 2023 diff --git a/docs/source/conf.py b/docs/source/conf.py index 67983b8..2982f14 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -29,7 +29,7 @@ author = 'Thieu' # The full version, including alpha/beta/rc tags -release = '1.0.3' +release = '1.0.4' # -- General configuration --------------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 96cbe8e..c4d5288 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,7 +6,7 @@ Welcome to OPFUNU's documentation! ================================== -.. image:: https://img.shields.io/badge/release-1.0.2-yellow.svg +.. image:: https://img.shields.io/badge/release-1.0.4-yellow.svg :target: https://github.com/thieu1995/opfunu/releases .. image:: https://img.shields.io/pypi/wheel/gensim.svg @@ -74,7 +74,9 @@ traditional functions with different dimensions are implemented. :caption: Introduction: pages/quick_start.rst - pages/notes.rst + pages/integrated.rst + pages/visualization.rst + pages/categories.rst .. toctree:: diff --git a/docs/source/pages/notes.rst b/docs/source/pages/categories.rst similarity index 99% rename from docs/source/pages/notes.rst rename to docs/source/pages/categories.rst index 799c3c1..51477d4 100644 --- a/docs/source/pages/notes.rst +++ b/docs/source/pages/categories.rst @@ -1,10 +1,7 @@ -Notes -===== +Function Categories +=================== -Categories ----------- - In general, unconstrained problems can be classified into two categories: test functions and real-world problems:: 1. Test functions are artificial problems, and can be used to evaluate the behavior of an algorithm in sometimes diverse and difficult situations. diff --git a/docs/source/pages/integrated.rst b/docs/source/pages/integrated.rst new file mode 100644 index 0000000..4d64a9f --- /dev/null +++ b/docs/source/pages/integrated.rst @@ -0,0 +1,77 @@ +Collaborative Libraries +======================= + +In this section, we will guide you how to integrate our library into other Optimization frameworks. + + +Mealpy Library +-------------- + +For example:: + + from opfunu.cec_based import cec2017 + f3 = cec2017.F32017(ndim=30) + + from mealpy import GA, FloatVar + + problem = { + "obj_func": f3.evaluate, + "bounds": FloatVar(lb=f3.lb, ub=f3.ub), + "minmax": "min", + } + model = GA.BaseGA(epoch=100, pop_size=50) + gbest = model.solve(problem_dict1) + print(f"Solution: {gbest.solution}, Fit: {gbest.target.fitness}") + + + +ScikitOpt Library +----------------- + +For example:: + + from opfunu.cec_based import cec2015 + f10 = cec2015.F102015(ndim=30) + + from sko.DE import DE + + de = DE(func=f10.evaluate, lb=f10.lb, ub=f10.ub, + size_pop=50, max_iter=800) + best_x, best_y = de.run() + print(f"best_x: {best_x}, best_y: {best_y}") + + + +Opytimizer Library +------------------ + +For example:: + + from opfunu.cec_based import cec2022 + f5 = cec2022.F52022(ndim=30) + + from opytimizer import Opytimizer + from opytimizer.core import Function + from opytimizer.optimizers.swarm import PSO + from opytimizer.spaces import SearchSpace + + space = SearchSpace(n_agents=20, n_variables=f5.ndim, + lower_bound=f5.lb, upper_bound=f5.ub) + optimizer = PSO() + function = Function(f5.evaluate) + + opt = Opytimizer(space, optimizer, function) + opt.start(n_iterations=1000) + + + +.. toctree:: + :maxdepth: 4 + + +.. toctree:: + :maxdepth: 4 + + +.. toctree:: + :maxdepth: 4 diff --git a/docs/source/pages/quick_start.rst b/docs/source/pages/quick_start.rst index cd0ba12..b85a198 100644 --- a/docs/source/pages/quick_start.rst +++ b/docs/source/pages/quick_start.rst @@ -8,7 +8,7 @@ Installation Install the `current PyPI release `_:: - $ pip install opfunu==1.0.3 + $ pip install opfunu==1.0.4 Or install the development version from GitHub:: @@ -46,7 +46,6 @@ Current Structure:: z_func.py utils operator.py - validator.py visualize.py __init__.py benchmark.py diff --git a/docs/source/pages/support.rst b/docs/source/pages/support.rst index 8f75bcf..18cbccc 100644 --- a/docs/source/pages/support.rst +++ b/docs/source/pages/support.rst @@ -6,6 +6,15 @@ If you are using opfunu in your project, we would appreciate citations: :: + @article{Van_Thieu_2024_Opfunu, + author = {Van Thieu, Nguyen}, + title = {Opfunu: An Open-source Python Library for Optimization Benchmark Functions}, + doi = {10.5334/jors.508}, + journal = {Journal of Open Research Software}, + month = {May}, + year = {2024} + } + @software{thieu_nguyen_2020_3711682, author = {Nguyen Van Thieu}, title = {Opfunu: An Open-source Python Library for Optimization Benchmark Functions}, diff --git a/docs/source/pages/utils.rst b/docs/source/pages/utils.rst index 3973278..88c1a32 100644 --- a/docs/source/pages/utils.rst +++ b/docs/source/pages/utils.rst @@ -1,14 +1,6 @@ opfunu.utils package ==================== -opfunu.utils.encoder module ---------------------------- - -.. automodule:: opfunu.utils.encoder - :members: - :undoc-members: - :show-inheritance: - opfunu.utils.operator module ---------------------------- @@ -17,14 +9,6 @@ opfunu.utils.operator module :undoc-members: :show-inheritance: -opfunu.utils.validator module ------------------------------ - -.. automodule:: opfunu.utils.validator - :members: - :undoc-members: - :show-inheritance: - opfunu.utils.visualize module ----------------------------- diff --git a/docs/source/pages/visualization.rst b/docs/source/pages/visualization.rst new file mode 100644 index 0000000..244865b --- /dev/null +++ b/docs/source/pages/visualization.rst @@ -0,0 +1,71 @@ +Visualization +============= + +Inside Function +--------------- + +You can use our visualization module to draw our function. + +Code:: + + from opfunu.cec_based import F12010 + + # Visualize opfunu function using method in object + f0 = F12010() + + f0.plot_2d(selected_dims=(2, 3), n_points=300, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, title="Contour map of the F1 CEC 2010 function", + x_label=None, y_label=None, figsize=(10, 8), filename="2d-f12010", exts=(".png", ".pdf"), verbose=True) + + f0.plot_3d(selected_dims=(1, 6), n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, title="3D visualization of the F1 CEC 2010 function", + x_label=None, y_label=None, figsize=(10, 8), filename="3d-f12010", exts=(".png", ".pdf"), verbose=True) + + ## Visualize opfunu function using utility function + from opfunu import draw_2d, draw_3d + + draw_2d(f0.evaluate, f0.lb, f0.ub, selected_dims=(2, 3), n_points=300) + draw_3d(f0.evaluate, f0.lb, f0.ub, selected_dims=(2, 3), n_points=300) + + +Custom Function +--------------- + +You can also use our visualization module to draw your custom function. + +Code:: + + from opfunu import draw_2d, draw_3d + + ## Define a custom function, for example. I will use mealpy problem as an example + from mealpy import Problem, FloatVar + import numpy as np + + # Our custom problem class + class Squared(Problem): + def __init__(self, bounds=None, minmax="min", data=None, **kwargs): + self.data = data + super().__init__(bounds, minmax, **kwargs) + + def obj_func(self, solution): + x = self.decode_solution(solution)["my_var"] + return np.sum(x ** 2) + + bound = FloatVar(lb=(-10., )*20, ub=(10., )*20, name="my_var") + custom_squared = Squared(bounds=bound, minmax="min", data="Amazing", name="Squared") + + ## Visualize function using utility function + draw_2d(custom_squared.obj_func, custom_squared.lb, custom_squared.ub, selected_dims=(2, 3), n_points=300) + draw_3d(custom_squared.obj_func, custom_squared.lb, custom_squared.ub, selected_dims=(2, 3), n_points=300) + + +.. toctree:: + :maxdepth: 4 + + +.. toctree:: + :maxdepth: 4 + + +.. toctree:: + :maxdepth: 4 diff --git a/examples.md b/examples.md index 596fdc1..eedb02f 100644 --- a/examples.md +++ b/examples.md @@ -7,11 +7,11 @@ import opfunu import numpy as np # get all the available functions accepting ANY dimension -any_dim_cec = opfunu.get_cecs(None) +any_dim_cec = opfunu.get_cec_based_functions(None) print(any_dim_cec) # get all the available separable functions accepting 2D -separable_2d_cec = opfunu.get_cecs( +separable_2d_cec = opfunu.get_cec_based_functions( ndim=2, # dimension separable=True, ) @@ -75,14 +75,14 @@ print(problem.x_global) print(problem.is_succeed(problem.x_global)) # # get all the available separable functions accepting 2D -my_list = opfunu.get_cecs( +my_list = opfunu.get_cec_based_functions( ndim=2, # dimension rotated=True ) print(my_list) # --> 41 ## Get all noise function -my_list = opfunu.get_cecs( +my_list = opfunu.get_cec_based_functions( randomized_term=True ) print(my_list) diff --git a/examples/basic_use_cases_benchmark.py b/examples/basic_use_cases_benchmark.py index d520f84..68b8cc9 100644 --- a/examples/basic_use_cases_benchmark.py +++ b/examples/basic_use_cases_benchmark.py @@ -10,11 +10,11 @@ if __name__ == '__main__': # get all the available functions accepting ANY dimension - any_dim_functions = opfunu.get_functions(None) + any_dim_functions = opfunu.get_name_based_functions(None) print(any_dim_functions) # get all the available differentiable functions accepting 2D - differentiable_2d_functions = opfunu.get_functions( + differentiable_2d_functions = opfunu.get_name_based_functions( ndim=2, # dimension differentiable=True, ) diff --git a/examples/basic_use_cases_cec.py b/examples/basic_use_cases_cec.py index 84f38ff..52d8b2b 100644 --- a/examples/basic_use_cases_cec.py +++ b/examples/basic_use_cases_cec.py @@ -10,11 +10,11 @@ if __name__ == '__main__': # # get all the available functions accepting ANY dimension - # any_dim_cec = opfunu.get_cecs(None) + # any_dim_cec = opfunu.get_cec_based_functions(None) # print(any_dim_cec) # # # get all the available separable functions accepting 2D - # separable_2d_cec = opfunu.get_cecs( + # separable_2d_cec = opfunu.get_cec_based_functions( # ndim=2, # dimension # separable=True, # ) @@ -77,14 +77,14 @@ print(problem.is_succeed(problem.x_global)) # # get all the available separable functions accepting 2D - my_list = opfunu.get_cecs( + my_list = opfunu.get_cec_based_functions( ndim=2, # dimension rotated=True ) print(my_list) # --> 41 ## Get all noise function - my_list = opfunu.get_cecs( + my_list = opfunu.get_cec_based_functions( randomized_term=True ) print(my_list) diff --git a/examples/example_with_mealpy.py b/examples/example_with_mealpy.py index 605eb71..0bdf813 100644 --- a/examples/example_with_mealpy.py +++ b/examples/example_with_mealpy.py @@ -4,6 +4,8 @@ # Github: https://github.com/thieu1995 % # --------------------------------------------------% +## Examples with Mealpy <= 2.5.4 + from mealpy.bio_based import SMA from opfunu.name_based import Ackley02 @@ -20,10 +22,27 @@ } ## Run the algorithm -model = SMA.BaseSMA(epoch=100, pop_size=50, pr=0.03) +model = SMA.OriginalSMA(epoch=100, pop_size=50, pr=0.03) best_position, best_fitness = model.solve(problem_dict1) print(f"Best solution: {best_position}, Best fitness: {best_fitness}") print(ackey.n_fe) print(ackey.f_global) print(ackey.x_global) + + +## Examples with Mealpy >= 3.0.0 + +from opfunu.cec_based import cec2017 +f3 = cec2017.F32017(ndim=30) + +from mealpy import GA, FloatVar + +problem = { + "obj_func": f3.evaluate, + "bounds": FloatVar(lb=f3.lb, ub=f3.ub), + "minmax": "min", +} +model = GA.BaseGA(epoch=100, pop_size=50) +gbest = model.solve(problem_dict1) +print(f"Solution: {gbest.solution}, Fit: {gbest.target.fitness}") diff --git a/examples/visualize/custom_function.py b/examples/visualize/custom_function.py new file mode 100644 index 0000000..392f7df --- /dev/null +++ b/examples/visualize/custom_function.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# Created by "Thieu" at 17:03, 24/05/2024 ----------% +# Email: nguyenthieu2102@gmail.com % +# Github: https://github.com/thieu1995 % +# --------------------------------------------------% + +from opfunu import draw_2d, draw_3d + +## Define a custom function, for example. I will use mealpy problem as an example +from mealpy import Problem, FloatVar +import numpy as np + +# Our custom problem class +class Squared(Problem): + def __init__(self, bounds=None, minmax="min", data=None, **kwargs): + self.data = data + super().__init__(bounds, minmax, **kwargs) + + def obj_func(self, solution): + x = self.decode_solution(solution)["my_var"] + return np.sum(x ** 2) + +bound = FloatVar(lb=(-10., )*20, ub=(10., )*20, name="my_var") +custom_squared = Squared(bounds=bound, minmax="min", data="Amazing", name="Squared") + +## Visualize function using utility function +draw_2d(custom_squared.obj_func, custom_squared.lb, custom_squared.ub, selected_dims=(2, 3), n_points=300) +draw_3d(custom_squared.obj_func, custom_squared.lb, custom_squared.ub, selected_dims=(2, 3), n_points=300) diff --git a/examples/visualize/inside_function.py b/examples/visualize/inside_function.py new file mode 100644 index 0000000..24909f7 --- /dev/null +++ b/examples/visualize/inside_function.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# Created by "Thieu" at 16:55, 24/05/2024 ----------% +# Email: nguyenthieu2102@gmail.com % +# Github: https://github.com/thieu1995 % +# --------------------------------------------------% + +from opfunu.cec_based import F12010 + +# Visualize opfunu function using method in object +f0 = F12010() + +f0.plot_2d(selected_dims=(2, 3), n_points=300, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, title="Contour map of the F1 CEC 2010 function", + x_label=None, y_label=None, figsize=(10, 8), filename="2d-f12010", exts=(".png", ".pdf"), verbose=True) + +f0.plot_3d(selected_dims=(1, 6), n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, title="3D visualization of the F1 CEC 2010 function", + x_label=None, y_label=None, figsize=(10, 8), filename="3d-f12010", exts=(".png", ".pdf"), verbose=True) + +## Visualize opfunu function using utility function +from opfunu import draw_2d, draw_3d + +draw_2d(f0.evaluate, f0.lb, f0.ub, selected_dims=(2, 3), n_points=300) +draw_3d(f0.evaluate, f0.lb, f0.ub, selected_dims=(2, 3), n_points=300) diff --git a/examples/visualize/plot_latex.py b/examples/visualize/plot_latex.py new file mode 100644 index 0000000..1310680 --- /dev/null +++ b/examples/visualize/plot_latex.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# Created by "Thieu" at 21:17, 04/06/2024 ----------% +# Email: nguyenthieu2102@gmail.com % +# Github: https://github.com/thieu1995 % +# --------------------------------------------------% + +from opfunu.cec_based import F12010 +from opfunu.name_based import Ackley02 +from opfunu.utils.visualize import draw_latex + +# Visualize opfunu function using method in object +f0 = F12010() +f1 = Ackley02() + +## Plot using function inside the object +f0.plot_latex(f0.latex_formula, title="Latex equation") +f1.plot_latex(f1.latex_formula_global_optimum, title="Global optimum") + +## Plot using module +draw_latex(f0.latex_formula_bounds, title="Boundary for Function") +draw_latex(f1.latex_formula_dimension, title=None) diff --git a/opfunu/__init__.py b/opfunu/__init__.py index 90a5c88..4c418fa 100644 --- a/opfunu/__init__.py +++ b/opfunu/__init__.py @@ -21,19 +21,18 @@ # >>> print(f1.get_paras()) # Print the parameters of function if has # >>> # >>> Plot 2d or plot 3d contours -# >>> Warning ! Only working on 2d functions objects ! -# >>> Warning !! change n_space to reduce the computing time +# >>> Warning !! change n_points to reduce the computing time # >>> # >>> import opfunu # >>> f2 = opfunu.cec_based.F22005(ndim=2) -# >>> opfunu.plot_2d(f22005, n_space=1000, ax=None) -# >>> opfunu.plot_3d(f22005, n_space=1000, ax=None) +# >>> f2.plot_2d(selected_dims=(2, 3), n_points=300) +# >>> f2.plot_3d(selected_dims=(1, 4), n_points=300) -__version__ = "1.0.3" +__version__ = "1.0.4" import inspect import re -from .utils import * +from .utils.visualize import draw_2d, draw_3d, draw_latex from . import name_based from . import cec_based @@ -47,13 +46,13 @@ def get_functions_by_classname(name=None): """ Parameters ---------- - name : Classname of the function + name : The exact classname of the function (no difference among lowercase, uppercase, mix) Returns ------- List of the functions, but all the classname are different, so the result is list of 1 function or list of empty """ - functions = [cls for classname, cls in ALL_DATABASE if (classname not in EXCLUDES and (classname == name or classname.lower() == name))] + functions = [cls for classname, cls in ALL_DATABASE if (classname not in EXCLUDES and (classname.lower() == name.lower()))] return functions @@ -61,13 +60,13 @@ def get_functions_based_classname(name=None): """ Parameters ---------- - name : Name that is a substring of classname + name : The substring of classname of the function Returns ------- List of the functions """ - functions = [cls for classname, cls in ALL_DATABASE if (classname not in EXCLUDES and re.search(name, classname))] + functions = [cls for classname, cls in ALL_DATABASE if (classname not in EXCLUDES and re.search(name.lower(), classname.lower()))] return functions @@ -75,7 +74,7 @@ def get_functions_by_ndim(ndim=None): """ Parameters ---------- - ndim : Number of dimensions that function supported + ndim : The exact number of dimensions that function has and not able to change Returns ------- @@ -91,7 +90,7 @@ def get_functions_based_ndim(ndim=None): """ Parameters ---------- - ndim : Number of dimensions that function has as default value + ndim : Number of dimensions that function supported Returns ------- @@ -99,19 +98,19 @@ def get_functions_based_ndim(ndim=None): """ functions = [cls for classname, cls in ALL_DATABASE if classname not in EXCLUDES] if type(ndim) is int and ndim > 1: - return list(filter(lambda f: ndim in f().dim_supported, functions)) + return list(filter(lambda f: (f().dim_default == ndim or f().dim_changeable == True), functions)) return functions -def get_all_named_functions(): +def get_all_name_based_functions(): return [cls for classname, cls in FUNC_DATABASE if classname not in EXCLUDES] -def get_all_cec_functions(): +def get_all_cec_based_functions(): return [cls for classname, cls in CEC_DATABASE if classname not in EXCLUDES] -def get_functions(ndim, continuous=None, linear=None, convex=None, unimodal=None, separable=None, +def get_name_based_functions(ndim, continuous=None, linear=None, convex=None, unimodal=None, separable=None, differentiable=None, scalable=None, randomized_term=None, parametric=None, modality=None): functions = [cls for classname, cls in FUNC_DATABASE if classname not in EXCLUDES] functions = list(filter(lambda f: f().is_ndim_compatible(ndim), functions)) @@ -129,7 +128,7 @@ def get_functions(ndim, continuous=None, linear=None, convex=None, unimodal=None return functions -def get_cecs(ndim=None, continuous=None, linear=None, convex=None, unimodal=None, separable=None, differentiable=None, +def get_cec_based_functions(ndim=None, continuous=None, linear=None, convex=None, unimodal=None, separable=None, differentiable=None, scalable=None, randomized_term=None, parametric=True, shifted=True, rotated=None , modality=None): functions = [cls for classname, cls in CEC_DATABASE if classname not in EXCLUDES] functions = list(filter(lambda f: f().is_ndim_compatible(ndim), functions)) diff --git a/opfunu/benchmark.py b/opfunu/benchmark.py index 0113afb..ee39e13 100644 --- a/opfunu/benchmark.py +++ b/opfunu/benchmark.py @@ -5,6 +5,7 @@ # --------------------------------------------------% import numpy as np +from opfunu.utils.visualize import draw_2d, draw_3d, draw_latex class Benchmark: @@ -52,6 +53,7 @@ class Benchmark: parametric = True modality = True # Number of ambiguous peaks, unknown # peaks + # n_basins = 1 # n_valleys = 1 @@ -60,6 +62,7 @@ def __init__(self): self._ndim = None self.dim_changeable = False self.dim_default = 2 + self.dim_supported = [] self.f_global = None self.x_global = None self.n_fe = 0 @@ -249,3 +252,124 @@ def create_solution(self) -> np.ndarray: The random solution """ return np.random.uniform(self.lb, self.ub) + + def plot_latex(self, latex, title="Latex equation", figsize=(8, 3), dpi=500, filename=None, exts=(".png", ".pdf"), verbose=True): + """ + Draw latex equation. + + Parameters + ---------- + latex : equation + Your latex equation, you can test on the website: https://latex.codecogs.com/ + title : str + Title for the figure + figsize : tuple, default=(10, 8) + The figure size with format of tuple (width, height) + dpi : int, default=500 + The dot per inches (DPI) - indicate the number of dots per inch. + filename : str, default = None + Set the file name, If None, the file will not be saved + exts : list, tuple, np.ndarray + The list of extensions to save file, for example: (".png", ".pdf", ".jpg") + verbose : bool + Show the figure or not. It will not show on linux system. + """ + draw_latex(latex, title=title, figsize=figsize, dpi=dpi, filename=filename, exts=exts, verbose=verbose) + + def plot_2d(self, selected_dims=None, n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, fixed_strategy="mean", fixed_values=None, + title="Contour map of the function", x_label=None, y_label=None, figsize=(10, 8), filename=None, exts=(".png", ".pdf"), verbose=True): + """ + Draw 2D contour of the function. + + Parameters + ---------- + selected_dims : list, tuple, np.ndarray + The selected dimensions you want to draw. + If your function has only 2 dimensions, it will select (1, 2) automatically. + n_points : int + The number of points that will be used to draw the contour + ct_cmap : str + The cmap of matplotlib.pyplot.contourf function (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html) + ct_levels : int + The levels parameter of contourf function + ct_alpha : float + The alpha parameter of contourf function + fixed_strategy : str + The selected strategy to set values for other dimensions. + When your function has > 2 dimensions, you need to set a fixed value for other dimensions to be able to calculate value. + List of available strategy: ["min", "max", "mean', "values", "zero"] + + min: Set the other dimensions by its lower bound + + max: Set the other dimensions by its upper bound + + mean: Set the other dimensions by it average value (lower bound + upper bound) / 2 + + zero: Set the other dimensions by 0 + + values: Set the other dimensions by your passed values through the parameter: `fixed_values`. + + fixed_values : list, tuple, np.ndarray + Fixed values for all dimensions (length should be the same as lower bound), the selected dimensions will be replaced in the drawing process. + title : str + Title for the figure + x_label : str + Set the x label + y_label : str + Set the y label + figsize : tuple, default=(10, 8) + The figure size with format of tuple (width, height) + filename : str, default = None + Set the file name, If None, the file will not be saved + exts : list, tuple, np.ndarray + The list of extensions to save file, for example: (".png", ".pdf", ".jpg") + verbose : bool + Show the figure or not. It will not show on linux system. + """ + draw_2d(self.evaluate, self.lb, self.ub, selected_dims=selected_dims, n_points=n_points, + ct_cmap=ct_cmap, ct_levels=ct_levels, ct_alpha=ct_alpha, fixed_strategy=fixed_strategy, fixed_values=fixed_values, + title=title, x_label=x_label, y_label=y_label, figsize=figsize, filename=filename, exts=exts, verbose=verbose) + + def plot_3d(self, selected_dims=None, n_points=500, ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, fixed_strategy="mean", fixed_values=None, + title="3D visualization of the function", x_label=None, y_label=None, figsize=(10, 8), filename=None, exts=(".png", ".pdf"), verbose=True): + """ + Draw 3D of the function. + + Parameters + ---------- + selected_dims : list, tuple, np.ndarray + The selected dimensions you want to draw. + If your function has only 2 dimensions, it will select (1, 2) automatically. + n_points : int + The number of points that will be used to draw the contour + ct_cmap : str + The cmap of matplotlib.pyplot.contourf function (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html) + ct_levels : int + The levels parameter of contourf function + ct_alpha : float + The alpha parameter of contourf function + fixed_strategy : str + The selected strategy to set values for other dimensions. + When your function has > 2 dimensions, you need to set a fixed value for other dimensions to be able to calculate value. + List of available strategy: ["min", "max", "mean', "values", "zero"] + + min: Set the other dimensions by its lower bound + + max: Set the other dimensions by its upper bound + + mean: Set the other dimensions by it average value (lower bound + upper bound) / 2 + + zero: Set the other dimensions by 0 + + values: Set the other dimensions by your passed values through the parameter: `fixed_values`. + + fixed_values : list, tuple, np.ndarray + Fixed values for all dimensions (length should be the same as lower bound), the selected dimensions will be replaced in the drawing process. + title : str + Title for the figure + x_label : str + Set the x label + y_label : str + Set the y label + figsize : tuple, default=(10, 8) + The figure size with format of tuple (width, height) + filename : str, default = None + Set the file name, If None, the file will not be saved + exts : list, tuple, np.ndarray + The list of extensions to save file, for example: (".png", ".pdf", ".jpg") + verbose : bool + Show the figure or not. It will not show on linux system. + """ + draw_3d(self.evaluate, self.lb, self.ub, selected_dims=selected_dims, n_points=n_points, + ct_cmap=ct_cmap, ct_levels=ct_levels, ct_alpha=ct_alpha, fixed_strategy=fixed_strategy, fixed_values=fixed_values, + title=title, x_label=x_label, y_label=y_label, figsize=figsize, filename=filename, exts=exts, verbose=verbose) diff --git a/opfunu/cec_based/cec.py b/opfunu/cec_based/cec.py index 84651e9..b81dcc5 100644 --- a/opfunu/cec_based/cec.py +++ b/opfunu/cec_based/cec.py @@ -204,10 +204,10 @@ def check_ndim_and_bounds(self, ndim=None, dim_max=None, bounds=None, default_bo if self.dim_changeable: if type(ndim) is int and ndim > 1: if dim_max is None or ndim <= dim_max: - # Check if ndim in supported dimensions - if self.dim_supported is not None and ndim not in self.dim_supported: - raise ValueError(f'{self.__class__.__name__} ndim not in supported dimensions ' - f'{self.dim_supported}') + # # Check if ndim in supported dimensions + # if self.dim_supported is not None and ndim not in self.dim_supported: + # raise ValueError(f'{self.__class__.__name__} ndim not in supported dimensions ' + # f'{self.dim_supported}') self._ndim = int(ndim) self._bounds = np.array([default_bounds[0] for _ in range(self._ndim)]) else: diff --git a/opfunu/cec_based/cec2017.py b/opfunu/cec_based/cec2017.py index c5f8114..fa35410 100644 --- a/opfunu/cec_based/cec2017.py +++ b/opfunu/cec_based/cec2017.py @@ -321,7 +321,7 @@ def __init__(self, ndim=None, bounds=None, f_shift="shift_data_10", f_matrix="M_ self.f_global = f_bias self.x_global = self.f_shift self.n_funcs = 3 - self.p = np.array([0.2, 0.2, 0.2]) + self.p = np.array([0.2, 0.4, 0.4]) self.n1 = int(np.ceil(self.p[0] * self.ndim)) self.n2 = int(np.ceil(self.p[1] * self.ndim)) + self.n1 self.idx1, self.idx2, self.idx3 = self.f_shuffle[:self.n1], self.f_shuffle[self.n1:self.n2], self.f_shuffle[self.n2:self.ndim] @@ -544,7 +544,7 @@ class F172017(F102017): def __init__(self, ndim=None, bounds=None, f_shift="shift_data_17", f_matrix="M_17_D", f_shuffle="shuffle_data_17_D", f_bias=1700.): super().__init__(ndim, bounds, f_shift, f_matrix, f_shuffle, f_bias) self.n_funcs = 5 - self.p = np.array([0.1, 0.2, 0.2, 0.2, 0.3]) + self.p = np.array([0.2, 0.2, 0.2, 0.2, 0.2]) self.n1 = int(np.ceil(self.p[0] * self.ndim)) self.n2 = int(np.ceil(self.p[1] * self.ndim)) + self.n1 self.n3 = int(np.ceil(self.p[2] * self.ndim)) + self.n2 diff --git a/opfunu/dimension_based/__init__.py b/opfunu/dimension_based/__init__.py deleted file mode 100644 index 48b12e0..0000000 --- a/opfunu/dimension_based/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -# ------------------------------------------------------------------------------------------------------% -# Created by "Thieu Nguyen" at 11:23, 16/03/2020 % -# % -# Email: nguyenthieu2102@gmail.com % -# Homepage: https://www.researchgate.net/profile/Thieu_Nguyen6 % -# Github: https://github.com/thieu1995 % -#-------------------------------------------------------------------------------------------------------% - diff --git a/opfunu/dimension_based/benchmark2d.py b/opfunu/dimension_based/benchmark2d.py deleted file mode 100644 index 9cb11eb..0000000 --- a/opfunu/dimension_based/benchmark2d.py +++ /dev/null @@ -1,431 +0,0 @@ -#!/usr/bin/env python # -# ------------------------------------------------------------------------------------------------------# -# Created by "Thieu Nguyen" at 02:54, 06/12/2019 # -# # -# Email: nguyenthieu2102@gmail.com # -# Homepage: https://www.researchgate.net/profile/Thieu_Nguyen6 # -# Github: https://github.com/thieu1995 # -#-------------------------------------------------------------------------------------------------------# - -import numpy as np - -class Functions: - """ - This class of functions is belongs to 2-dimensional space - """ - - def _ackley_n2__(self, solution=None): - """ - Class: unimodal, convex, differentiable, non-separable - Global: one global minimum fx = -200, at [0, 0] - - @param solution: A numpy array include 2 items like: [10, 22] - """ - n = len(solution) - assert (n == 2, 'Ackley N. 2 function is only defined on a 2D space.') - return -200*np.exp(-0.2*np.sqrt(np.sum(solution**2))) - - - def _ackley_n3__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable - Global: one global minimum fx = −195.629028238419, at [±0.682584587365898,−0.36075325513719] - Link: http://benchmarkfcns.xyz/benchmarkfcns/ackleyn3fcn.html - - @param solution: A numpy array include 2 items like: [10, 22] - """ - d = len(solution) - assert (d == 2, 'Ackley N. 3 function is only defined on a 2D space.') - return -200*np.exp(-0.2*np.sqrt(np.sum(solution**2))) + 5*np.exp(np.cos(3*solution[0]) + np.sin(3*solution[1])) - - - def _adjiman__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable - Global: if x in [-1, 2], y in [-1, 1] cube => global min fx = -2.02181, at [0, 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/adjimanfcn.html - - @param solution: A numpy array include 2 items like: [10, 22] - """ - d = len(solution) - assert (d == 2, 'Adjiman function is only defined on a 2D space.') - return np.cos(solution[0]) * np.sin(solution[1]) - solution[0] / (solution[1]**2 + 1) - - def _bartels_conn__(self, solution=None): - """ - Class: multimodal, non-convex, non-differentiable, non-separable - Global: one global minimum fx = 1, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/bartelsconnfcn.html - - @param solution: A numpy array include 2 items like: [10, 22] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Bartels conn function is only defined on a 2D space.') - return np.abs(solution[0]**2 + solution[1]**2 + solution[0] * solution[1]) + np.abs(np.sin(solution[0])) + \ - np.abs(np.cos(solution[1])) - - def _beale__(self, solution=None): - """ - Class: multimodal, non-convex, continuous - Global: one global minimum fx = 0, at [3, 0.5] - Link: http://benchmarkfcns.xyz/benchmarkfcns/bealefcn.html - - @param solution: A numpy array include 2 items in range: [-4.5, 4.5], [-4.5, 4.5] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Beale function is only defined on a 2D space.') - return (1.5-solution[0]+solution[0]*solution[1])**2 + (2.25-solution[0]+solution[0]*solution[1]**2)**2 +\ - (2.625-solution[0]+solution[0]*solution[1]**3)**2 - - def _bird__(self, solution=None): - """ - Class: multimodal, non-convex, non-separable, differentiable - Global: 2 global minimum fx= -106.764537, at ( 4.70104 , 3.15294 ) and ( − 1.58214 , − 3.13024 ) . - Link: http://benchmarkfcns.xyz/benchmarkfcns/birdfcn.html - - @param solution: A numpy array include 2 items in range: [-2pi, 2pi], [-2pi, 2pi] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Bird function is only defined on a 2D space.') - return np.sin(solution[0])*np.exp((1 - np.cos(solution[1]))**2) + \ - np.cos(solution[1]) * np.exp( (1-np.sin(solution[0]))**2 ) + (solution[0] - solution[1])**2 - - def _bohachevskyn_n1__(self, solution=None): - """ - Class: unimodal, convex, continuous - Global: global minimum fx= 0, at ( 0, 0 ) - Link: http://benchmarkfcns.xyz/benchmarkfcns/bohachevskyn1fcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100], [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Bohachevskyn N.1 function is only defined on a 2D space.') - return solution[0]**2 + 2*solution[1]**2 - 0.3*np.cos(3*solution[0]*np.pi) - 0.4*np.cos(4*solution[1]*np.pi) + 0.7 - - - def _bohachevskyn_n2__(self, solution=None): - """ - Class: multi-modal, non-convex, non-separable, differentiable - Global: global minimum fx= 0, at ( 0, 0 ) - Link: http://benchmarkfcns.xyz/benchmarkfcns/bohachevskyn2fcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100], [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Bohachevskyn N.2 function is only defined on a 2D space.') - return solution[0] ** 2 + 2 * solution[1] ** 2 - 0.3 * np.cos(3 * solution[0] * np.pi) * np.cos(4 * solution[1] * np.pi) + 0.3 - - - def _booth__(self, solution=None): - """ - Class: unimodal, convex, non-separable, differentiable, continuous - Global: one global minimum fx= 0, at ( 1, 3 ) - Link: http://benchmarkfcns.xyz/benchmarkfcns/boothfcn.html - - @param solution: A numpy array include 2 items in range: [-10, 10], [-10, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Brooth function is only defined on a 2D space.') - return (solution[0]+2*solution[1]-7)**2 + (2*solution[0]+solution[1]-5)**2 - - - def _brent__(self, solution=None): - """ - Class: unimodal, convex, non-separable, differentiable - Global: one global minimum fx= e^(-200), at ( -10 -10 ) - Link: http://benchmarkfcns.xyz/benchmarkfcns/brentfcn.html - - @param solution: A numpy array include 2 items in range: [-20, 0], [-20, 0] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Brent function is only defined on a 2D space.') - return (solution[0]+10)**2 + (solution[1]+10)**2 + np.exp(-solution[0]**2-solution[1]**2) - - - def _bukin_n6__(self, solution=None): - """ - Class: multimodal, convex, non-separable, non-differentiable, continuous - Global: one global minimum fx= 0, at ( -10, 1) - Link: http://benchmarkfcns.xyz/benchmarkfcns/bukinn6fcn.html - - @param solution: A numpy array include 2 items in range: [-15, -5], [-3, 3] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Bukin N.6 function is only defined on a 2D space.') - return 100*np.sqrt(np.abs(solution[1] - 0.01*solution[0]**2)) + 0.01*np.abs(solution[0]+10) - - - def _cross_in_tray__(self, solution=None): - """ - Class: multimodal, non-convex, non-separable, non-differentiable, continuous - Global: 4 global minimum fx= -2.06261218, at (±1.349406685353340,±1.349406608602084) - Link: http://benchmarkfcns.xyz/benchmarkfcns/crossintrayfcn.html - - @param solution: A numpy array include 2 items in range: [-10, 10], [-10, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Bukin N.6 function is only defined on a 2D space.') - t1 = np.exp( np.abs(100 - np.sqrt(np.sum(solution**2))/np.pi ) ) - t2 = np.sin(solution[0]) * np.cos(solution[1]) - return -0.0001*(np.abs(t1*t2) + 1)**0.1 - - - def _deckkers_aarts__(self, solution=None): - """ - Class: multimodal, non-convex, non-separable, differentiable, continuous - Global: 1 global minimum fx = −24771.09375, at ( 0 , ± 15 ) . - Link: http://benchmarkfcns.xyz/benchmarkfcns/deckkersaartsfcn.html - - @param solution: A numpy array include 2 items in range: [-20, 20], [-20, 20] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Deckkers Aarts function is only defined on a 2D space.') - t1 = solution[0]**2 - t2 = solution[1]**2 - return 10**5*t1 + t2 - (t1 + t2)**2 + 10**(-5) * (t1 + t2)**4 - - - def _drop_wave__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous - Global: 1 global minimum fx = −1 at ( 0 , 0 ) . - Link: http://benchmarkfcns.xyz/benchmarkfcns/dropwavefcn.html - - @param solution: A numpy array include 2 items in range: [-5.2, 5.2], [-5.2, 5.2] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Drop wave function is only defined on a 2D space.') - return -(1+np.cos(12*np.sqrt(np.sum(solution*2)))) / (0.5 * np.sum(solution**2) + 2) - - - def _easom__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, separable - Global: 1 global minimum fx = −1 at ( pi, pi ) . - Link: http://benchmarkfcns.xyz/benchmarkfcns/easomfcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100], [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Easom function is only defined on a 2D space.') - return -np.cos(solution[0])*np.cos(solution[1])*np.exp(-(solution[0] - np.pi)**2 - (solution[1] - np.pi)**2) - - - def _egg_crate__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, separable - Global: global minimum fx = 0 at ( 0, 0 ) . - Link: http://benchmarkfcns.xyz/benchmarkfcns/eggcratefcn.html - - @param solution: A numpy array include 2 items in range: [-5, 5] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Egg Crate function is only defined on a 2D space.') - return np.sum(solution**2) + 25 * (np.sin(solution[0])**2 + np.sin(solution[1])**2) - - - def _goldstein_price__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, non-separable - Global: global minimum fx = 3 at ( 0, -1 ) . - Link: http://benchmarkfcns.xyz/benchmarkfcns/goldsteinpricefcn.html - - @param solution: A numpy array include 2 items in range: [-2, 2] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Goldstein price function is only defined on a 2D space.') - t1 = 18 - 32*solution[0] + 12*solution[0]**2 + 4*solution[1] - 36*solution[0]*solution[1] + 27*solution[1]**2 - t2 = 19 - 14*solution[0]+3*solution[0]**2 - 14*solution[1] + 6*solution[0]*solution[1] + 3*solution[1]**2 - t3 = (np.sum(solution) + 1)**2 - return (1+t3*t2) * (30 + (2*solution[0]-3*solution[1])**2 * t1) - - - def _himmelblau__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous - Global: 4 global optima, fx = 0 at (3, 2), (-2.85118, 3.283186), (−3.779310,−3.283186), (3.584458,−1.848126) - - Link: http://benchmarkfcns.xyz/benchmarkfcns/himmelblaufcn.html - - @param solution: A numpy array include 2 items in range: [-6, 6] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Himmelblau function is only defined on a 2D space.') - return (solution[0]**2+solution[1]-11)**2 + (solution[0] + solution[1]**2 - 7)**2 - - - def _holder_table__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, non-differentiable, non-separable - Global: 4 global optima, fx = -19.2085 at (±8.05502,±9.66459) - - Link: http://benchmarkfcns.xyz/benchmarkfcns/holdertablefcn.html - - @param solution: A numpy array include 2 items in range: [-10, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Holder Table function is only defined on a 2D space.') - return -np.abs(np.sin(solution[0])*np.cos(solution[1])*np.abs(1 - np.sqrt(np.sum(solution**2))/np.pi)) - - - def _keane__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, non-separable - Global: 2 global optima, fx = 0.673667521146855 at (1.393249070031784,0), (0,1.393249070031784) - - Link: http://benchmarkfcns.xyz/benchmarkfcns/kealefcn.html - - @param solution: A numpy array include 2 items in range: [0, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Keane function is only defined on a 2D space.') - return -np.sin(solution[0]-solution[1])**2 * np.sin(solution[0] + solution[1])**2 / np.sqrt(np.sum(solution**2)) - - - def _leon__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0 at [0, 10] - - Link: http://benchmarkfcns.xyz/benchmarkfcns/leonfcn.html - - @param solution: A numpy array include 2 items in range: [0, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Leon function is only defined on a 2D space.') - return 100*(solution[1]-solution[0]**3)**2 + (1-solution[0])**2 - - - def _levi_n13__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0 at [1, 1] - - Link: http://benchmarkfcns.xyz/benchmarkfcns/levin13fcn.html - - @param solution: A numpy array include 2 items in range: [-10, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Levi N.13 function is only defined on a 2D space.') - return np.sin(3*solution[0]*np.pi)**2 + (solution[0]-1)**2*(1+np.sin(3*solution[1]**np.pi)**2) +\ - (solution[1]-1)**2*(1 + np.sin(2*solution[1]*np.pi)**2) - - - def _matyas__(self, solution=None): - """ - Class: uni-modal, convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0 at [0, 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/matyasfcn.html - - @param solution: A numpy array include 2 items in range: [-10, 10] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Matyas function is only defined on a 2D space.') - return 0.26*np.sum(solution**2) - 0.48*solution[0]*solution[1] - - - def _mc_cormick__(self, solution=None): - """ - Class: multi-modal, convex, continuous, differentiable, non-scalable - Global: 1 global optima, fx = -1.9133 at [-0.547, -1.547] - Link: http://benchmarkfcns.xyz/benchmarkfcns/mccormickfcn.html - - @param solution: A numpy array include 2 items in range: [-1.5, 4], [-3, 3] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Mc Cormick function is only defined on a 2D space.') - return np.sin(solution[0]+solution[1]) + (solution[0] - solution[1])**2 - 1.5*solution[0] + 2.5*solution[1] + 1 - - - def _schaffer_n1__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0 at [0, 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schaffern1fcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Scheffer N.1 function is only defined on a 2D space.') - return 0.5 + (np.sin(np.sum(solution**2)**2)**2 - 0.5) / (1 + 0.001*np.sum(solution**2))**2 - - - def _schaffer_n2__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0 at [0, 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schaffern2fcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Scheffer N.2 function is only defined on a 2D space.') - return 0.5 + (np.sin(solution[0]**2 - solution[1]**2)**2 - 0.5) / (1 + 0.001*np.sum(solution**2))**2 - - - def _schaffer_n3__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0.00156685 at [0, 1.253115] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schaffern3fcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Scheffer N.3 function is only defined on a 2D space.') - return 0.5 + (np.sin(np.cos(np.abs( solution[0]**2 - solution[1]**2 ))) - 0.5) / (1 + 0.001*np.sum(solution**2))**2 - - - def _schaffer_n4__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0.292579 at [0, 1.253115] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schaffern4fcn.html - - @param solution: A numpy array include 2 items in range: [-100, 100] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Scheffer N.4 function is only defined on a 2D space.') - return 0.5 + (np.cos(np.sin(np.abs( solution[0]**2 - solution[1]**2 ))) - 0.5) / (1 + 0.001*np.sum(solution**2))**2 - - - def _three_hump_camel__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global optima, fx = 0 at [0, 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/threehumpcamelfcn.html - - @param solution: A numpy array include 2 items in range: [-5, 5] - @return: fx - """ - d = len(solution) - assert (d == 2, 'Scheffer N.3 function is only defined on a 2D space.') - return 2*solution[0]**2 - 1.05*solution[0]**4 + solution[0]**6/6 + solution[0]*solution[1] + solution[1]**2 - - - diff --git a/opfunu/dimension_based/benchmark3d.py b/opfunu/dimension_based/benchmark3d.py deleted file mode 100644 index 61f2099..0000000 --- a/opfunu/dimension_based/benchmark3d.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python # -# ------------------------------------------------------------------------------------------------------# -# Created by "Thieu Nguyen" at 02:52, 07/12/2019 # -# # -# Email: nguyenthieu2102@gmail.com # -# Homepage: https://www.researchgate.net/profile/Thieu_Nguyen6 # -# Github: https://github.com/thieu1995 # -#-------------------------------------------------------------------------------------------------------# - - -import numpy as np - -class Functions: - """ - This class of functions is belongs to 3-dimensional space - """ - - def _wolfe__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous, differentiable, non-separable - Global: 1 global minimum fx = 0, [0, 0, 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/wolfefcn.html - - @param solution: A numpy array include 3 items like: [0.2, 0.22, 0.5], limited range: [0, 2] - """ - n = len(solution) - assert (n == 3, 'Wolfe function is only defined on a 3D space.') - return 4/3 * (solution[0]**2 + solution[1]**2 - solution[0]*solution[1])**0.75 + solution[2] diff --git a/opfunu/dimension_based/benchmarknd.py b/opfunu/dimension_based/benchmarknd.py deleted file mode 100644 index 5313746..0000000 --- a/opfunu/dimension_based/benchmarknd.py +++ /dev/null @@ -1,470 +0,0 @@ -#!/usr/bin/env python # -# ------------------------------------------------------------------------------------------------------# -# Created by "Thieu Nguyen" at 17:44, 06/12/2019 # -# # -# Email: nguyenthieu2102@gmail.com # -# Homepage: https://www.researchgate.net/profile/Thieu_Nguyen6 # -# Github: https://github.com/thieu1995 # -#-------------------------------------------------------------------------------------------------------# - -import numpy as np - -class Functions: - """ - This class of functions is belongs to n-dimensional space - """ - - def _ackley__(self, solution=None, a=20, b=0.2, c=2 * np.pi): - """ - Class: multimodal, continuous, non-convex, differentiable, n-dimensional space. - Global: one global minimum fx = 0, at [0, 0,...0] - - @param solution: A numpy array like: [1, 2, 10, 4, ...] - @return: fx - """ - result1 = np.sum(solution ** 2) - result2 = np.sum(np.cos(c * solution)) - lin = 1 / len(solution) - return -a * np.exp(-b * np.sqrt(lin * result1)) - np.exp(lin * result2) + a + np.exp(1) - - - def _ackley_n4__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable, n-dimensional space. - Global: on 2-d space, 1 global min fx = -4.590101633799122, at [−1.51, −0.755] - Link: http://benchmarkfcns.xyz/benchmarkfcns/ackleyn4fcn.html - - @param solution: A numpy array include 2 items like: [-35, 35, -35, ...] - """ - d = len(solution) - score = 0.0 - for i in range(0, d-1): - score += ( np.exp(-0.2*np.sqrt(solution[i]**2 + solution[i+1]**2)) + 3*(np.cos(2*solution[i]) + np.sin(2*solution[i+1])) ) - return score - - - def _alpine_n1__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable, n-dimensional space. - Global: one global minimum fx = 0, at [0, 0,...0] - - @param solution: A numpy array like: [1, 2, 10, 4, ...] - @return: fx - """ - return np.sum(np.abs(solution*np.sin(solution) + 0.1 * solution)) - - - def _alpine_n2__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable, n-dimensional space. - Global: one global minimum fx = 2.808^n, at [7.917, ..., 7.917] - Link: http://benchmarkfcns.xyz/benchmarkfcns/alpinen2fcn.html - - @param solution: A numpy array like: [1, 2, 10, 4, ...] - @return: fx - """ - return np.prod(np.sqrt(solution)*np.sin(solution)) - - - def _brown__(self, solution=None): - """ - Class: uni-modal, convex, differentiable, non-separable - Global: one global minimum fx = 0 at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/brownfcn.html - - @param solution: A numpy array with x_i in [-1, 4] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d-1): - result += (solution[i]**2)**(solution[i+1]**2+1) + (solution[i+1]**2)**(solution[i]**2+1) - return result - - - def _exponential__(self, solution=None): - """ - Class: uni-modal, convex, differentiable, non-separable, continuous - Global: one global minimum fx = 0, at [0,...,0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/exponentialfcn.html - - @param solution: A numpy array with x_i in [-1, 1] - @return: fx - """ - return -np.exp(0-.5*np.sum(solution**2)) - - - def _griewank__(self, solution=None): - """ - Class: uni-modal, non-convex, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/griewankfcn.html - - @param solution: A numpy array with x_i in [-600, 600] - @return: fx - """ - d = len(solution) - result = 1 + np.sum(solution**2) / 4000 - prod = 1.0 - for i in range(0, d): - prod *= np.cos(solution[i]/np.sqrt(i+1)) - return result - prod - - - def _happy_cat__(self, solution=None, alpha=1.0/8): - """ - Class: multimodal, non-convex, differentiable, non-separable, parametric - Global: one global minimum fx = 0, at [-1, ..., -1] - Link: http://benchmarkfcns.xyz/benchmarkfcns/happycatfcn.html - - @param solution: A numpy array with x_i in [-2, 2] - @return: fx - """ - - return ((np.sum(solution**2) - len(solution))**2)**alpha + (0.5*np.sum(solution**2)+np.sum(solution))/len(solution) + 0.5 - - - def _periodic__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable, continuous - Global: one global minimum fx = 0.9, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/periodicfcn.html - - @param solution: A numpy array with x_i in [-10, 10] - @return: fx - """ - return 1 + np.sum(np.sin(solution)**2) - 0.1*np.exp(np.sum(solution**2)) - - - def _powell_sum__(self, solution=None): - """ - Class: uni-modal, convex, non-differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/powellsumfcn.html - - @param solution: A numpy array with x_i in [-1, 1] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d): - result += np.abs(solution[i])**(i+2) - return result - - - def _qing__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable, continuous - Global: one global minimum fx = 0, at (±√i,…,±√i) - Link: http://benchmarkfcns.xyz/benchmarkfcns/qingfcn.html - - @param solution: A numpy array with x_i in [-500, 500] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d): - result += (solution[i]**2 - i - 1)**2 - return result - - - def _quartic__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, separable, continuous, random - Global: one global minimum fx = 0 + random, at (0, ...,0) - Link: http://benchmarkfcns.xyz/benchmarkfcns/quarticfcn.html - - @param solution: A numpy array with x_i in [-1.28, 1.28] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d): - result+= (i+1)*solution[i]**4 - return result+np.random.uniform(0, 1) - - - def _rastrigin__(self, solution=None): - """ - Class: multimodal, convex, differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/rastriginfcn.html - - @param solution: A numpy array with x_in in [-5.12, 5.12] - @return: fx - """ - return 10*len(solution) + np.sum(solution**2-10*np.cos(2*solution*np.pi)) - - - def _ridge__(self, solution=None, d=2, alpha=0.5): - """ - Class: uni-model, non-convex, differentiable, non-separable - Global: - Link: http://benchmarkfcns.xyz/benchmarkfcns/ridgefcn.html - - @param solution: A numpy array with x_i in [-5, 5] - @return: fx - """ - t1 = solution[1:] - return solution[0] + d*np.sum(t1**2)**alpha - - - def _rosenbrock__(self, solution=None, a=1, b=100): - """ - Class: multimodal, non-convex, differentiable, non-separable, continuous - Global: 1 global optima, fx = 0, x = [1, ..., 1] - Link: http://benchmarkfcns.xyz/benchmarkfcns/rosenbrockfcn.html - - @param solution: A numpy array with x_i in [-5, 10] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d-1): - result += b*(solution[i+1] - solution[i]**2)**2 + (a-solution[i])**2 - return result - - - def _salomon__(self, solution=None): - """ - Class: multimodal, non-convex, differentiable, non-separable, continuous - Global: 1 global optima, fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/salomonfcn.html - - @param solution: A numpy array with x_i in [-100, 100] - @return: fx - """ - return 1 - np.cos(2*np.pi*np.sqrt(np.sum(solution**2))) + 0.1*np.sqrt(np.sum(solution**2)) - - - def _schwefel_2_20__(self, solution=None): - """ - Class: uni-modal, convex, non-differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schwefel220fcn.html - - @param solution: A numpy array with x_i in [-100, 100] - @return: fx - """ - return np.sum(np.abs(solution)) - - - def _schwefel_2_21__(self, solution=None): - """ - Class: uni-modal, convex, non-differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schwefel221fcn.html - - @param solution: A numpy array with x_i in [-100, 100] - @return: fx - """ - return np.max(np.abs(solution)) - - - def _schwefel_2_22__(self, solution=None): - """ - Class: uni-modal, convex, non-differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schwefel222fcn.html - - @param solution: A numpy array with x_i in [-100, 100] - @return: fx - """ - return np.sum(np.abs(solution)) + np.prod(np.abs(solution)) - - - def _schwefel_2_23__(self, solution=None): - """ - Class: uni-modal, convex, differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schwefel221fcn.html - - @param solution: A numpy array with x_i in [-10, 10] - @return: fx - """ - return np.sum(solution**10) - - - def _schwefel__(self, solution=None): - """ - Class: multi-modal, non-convex, non-differentiable, non-separable, continuous - Global: one global minimum fx = 0, at [420.9687, ..., 420.9687] - Link: http://benchmarkfcns.xyz/benchmarkfcns/schwefelfcn.html - - @param solution: A numpy array with x_i in [-500, 500] - @return: fx - """ - return 418.9829*len(solution) - np.sum(solution*np.sin(np.sqrt(np.abs(solution)))) - - - def _shubert_3__(self, solution=None): - """ - Class: multi-modal, non-convex, differentiable, separable, continuous - Global: one global minimum fx = -29.6733337 - Link: http://benchmarkfcns.xyz/benchmarkfcns/shubert3fcn.html - - @param solution: A numpy array with x_i in [-10, 10] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d): - for j in range(1, 6): - result+= j*np.sin((j+1)*solution[i] + j) - return result - - def _shubert_4__(self, solution=None): - """ - Class: multi-modal, non-convex, differentiable, separable, continuous - Global: one global minimum fx = -25.740858 - Link: http://benchmarkfcns.xyz/benchmarkfcns/shubert4fcn.html - - @param solution: A numpy array with x_i in [-10, 10] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d): - for j in range(1, 6): - result += j * np.cos((j + 1) * solution[i] + j) - return result - - - def _shubert__(self, solution=None): - """ - Class: multi-modal, non-convex, differentiable, non-separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/shubertfcn.html - - @param solution: A numpy array with x_i in [-100, 100] - @return: fx - """ - d = len(solution) - prod = 1.0 - for i in range(0, d): - result = 0 - for j in range(1, 6): - result += np.cos((j + 1) * solution[i] + j) - prod *= result - return prod - - - def _sphere__(self, solution=None): - """ - Class: uni-modal, convex, differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/spherefcn.html - - @param solution: A numpy array with x_i in [-5.12, 5.12] - @return: fx - """ - return np.sum(solution**2) - - - def _styblinski__(self, solution=None): - """ - Class: multi-modal, non-convex, continuous - Global: one global minimum fx = -39.16599 * d , at [-2.903534, ..., -2.903534] - Link: http://benchmarkfcns.xyz/benchmarkfcns/styblinskitankfcn.html - - @param solution: A numpy array with x_i in [-5, 5] - @return: fx - """ - return 0.5*np.sum(solution**4 - 16*solution**2 + 5*solution) - - - def _sum_squres__(self, solution=None): - """ - Class: uni-modal, convex, differentiable, separable, continuous - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/sumsquaresfcn.html - - @param solution: A numpy array with x_i in [-10, 10] - @return: fx - """ - d = len(solution) - result = 0.0 - for i in range(0, d): - result = (i+1)*solution[i]**2 - return result - - - def _xin_she_yang__(self, solution=None): - """ - Class: multi-modal, non-convex, non-differentiable, separable, random - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/xinsheyangn1fcn.html - - @param solution: A numpy array with x_i in [-5, 5] - @return: fx - """ - d = len(solution) - result = 0 - for i in range(0, d): - result += np.random.uniform(0, 1) * np.abs(solution[i])**(i+1) - return result - - - def _xin_she_yang_n2__(self, solution=None): - """ - Class: multi-modal, non-convex, non-differentiable, non-separable - Global: one global minimum fx = 0, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/xinsheyangn2fcn.html - - @param solution: A numpy array with x_i in [-2pi, 2pi] - @return: fx - """ - return np.sum(np.abs(solution))*np.exp(-np.sum(np.sin(solution**2))) - - - def _xin_she_yang_n3__(self, solution=None, m=5, beta=15): - """ - Class: uni-modal, non-convex, differentiable, non-separable, parametric - Global: one global minimum fx = -1, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/xinsheyangn3fcn.html - - @param solution: A numpy array with x_i in [-2pi, 2pi] - @return: fx - """ - t1 = np.exp(-np.sum( np.power(solution/beta, 2*m))) - t2 = -2*np.exp(-np.sum(solution**2)) - t3 = np.prod(np.cos(solution)**2) - return t1 + t2*t3 - - - def _xin_she_yang_n4__(self, solution=None): - """ - Class: multi-modal, non-convex, non-differentiable, non-separable - Global: one global minimum fx = -1, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/xinsheyangn4fcn.html - - @param solution: A numpy array with x_i in [-10, 10] - @return: fx - """ - t1 = np.sum(np.sin(solution)**2) - t2 = -np.exp(-np.sum(solution**2)) - t3 = -np.exp(np.sum(np.sin(np.sqrt(np.abs(solution)))**2)) - return (t1 + t2) * t3 - - - def _zakharov__(self, solution=None): - """ - Class: uni-modal, convex, continuous - Global: one global minimum fx = -1, at [0, ..., 0] - Link: http://benchmarkfcns.xyz/benchmarkfcns/zakharov.html - - @param solution: A numpy array with x_i in [-5, 10] - @return: fx - """ - t1 = np.sum(solution**2) - t2 = 0 - d = len(solution) - for i in range(0, d): - t2 += 0.5*(i+1)*solution[i] - return t1 + t2**2 + t2**4 - - - - - - diff --git a/opfunu/name_based/l_func.py b/opfunu/name_based/l_func.py index 82e4d2b..d9e760b 100644 --- a/opfunu/name_based/l_func.py +++ b/opfunu/name_based/l_func.py @@ -107,8 +107,7 @@ class LennardJones(Benchmark): def __init__(self, ndim=None, bounds=None): super().__init__() - if ndim not in range(6, 61): - raise ValueError("LennardJones dimensions must be in (6, 60)") + self.dim_supported = list(range(6, 61)) self.dim_changeable = True self.dim_default = 6 self.check_ndim_and_bounds(ndim, bounds, np.array([[-4., 4.] for _ in range(self.dim_default)])) @@ -121,6 +120,8 @@ def __init__(self, ndim=None, bounds=None): self.x_global = np.zeros(self.ndim) def evaluate(self, x, *args): + if self.ndim not in self.dim_supported: + raise ValueError(f"{self.__class__.__name__} problem is only supported ndim in {self.dim_supported}!") self.check_solution(x) self.n_fe += 1 k = int(self.ndim / 3) diff --git a/opfunu/utils/__init__.py b/opfunu/utils/__init__.py index 8acc67e..849d0cc 100644 --- a/opfunu/utils/__init__.py +++ b/opfunu/utils/__init__.py @@ -3,5 +3,3 @@ # Email: nguyenthieu2102@gmail.com % # Github: https://github.com/thieu1995 % # --------------------------------------------------% - -from .visualize import * diff --git a/opfunu/utils/encoder.py b/opfunu/utils/encoder.py deleted file mode 100644 index 6d175e0..0000000 --- a/opfunu/utils/encoder.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -# Created by "Thieu" at 13:58, 09/05/2023 ----------% -# Email: nguyenthieu2102@gmail.com % -# Github: https://github.com/thieu1995 % -# --------------------------------------------------% - -import numpy as np - - -class LabelEncoder: - """ - Encode categorical features as integer labels. - """ - - def __init__(self): - self.unique_labels = None - self.label_to_index = {} - - def fit(self, y): - """ - Fit label encoder to a given set of labels. - - Parameters - ---------- - y : array-like - Labels to encode. - """ - self.unique_labels = np.unique(y) - self.label_to_index = {label: i for i, label in enumerate(self.unique_labels)} - - def transform(self, y): - """ - Transform labels to encoded integer labels. - - Parameters - ---------- - y : array-like - Labels to encode. - - Returns - ------- - encoded_labels : array-like - Encoded integer labels. - """ - if self.unique_labels is None: - raise ValueError("Label encoder has not been fit yet.") - return np.array([self.label_to_index[label] for label in y]) - - def fit_transform(self, y): - """ - Fit label encoder and return encoded labels. - - Parameters - ---------- - y : array-like of shape (n_samples,) - Target values. - - Returns - ------- - y : array-like of shape (n_samples,) - Encoded labels. - """ - self.fit(y) - return self.transform(y) - - def inverse_transform(self, y): - """ - Transform integer labels to original labels. - - Parameters - ---------- - y : array-like - Encoded integer labels. - - Returns - ------- - original_labels : array-like - Original labels. - """ - if self.unique_labels is None: - raise ValueError("Label encoder has not been fit yet.") - return np.array([self.unique_labels[i] if i in self.label_to_index.values() else "unknown" for i in y]) diff --git a/opfunu/utils/operator.py b/opfunu/utils/operator.py index 9480d2b..f087f02 100644 --- a/opfunu/utils/operator.py +++ b/opfunu/utils/operator.py @@ -240,13 +240,19 @@ def gz_func(x): def katsuura_func(x): + # TODO: New function failed to pass 5 test cases. + # powers_of_two = 2 ** np.arange(1, 34) + # reciprocals_of_two = 1 / powers_of_two + # for idx in range(0, ndim): + # temp = np.sum(np.abs(powers_of_two * x[idx] - np.round(powers_of_two * x[idx])) * reciprocals_of_two) + # result *= (1 + (idx + 1) * temp) ** (10.0 / ndim ** 1.2) + # return (result - 1) * 10 / ndim ** 2 + x = np.array(x).ravel() ndim = len(x) result = 1.0 - powers_of_two = 2 ** np.arange(1, 34) - reciprocals_of_two = 1 / powers_of_two for idx in range(0, ndim): - temp = np.sum(np.abs(powers_of_two * x[idx] - np.round(powers_of_two * x[idx])) * reciprocals_of_two) + temp = np.sum([np.abs(2 ** j * x[idx] - np.round(2 ** j * x[idx])) / 2 ** j for j in range(1, 33)]) result *= (1 + (idx + 1) * temp) ** (10.0 / ndim ** 1.2) return (result - 1) * 10 / ndim ** 2 diff --git a/opfunu/utils/validator.py b/opfunu/utils/validator.py deleted file mode 100644 index 6d36c75..0000000 --- a/opfunu/utils/validator.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# Created by "Thieu" at 21:39, 29/06/2022 ----------% -# Email: nguyenthieu2102@gmail.com % -# Github: https://github.com/thieu1995 % -# --------------------------------------------------% - -import operator -import numpy as np - - -def is_in_bound(value, bound): - ops = None - if type(bound) is tuple: - ops = operator.lt - elif type(bound) is list: - ops = operator.le - if bound[0] == float("-inf") and bound[1] == float("inf"): - return True - elif bound[0] == float("-inf") and ops(value, bound[1]): - return True - elif ops(bound[0], value) and bound[1] == float("inf"): - return True - elif ops(bound[0], value) and ops(value, bound[1]): - return True - return False - - -def is_str_in_list(value: str, my_list: list): - if type(value) == str and my_list is not None: - return True if value in my_list else False - return False - - -def check_int(name: str, value: int, bound=None): - if type(value) in [int, float]: - if bound is None: - return int(value) - elif is_in_bound(value, bound): - return int(value) - bound = "" if bound is None else f"and value should be in range: {bound}" - raise ValueError(f"'{name}' is an integer {bound}.") - - -def check_float(name: str, value: int, bound=None): - if type(value) in [int, float]: - if bound is None: - return float(value) - elif is_in_bound(value, bound): - return float(value) - bound = "" if bound is None else f"and value should be in range: {bound}" - raise ValueError(f"'{name}' is a float {bound}.") - - -def check_str(name: str, value: str, bound=None): - if type(value) is str: - if bound is None or is_str_in_list(value, bound): - return value - bound = "" if bound is None else f"and value should be one of this: {bound}" - raise ValueError(f"'{name}' is a string {bound}.") - - -def check_bool(name: str, value: bool, bound=(True, False)): - if type(value) is bool: - if value in bound: - return value - bound = "" if bound is None else f"and value should be one of this: {bound}" - raise ValueError(f"'{name}' is a boolean {bound}.") - - -def check_tuple_int(name: str, values: tuple, bounds=None): - if type(values) in [tuple, list] and len(values) > 1: - value_flag = [type(item) == int for item in values] - if np.all(value_flag): - if bounds is not None and len(bounds) == len(values): - value_flag = [is_in_bound(item, bound) for item, bound in zip(values, bounds)] - if np.all(value_flag): - return values - else: - return values - bounds = "" if bounds is None else f"and values should be in range: {bounds}" - raise ValueError(f"'{name}' are integer {bounds}.") - - -def check_tuple_float(name: str, values: tuple, bounds=None): - if type(values) in [tuple, list] and len(values) > 1: - value_flag = [type(item) in [int, float] for item in values] - if np.all(value_flag): - if bounds is not None and len(bounds) == len(values): - value_flag = [is_in_bound(item, bound) for item, bound in zip(values, bounds)] - if np.all(value_flag): - return values - else: - return values - bounds = "" if bounds is None else f"and values should be in range: {bounds}" - raise ValueError(f"'{name}' are float {bounds}.") diff --git a/opfunu/utils/visualize.py b/opfunu/utils/visualize.py index 543995d..54522dd 100644 --- a/opfunu/utils/visualize.py +++ b/opfunu/utils/visualize.py @@ -4,80 +4,373 @@ # Github: https://github.com/thieu1995 % # --------------------------------------------------% +import re from io import BytesIO +from pathlib import Path import matplotlib.pyplot as plt +import platform import numpy as np import requests from PIL import Image from matplotlib import cm +from matplotlib.ticker import ScalarFormatter, FuncFormatter + cmap = [(0, '#2f9599'), (0.45, '#eeeeee'), (1, '#8800ff')] cmap = cm.colors.LinearSegmentedColormap.from_list('Custom', cmap, N=256) +SUPPORTED_ARRAY = (list, tuple, np.ndarray) + + +def __clean_filename__(filename): + chars_to_remove = ["`", "~", "!", "@", "#", "$", "%", "^", "&", "*", ":", ",", "<", ">", ";", "+", "|"] + regular_expression = '[' + re.escape(''.join(chars_to_remove)) + ']' + + temp = filename.encode("ascii", "ignore") + fname = temp.decode() # Removed all non-ascii characters + fname = re.sub(regular_expression, '', fname) # Removed all special characters + fname.replace("_", "-") # Replaced _ by - + return fname + + +def __check_filepath__(filename): + filename.replace("\\", "/") # For better handling the parent folder + if "/" in filename: + list_names = filename.split("/")[:-1] # Remove last element because it is filename + filepath = "/".join(list_names) + Path(filepath).mkdir(parents=True, exist_ok=True) + return filename -def plot_latex_formula(latex): - base_url = r'https://latex.codecogs.com/png.latex?\dpi{400}' +def custom_formatter(x, pos): + # Define a custom tick formatter function + if abs(x) >= 1e4: + return '{:.1e}'.format(x) + else: + return '{:.2f}'.format(x) + + +def draw_latex(latex, title="Latex equation", figsize=(8, 3), dpi=500, + filename=None, exts=(".png", ".pdf"), verbose=True): + """ + Draw latex equation. + + Parameters + ---------- + latex : equation + Your latex equation, you can test on the website: https://latex.codecogs.com/ + title : str + Title for the figure + figsize : tuple, default=(10, 8) + The figure size with format of tuple (width, height) + dpi : int, default=500 + The dot per inches (DPI) - indicate the number of dots per inch. + filename : str, default = None + Set the file name, If None, the file will not be saved + exts : list, tuple, np.ndarray + The list of extensions to save file, for example: (".png", ".pdf", ".jpg") + verbose : bool + Show the figure or not. It will not show on linux system. + """ + base_url = 'https://latex.codecogs.com/png.latex?\dpi{' + str(dpi) + '}' url = f'{base_url}{latex}' response = requests.get(url) img = Image.open(BytesIO(response.content)) + plt.rcParams.update({'font.size': 14}) + plt.figure(figsize=figsize, dpi=dpi) plt.imshow(img) + plt.title(title) plt.axis('off') - plt.show() + + if filename is not None: + filepath = __check_filepath__(__clean_filename__(filename)) + for idx, ext in enumerate(exts): + plt.savefig(f"{filepath}{ext}", bbox_inches='tight') + # img.convert('RGB').save(f'{filepath}{ext}') + if platform.system() != "Linux" and verbose: + plt.show() + plt.close() -def plot_2d(func, n_space=1000, cmap=cmap, XYZ=None, ax=None, show=True): - X_domain, Y_domain = func.bounds - if XYZ is None: - X, Y = np.linspace(*X_domain, n_space), np.linspace(*Y_domain, n_space) - X, Y = np.meshgrid(X, Y) - XY = np.array([X, Y]) - Z = np.apply_along_axis(func.evaluate, 0, XY) +def draw_2d(func, lb=None, ub=None, selected_dims=None, n_points=1000, + ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, + title="Contour map of the function", x_label=None, y_label=None, + figsize=(10, 8), filename=None, exts=(".png", ".pdf"), verbose=True): + """ + Draw 2D contour of the function. + + Parameters + ---------- + func : callable + The callable function that is used to calculate the value + lb : list, tuple, np.ndarray + The lower bound of the variables, should be a list, tuple, or numpy array. + ub : list, tuple, np.ndarray + The upper bound of the variables, should be a list, tuple, or numpy array. + selected_dims : list, tuple, np.ndarray + The selected dimensions you want to draw. + If your function has only 2 dimensions, it will select (1, 2) automatically. + n_points : int + The number of points that will be used to draw the contour + ct_cmap : str + The cmap of matplotlib.pyplot.contourf function (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html) + ct_levels : int + The levels parameter of contourf function + ct_alpha : float + The alpha parameter of contourf function + fixed_strategy : str + The selected strategy to set values for other dimensions. + When your function has > 2 dimensions, you need to set a fixed value for other dimensions to be able to calculate value. + List of available strategy: ["min", "max", "mean', "values", "zero"] + + min: Set the other dimensions by its lower bound + + max: Set the other dimensions by its upper bound + + mean: Set the other dimensions by it average value (lower bound + upper bound) / 2 + + zero: Set the other dimensions by 0 + + values: Set the other dimensions by your passed values through the parameter: `fixed_values`. + + fixed_values : list, tuple, np.ndarray + Fixed values for all dimensions (length should be the same as lower bound), the selected dimensions will be replaced in the drawing process. + title : str + Title for the figure + x_label : str + Set the x label + y_label : str + Set the y label + figsize : tuple, default=(10, 8) + The figure size with format of tuple (width, height) + filename : str, default = None + Set the file name, If None, the file will not be saved + exts : list, tuple, np.ndarray + The list of extensions to save file, for example: (".png", ".pdf", ".jpg") + verbose : bool + Show the figure or not. It will not show on linux system. + """ + if isinstance(lb, SUPPORTED_ARRAY) and isinstance(ub, SUPPORTED_ARRAY): + if len(lb) == len(ub): + lb = np.array(lb) + ub = np.array(ub) + else: + raise ValueError(f"Length of lb and ub should be equal.") + else: + raise TypeError(f"Type of lb and ub should be a list, tuple or np.ndarray.") + if len(lb) == 2 or selected_dims is None: + selected_dims = (1, 2) + if isinstance(selected_dims, SUPPORTED_ARRAY) and len(selected_dims) == 2: + selected_dims = tuple(selected_dims) else: - X, Y, Z = XYZ - - # create new ax if None - if ax is None: - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) - # add contours and contours lines - # ax.contour(X, Y, Z, levels=30, linewidths=0.5, colors='#999') - ax.contourf(X, Y, Z, levels=30, cmap=cmap, alpha=0.7) - - # add labels and set equal aspect ratio - ax.set_xlabel('X') - ax.set_ylabel('Y') - ax.set_aspect(aspect='equal') - if show: + raise TypeError(f"selected_dims should be a list of 2 selected dimensions.") + + selected_dims = (min(selected_dims), max(selected_dims)) + if selected_dims[0] < 1 or selected_dims[1] > len(lb): + raise ValueError(f"The selected_dims's values should >= 1 and <= {len(lb)}.") + idx_dims = (selected_dims[0] - 1, selected_dims[1] - 1) + + # Generate a grid of points for the d1-th and d2-th dimensions + d1 = np.linspace(lb[idx_dims[0]], ub[idx_dims[0]], n_points) + d2 = np.linspace(lb[idx_dims[1]], ub[idx_dims[1]], n_points) + D1, D2 = np.meshgrid(d1, d2) + + # Fix the other dimensions to zero (or another value within the domain) + if fixed_strategy == "mean": + mm_values = (lb + ub) / 2 + elif fixed_strategy == "min": + mm_values = lb + elif fixed_strategy == "max": + mm_values = ub + elif fixed_strategy == "values": + mm_values = fixed_values + else: + mm_values = np.zeros(len(lb)) + + # Combine the fixed and varying dimensions into a single array + solution = np.full((n_points, n_points, len(lb)), np.array(mm_values)) + solution[:, :, idx_dims[0]] = D1 # d1-th dimension + solution[:, :, idx_dims[1]] = D2 # d2-th dimension + + # Compute the function values using vectorized operations + Z = np.apply_along_axis(func, axis=-1, arr=solution) + + # Plot the function + plt.rcParams.update({'font.size': 14}) + plt.figure(figsize=figsize) + + cont = plt.contourf(D1, D2, Z, levels=ct_levels, cmap=ct_cmap, alpha=ct_alpha) + cbar = plt.colorbar(cont) + cbar.set_label('Function Value') + # cbar.formatter = ScalarFormatter() + # cbar.formatter.set_scientific(True) + # cbar.formatter.set_powerlimits((-2, 2)) + # cbar.update_ticks() + + cbar.formatter = FuncFormatter(custom_formatter) + cbar.update_ticks() + + plt.title(title) + if x_label is None: + x_label = f'Dimension X{selected_dims[0]}' + if y_label is None: + y_label = f'Dimension X{selected_dims[1]}' + plt.xlabel(x_label) + plt.ylabel(y_label) + + if filename is not None: + filepath = __check_filepath__(__clean_filename__(filename)) + for idx, ext in enumerate(exts): + plt.savefig(f"{filepath}{ext}", bbox_inches='tight') + if platform.system() != "Linux" and verbose: plt.show() + plt.close() -def plot_3d(func, n_space=1000, cmap=cmap, XYZ=None, ax=None, show=True): - X_domain, Y_domain = func.bounds - if XYZ is None: - X, Y = np.linspace(*X_domain, n_space), np.linspace(*Y_domain, n_space) - X, Y = np.meshgrid(X, Y) - XY = np.array([X, Y]) - Z = np.apply_along_axis(func.evaluate, 0, XY) +def draw_3d(func, lb=None, ub=None, selected_dims=None, n_points=1000, + ct_cmap="viridis", ct_levels=30, ct_alpha=0.7, + fixed_strategy="mean", fixed_values=None, + title="3D visualization of the function", x_label=None, y_label=None, + figsize=(10, 8), filename=None, exts=(".png", ".pdf"), verbose=True): + """ + Draw 3D of the function. + + Parameters + ---------- + func : callable + The callable function that is used to calculate the value + lb : list, tuple, np.ndarray + The lower bound of the variables, should be a list, tuple, or numpy array. + ub : list, tuple, np.ndarray + The upper bound of the variables, should be a list, tuple, or numpy array. + selected_dims : list, tuple, np.ndarray + The selected dimensions you want to draw. + If your function has only 2 dimensions, it will select (1, 2) automatically. + n_points : int + The number of points that will be used to draw the contour + ct_cmap : str + The cmap of matplotlib.pyplot.contourf function (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html) + ct_levels : int + The levels parameter of contourf function + ct_alpha : float + The alpha parameter of contourf function + fixed_strategy : str + The selected strategy to set values for other dimensions. + When your function has > 2 dimensions, you need to set a fixed value for other dimensions to be able to calculate value. + List of available strategy: ["min", "max", "mean', "values", "zero"] + + min: Set the other dimensions by its lower bound + + max: Set the other dimensions by its upper bound + + mean: Set the other dimensions by it average value (lower bound + upper bound) / 2 + + zero: Set the other dimensions by 0 + + values: Set the other dimensions by your passed values through the parameter: `fixed_values`. + + fixed_values : list, tuple, np.ndarray + Fixed values for all dimensions (length should be the same as lower bound), the selected dimensions will be replaced in the drawing process. + title : str + Title for the figure + x_label : str + Set the x label + y_label : str + Set the y label + figsize : tuple, default=(10, 8) + The figure size with format of tuple (width, height) + filename : str, default = None + Set the file name, If None, the file will not be saved + exts : list, tuple, np.ndarray + The list of extensions to save file, for example: (".png", ".pdf", ".jpg") + verbose : bool + Show the figure or not. It will not show on linux system. + """ + if isinstance(lb, SUPPORTED_ARRAY) and isinstance(ub, SUPPORTED_ARRAY): + if len(lb) == len(ub): + lb = np.array(lb) + ub = np.array(ub) + else: + raise ValueError(f"Length of lb and ub should be equal.") + else: + raise TypeError(f"Type of lb and ub should be a list, tuple or np.ndarray.") + if len(lb) == 2 or selected_dims is None: + selected_dims = (1, 2) + if isinstance(selected_dims, SUPPORTED_ARRAY) and len(selected_dims) == 2: + selected_dims = tuple(selected_dims) else: - X, Y, Z = XYZ - - # create new ax if None - if ax is None: - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1, projection='3d') - - # Plot the surface. - ax.plot_surface(X, Y, Z, cmap=cmap, linewidth=0, antialiased=True, alpha=0.7) - ax.contour(X, Y, Z, zdir='z', levels=30, offset=np.min(Z), cmap=cmap) - - ax.xaxis.pane.fill = False - ax.yaxis.pane.fill = False - ax.zaxis.pane.fill = False - ax.set_xlabel('X') - ax.set_ylabel('Y') - ax.xaxis.set_tick_params(labelsize=8) - ax.yaxis.set_tick_params(labelsize=8) - ax.zaxis.set_tick_params(labelsize=8) - if show: + raise TypeError(f"selected_dims should be a list of 2 selected dimensions.") + + selected_dims = (min(selected_dims), max(selected_dims)) + if selected_dims[0] < 1 or selected_dims[1] > len(lb): + raise ValueError(f"The selected_dims's values should >= 1 and <= {len(lb)}.") + idx_dims = (selected_dims[0] - 1, selected_dims[1] - 1) + + # Generate a grid of points for the d1-th and d2-th dimensions + d1 = np.linspace(lb[idx_dims[0]], ub[idx_dims[0]], n_points) + d2 = np.linspace(lb[idx_dims[1]], ub[idx_dims[1]], n_points) + D1, D2 = np.meshgrid(d1, d2) + + # Fix the other dimensions to zero (or another value within the domain) + if fixed_strategy == "mean": + mm_values = (lb + ub) / 2 + elif fixed_strategy == "min": + mm_values = lb + elif fixed_strategy == "max": + mm_values = ub + elif fixed_strategy == "values": + mm_values = fixed_values + else: + mm_values = np.zeros(len(lb)) + + # Combine the fixed and varying dimensions into a single array + solution = np.full((n_points, n_points, len(lb)), np.array(mm_values)) + solution[:, :, idx_dims[0]] = D1 # d1-th dimension + solution[:, :, idx_dims[1]] = D2 # d2-th dimension + + # Compute the function values using vectorized operations + Z = np.apply_along_axis(func, axis=-1, arr=solution) + + # Plot the function + plt.rcParams.update({'font.size': 14}) + + # Create a 3D plot + fig = plt.figure(figsize=figsize) + ax = fig.add_subplot(111, projection='3d') + + # Plot the surface + surf = ax.plot_surface(D1, D2, Z, cmap=ct_cmap, edgecolor='none', alpha=ct_alpha) + + # Add contours + ax.contour(D1, D2, Z, zdir='z', offset=np.min(Z), cmap=ct_cmap, levels=ct_levels) + + # Add a color bar which maps values to colors + cbar_ax = fig.add_axes([0.825, 0.175, 0.05, 0.55]) + cbar = fig.colorbar(surf, cax=cbar_ax, label='Function Value', shrink=0.5, aspect=5,) + # cbar = fig.colorbar(surf, ax=ax, shrink=0.5, aspect=5, label='Function Value') + cbar.formatter = ScalarFormatter() + cbar.formatter.set_scientific(True) + cbar.formatter.set_powerlimits((-2, 2)) + cbar.update_ticks() + + # Apply the custom tick formatter to the color bar + cbar_ax.yaxis.set_major_formatter(FuncFormatter(custom_formatter)) + + if x_label is None: + x_label = f'Dimension X{selected_dims[0]}' + if y_label is None: + y_label = f'Dimension X{selected_dims[1]}' + # Set plot title and labels with increased padding + ax.set_title(title) + ax.set_xlabel(x_label, labelpad=10) + ax.set_ylabel(y_label, labelpad=10) + ax.set_zlabel("Function Value", labelpad=16) + + # Set the elevation and azimuth angles to adjust the viewing angle + ax.view_init(elev=20, azim=-120) + + # Use scientific notation for large tick values + formatter = ScalarFormatter() + formatter.set_scientific(True) + formatter.set_powerlimits((-2, 2)) + # Apply the custom tick formatter to the z-axis + ax.zaxis.set_major_formatter(FuncFormatter(custom_formatter)) + + if filename is not None: + filepath = __check_filepath__(__clean_filename__(filename)) + for idx, ext in enumerate(exts): + plt.savefig(f"{filepath}{ext}", bbox_inches='tight') + if platform.system() != "Linux" and verbose: plt.show() + plt.close() diff --git a/tests/cec_based/test_cec2013.py b/tests/cec_based/test_cec2013.py index a07360d..a27d397 100644 --- a/tests/cec_based/test_cec2013.py +++ b/tests/cec_based/test_cec2013.py @@ -403,7 +403,7 @@ def test_F282013_results(): def test_all_optimal_results(): ndim = 30 known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2013' and x.__name__ not in known_failing] for function in all_functions: problem = function(ndim=ndim) diff --git a/tests/cec_based/test_cec2014.py b/tests/cec_based/test_cec2014.py index 1e7de3a..d43f982 100644 --- a/tests/cec_based/test_cec2014.py +++ b/tests/cec_based/test_cec2014.py @@ -430,7 +430,7 @@ def test_F302014_results(): def test_all_optimal_results(): known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2014' and x.__name__ not in known_failing] for function in all_functions: problem = function() diff --git a/tests/cec_based/test_cec2015.py b/tests/cec_based/test_cec2015.py index 1dc45f7..7a9d853 100644 --- a/tests/cec_based/test_cec2015.py +++ b/tests/cec_based/test_cec2015.py @@ -220,7 +220,7 @@ def test_F152015_results(): def test_all_optimal_results(): known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2015' and x.__name__ not in known_failing] for function in all_functions: problem = function() diff --git a/tests/cec_based/test_cec2017.py b/tests/cec_based/test_cec2017.py index f59c939..d960cb9 100644 --- a/tests/cec_based/test_cec2017.py +++ b/tests/cec_based/test_cec2017.py @@ -417,7 +417,7 @@ def test_F292017_results(): def test_all_optimal_results(): ndim = 30 known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2017' and x.__name__ not in known_failing] for function in all_functions: problem = function(ndim=ndim) diff --git a/tests/cec_based/test_cec2019.py b/tests/cec_based/test_cec2019.py index ec276c3..926e856 100644 --- a/tests/cec_based/test_cec2019.py +++ b/tests/cec_based/test_cec2019.py @@ -150,7 +150,7 @@ def test_F102019_results(): def test_all_optimal_results(): known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2019' and x.__name__ not in known_failing] for function in all_functions: problem = function(10) diff --git a/tests/cec_based/test_cec2020.py b/tests/cec_based/test_cec2020.py index 0a87dc0..3f5da66 100644 --- a/tests/cec_based/test_cec2020.py +++ b/tests/cec_based/test_cec2020.py @@ -151,7 +151,7 @@ def test_F102020_results(): def test_all_optimal_results(): ndim = 30 known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2020' and x.__name__ not in known_failing] for function in all_functions: problem = function(ndim=ndim) diff --git a/tests/cec_based/test_cec2021.py b/tests/cec_based/test_cec2021.py index ccf0b40..6c1d985 100644 --- a/tests/cec_based/test_cec2021.py +++ b/tests/cec_based/test_cec2021.py @@ -151,7 +151,7 @@ def test_F102021_results(): def test_all_optimal_results(): ndim = 10 known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2021' and x.__name__ not in known_failing] for function in all_functions: problem = function(ndim=ndim) diff --git a/tests/cec_based/test_cec2022.py b/tests/cec_based/test_cec2022.py index dcb094c..8d2606a 100644 --- a/tests/cec_based/test_cec2022.py +++ b/tests/cec_based/test_cec2022.py @@ -179,7 +179,7 @@ def test_F122022_results(): def test_all_optimal_results(): ndim = 10 known_failing = [] - all_functions = [x for x in opfunu.get_all_cec_functions() + all_functions = [x for x in opfunu.get_all_cec_based_functions() if x.__name__[-4:] == '2022' and x.__name__ not in known_failing] for function in all_functions: problem = function(ndim=ndim) diff --git a/tests/cec_based/test_cec_based.py b/tests/cec_based/test_cec_based.py index a825875..4704820 100644 --- a/tests/cec_based/test_cec_based.py +++ b/tests/cec_based/test_cec_based.py @@ -4,11 +4,11 @@ # --------------------------------------------------% import numpy as np -from opfunu import get_all_cec_functions +from opfunu import get_all_cec_based_functions def test_whenNdimNone_thenDefaultNdimUsed(): - allFunctions = get_all_cec_functions() + allFunctions = get_all_cec_based_functions() for f in allFunctions: f_default = f() assert f_default.ndim == f_default.dim_default, f'{f.__name__} failed to have ndim == dim_default' @@ -16,7 +16,7 @@ def test_whenNdimNone_thenDefaultNdimUsed(): def test_whenEvaulateDefaultNdim_thenHasResult(): known_failing = [] - all_functions = [x for x in get_all_cec_functions() if x.__name__ not in known_failing] + all_functions = [x for x in get_all_cec_based_functions() if x.__name__ not in known_failing] failing = [] for f in all_functions: f_default = f() @@ -33,7 +33,7 @@ def test_whenEvaulateDefaultNdim_thenHasResult(): def test_whenEvaulateWith_x_global_then_f_global(): # The following are broken or have incorrect or unknown correct , values. known_failing = ['F72008'] - all_functions = [x for x in get_all_cec_functions() if x.__name__ not in known_failing] + all_functions = [x for x in get_all_cec_based_functions() if x.__name__ not in known_failing] failing = [] for f in all_functions: f_default = f()